split common

This commit is contained in:
dfsek
2021-06-22 23:56:34 -07:00
parent 10f633be9a
commit cf214cc274
548 changed files with 70 additions and 5 deletions

View File

@@ -0,0 +1,61 @@
package com.dfsek.terra.addon;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.addons.annotations.Addon;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class AddonClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public AddonClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public AddonClassLoader(URL[] urls) {
super(urls);
}
@SuppressWarnings("unchecked")
public static Set<Class<? extends TerraAddon>> fetchAddonClasses(File file) throws IOException {
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
AddonClassLoader loader = new AddonClassLoader(new URL[] {file.toURI().toURL()}, AddonClassLoader.class.getClassLoader());
Set<Class<? extends TerraAddon>> set = new HashSet<>();
while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.');
try {
Class<?> clazz = loader.loadClass(className);
Addon addon = clazz.getAnnotation(Addon.class);
if(addon == null) continue;
if(!TerraAddon.class.isAssignableFrom(clazz))
throw new IllegalArgumentException("Addon class \"" + clazz + "\" must extend TerraAddon.");
set.add((Class<? extends TerraAddon>) clazz);
} catch(ClassNotFoundException e) {
throw new IllegalStateException(e); // this should literally never happen, if it does something is very wrong
}
}
return set;
}
}

View File

@@ -0,0 +1,42 @@
package com.dfsek.terra.addon;
import com.dfsek.terra.addon.exception.AddonLoadException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class AddonPool {
private final Map<String, PreLoadAddon> pool = new HashMap<>();
public void add(PreLoadAddon addon) throws AddonLoadException {
if(pool.containsKey(addon.getId())) {
String message = "Duplicate addon ID: " +
addon.getId() + "; original ID from file: " +
pool.get(addon.getId()).getFile().getAbsolutePath() +
", class: " +
pool.get(addon.getId()).getAddonClass().getCanonicalName() +
"Duplicate ID from file: " +
addon.getFile().getAbsolutePath() +
", class: " +
addon.getAddonClass().getCanonicalName();
throw new AddonLoadException(message);
}
pool.put(addon.getId(), addon);
}
public PreLoadAddon get(String id) {
return pool.get(id);
}
public void buildAll() throws AddonLoadException {
for(PreLoadAddon value : pool.values()) {
value.rebuildDependencies(this, value, true);
}
}
public Set<PreLoadAddon> getAddons() {
return new HashSet<>(pool.values());
}
}

View File

@@ -0,0 +1,58 @@
package com.dfsek.terra.addon;
import com.dfsek.terra.addon.exception.AddonLoadException;
import com.dfsek.terra.addon.exception.CircularDependencyException;
import com.dfsek.terra.addon.exception.DependencyMissingException;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.addons.annotations.Addon;
import com.dfsek.terra.api.addons.annotations.Depends;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PreLoadAddon {
private final List<PreLoadAddon> depends = new ArrayList<>();
private final Class<? extends TerraAddon> addonClass;
private final String id;
private final String[] dependencies;
private final File file;
public PreLoadAddon(Class<? extends TerraAddon> addonClass, File file) {
this.addonClass = addonClass;
this.id = addonClass.getAnnotation(Addon.class).value();
this.file = file;
Depends depends = addonClass.getAnnotation(Depends.class);
this.dependencies = depends == null ? new String[] {} : depends.value();
}
public List<PreLoadAddon> getDepends() {
return depends;
}
public void rebuildDependencies(AddonPool pool, PreLoadAddon origin, boolean levelG1) throws AddonLoadException {
if(this.equals(origin) && !levelG1)
throw new CircularDependencyException("Detected circular dependency in addon \"" + id + "\", dependencies: " + Arrays.toString(dependencies));
for(String dependency : dependencies) {
PreLoadAddon preLoadAddon = pool.get(dependency);
if(preLoadAddon == null)
throw new DependencyMissingException("Dependency " + dependency + " was not found. Please install " + dependency + " to use " + id + ".");
depends.add(preLoadAddon);
preLoadAddon.rebuildDependencies(pool, origin, false);
}
}
public String getId() {
return id;
}
public Class<? extends TerraAddon> getAddonClass() {
return addonClass;
}
public File getFile() {
return file;
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.addon.exception;
public class AddonLoadException extends Exception {
private static final long serialVersionUID = -4949084729296580176L;
public AddonLoadException(String message) {
super(message);
}
public AddonLoadException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.addon.exception;
public class CircularDependencyException extends AddonLoadException {
private static final long serialVersionUID = 7398510879124125121L;
public CircularDependencyException(String message) {
super(message);
}
public CircularDependencyException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.addon.exception;
public class DependencyMissingException extends AddonLoadException {
private static final long serialVersionUID = -8419489102208521583L;
public DependencyMissingException(String message) {
super(message);
}
public DependencyMissingException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api;
import com.dfsek.tectonic.loading.TypeRegistry;
public interface LoaderRegistrar {
void register(TypeRegistry registry);
}

View File

@@ -0,0 +1,76 @@
package com.dfsek.terra.api;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.event.EventManager;
import com.dfsek.terra.api.platform.handle.ItemHandle;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.LockedRegistry;
import com.dfsek.terra.api.util.JarUtil;
import com.dfsek.terra.api.util.logging.DebugLogger;
import com.dfsek.terra.api.util.logging.Logger;
import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.world.TerraWorld;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.jar.JarFile;
/**
* Represents a Terra mod/plugin instance.
*/
public interface TerraPlugin extends LoaderRegistrar {
WorldHandle getWorldHandle();
TerraWorld getWorld(World world);
Logger logger();
PluginConfig getTerraConfig();
File getDataFolder();
Language getLanguage();
CheckedRegistry<ConfigPack> getConfigRegistry();
LockedRegistry<TerraAddon> getAddons();
boolean reload();
ItemHandle getItemHandle();
void saveDefaultConfig();
String platformName();
DebugLogger getDebugLogger();
EventManager getEventManager();
default String getVersion() {
return "@VERSION@";
}
/**
* Runs a task that may or may not be thread safe, depending on platform.
* <p>
* Allows platforms to define what code is safe to be run asynchronously.
*
* @param task Task to be run.
*/
default void runPossiblyUnsafeTask(Runnable task) {
task.run();
}
Profiler getProfiler();
default JarFile getModJar() throws URISyntaxException, IOException {
return JarUtil.getJarFile();
}
}

View File

@@ -0,0 +1,49 @@
package com.dfsek.terra.api.addons;
import com.dfsek.terra.api.addons.annotations.Addon;
import com.dfsek.terra.api.addons.annotations.Author;
import com.dfsek.terra.api.addons.annotations.Version;
import org.jetbrains.annotations.NotNull;
/**
* Represents an entry point for an addon. Implementations must be annotated with {@link Addon}.
*/
public abstract class TerraAddon {
/**
* Gets the version of this addon.
*
* @return Addon version.
*/
public final @NotNull String getVersion() {
Version version = getClass().getAnnotation(Version.class);
return version == null ? "0.1.0" : version.value();
}
/**
* Gets the author of this addon.
*
* @return Addon author.
*/
public final @NotNull String getAuthor() {
Author author = getClass().getAnnotation(Author.class);
return author == null ? "Anon Y. Mous" : author.value();
}
/**
* Gets the name (ID) of this addon.
*
* @return Addon ID.
*/
public final @NotNull String getName() {
Addon addon = getClass().getAnnotation(Addon.class);
if(addon == null)
throw new IllegalStateException("Addon annotation not present"); // This should never happen; the presence of this annotation is checked by the addon loader.
return addon.value();
}
/**
* Invoked immediately after an addon is loaded.
*/
public abstract void initialize();
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies that the annotated class is an entry point for a Terra addon.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Addon {
/**
* @return The ID of the addon.
*/
@NotNull String value();
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Optional annotation that specifies the author of an addon.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Author {
/**
* @return Name of the addon author.
*/
@NotNull String value();
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Optional annotation that specifies dependencies of an addon.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Depends {
/**
* @return All addons this addon is dependent upon.
*/
@NotNull String[] value();
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Optional annotation that specifies the version of an addon.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Version {
/**
* @return Version of the addon.
*/
@NotNull String value();
}

View File

@@ -0,0 +1,17 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.command.exception.CommandException;
import com.dfsek.terra.api.command.exception.MalformedCommandException;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.List;
public interface CommandManager {
void execute(String command, CommandSender sender, List<String> args) throws CommandException;
void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException;
List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException;
int getMaxArgumentDepth();
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.platform.CommandSender;
public interface CommandTemplate {
void execute(CommandSender sender);
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class ExecutionState {
private final Set<String> switches = new HashSet<>();
private final Map<String, String> args = new HashMap<>();
private final CommandSender sender;
protected ExecutionState(CommandSender sender) {
this.sender = sender;
}
protected void addSwitch(String flag) {
switches.add(flag);
}
protected void addArgument(String arg, String value) {
args.put(arg, value);
}
public String getArgument(String argument) {
return args.get(argument);
}
public boolean hasSwitch(String flag) {
return switches.contains(flag);
}
public CommandSender getSender() {
return sender;
}
}

View File

@@ -0,0 +1,283 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.annotation.Argument;
import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.Subcommand;
import com.dfsek.terra.api.command.annotation.Switch;
import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget;
import com.dfsek.terra.api.command.annotation.inject.SwitchTarget;
import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.command.arg.ArgumentParser;
import com.dfsek.terra.api.command.exception.CommandException;
import com.dfsek.terra.api.command.exception.ExecutionException;
import com.dfsek.terra.api.command.exception.InvalidArgumentsException;
import com.dfsek.terra.api.command.exception.MalformedCommandException;
import com.dfsek.terra.api.command.exception.SwitchFormatException;
import com.dfsek.terra.api.command.tab.TabCompleter;
import com.dfsek.terra.api.injection.Injector;
import com.dfsek.terra.api.injection.exception.InjectionException;
import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.api.util.ReflectionUtil;
import com.dfsek.terra.world.TerraWorld;
import net.jafama.FastMath;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TerraCommandManager implements CommandManager {
private final Map<String, CommandHolder> commands = new HashMap<>();
private final Injector<TerraPlugin> pluginInjector;
private final TerraPlugin main;
public TerraCommandManager(TerraPlugin main) {
this.main = main;
this.pluginInjector = new Injector<>(main);
pluginInjector.addExplicitTarget(TerraPlugin.class);
}
@Override
public void execute(String commandName, CommandSender sender, List<String> argsIn) throws CommandException {
if(!commands.containsKey(commandName)) throw new InvalidArgumentsException("No such command \"" + commandName + "\"");
execute(commands.get(commandName), sender, new ArrayList<>(argsIn));
}
private void execute(CommandHolder commandHolder, CommandSender sender, List<String> args) throws CommandException {
Class<? extends CommandTemplate> commandClass = commandHolder.clazz;
if(commandClass.isAnnotationPresent(DebugCommand.class) && !main.getTerraConfig().isDebugCommands()) {
sender.sendMessage("Command must be executed with debug commands enabled.");
return;
}
if(commandClass.isAnnotationPresent(PlayerCommand.class) && !(sender instanceof Player)) {
sender.sendMessage("Command must be executed by player.");
return;
}
if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !(((Player) sender).getWorld()).isTerraWorld())) {
sender.sendMessage("Command must be executed in a Terra world.");
return;
}
List<String> ogArgs = new ArrayList<>(args);
ExecutionState state = new ExecutionState(sender);
if(!commandClass.isAnnotationPresent(Command.class)) {
invoke(commandClass, state, commandHolder);
return;
}
Command command = commandClass.getAnnotation(Command.class);
if(command.arguments().length == 0 && command.subcommands().length == 0) {
if(args.isEmpty()) {
invoke(commandClass, state, commandHolder);
return;
} else throw new InvalidArgumentsException("Expected 0 arguments, found " + args.size());
}
if(!args.isEmpty() && commandHolder.subcommands.containsKey(args.get(0))) {
String c = args.get(0);
args.remove(0);
execute(commandHolder.subcommands.get(c), sender, args);
return;
}
boolean req = true;
for(Argument argument : command.arguments()) {
if(!req && argument.required()) {
throw new MalformedCommandException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments()));
}
req = argument.required();
if(args.isEmpty()) {
if(req) throw new InvalidArgumentsException("Invalid arguments: " + ogArgs + ", usage: " + command.usage());
break;
}
String arg = args.get(0);
if(arg.startsWith("-")) { // switches have started.
if(req) throw new InvalidArgumentsException("Switches must come after arguments.");
break;
}
state.addArgument(argument.value(), args.remove(0));
}
while(!args.isEmpty()) {
String aSwitch = args.remove(0);
if(!aSwitch.startsWith("-")) throw new SwitchFormatException("Invalid switch \"" + aSwitch + "\"");
String val = aSwitch.substring(1); // remove dash
if(!commandHolder.switches.containsKey(val)) throw new SwitchFormatException("No such switch \"" + aSwitch + "\"");
state.addSwitch(commandHolder.switches.get(val));
}
invoke(commandClass, state, commandHolder);
}
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state, CommandHolder holder) throws CommandException {
try {
CommandTemplate template = clazz.getConstructor().newInstance();
pluginInjector.inject(template);
for(Field field : ReflectionUtil.getFields(clazz)) {
if(field.isAnnotationPresent(ArgumentTarget.class)) {
ArgumentTarget argumentTarget = field.getAnnotation(ArgumentTarget.class);
if(!holder.argumentMap.containsKey(argumentTarget.value())) {
throw new MalformedCommandException("Argument Target specifies nonexistent argument \"" + argumentTarget.value() + "\"");
}
String argument = argumentTarget.value();
ArgumentParser<?> argumentParser = holder.argumentMap.get(argumentTarget.value()).argumentParser().getConstructor().newInstance();
pluginInjector.inject(argumentParser);
field.setAccessible(true);
String value = state.getArgument(argument);
if(value == null) value = holder.argumentMap.get(argumentTarget.value()).defaultValue();
field.set(template, argumentParser.parse(state.getSender(), value));
}
if(field.isAnnotationPresent(SwitchTarget.class)) {
SwitchTarget switchTarget = field.getAnnotation(SwitchTarget.class);
if(!holder.switches.containsValue(switchTarget.value())) {
throw new MalformedCommandException("Switch Target specifies nonexistent switch \"" + switchTarget.value() + "\"");
}
if(!(field.getType() == boolean.class)) {
throw new MalformedCommandException("Switch Target must be of type boolean.");
}
field.setAccessible(true);
field.setBoolean(template, state.hasSwitch(switchTarget.value()));
}
}
try {
template.execute(state.getSender());
} catch(Throwable e) {
throw new ExecutionException("Failed to execute command: " + e.getMessage(), e);
}
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
throw new MalformedCommandException("Unable to reflectively instantiate command: ", e);
}
}
@Override
public void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
commands.put(name, new CommandHolder(clazz));
}
@Override
public List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException {
if(args.isEmpty()) return new ArrayList<>(commands.keySet()).stream().sorted(String::compareTo).collect(Collectors.toList());
if(!commands.containsKey(command)) return Collections.emptyList();
return tabComplete(commands.get(command), sender, new ArrayList<>(args)).stream().filter(s -> s.toLowerCase().startsWith(args.get(args.size() - 1).toLowerCase())).sorted(String::compareTo).collect(Collectors.toList());
}
@Override
public int getMaxArgumentDepth() {
int max = 0;
for(CommandHolder value : commands.values()) {
max = FastMath.max(getMaxArgumentDepth(value), max);
}
return max;
}
private int getMaxArgumentDepth(CommandHolder holder) {
int max = 0;
max = FastMath.max(holder.arguments.size() + holder.switchList.size(), max);
for(CommandHolder value : holder.subcommands.values()) {
max = FastMath.max(max, getMaxArgumentDepth(value) + 1);
}
return max;
}
private List<String> tabComplete(CommandHolder holder, CommandSender sender, List<String> args) throws CommandException {
if(args.isEmpty()) return Collections.emptyList();
List<String> completions = new ArrayList<>();
if(args.size() == 1) {
completions.addAll(holder.subcommands.keySet());
}
if(holder.subcommands.containsKey(args.get(0))) {
List<String> newArgs = new ArrayList<>(args);
newArgs.remove(0);
completions.addAll(tabComplete(holder.subcommands.get(args.get(0)), sender, newArgs));
}
try {
if(args.size() <= holder.arguments.size()) {
TabCompleter completer = holder.arguments.get(args.size() - 1).tabCompleter().getConstructor().newInstance();
pluginInjector.inject(completer);
completions.addAll(completer.complete(sender));
}
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
throw new MalformedCommandException("Unable to reflectively instantiate tab-completer: ", e);
}
return completions;
}
/**
* Pre-processes command metadata.
*/
private static final class CommandHolder {
private final Class<? extends CommandTemplate> clazz;
private final Map<String, CommandHolder> subcommands = new HashMap<>();
private final Map<String, String> switches = new HashMap<>();
private final List<Argument> arguments;
private final List<Switch> switchList;
private final Map<String, Argument> argumentMap = new HashMap<>();
private CommandHolder(Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
this.clazz = clazz;
if(clazz.isAnnotationPresent(Command.class)) {
Command command = clazz.getAnnotation(Command.class);
for(Subcommand subcommand : command.subcommands()) {
if(subcommands.containsKey(subcommand.value()))
throw new MalformedCommandException("Duplicate subcommand: " + subcommand);
CommandHolder holder = new CommandHolder(subcommand.clazz());
subcommands.put(subcommand.value(), holder);
for(String alias : subcommand.aliases()) {
subcommands.put(alias, holder);
}
}
for(Switch aSwitch : command.switches()) {
if(switches.containsKey(aSwitch.value())) throw new MalformedCommandException("Duplicate switch: " + aSwitch);
switches.put(aSwitch.value(), aSwitch.value());
for(String alias : aSwitch.aliases()) {
switches.put(alias, aSwitch.value());
}
}
for(Argument argument : command.arguments()) {
if(argumentMap.containsKey(argument.value())) throw new MalformedCommandException("Duplicate argument: " + argument);
argumentMap.put(argument.value(), argument);
}
arguments = Arrays.asList(command.arguments());
switchList = Arrays.asList(command.switches());
} else {
arguments = Collections.emptyList();
switchList = Collections.emptyList();
}
}
}
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra.api.command.annotation;
import com.dfsek.terra.api.command.arg.ArgumentParser;
import com.dfsek.terra.api.command.arg.StringArgumentParser;
import com.dfsek.terra.api.command.tab.NothingCompleter;
import com.dfsek.terra.api.command.tab.TabCompleter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Argument {
String value();
boolean required() default true;
Class<? extends TabCompleter> tabCompleter() default NothingCompleter.class;
Class<? extends ArgumentParser<?>> argumentParser() default StringArgumentParser.class;
String defaultValue() default "";
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
Argument[] arguments() default {};
Switch[] switches() default {};
Subcommand[] subcommands() default {};
String usage() default "";
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.command.annotation;
import com.dfsek.terra.api.command.CommandTemplate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Subcommand {
String value();
String[] aliases() default {};
Class<? extends CommandTemplate> clazz();
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Switch {
String value();
String[] aliases() default {};
}

View File

@@ -0,0 +1,12 @@
package com.dfsek.terra.api.command.annotation.inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ArgumentTarget {
String value();
}

View File

@@ -0,0 +1,12 @@
package com.dfsek.terra.api.command.annotation.inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SwitchTarget {
String value();
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation.type;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Command may only be executed with debug mode enabled.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DebugCommand {
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation.type;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks command as player-only
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PlayerCommand {
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation.type;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Command may only be executed in a Terra world.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WorldCommand {
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public interface ArgumentParser<T> {
T parse(CommandSender sender, String arg);
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public class DoubleArgumentParser implements ArgumentParser<Double> {
@Override
public Double parse(CommandSender sender, String arg) {
return arg == null ? null : Double.parseDouble(arg);
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public class IntegerArgumentParser implements ArgumentParser<Integer> {
@Override
public Integer parse(CommandSender sender, String arg) {
return arg == null ? null : Integer.parseInt(arg);
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public class StringArgumentParser implements ArgumentParser<String> {
@Override
public String parse(CommandSender sender, String arg) {
return arg;
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public abstract class CommandException extends Exception {
private static final long serialVersionUID = -2955328495045879822L;
public CommandException(String message) {
super(message);
}
public CommandException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public class ExecutionException extends CommandException {
private static final long serialVersionUID = -6345523475880607959L;
public ExecutionException(String message) {
super(message);
}
public ExecutionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public class InvalidArgumentsException extends CommandException {
private static final long serialVersionUID = 7563619667472569824L;
public InvalidArgumentsException(String message) {
super(message);
}
public InvalidArgumentsException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,16 @@
package com.dfsek.terra.api.command.exception;
/**
* Thrown when command is incorrectly defined.
*/
public class MalformedCommandException extends CommandException {
private static final long serialVersionUID = -5417760860407895496L;
public MalformedCommandException(String message) {
super(message);
}
public MalformedCommandException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public class SwitchFormatException extends CommandException {
private static final long serialVersionUID = -965858989317844628L;
public SwitchFormatException(String message) {
super(message);
}
public SwitchFormatException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.tab;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.Collections;
import java.util.List;
public class NothingCompleter implements TabCompleter {
@Override
public List<String> complete(CommandSender sender) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.api.command.tab;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.List;
public interface TabCompleter {
List<String> complete(CommandSender sender);
}

View File

@@ -0,0 +1,12 @@
package com.dfsek.terra.api.event;
import com.dfsek.terra.api.event.events.Event;
/**
* Marker interface for a class that contains event listener methods.
*
* @see Event
* @see EventManager
*/
public interface EventListener {
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra.api.event;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.event.events.Event;
/**
* Manages event registration and triggering.
*/
public interface EventManager {
/**
* Call an event, and return the execution status.
*
* @param event Event to pass to all registered EventListeners.
* @return False if the event is cancellable and has been cancelled, otherwise true.
*/
boolean callEvent(Event event);
/**
* Register an {@link EventListener} under an {@link TerraAddon}.
*
* @param addon Addon to register listener for.
* @param listener Listener to register.
*/
void registerListener(TerraAddon addon, EventListener listener);
}

View File

@@ -0,0 +1,110 @@
package com.dfsek.terra.api.event;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.event.annotations.Global;
import com.dfsek.terra.api.event.annotations.Priority;
import com.dfsek.terra.api.event.events.Cancellable;
import com.dfsek.terra.api.event.events.Event;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.util.ReflectionUtil;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TerraEventManager implements EventManager {
private final Map<Class<? extends Event>, List<ListenerHolder>> listeners = new HashMap<>();
private final TerraPlugin main;
public TerraEventManager(TerraPlugin main) {
this.main = main;
}
@Override
public boolean callEvent(Event event) {
listeners.getOrDefault(event.getClass(), Collections.emptyList()).forEach(listenerHolder -> {
try {
if(event instanceof PackEvent && !listenerHolder.global) {
PackEvent packEvent = (PackEvent) event;
if(packEvent
.getPack()
.getTemplate()
.getAddons()
.contains(listenerHolder.addon)) {
listenerHolder.method.invoke(listenerHolder.listener, event);
}
} else {
listenerHolder.method.invoke(listenerHolder.listener, event);
}
} catch(InvocationTargetException e) {
StringWriter writer = new StringWriter();
e.getTargetException().printStackTrace(new PrintWriter(writer));
main.logger().warning("Exception occurred during event handling:");
main.logger().warning(writer.toString());
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
} catch(Exception e) {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
main.logger().warning("Exception occurred during event handling:");
main.logger().warning(writer.toString());
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
}
}
);
if(event instanceof Cancellable) return !((Cancellable) event).isCancelled();
return true;
}
@SuppressWarnings("unchecked")
@Override
public void registerListener(TerraAddon addon, EventListener listener) {
Class<? extends EventListener> listenerClass = listener.getClass();
Method[] methods = ReflectionUtil.getMethods(listenerClass);
for(Method method : methods) {
if(method.getParameterCount() != 1) continue; // Check that parameter count is only 1.
Class<?> eventParam = method.getParameterTypes()[0];
if(!Event.class.isAssignableFrom(eventParam)) continue; // Check that parameter is an Event.
Priority p = method.getAnnotation(Priority.class);
int priority = p == null ? 0 : p.value();
method.setAccessible(true);
List<ListenerHolder> holders = listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new ArrayList<>());
holders.add(new ListenerHolder(method, listener, priority, addon, method.getAnnotation(Global.class) != null));
holders.sort(Comparator.comparingInt(ListenerHolder::getPriority)); // Sort priorities.
}
}
private static final class ListenerHolder {
private final Method method;
private final EventListener listener;
private final int priority;
private final TerraAddon addon;
private final boolean global;
private ListenerHolder(Method method, EventListener listener, int priority, TerraAddon addon, boolean global) {
this.method = method;
this.listener = listener;
this.priority = priority;
this.addon = addon;
this.global = global;
}
public int getPriority() {
return priority;
}
}
}

View File

@@ -0,0 +1,17 @@
package com.dfsek.terra.api.event.annotations;
import com.dfsek.terra.api.event.events.PackEvent;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies that an event handler is to handle all {@link PackEvent}s, regardless of whether the pack
* depends on the addon's listener.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Global {
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.event.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotated listener methods will have a specific priority set.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Priority {
/**
* Highest possible priority. Listeners with this priority will always be invoked last.
*/
int HIGHEST = Integer.MAX_VALUE;
/**
* Lowest possible priority. Listeners with this priority will always be invoked first.
*/
int LOWEST = Integer.MIN_VALUE;
/**
* Default priority.
*/
int NORMAL = 0;
/**
* High priority.
*/
int HIGH = 1;
/**
* Low Priority.
*/
int LOW = -1;
/**
* @return Priority of this event. Events are executed from lowest to highest priorities.
*/
int value();
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.event.events;
import com.dfsek.terra.api.util.mutable.MutableBoolean;
/**
* Abstract class containing basic {@link Cancellable} implementation.
*/
public abstract class AbstractCancellable implements Cancellable {
private final MutableBoolean cancelled = new MutableBoolean(false);
@Override
public boolean isCancelled() {
return cancelled.get();
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled.set(cancelled);
}
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.api.event.events;
/**
* Events that implement this interface may be cancelled.
* <p>
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
*/
public interface Cancellable extends Event {
/**
* Get the cancellation status of the event.
*
* @return Whether event is cancelled.
*/
boolean isCancelled();
/**
* Set the cancellation status of the event.
*
* @param cancelled Whether event is cancelled.
*/
void setCancelled(boolean cancelled);
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.event.events;
/**
* An event that addons may listen to.
*/
public interface Event {
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.event.events;
import com.dfsek.terra.api.event.annotations.Global;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* An event with functionality directly linked to a {@link ConfigPack}.
* <p>
* PackEvents are only invoked when the pack specifies the addon in its
* {@code addon} key (or when the listener is annotated {@link Global}).
*/
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
public interface PackEvent extends Event {
/**
* Get the {@link ConfigPack} associated with this event.
*
* @return ConfigPack associated with the event.
*/
ConfigPack getPack();
}

View File

@@ -0,0 +1,37 @@
package com.dfsek.terra.api.event.events.config;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* An event related to the loading process of config packs.
*/
public abstract class ConfigPackLoadEvent implements PackEvent {
private final ConfigPack pack;
private final ExceptionalConsumer<ConfigTemplate> configLoader;
public ConfigPackLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configLoader) {
this.pack = pack;
this.configLoader = configLoader;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Load a custom {@link ConfigTemplate} using the pack manifest.
*
* @param template Template to register.
*/
public void loadTemplate(ConfigTemplate template) throws ConfigException {
configLoader.accept(template);
}
public interface ExceptionalConsumer<T extends ConfigTemplate> {
void accept(T value) throws ConfigException;
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.event.events.config;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* Called when a config pack has finished loading.
*/
public class ConfigPackPostLoadEvent extends ConfigPackLoadEvent {
public ConfigPackPostLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> loader) {
super(pack, loader);
}
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.event.events.config;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* Called before a config pack's registries are filled. At this point, the pack manifest has been loaded, and all registries are empty.
*/
public class ConfigPackPreLoadEvent extends ConfigPackLoadEvent {
public ConfigPackPreLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configLoader) {
super(pack, configLoader);
}
}

View File

@@ -0,0 +1,31 @@
package com.dfsek.terra.api.event.events.world;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.world.TerraWorld;
/**
* Called upon initialization of a TerraWorld.
*/
public class TerraWorldLoadEvent implements PackEvent {
private final TerraWorld world;
private final ConfigPack pack;
public TerraWorldLoadEvent(TerraWorld world, ConfigPack pack) {
this.world = world;
this.pack = pack;
}
public TerraWorld getWorld() {
return world;
}
public ConfigPack getPack() {
return pack;
}
public WorldConfig getWorldConfig() {
return world.getConfig();
}
}

View File

@@ -0,0 +1,45 @@
package com.dfsek.terra.api.event.events.world.generation;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.entity.Entity;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedEntity;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* Called when an entity is spawned via {@link BufferedEntity}.
*/
public class EntitySpawnEvent implements PackEvent {
private final ConfigPack pack;
private final Entity entity;
private final Location location;
public EntitySpawnEvent(ConfigPack pack, Entity entity, Location location) {
this.pack = pack;
this.entity = entity;
this.location = location;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Get the entity that triggered the event.
*
* @return The entity.
*/
public Entity getEntity() {
return entity;
}
/**
* Get the location of the entity.
*
* @return Location of the entity.
*/
public Location getLocation() {
return location;
}
}

View File

@@ -0,0 +1,80 @@
package com.dfsek.terra.api.event.events.world.generation;
import com.dfsek.terra.api.event.events.AbstractCancellable;
import com.dfsek.terra.api.event.events.Cancellable;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.state.Container;
import com.dfsek.terra.api.structures.loot.LootTable;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication;
import com.dfsek.terra.config.pack.ConfigPack;
import org.jetbrains.annotations.NotNull;
/**
* Called when loot is populated via {@link BufferedLootApplication}.
*/
public class LootPopulateEvent extends AbstractCancellable implements PackEvent, Cancellable {
private final Block block;
private final Container container;
private LootTable table;
private final ConfigPack pack;
private final StructureScript script;
public LootPopulateEvent(Block block, Container container, LootTable table, ConfigPack pack, StructureScript script) {
this.block = block;
this.container = container;
this.table = table;
this.pack = pack;
this.script = script;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Get the block containing the tile entity loot is applied to.
*
* @return Block at which loot is applied.
*/
public Block getBlock() {
return block;
}
/**
* Get the {@link Container} representing the inventory.
*
* @return Inventory recieving loot.
*/
public Container getContainer() {
return container;
}
/**
* Get the loot table to be populated.
* @return Loot table.
*/
public LootTable getTable() {
return table;
}
/**
* Set the loot table to be populated.
*
* @param table New loot table.
*/
public void setTable(@NotNull LootTable table) {
this.table = table;
}
/**
* Get the script used to generate the structure.
*
* @return Structure script.
*/
public StructureScript getStructureScript() {
return script;
}
}

View File

@@ -0,0 +1,78 @@
package com.dfsek.terra.api.injection;
import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.injection.exception.InjectionException;
import com.dfsek.terra.api.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
/**
* Dynamic dependency injector.
* <p>
* Stores an object to inject, and injects it into objects passed to {@link #inject(Object)}.
*
* @param <T> Type of object to inject.
*/
public class Injector<T> {
private final T value;
private final Set<Class<? extends T>> targets = new HashSet<>();
/**
* Instantiate an Injector with a value to inject
*
* @param value Value to inject
*/
public Injector(T value) {
this.value = value;
}
/**
* Add an explicit class as a target. Useful for applications where subclasses may cause issues with DI.
*
* @param target Target class type.
*/
public void addExplicitTarget(Class<? extends T> target) {
targets.add(target);
}
/**
* Inject the stored object into an object.
* <p>
* Injects the stored object into any non-static, non-final fields
* annotated with {@link Inject},
* with type matching the stored object or any explicit targets
* ({@link #addExplicitTarget(Class)}.
*
* @param object Object to inject into
* @throws InjectionException If:
* <ul>
* <li>Matching field annotated with {@link Inject} is final</li>
* <li>Matching field annotated with {@link Inject} is static</li>
* <li>A reflective access exception occurs</li>
* </ul>
*/
public void inject(Object object) throws InjectionException {
for(Field field : ReflectionUtil.getFields(object.getClass())) {
Inject inject = field.getAnnotation(Inject.class);
if(inject == null) continue;
if(value.getClass().equals(field.getType()) || targets.contains(field.getType())) {
int mod = field.getModifiers();
if(Modifier.isFinal(mod)) {
throw new InjectionException("Attempted to inject final field: " + field);
}
if(Modifier.isStatic(mod)) {
throw new InjectionException("Attempted to inject static field: " + field);
}
field.setAccessible(true);
try {
field.set(object, value);
} catch(IllegalAccessException e) {
throw new InjectionException("Failed to inject field: " + field, e);
}
}
}
}
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.injection.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies that a field is a target for dependency injection.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.injection.exception;
import com.dfsek.terra.api.injection.Injector;
/**
* Thrown when dynamic dependency injection cannot be completed by an {@link Injector}.
*/
public class InjectionException extends Exception {
private static final long serialVersionUID = -6929631447064215387L;
public InjectionException(String message) {
super(message);
}
public InjectionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,73 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
import java.util.Random;
/**
* Class to procedurally determine the spawn point of an object based on a grid with padding between cells.
*/
public class GridSpawn {
private final int separation;
private final int width;
private final int salt;
public GridSpawn(int width, int separation, int salt) {
this.separation = separation;
this.width = width;
this.salt = salt;
}
/**
* Get nearest spawn point
*
* @param x X coordinate
* @param z Z coordinate
* @param seed Seed for RNG
* @return Vector representing nearest spawnpoint
*/
public Vector3 getNearestSpawn(int x, int z, long seed) {
int structureChunkX = x / (width + 2 * separation);
int structureChunkZ = z / (width + 2 * separation);
List<Vector3> zones = new GlueList<>();
for(int xi = structureChunkX - 1; xi <= structureChunkX + 1; xi++) {
for(int zi = structureChunkZ - 1; zi <= structureChunkZ + 1; zi++) {
zones.add(getChunkSpawn(xi, zi, seed));
}
}
Vector3 shortest = zones.get(0);
Vector3 compare = new Vector3(x, 0, z);
for(Vector3 v : zones) {
if(compare.distanceSquared(shortest) > compare.distanceSquared(v)) shortest = v.clone();
}
return shortest;
}
/**
* Get the X/Z coordinates of the spawn point in the nearest Chunk (not Minecraft chunk)
*
* @param structureChunkX Chunk X coordinate
* @param structureChunkZ Chunk Z coordinate
* @param seed Seed for RNG
* @return Vector representing spawnpoint
*/
public Vector3 getChunkSpawn(int structureChunkX, int structureChunkZ, long seed) {
Random r = new FastRandom(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed + salt));
int offsetX = r.nextInt(width);
int offsetZ = r.nextInt(width);
int sx = structureChunkX * (width + 2 * separation) + offsetX;
int sz = structureChunkZ * (width + 2 * separation) + offsetZ;
return new Vector3(sx, 0, sz);
}
public int getWidth() {
return width;
}
public int getSeparation() {
return separation;
}
}

View File

@@ -0,0 +1,193 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.world.generation.math.samplers.Sampler;
import net.jafama.FastMath;
import java.util.List;
import java.util.Random;
/**
* Utility class for mathematical functions.
*/
public final class MathUtil {
/**
* Epsilon for fuzzy floating point comparisons.
*/
public static final double EPSILON = 1.0E-5;
/**
* Derivative constant.
*/
private static final double DERIVATIVE_DIST = 0.55;
/**
* Gets the standard deviation of an array of doubles.
*
* @param numArray The array of numbers to calculate the standard deviation of.
* @return double - The standard deviation.
*/
public static double standardDeviation(List<Number> numArray) {
double sum = 0.0, standardDeviation = 0.0;
int length = numArray.size();
for(Number num : numArray) {
sum += num.doubleValue();
}
double mean = sum / length;
for(Number num : numArray) {
standardDeviation += FastMath.pow2(num.doubleValue() - mean);
}
return FastMath.sqrt(standardDeviation / length);
}
/**
* Gets the carver seed for a chunk.
*
* @param chunkX Chunk's X coordinate
* @param chunkZ Chunk's Z coordinate
* @param seed World seed
* @return long - The carver seed.
*/
public static long getCarverChunkSeed(int chunkX, int chunkZ, long seed) {
Random r = new FastRandom(seed);
return chunkX * r.nextLong() ^ chunkZ * r.nextLong() ^ seed;
}
public static long hashToLong(String s) {
if(s == null) {
return 0;
}
long hash = 0;
for(char c : s.toCharArray()) {
hash = 31L * hash + c;
}
return hash;
}
/**
* Compare 2 floating-point values with epsilon to account for rounding errors
*
* @param a Value 1
* @param b Value 2
* @return Whether these values are equal
*/
public static boolean equals(double a, double b) {
return a == b || FastMath.abs(a - b) < EPSILON;
}
public static double derivative(Sampler sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
public static int normalizeIndex(double val, int size) {
return FastMath.max(FastMath.min(FastMath.floorToInt(((val + 1D) / 2D) * size), size - 1), 0);
}
public static long squash(int first, int last) {
return (((long) first) << 32) | (last & 0xffffffffL);
}
/**
* Clamp value to range of [-1, 1]
*
* @param in Value to clamp
* @return Clamped value
*/
public static double clamp(double in) {
return FastMath.min(FastMath.max(in, -1), 1);
}
/**
* Compute the value in a normally distributed data set that has probability p.
*
* @param p Probability of value
* @param mu Mean of data
* @param sigma Standard deviation of data
* @return Value corresponding to input probability
*/
public static double normalInverse(double p, double mu, double sigma) {
if(p < 0 || p > 1)
throw new IllegalArgumentException("Probability must be in range [0, 1]");
if(sigma < 0)
throw new IllegalArgumentException("Standard deviation must be positive.");
if(p == 0)
return Double.NEGATIVE_INFINITY;
if(p == 1)
return Double.POSITIVE_INFINITY;
if(sigma == 0)
return mu;
double q, r, val;
q = p - 0.5;
if(FastMath.abs(q) <= .425) {
r = .180625 - q * q;
val =
q * (((((((r * 2509.0809287301226727 +
33430.575583588128105) * r + 67265.770927008700853) * r +
45921.953931549871457) * r + 13731.693765509461125) * r +
1971.5909503065514427) * r + 133.14166789178437745) * r +
3.387132872796366608)
/ (((((((r * 5226.495278852854561 +
28729.085735721942674) * r + 39307.89580009271061) * r +
21213.794301586595867) * r + 5394.1960214247511077) * r +
687.1870074920579083) * r + 42.313330701600911252) * r + 1);
} else {
if(q > 0) {
r = 1 - p;
} else {
r = p;
}
r = FastMath.sqrt(-FastMath.log(r));
if(r <= 5) {
r += -1.6;
val = (((((((r * 7.7454501427834140764e-4 +
.0227238449892691845833) * r + .24178072517745061177) *
r + 1.27045825245236838258) * r +
3.64784832476320460504) * r + 5.7694972214606914055) *
r + 4.6303378461565452959) * r +
1.42343711074968357734)
/ (((((((r *
1.05075007164441684324e-9 + 5.475938084995344946e-4) *
r + .0151986665636164571966) * r +
.14810397642748007459) * r + .68976733498510000455) *
r + 1.6763848301838038494) * r +
2.05319162663775882187) * r + 1);
} else {
r += -5;
val = (((((((r * 2.01033439929228813265e-7 +
2.71155556874348757815e-5) * r +
.0012426609473880784386) * r + .026532189526576123093) *
r + .29656057182850489123) * r +
1.7848265399172913358) * r + 5.4637849111641143699) *
r + 6.6579046435011037772)
/ (((((((r *
2.04426310338993978564e-15 + 1.4215117583164458887e-7) *
r + 1.8463183175100546818e-5) * r +
7.868691311456132591e-4) * r + .0148753612908506148525)
* r + .13692988092273580531) * r +
.59983220655588793769) * r + 1);
}
if(q < 0.0) {
val = -val;
}
}
return mu + sigma * val;
}
}

View File

@@ -0,0 +1,123 @@
package com.dfsek.terra.api.math;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
public class Range implements Iterable<Integer> {
private int min;
private int max;
public Range(int min, int max) {
if(min > max) throw new IllegalArgumentException("Minimum must not be grater than maximum!");
this.max = max;
this.min = min;
}
public boolean isInRange(int test) {
return test >= min && test < max;
}
public int getMax() {
return max;
}
public Range setMax(int max) {
this.max = max;
return this;
}
public int getMin() {
return min;
}
public Range setMin(int min) {
this.min = min;
return this;
}
public int getRange() {
return max - min;
}
public Range multiply(int mult) {
min *= mult;
max *= mult;
return this;
}
public Range reflect(int pt) {
return new Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
}
public int get(Random r) {
return r.nextInt((max - min) + 1) + min;
}
public Range intersects(com.dfsek.terra.api.math.Range other) {
try {
return new Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
} catch(IllegalArgumentException e) {
return null;
}
}
public Range add(int add) {
this.min += add;
this.max += add;
return this;
}
public Range sub(int sub) {
this.min -= sub;
this.max -= sub;
return this;
}
@Override
public String toString() {
return "Min: " + getMin() + ", Max:" + getMax();
}
@Override
public int hashCode() {
return min * 31 + max;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof com.dfsek.terra.api.math.Range)) return false;
Range other = (com.dfsek.terra.api.math.Range) obj;
return other.getMin() == this.getMin() && other.getMax() == this.getMax();
}
@NotNull
@Override
public Iterator<Integer> iterator() {
return new RangeIterator(this);
}
private static class RangeIterator implements Iterator<Integer> {
private final Range m;
private Integer current;
public RangeIterator(Range m) {
this.m = m;
current = m.getMin();
}
@Override
public boolean hasNext() {
return current < m.getMax();
}
@Override
public Integer next() {
current++;
return current - 1;
}
}
}

View File

@@ -0,0 +1,32 @@
package com.dfsek.terra.api.math.noise;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.math.vector.Vector3;
public interface NoiseSampler {
/**
* 2D noise at given position using current settings
* <p>
* Noise output bounded between -1...1
*/
double getNoise(double x, double y);
/**
* 3D noise at given position using current settings
* <p>
* Noise output bounded between -1...1
*/
double getNoise(double x, double y, double z);
default double getNoise(Vector3 vector3) {
return getNoise(vector3.getX(), vector3.getY(), vector3.getZ());
}
default double getNoise(Vector2 vector2) {
return getNoise(vector2.getX(), vector2.getZ());
}
double getNoiseSeeded(int seed, double x, double y);
double getNoiseSeeded(int seed, double x, double y, double z);
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.math.noise.normalizer;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import net.jafama.FastMath;
public class ClampNormalizer extends Normalizer {
private final double min;
private final double max;
public ClampNormalizer(NoiseSampler sampler, double min, double max) {
super(sampler);
this.min = min;
this.max = max;
}
@Override
public double normalize(double in) {
return FastMath.max(FastMath.min(in, max), min);
}
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.api.math.noise.normalizer;
import com.dfsek.terra.api.math.noise.NoiseSampler;
/**
* Normalizer to linearly scale data's range.
*/
public class LinearNormalizer extends Normalizer {
private final double min;
private final double max;
public LinearNormalizer(NoiseSampler sampler, double min, double max) {
super(sampler);
this.min = min;
this.max = max;
}
@Override
public double normalize(double in) {
return (in - min) * (2 / (max - min)) - 1;
}
}

View File

@@ -0,0 +1,45 @@
package com.dfsek.terra.api.math.noise.normalizer;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import net.jafama.FastMath;
/**
* Normalizer to redistribute normally distributed data to a continuous distribution via an automatically generated lookup table.
*/
public class NormalNormalizer extends Normalizer {
private final double[] lookup;
public NormalNormalizer(NoiseSampler sampler, int buckets, double mean, double standardDeviation) {
super(sampler);
this.lookup = new double[buckets];
for(int i = 0; i < buckets; i++) {
lookup[i] = MathUtil.normalInverse((double) i / buckets, mean, standardDeviation);
}
}
@Override
public double normalize(double in) {
int start = 0;
int end = lookup.length - 1;
while(start + 1 < end) {
int mid = start + (end - start) / 2;
if(lookup[mid] <= in) {
start = mid;
} else {
end = mid;
}
}
double left = FastMath.abs(lookup[start] - in);
double right = FastMath.abs(lookup[end] - in);
double fin;
if(left <= right) {
fin = (double) start / (lookup.length);
} else fin = (double) end / (lookup.length);
return (fin - 0.5) * 2;
}
}

View File

@@ -0,0 +1,33 @@
package com.dfsek.terra.api.math.noise.normalizer;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public abstract class Normalizer implements NoiseSampler {
private final NoiseSampler sampler;
public Normalizer(NoiseSampler sampler) {
this.sampler = sampler;
}
public abstract double normalize(double in);
@Override
public double getNoise(double x, double y) {
return normalize(sampler.getNoise(x, y));
}
@Override
public double getNoise(double x, double y, double z) {
return normalize(sampler.getNoise(x, y, z));
}
@Override
public double getNoiseSeeded(int seed, double x, double y) {
return normalize(sampler.getNoiseSeeded(seed, x, y));
}
@Override
public double getNoiseSeeded(int seed, double x, double y, double z) {
return normalize(sampler.getNoiseSeeded(seed, x, y, z));
}
}

View File

@@ -0,0 +1,44 @@
package com.dfsek.terra.api.math.noise.samplers;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public class DomainWarpedSampler implements NoiseSampler {
private final NoiseSampler function;
private final NoiseSampler warp;
private final int seed;
private final double amplitude;
public DomainWarpedSampler(NoiseSampler function, NoiseSampler warp, int seed, double amplitude) {
this.function = function;
this.warp = warp;
this.seed = seed;
this.amplitude = amplitude;
}
@Override
public double getNoise(double x, double y) {
return getNoiseSeeded(seed, x, y);
}
@Override
public double getNoise(double x, double y, double z) {
return getNoiseSeeded(seed, x, y, z);
}
@Override
public double getNoiseSeeded(int seed, double x, double y) {
return function.getNoise(
x + warp.getNoiseSeeded(seed, x, y) * amplitude,
y + warp.getNoiseSeeded(seed + 1, x, y) * amplitude
);
}
@Override
public double getNoiseSeeded(int seed, double x, double y, double z) {
return function.getNoise(
x + warp.getNoiseSeeded(seed, x, y, z) * amplitude,
y + warp.getNoiseSeeded(seed + 1, x, y, z) * amplitude,
z + warp.getNoiseSeeded(seed + 2, x, y, z) * amplitude
);
}
}

View File

@@ -0,0 +1,67 @@
package com.dfsek.terra.api.math.noise.samplers;
import com.dfsek.paralithic.Expression;
import com.dfsek.paralithic.eval.parser.Parser;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.api.math.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.api.math.paralithic.noise.NoiseFunction3;
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import java.util.Map;
/**
* Sampler3D implementation using Paralithic expression
*/
public class ExpressionSampler implements NoiseSampler {
private final Expression expression;
public ExpressionSampler(String equation, Scope parent, long seed, Map<String, NoiseSeeded> functions, Map<String, FunctionTemplate> definedFunctions) throws ParseException {
Parser parser = new Parser();
Scope scope = new Scope().withParent(parent);
scope.addInvocationVariable("x");
scope.addInvocationVariable("y");
scope.addInvocationVariable("z");
functions.forEach((id, noise) -> {
switch(noise.getDimensions()) {
case 2:
parser.registerFunction(id, new NoiseFunction2(noise.apply(seed)));
break;
case 3:
parser.registerFunction(id, new NoiseFunction3(noise.apply(seed)));
break;
}
});
for(Map.Entry<String, FunctionTemplate> entry : definedFunctions.entrySet()) {
parser.registerFunction(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue(), parser, parent));
}
this.expression = parser.parse(equation, scope);
}
@Override
public double getNoise(double x, double y) {
return getNoise(x, 0, y);
}
@Override
public double getNoise(double x, double y, double z) {
return expression.evaluate(x, y, z);
}
@Override
public double getNoiseSeeded(int seed, double x, double y) {
return getNoise(x, y);
}
@Override
public double getNoiseSeeded(int seed, double x, double y, double z) {
return getNoise(x, y, z);
}
}

View File

@@ -0,0 +1,70 @@
package com.dfsek.terra.api.math.noise.samplers;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import net.jafama.FastMath;
import java.awt.image.BufferedImage;
public class ImageSampler implements NoiseSampler {
private final BufferedImage image;
private final Channel channel;
private final double frequency;
public ImageSampler(BufferedImage image, Channel channel, double frequency) {
this.image = image;
this.channel = channel;
this.frequency = frequency;
}
@Override
public double getNoise(double x, double y) {
return ((channel.getChannel(image.getRGB(FastMath.floorMod(FastMath.floorToInt(x * frequency), image.getWidth()), FastMath.floorMod(FastMath.floorToInt(y * frequency), image.getHeight()))) / 255D) - 0.5) * 2;
}
@Override
public double getNoise(double x, double y, double z) {
return getNoise(x, y);
}
@Override
public double getNoiseSeeded(int seed, double x, double y) {
return getNoise(x, y);
}
@Override
public double getNoiseSeeded(int seed, double x, double y, double z) {
return getNoise(x, y, z);
}
public enum Channel {
RED {
@Override
public int getChannel(int mashed) {
return (mashed >> 16) & 0xff;
}
}, GREEN {
@Override
public int getChannel(int mashed) {
return (mashed >> 8) & 0xff;
}
}, BLUE {
@Override
public int getChannel(int mashed) {
return mashed & 0xff;
}
}, GRAYSCALE {
@Override
public int getChannel(int mashed) {
return (RED.getChannel(mashed) + GREEN.getChannel(mashed) + BLUE.getChannel(mashed)) / 3;
}
}, ALPHA {
@Override
public int getChannel(int mashed) {
return (mashed >> 24) & 0xff;
}
};
public abstract int getChannel(int mashed);
}
}

View File

@@ -0,0 +1,59 @@
package com.dfsek.terra.api.math.noise.samplers;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public class KernelSampler implements NoiseSampler {
private final double[][] kernel;
private final NoiseSampler in;
private double frequency = 1;
public KernelSampler(double[][] kernel, NoiseSampler in) {
this.kernel = kernel;
this.in = in;
}
@Override
public double getNoise(double x, double y) {
return getNoiseSeeded(0, x, y);
}
@Override
public double getNoise(double x, double y, double z) {
return getNoiseSeeded(0, x, y, z);
}
@Override
public double getNoiseSeeded(int seed, double x, double y) {
x *= frequency;
y *= frequency;
double accumulator = 0;
for(int kx = 0; kx < kernel.length; kx++) {
for(int ky = 0; ky < kernel[kx].length; ky++) {
accumulator += in.getNoise(x + kx, y + ky) * kernel[kx][ky];
}
}
return accumulator;
}
@Override
public double getNoiseSeeded(int seed, double x, double y, double z) {
x *= frequency;
y *= frequency;
z *= frequency;
double accumulator = 0;
for(int kx = 0; kx < kernel.length; kx++) {
for(int ky = 0; ky < kernel[kx].length; ky++) {
accumulator += in.getNoise(x + kx, y, z + ky) * kernel[kx][ky];
}
}
return accumulator;
}
public void setFrequency(double frequency) {
this.frequency = frequency;
}
}

View File

@@ -0,0 +1,565 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.noise.samplers.noise.simplex.OpenSimplex2Sampler;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.math.vector.Vector3;
/**
* NoiseSampler implementation for Cellular (Voronoi/Worley) Noise.
*/
public class CellularSampler extends NoiseFunction {
private static final double[] RAND_VECS_3D = {
-0.7292736885d, -0.6618439697d, 0.1735581948d, 0, 0.790292081d, -0.5480887466d, -0.2739291014d, 0, 0.7217578935d, 0.6226212466d,
-0.3023380997d, 0, 0.565683137d, -0.8208298145d, -0.0790000257d, 0, 0.760049034d, -0.5555979497d, -0.3370999617d, 0,
0.3713945616d, 0.5011264475d, 0.7816254623d, 0, -0.1277062463d, -0.4254438999d, -0.8959289049d, 0, -0.2881560924d,
-0.5815838982d, 0.7607405838d, 0, 0.5849561111d, -0.662820239d, -0.4674352136d, 0, 0.3307171178d, 0.0391653737d, 0.94291689d, 0,
0.8712121778d, -0.4113374369d, -0.2679381538d, 0, 0.580981015d, 0.7021915846d, 0.4115677815d, 0, 0.503756873d, 0.6330056931d,
-0.5878203852d, 0, 0.4493712205d, 0.601390195d, 0.6606022552d, 0, -0.6878403724d, 0.09018890807d, -0.7202371714d, 0,
-0.5958956522d, -0.6469350577d, 0.475797649d, 0, -0.5127052122d, 0.1946921978d, -0.8361987284d, 0, -0.9911507142d,
-0.05410276466d, -0.1212153153d, 0, -0.2149721042d, 0.9720882117d, -0.09397607749d, 0, -0.7518650936d, -0.5428057603d,
0.3742469607d, 0, 0.5237068895d, 0.8516377189d, -0.02107817834d, 0, 0.6333504779d, 0.1926167129d, -0.7495104896d, 0,
-0.06788241606d, 0.3998305789d, 0.9140719259d, 0, -0.5538628599d, -0.4729896695d, -0.6852128902d, 0, -0.7261455366d,
-0.5911990757d, 0.3509933228d, 0, -0.9229274737d, -0.1782808786d, 0.3412049336d, 0, -0.6968815002d, 0.6511274338d,
0.3006480328d, 0, 0.9608044783d, -0.2098363234d, -0.1811724921d, 0, 0.06817146062d, -0.9743405129d, 0.2145069156d, 0,
-0.3577285196d, -0.6697087264d, -0.6507845481d, 0, -0.1868621131d, 0.7648617052d, -0.6164974636d, 0, -0.6541697588d,
0.3967914832d, 0.6439087246d, 0, 0.6993340405d, -0.6164538506d, 0.3618239211d, 0, -0.1546665739d, 0.6291283928d, 0.7617583057d,
0, -0.6841612949d, -0.2580482182d, -0.6821542638d, 0, 0.5383980957d, 0.4258654885d, 0.7271630328d, 0, -0.5026987823d,
-0.7939832935d, -0.3418836993d, 0, 0.3202971715d, 0.2834415347d, 0.9039195862d, 0, 0.8683227101d, -0.0003762656404d,
-0.4959995258d, 0, 0.791120031d, -0.08511045745d, 0.6057105799d, 0, -0.04011016052d, -0.4397248749d, 0.8972364289d, 0,
0.9145119872d, 0.3579346169d, -0.1885487608d, 0, -0.9612039066d, -0.2756484276d, 0.01024666929d, 0, 0.6510361721d,
-0.2877799159d, -0.7023778346d, 0, -0.2041786351d, 0.7365237271d, 0.644859585d, 0, -0.7718263711d, 0.3790626912d, 0.5104855816d,
0, -0.3060082741d, -0.7692987727d, 0.5608371729d, 0, 0.454007341d, -0.5024843065d, 0.7357899537d, 0, 0.4816795475d,
0.6021208291d, -0.6367380315d, 0, 0.6961980369d, -0.3222197429d, 0.641469197d, 0, -0.6532160499d, -0.6781148932d, 0.3368515753d,
0, 0.5089301236d, -0.6154662304d, -0.6018234363d, 0, -0.1635919754d, -0.9133604627d, -0.372840892d, 0, 0.52408019d,
-0.8437664109d, 0.1157505864d, 0, 0.5902587356d, 0.4983817807d, -0.6349883666d, 0, 0.5863227872d, 0.494764745d, 0.6414307729d,
0, 0.6779335087d, 0.2341345225d, 0.6968408593d, 0, 0.7177054546d, -0.6858979348d, 0.120178631d, 0, -0.5328819713d,
-0.5205125012d, 0.6671608058d, 0, -0.8654874251d, -0.0700727088d, -0.4960053754d, 0, -0.2861810166d, 0.7952089234d,
0.5345495242d, 0, -0.04849529634d, 0.9810836427d, -0.1874115585d, 0, -0.6358521667d, 0.6058348682d, 0.4781800233d, 0,
0.6254794696d, -0.2861619734d, 0.7258696564d, 0, -0.2585259868d, 0.5061949264d, -0.8227581726d, 0, 0.02136306781d,
0.5064016808d, -0.8620330371d, 0, 0.200111773d, 0.8599263484d, 0.4695550591d, 0, 0.4743561372d, 0.6014985084d, -0.6427953014d,
0, 0.6622993731d, -0.5202474575d, -0.5391679918d, 0, 0.08084972818d, -0.6532720452d, 0.7527940996d, 0, -0.6893687501d,
0.0592860349d, 0.7219805347d, 0, -0.1121887082d, -0.9673185067d, 0.2273952515d, 0, 0.7344116094d, 0.5979668656d, -0.3210532909d,
0, 0.5789393465d, -0.2488849713d, 0.7764570201d, 0, 0.6988182827d, 0.3557169806d, -0.6205791146d, 0, -0.8636845529d,
-0.2748771249d, -0.4224826141d, 0, -0.4247027957d, -0.4640880967d, 0.777335046d, 0, 0.5257722489d, -0.8427017621d,
0.1158329937d, 0, 0.9343830603d, 0.316302472d, -0.1639543925d, 0, -0.1016836419d, -0.8057303073d, -0.5834887393d, 0,
-0.6529238969d, 0.50602126d, -0.5635892736d, 0, -0.2465286165d, -0.9668205684d, -0.06694497494d, 0, -0.9776897119d,
-0.2099250524d, -0.007368825344d, 0, 0.7736893337d, 0.5734244712d, 0.2694238123d, 0, -0.6095087895d, 0.4995678998d,
0.6155736747d, 0, 0.5794535482d, 0.7434546771d, 0.3339292269d, 0, -0.8226211154d, 0.08142581855d, 0.5627293636d, 0,
-0.510385483d, 0.4703667658d, 0.7199039967d, 0, -0.5764971849d, -0.07231656274d, -0.8138926898d, 0, 0.7250628871d,
0.3949971505d, -0.5641463116d, 0, -0.1525424005d, 0.4860840828d, -0.8604958341d, 0, -0.5550976208d, -0.4957820792d,
0.667882296d, 0, -0.1883614327d, 0.9145869398d, 0.357841725d, 0, 0.7625556724d, -0.5414408243d, -0.3540489801d, 0,
-0.5870231946d, -0.3226498013d, -0.7424963803d, 0, 0.3051124198d, 0.2262544068d, -0.9250488391d, 0, 0.6379576059d, 0.577242424d,
-0.5097070502d, 0, -0.5966775796d, 0.1454852398d, -0.7891830656d, 0, -0.658330573d, 0.6555487542d, -0.3699414651d, 0,
0.7434892426d, 0.2351084581d, 0.6260573129d, 0, 0.5562114096d, 0.8264360377d, -0.0873632843d, 0, -0.3028940016d, -0.8251527185d,
0.4768419182d, 0, 0.1129343818d, -0.985888439d, -0.1235710781d, 0, 0.5937652891d, -0.5896813806d, 0.5474656618d, 0,
0.6757964092d, -0.5835758614d, -0.4502648413d, 0, 0.7242302609d, -0.1152719764d, 0.6798550586d, 0, -0.9511914166d,
0.0753623979d, -0.2992580792d, 0, 0.2539470961d, -0.1886339355d, 0.9486454084d, 0, 0.571433621d, -0.1679450851d, -0.8032795685d,
0, -0.06778234979d, 0.3978269256d, 0.9149531629d, 0, 0.6074972649d, 0.733060024d, -0.3058922593d, 0, -0.5435478392d,
0.1675822484d, 0.8224791405d, 0, -0.5876678086d, -0.3380045064d, -0.7351186982d, 0, -0.7967562402d, 0.04097822706d,
-0.6029098428d, 0, -0.1996350917d, 0.8706294745d, 0.4496111079d, 0, -0.02787660336d, -0.9106232682d, -0.4122962022d, 0,
-0.7797625996d, -0.6257634692d, 0.01975775581d, 0, -0.5211232846d, 0.7401644346d, -0.4249554471d, 0, 0.8575424857d,
0.4053272873d, -0.3167501783d, 0, 0.1045223322d, 0.8390195772d, -0.5339674439d, 0, 0.3501822831d, 0.9242524096d, -0.1520850155d,
0, 0.1987849858d, 0.07647613266d, 0.9770547224d, 0, 0.7845996363d, 0.6066256811d, -0.1280964233d, 0, 0.09006737436d,
-0.9750989929d, -0.2026569073d, 0, -0.8274343547d, -0.542299559d, 0.1458203587d, 0, -0.3485797732d, -0.415802277d, 0.840000362d,
0, -0.2471778936d, -0.7304819962d, -0.6366310879d, 0, -0.3700154943d, 0.8577948156d, 0.3567584454d, 0, 0.5913394901d,
-0.548311967d, -0.5913303597d, 0, 0.1204873514d, -0.7626472379d, -0.6354935001d, 0, 0.616959265d, 0.03079647928d, 0.7863922953d,
0, 0.1258156836d, -0.6640829889d, -0.7369967419d, 0, -0.6477565124d, -0.1740147258d, -0.7417077429d, 0, 0.6217889313d,
-0.7804430448d, -0.06547655076d, 0, 0.6589943422d, -0.6096987708d, 0.4404473475d, 0, -0.2689837504d, -0.6732403169d,
-0.6887635427d, 0, -0.3849775103d, 0.5676542638d, 0.7277093879d, 0, 0.5754444408d, 0.8110471154d, -0.1051963504d, 0,
0.9141593684d, 0.3832947817d, 0.131900567d, 0, -0.107925319d, 0.9245493968d, 0.3654593525d, 0, 0.377977089d, 0.3043148782d,
0.8743716458d, 0, -0.2142885215d, -0.8259286236d, 0.5214617324d, 0, 0.5802544474d, 0.4148098596d, -0.7008834116d, 0,
-0.1982660881d, 0.8567161266d, -0.4761596756d, 0, -0.03381553704d, 0.3773180787d, -0.9254661404d, 0, -0.6867922841d,
-0.6656597827d, 0.2919133642d, 0, 0.7731742607d, -0.2875793547d, -0.5652430251d, 0, -0.09655941928d, 0.9193708367d,
-0.3813575004d, 0, 0.2715702457d, -0.9577909544d, -0.09426605581d, 0, 0.2451015704d, -0.6917998565d, -0.6792188003d, 0,
0.977700782d, -0.1753855374d, 0.1155036542d, 0, -0.5224739938d, 0.8521606816d, 0.02903615945d, 0, -0.7734880599d,
-0.5261292347d, 0.3534179531d, 0, -0.7134492443d, -0.269547243d, 0.6467878011d, 0, 0.1644037271d, 0.5105846203d, -0.8439637196d,
0, 0.6494635788d, 0.05585611296d, 0.7583384168d, 0, -0.4711970882d, 0.5017280509d, -0.7254255765d, 0, -0.6335764307d,
-0.2381686273d, -0.7361091029d, 0, -0.9021533097d, -0.270947803d, -0.3357181763d, 0, -0.3793711033d, 0.872258117d,
0.3086152025d, 0, -0.6855598966d, -0.3250143309d, 0.6514394162d, 0, 0.2900942212d, -0.7799057743d, -0.5546100667d, 0,
-0.2098319339d, 0.85037073d, 0.4825351604d, 0, -0.4592603758d, 0.6598504336d, -0.5947077538d, 0, 0.8715945488d, 0.09616365406d,
-0.4807031248d, 0, -0.6776666319d, 0.7118504878d, -0.1844907016d, 0, 0.7044377633d, 0.312427597d, 0.637304036d, 0,
-0.7052318886d, -0.2401093292d, -0.6670798253d, 0, 0.081921007d, -0.7207336136d, -0.6883545647d, 0, -0.6993680906d,
-0.5875763221d, -0.4069869034d, 0, -0.1281454481d, 0.6419895885d, 0.7559286424d, 0, -0.6337388239d, -0.6785471501d,
-0.3714146849d, 0, 0.5565051903d, -0.2168887573d, -0.8020356851d, 0, -0.5791554484d, 0.7244372011d, -0.3738578718d, 0,
0.1175779076d, -0.7096451073d, 0.6946792478d, 0, -0.6134619607d, 0.1323631078d, 0.7785527795d, 0, 0.6984635305d,
-0.02980516237d, -0.715024719d, 0, 0.8318082963d, -0.3930171956d, 0.3919597455d, 0, 0.1469576422d, 0.05541651717d,
-0.9875892167d, 0, 0.708868575d, -0.2690503865d, 0.6520101478d, 0, 0.2726053183d, 0.67369766d, -0.68688995d, 0, -0.6591295371d,
0.3035458599d, -0.6880466294d, 0, 0.4815131379d, -0.7528270071d, 0.4487723203d, 0, 0.9430009463d, 0.1675647412d, -0.2875261255d,
0, 0.434802957d, 0.7695304522d, -0.4677277752d, 0, 0.3931996188d, 0.594473625d, 0.7014236729d, 0, 0.7254336655d, -0.603925654d,
0.3301814672d, 0, 0.7590235227d, -0.6506083235d, 0.02433313207d, 0, -0.8552768592d, -0.3430042733d, 0.3883935666d, 0,
-0.6139746835d, 0.6981725247d, 0.3682257648d, 0, -0.7465905486d, -0.5752009504d, 0.3342849376d, 0, 0.5730065677d, 0.810555537d,
-0.1210916791d, 0, -0.9225877367d, -0.3475211012d, -0.167514036d, 0, -0.7105816789d, -0.4719692027d, -0.5218416899d, 0,
-0.08564609717d, 0.3583001386d, 0.929669703d, 0, -0.8279697606d, -0.2043157126d, 0.5222271202d, 0, 0.427944023d, 0.278165994d,
0.8599346446d, 0, 0.5399079671d, -0.7857120652d, -0.3019204161d, 0, 0.5678404253d, -0.5495413974d, -0.6128307303d, 0,
-0.9896071041d, 0.1365639107d, -0.04503418428d, 0, -0.6154342638d, -0.6440875597d, 0.4543037336d, 0, 0.1074204368d,
-0.7946340692d, 0.5975094525d, 0, -0.3595449969d, -0.8885529948d, 0.28495784d, 0, -0.2180405296d, 0.1529888965d, 0.9638738118d,
0, -0.7277432317d, -0.6164050508d, -0.3007234646d, 0, 0.7249729114d, -0.00669719484d, 0.6887448187d, 0, -0.5553659455d,
-0.5336586252d, 0.6377908264d, 0, 0.5137558015d, 0.7976208196d, -0.3160000073d, 0, -0.3794024848d, 0.9245608561d,
-0.03522751494d, 0, 0.8229248658d, 0.2745365933d, -0.4974176556d, 0, -0.5404114394d, 0.6091141441d, 0.5804613989d, 0,
0.8036581901d, -0.2703029469d, 0.5301601931d, 0, 0.6044318879d, 0.6832968393d, 0.4095943388d, 0, 0.06389988817d, 0.9658208605d,
-0.2512108074d, 0, 0.1087113286d, 0.7402471173d, -0.6634877936d, 0, -0.713427712d, -0.6926784018d, 0.1059128479d, 0,
0.6458897819d, -0.5724548511d, -0.5050958653d, 0, -0.6553931414d, 0.7381471625d, 0.159995615d, 0, 0.3910961323d, 0.9188871375d,
-0.05186755998d, 0, -0.4879022471d, -0.5904376907d, 0.6429111375d, 0, 0.6014790094d, 0.7707441366d, -0.2101820095d, 0,
-0.5677173047d, 0.7511360995d, 0.3368851762d, 0, 0.7858573506d, 0.226674665d, 0.5753666838d, 0, -0.4520345543d, -0.604222686d,
-0.6561857263d, 0, 0.002272116345d, 0.4132844051d, -0.9105991643d, 0, -0.5815751419d, -0.5162925989d, 0.6286591339d, 0,
-0.03703704785d, 0.8273785755d, 0.5604221175d, 0, -0.5119692504d, 0.7953543429d, -0.3244980058d, 0, -0.2682417366d,
-0.9572290247d, -0.1084387619d, 0, -0.2322482736d, -0.9679131102d, -0.09594243324d, 0, 0.3554328906d, -0.8881505545d,
0.2913006227d, 0, 0.7346520519d, -0.4371373164d, 0.5188422971d, 0, 0.9985120116d, 0.04659011161d, -0.02833944577d, 0,
-0.3727687496d, -0.9082481361d, 0.1900757285d, 0, 0.91737377d, -0.3483642108d, 0.1925298489d, 0, 0.2714911074d, 0.4147529736d,
-0.8684886582d, 0, 0.5131763485d, -0.7116334161d, 0.4798207128d, 0, -0.8737353606d, 0.18886992d, -0.4482350644d, 0,
0.8460043821d, -0.3725217914d, 0.3814499973d, 0, 0.8978727456d, -0.1780209141d, -0.4026575304d, 0, 0.2178065647d,
-0.9698322841d, -0.1094789531d, 0, -0.1518031304d, -0.7788918132d, -0.6085091231d, 0, -0.2600384876d, -0.4755398075d,
-0.8403819825d, 0, 0.572313509d, -0.7474340931d, -0.3373418503d, 0, -0.7174141009d, 0.1699017182d, -0.6756111411d, 0,
-0.684180784d, 0.02145707593d, -0.7289967412d, 0, -0.2007447902d, 0.06555605789d, -0.9774476623d, 0, -0.1148803697d,
-0.8044887315d, 0.5827524187d, 0, -0.7870349638d, 0.03447489231d, 0.6159443543d, 0, -0.2015596421d, 0.6859872284d,
0.6991389226d, 0, -0.08581082512d, -0.10920836d, -0.9903080513d, 0, 0.5532693395d, 0.7325250401d, -0.396610771d, 0,
-0.1842489331d, -0.9777375055d, -0.1004076743d, 0, 0.0775473789d, -0.9111505856d, 0.4047110257d, 0, 0.1399838409d,
0.7601631212d, -0.6344734459d, 0, 0.4484419361d, -0.845289248d, 0.2904925424d, 0
};
private static final double[] RAND_VECS_2D = {
-0.2700222198d, -0.9628540911d, 0.3863092627d, -0.9223693152d, 0.04444859006d, -0.999011673d, -0.5992523158d, -0.8005602176d,
-0.7819280288d, 0.6233687174d, 0.9464672271d, 0.3227999196d, -0.6514146797d, -0.7587218957d, 0.9378472289d, 0.347048376d,
-0.8497875957d, -0.5271252623d, -0.879042592d, 0.4767432447d, -0.892300288d, -0.4514423508d, -0.379844434d, -0.9250503802d,
-0.9951650832d, 0.0982163789d, 0.7724397808d, -0.6350880136d, 0.7573283322d, -0.6530343002d, -0.9928004525d, -0.119780055d,
-0.0532665713d, 0.9985803285d, 0.9754253726d, -0.2203300762d, -0.7665018163d, 0.6422421394d, 0.991636706d, 0.1290606184d,
-0.994696838d, 0.1028503788d, -0.5379205513d, -0.84299554d, 0.5022815471d, -0.8647041387d, 0.4559821461d, -0.8899889226d,
-0.8659131224d, -0.5001944266d, 0.0879458407d, -0.9961252577d, -0.5051684983d, 0.8630207346d, 0.7753185226d, -0.6315704146d,
-0.6921944612d, 0.7217110418d, -0.5191659449d, -0.8546734591d, 0.8978622882d, -0.4402764035d, -0.1706774107d, 0.9853269617d,
-0.9353430106d, -0.3537420705d, -0.9992404798d, 0.03896746794d, -0.2882064021d, -0.9575683108d, -0.9663811329d, 0.2571137995d,
-0.8759714238d, -0.4823630009d, -0.8303123018d, -0.5572983775d, 0.05110133755d, -0.9986934731d, -0.8558373281d, -0.5172450752d,
0.09887025282d, 0.9951003332d, 0.9189016087d, 0.3944867976d, -0.2439375892d, -0.9697909324d, -0.8121409387d, -0.5834613061d,
-0.9910431363d, 0.1335421355d, 0.8492423985d, -0.5280031709d, -0.9717838994d, -0.2358729591d, 0.9949457207d, 0.1004142068d,
0.6241065508d, -0.7813392434d, 0.662910307d, 0.7486988212d, -0.7197418176d, 0.6942418282d, -0.8143370775d, -0.5803922158d,
0.104521054d, -0.9945226741d, -0.1065926113d, -0.9943027784d, 0.445799684d, -0.8951327509d, 0.105547406d, 0.9944142724d,
-0.992790267d, 0.1198644477d, -0.8334366408d, 0.552615025d, 0.9115561563d, -0.4111755999d, 0.8285544909d, -0.5599084351d,
0.7217097654d, -0.6921957921d, 0.4940492677d, -0.8694339084d, -0.3652321272d, -0.9309164803d, -0.9696606758d, 0.2444548501d,
0.08925509731d, -0.996008799d, 0.5354071276d, -0.8445941083d, -0.1053576186d, 0.9944343981d, -0.9890284586d, 0.1477251101d,
0.004856104961d, 0.9999882091d, 0.9885598478d, 0.1508291331d, 0.9286129562d, -0.3710498316d, -0.5832393863d, -0.8123003252d,
0.3015207509d, 0.9534596146d, -0.9575110528d, 0.2883965738d, 0.9715802154d, -0.2367105511d, 0.229981792d, 0.9731949318d,
0.955763816d, -0.2941352207d, 0.740956116d, 0.6715534485d, -0.9971513787d, -0.07542630764d, 0.6905710663d, -0.7232645452d,
-0.290713703d, -0.9568100872d, 0.5912777791d, -0.8064679708d, -0.9454592212d, -0.325740481d, 0.6664455681d, 0.74555369d,
0.6236134912d, 0.7817328275d, 0.9126993851d, -0.4086316587d, -0.8191762011d, 0.5735419353d, -0.8812745759d, -0.4726046147d,
0.9953313627d, 0.09651672651d, 0.9855650846d, -0.1692969699d, -0.8495980887d, 0.5274306472d, 0.6174853946d, -0.7865823463d,
0.8508156371d, 0.52546432d, 0.9985032451d, -0.05469249926d, 0.1971371563d, -0.9803759185d, 0.6607855748d, -0.7505747292d,
-0.03097494063d, 0.9995201614d, -0.6731660801d, 0.739491331d, -0.7195018362d, -0.6944905383d, 0.9727511689d, 0.2318515979d,
0.9997059088d, -0.0242506907d, 0.4421787429d, -0.8969269532d, 0.9981350961d, -0.061043673d, -0.9173660799d, -0.3980445648d,
-0.8150056635d, -0.5794529907d, -0.8789331304d, 0.4769450202d, 0.0158605829d, 0.999874213d, -0.8095464474d, 0.5870558317d,
-0.9165898907d, -0.3998286786d, -0.8023542565d, 0.5968480938d, -0.5176737917d, 0.8555780767d, -0.8154407307d, -0.5788405779d,
0.4022010347d, -0.9155513791d, -0.9052556868d, -0.4248672045d, 0.7317445619d, 0.6815789728d, -0.5647632201d, -0.8252529947d,
-0.8403276335d, -0.5420788397d, -0.9314281527d, 0.363925262d, 0.5238198472d, 0.8518290719d, 0.7432803869d, -0.6689800195d,
-0.985371561d, -0.1704197369d, 0.4601468731d, 0.88784281d, 0.825855404d, 0.5638819483d, 0.6182366099d, 0.7859920446d,
0.8331502863d, -0.553046653d, 0.1500307506d, 0.9886813308d, -0.662330369d, -0.7492119075d, -0.668598664d, 0.743623444d,
0.7025606278d, 0.7116238924d, -0.5419389763d, -0.8404178401d, -0.3388616456d, 0.9408362159d, 0.8331530315d, 0.5530425174d,
-0.2989720662d, -0.9542618632d, 0.2638522993d, 0.9645630949d, 0.124108739d, -0.9922686234d, -0.7282649308d, -0.6852956957d,
0.6962500149d, 0.7177993569d, -0.9183535368d, 0.3957610156d, -0.6326102274d, -0.7744703352d, -0.9331891859d, -0.359385508d,
-0.1153779357d, -0.9933216659d, 0.9514974788d, -0.3076565421d, -0.08987977445d, -0.9959526224d, 0.6678496916d, 0.7442961705d,
0.7952400393d, -0.6062947138d, -0.6462007402d, -0.7631674805d, -0.2733598753d, 0.9619118351d, 0.9669590226d, -0.254931851d,
-0.9792894595d, 0.2024651934d, -0.5369502995d, -0.8436138784d, -0.270036471d, -0.9628500944d, -0.6400277131d, 0.7683518247d,
-0.7854537493d, -0.6189203566d, 0.06005905383d, -0.9981948257d, -0.02455770378d, 0.9996984141d, -0.65983623d, 0.751409442d,
-0.6253894466d, -0.7803127835d, -0.6210408851d, -0.7837781695d, 0.8348888491d, 0.5504185768d, -0.1592275245d, 0.9872419133d,
0.8367622488d, 0.5475663786d, -0.8675753916d, -0.4973056806d, -0.2022662628d, -0.9793305667d, 0.9399189937d, 0.3413975472d,
0.9877404807d, -0.1561049093d, -0.9034455656d, 0.4287028224d, 0.1269804218d, -0.9919052235d, -0.3819600854d, 0.924178821d,
0.9754625894d, 0.2201652486d, -0.3204015856d, -0.9472818081d, -0.9874760884d, 0.1577687387d, 0.02535348474d, -0.9996785487d,
0.4835130794d, -0.8753371362d, -0.2850799925d, -0.9585037287d, -0.06805516006d, -0.99768156d, -0.7885244045d, -0.6150034663d,
0.3185392127d, -0.9479096845d, 0.8880043089d, 0.4598351306d, 0.6476921488d, -0.7619021462d, 0.9820241299d, 0.1887554194d,
0.9357275128d, -0.3527237187d, -0.8894895414d, 0.4569555293d, 0.7922791302d, 0.6101588153d, 0.7483818261d, 0.6632681526d,
-0.7288929755d, -0.6846276581d, 0.8729032783d, -0.4878932944d, 0.8288345784d, 0.5594937369d, 0.08074567077d, 0.9967347374d,
0.9799148216d, -0.1994165048d, -0.580730673d, -0.8140957471d, -0.4700049791d, -0.8826637636d, 0.2409492979d, 0.9705377045d,
0.9437816757d, -0.3305694308d, -0.8927998638d, -0.4504535528d, -0.8069622304d, 0.5906030467d, 0.06258973166d, 0.9980393407d,
-0.9312597469d, 0.3643559849d, 0.5777449785d, 0.8162173362d, -0.3360095855d, -0.941858566d, 0.697932075d, -0.7161639607d,
-0.002008157227d, -0.9999979837d, -0.1827294312d, -0.9831632392d, -0.6523911722d, 0.7578824173d, -0.4302626911d, -0.9027037258d,
-0.9985126289d, -0.05452091251d, -0.01028102172d, -0.9999471489d, -0.4946071129d, 0.8691166802d, -0.2999350194d, 0.9539596344d,
0.8165471961d, 0.5772786819d, 0.2697460475d, 0.962931498d, -0.7306287391d, -0.6827749597d, -0.7590952064d, -0.6509796216d,
-0.907053853d, 0.4210146171d, -0.5104861064d, -0.8598860013d, 0.8613350597d, 0.5080373165d, 0.5007881595d, -0.8655698812d,
-0.654158152d, 0.7563577938d, -0.8382755311d, -0.545246856d, 0.6940070834d, 0.7199681717d, 0.06950936031d, 0.9975812994d,
0.1702942185d, -0.9853932612d, 0.2695973274d, 0.9629731466d, 0.5519612192d, -0.8338697815d, 0.225657487d, -0.9742067022d,
0.4215262855d, -0.9068161835d, 0.4881873305d, -0.8727388672d, -0.3683854996d, -0.9296731273d, -0.9825390578d, 0.1860564427d,
0.81256471d, 0.5828709909d, 0.3196460933d, -0.9475370046d, 0.9570913859d, 0.2897862643d, -0.6876655497d, -0.7260276109d,
-0.9988770922d, -0.047376731d, -0.1250179027d, 0.992154486d, -0.8280133617d, 0.560708367d, 0.9324863769d, -0.3612051451d,
0.6394653183d, 0.7688199442d, -0.01623847064d, -0.9998681473d, -0.9955014666d, -0.09474613458d, -0.81453315d, 0.580117012d,
0.4037327978d, -0.9148769469d, 0.9944263371d, 0.1054336766d, -0.1624711654d, 0.9867132919d, -0.9949487814d, -0.100383875d,
-0.6995302564d, 0.7146029809d, 0.5263414922d, -0.85027327d, -0.5395221479d, 0.841971408d, 0.6579370318d, 0.7530729462d,
0.01426758847d, -0.9998982128d, -0.6734383991d, 0.7392433447d, 0.639412098d, -0.7688642071d, 0.9211571421d, 0.3891908523d,
-0.146637214d, -0.9891903394d, -0.782318098d, 0.6228791163d, -0.5039610839d, -0.8637263605d, -0.7743120191d, -0.6328039957d,
};
private DistanceFunction distanceFunction = DistanceFunction.EuclideanSq;
private ReturnType returnType = ReturnType.Distance;
private double jitterModifier = 1.0;
private NoiseSampler noiseLookup;
public CellularSampler(int seed) {
super(seed);
noiseLookup = new OpenSimplex2Sampler(seed);
}
public void setDistanceFunction(DistanceFunction distanceFunction) {
this.distanceFunction = distanceFunction;
}
public void setJitterModifier(double jitterModifier) {
this.jitterModifier = jitterModifier;
}
public void setNoiseLookup(NoiseSampler noiseLookup) {
this.noiseLookup = noiseLookup;
}
public void setReturnType(ReturnType returnType) {
this.returnType = returnType;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
int xr = fastRound(x);
int yr = fastRound(y);
double distance0 = Double.MAX_VALUE;
double distance1 = Double.MAX_VALUE;
double distance2 = Double.MAX_VALUE;
int closestHash = 0;
double cellularJitter = 0.43701595 * jitterModifier;
int xPrimed = (xr - 1) * PRIME_X;
int yPrimedBase = (yr - 1) * PRIME_Y;
Vector2 center = new Vector2(x, y);
switch(distanceFunction) {
default:
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY;
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
center.setZ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
center.setZ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
center.setZ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
distance0 = fastSqrt(distance0);
if(returnType != ReturnType.CellValue) {
distance1 = fastSqrt(distance1);
}
}
switch(returnType) {
case CellValue:
return closestHash * (1 / 2147483648.0);
case Distance:
return distance0 - 1;
case Distance2:
return distance1 - 1;
case Distance2Add:
return (distance1 + distance0) * 0.5 - 1;
case Distance2Sub:
return distance1 - distance0 - 1;
case Distance2Mul:
return distance1 * distance0 * 0.5 - 1;
case Distance2Div:
return distance0 / distance1 - 1;
case NoiseLookup:
return noiseLookup.getNoise(center.getX(), center.getZ());
case Distance3:
return distance2 - 1;
case Distance3Add:
return (distance2 + distance0) * 0.5 - 1;
case Distance3Sub:
return distance2 - distance0 - 1;
case Distance3Mul:
return distance2 * distance0 - 1;
case Distance3Div:
return distance0 / distance2 - 1;
default:
return 0;
}
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
int xr = fastRound(x);
int yr = fastRound(y);
int zr = fastRound(z);
double distance0 = Double.MAX_VALUE;
double distance1 = Double.MAX_VALUE;
double distance2 = Double.MAX_VALUE;
int closestHash = 0;
double cellularJitter = 0.39614353 * jitterModifier;
int xPrimed = (xr - 1) * PRIME_X;
int yPrimedBase = (yr - 1) * PRIME_Y;
int zPrimedBase = (zr - 1) * PRIME_Z;
Vector3 center = new Vector3(x, y, z);
switch(distanceFunction) {
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
center.setY((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
center.setZ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
center.setY((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
center.setZ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) +
(vecX * vecX + vecY * vecY + vecZ * vecZ);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
center.setY((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
center.setZ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
default:
break;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
distance0 = fastSqrt(distance0);
if(returnType != ReturnType.CellValue) {
distance1 = fastSqrt(distance1);
}
}
switch(returnType) {
case CellValue:
return closestHash * (1 / 2147483648.0);
case Distance:
return distance0 - 1;
case Distance2:
return distance1 - 1;
case Distance2Add:
return (distance1 + distance0) * 0.5 - 1;
case Distance2Sub:
return distance1 - distance0 - 1;
case Distance2Mul:
return distance1 * distance0 * 0.5 - 1;
case Distance2Div:
return distance0 / distance1 - 1;
case NoiseLookup:
return noiseLookup.getNoise(center.getX(), center.getY(), center.getZ());
case Distance3:
return distance2 - 1;
case Distance3Add:
return (distance2 + distance0) * 0.5 - 1;
case Distance3Sub:
return distance2 - distance0 - 1;
case Distance3Mul:
return distance2 * distance0 - 1;
case Distance3Div:
return distance0 / distance2 - 1;
default:
return 0;
}
}
public enum DistanceFunction {
Euclidean,
EuclideanSq,
Manhattan,
Hybrid
}
public enum ReturnType {
CellValue,
Distance,
Distance2,
Distance2Add,
Distance2Sub,
Distance2Mul,
Distance2Div,
NoiseLookup,
Distance3,
Distance3Add,
Distance3Sub,
Distance3Mul,
Distance3Div
}
}

View File

@@ -0,0 +1,23 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
/**
* Sampler3D implementation that returns a constant.
*/
public class ConstantSampler extends NoiseFunction {
private final double constant;
public ConstantSampler(double constant) {
super(0);
this.constant = constant;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
return constant;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
return constant;
}
}

View File

@@ -0,0 +1,43 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
import com.dfsek.paralithic.Expression;
import com.dfsek.paralithic.eval.parser.Parser;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import java.util.Map;
/**
* NoiseSampler implementation using a Paralithic expression.
*/
public class ExpressionFunction extends NoiseFunction {
private final Expression expression;
public ExpressionFunction(Map<String, Function> functions, String eq, Map<String, Double> vars) throws ParseException {
super(0);
Parser p = new Parser();
Scope scope = new Scope();
scope.addInvocationVariable("x");
scope.addInvocationVariable("y");
scope.addInvocationVariable("z");
vars.forEach(scope::create);
functions.forEach(p::registerFunction);
expression = p.parse(eq, scope);
frequency = 1;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
return expression.evaluate(x, 0, y);
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
return expression.evaluate(x, y, z);
}
}

View File

@@ -0,0 +1,105 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
import com.dfsek.terra.api.math.noise.samplers.noise.random.WhiteNoiseSampler;
import net.jafama.FastMath;
public class GaborNoiseSampler extends NoiseFunction {
private final WhiteNoiseSampler rand;
private double k = 1.0;
private double a = 0.1;
private double f0 = 0.625;
private double kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / a);
private double omega0 = Math.PI * 0.25;
private boolean isotropic = true;
private double impulsesPerKernel = 64d;
private double impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
private double impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
private double g = FastMath.exp(-impulsesPerCell);
public GaborNoiseSampler(int seed) {
super(seed);
rand = new WhiteNoiseSampler(seed);
}
public void setIsotropic(boolean isotropic) {
this.isotropic = isotropic;
}
public void setImpulsesPerKernel(double impulsesPerKernel) {
this.impulsesPerKernel = impulsesPerKernel;
recalculateRadiusAndDensity();
}
public void setA(double a) {
this.a = a;
recalculateRadiusAndDensity();
}
public void setFrequency0(double f0) {
this.f0 = f0;
}
public void setRotation(double omega0) {
this.omega0 = Math.PI * omega0;
}
public void setDeviation(double k) {
this.k = k;
}
private void recalculateRadiusAndDensity() {
kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / this.a);
impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
g = FastMath.exp(-impulsesPerCell);
}
@Override
public double getNoiseRaw(int seed, double x, double z) {
return gaborNoise(seed, x, z);
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
return gaborNoise(seed, x, z);
}
private double gaborNoise(int seed, double x, double y) {
x /= kernelRadius;
y /= kernelRadius;
int xi = fastFloor(x);
int yi = fastFloor(y);
double xf = x - xi;
double yf = y - yi;
double noise = 0;
for(int dx = -1; dx <= 1; dx++) {
for(int dz = -1; dz <= 1; dz++) {
noise += calculateCell(seed, xi + dx, yi + dz, xf - dx, yf - dz);
}
}
return noise;
}
private double calculateCell(int seed, int xi, int yi, double x, double y) {
long mashedSeed = murmur64(31L * xi + yi) + seed;
double gaussianSource = (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
int impulses = 0;
while(gaussianSource > g) {
impulses++;
gaussianSource *= (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
}
double noise = 0;
for(int i = 0; i < impulses; i++) {
noise += rand.getNoiseRaw(mashedSeed++) * gabor(isotropic ? (rand.getNoiseRaw(mashedSeed++) + 1) * Math.PI : omega0, x * kernelRadius, y * kernelRadius);
}
return noise;
}
private double gabor(double omega_0, double x, double y) {
return k * (FastMath.exp(-Math.PI * (a * a) * (x * x + y * y)) * fastCos(2 * Math.PI * f0 * (x * fastCos(omega_0) + y * fastSin(omega_0))));
}
}

View File

@@ -0,0 +1,157 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import net.jafama.FastMath;
@SuppressWarnings("ManualMinMaxCalculation")
public abstract class NoiseFunction implements NoiseSampler {
// Hashing
protected static final int PRIME_X = 501125321;
protected static final int PRIME_Y = 1136930381;
protected static final int PRIME_Z = 1720413743;
protected double frequency = 0.02d;
protected int seed;
public NoiseFunction(int seed) {
this.seed = seed;
}
protected static int fastFloor(double f) {
return f >= 0 ? (int) f : (int) f - 1;
}
static final int precision = 100;
protected static int hash(int seed, int xPrimed, int yPrimed, int zPrimed) {
int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed;
hash *= 0x27d4eb2d;
return hash;
}
protected static int hash(int seed, int xPrimed, int yPrimed) {
int hash = seed ^ xPrimed ^ yPrimed;
hash *= 0x27d4eb2d;
return hash;
}
static final int modulus = 360 * precision;
protected static int fastRound(double f) {
return f >= 0 ? (int) (f + 0.5f) : (int) (f - 0.5);
}
protected static double lerp(double a, double b, double t) {
return a + t * (b - a);
}
protected static double interpHermite(double t) {
return t * t * (3 - 2 * t);
}
protected static double interpQuintic(double t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
protected static double cubicLerp(double a, double b, double c, double d, double t) {
double p = (d - c) - (a - b);
return t * t * t * p + t * t * ((a - b) - p) + t * (c - a) + b;
}
protected static double fastMin(double a, double b) {
return a < b ? a : b;
}
protected static double fastMax(double a, double b) {
return a > b ? a : b;
}
protected static double fastAbs(double f) {
return f < 0 ? -f : f;
}
protected static double fastSqrt(double f) {
return FastMath.sqrt(f);
}
static final double[] sin = new double[360 * 100]; // lookup table
static {
for(int i = 0; i < sin.length; i++) {
sin[i] = (float) Math.sin((double) (i) / (precision));
}
}
protected static int fastCeil(double f) {
int i = (int) f;
if(i < f) i++;
return i;
}
/**
* Murmur64 hashing function
*
* @param h Input value
* @return Hashed value
*/
protected static long murmur64(long h) {
h ^= h >>> 33;
h *= 0xff51afd7ed558ccdL;
h ^= h >>> 33;
h *= 0xc4ceb9fe1a85ec53L;
h ^= h >>> 33;
return h;
}
private static double sinLookup(int a) {
return a >= 0 ? sin[a % (modulus)] : -sin[-a % (modulus)];
}
protected static double fastSin(double a) {
return sinLookup((int) (a * precision + 0.5f));
}
protected static double fastCos(double a) {
return sinLookup((int) ((a + Math.PI / 2) * precision + 0.5f));
}
public void setSeed(int seed) {
this.seed = seed;
}
public double getFrequency() {
return frequency;
}
public void setFrequency(double frequency) {
this.frequency = frequency;
}
@Override
public double getNoise(double x, double y) {
return getNoiseSeeded(seed, x, y);
}
@Override
public double getNoise(double x, double y, double z) {
return getNoiseSeeded(seed, x, y, z);
}
@Override
public double getNoiseSeeded(int seed, double x, double y) {
return getNoiseRaw(seed, x * frequency, y * frequency);
}
@Override
public double getNoiseSeeded(int seed, double x, double y, double z) {
return getNoiseRaw(seed, x * frequency, y * frequency, z * frequency);
}
public abstract double getNoiseRaw(int seed, double x, double y);
public abstract double getNoiseRaw(int seed, double x, double y, double z);
}

View File

@@ -0,0 +1,46 @@
package com.dfsek.terra.api.math.noise.samplers.noise.fractal;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public class BrownianMotionSampler extends FractalNoiseFunction {
public BrownianMotionSampler(int seed, NoiseSampler input) {
super(seed, input);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
double sum = 0;
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double noise = input.getNoiseSeeded(seed++, x, y);
sum += noise * amp;
amp *= lerp(1.0, fastMin(noise + 1, 2) * 0.5, weightedStrength);
x *= lacunarity;
y *= lacunarity;
amp *= gain;
}
return sum;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
double sum = 0;
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double noise = input.getNoiseSeeded(seed++, x, y, z);
sum += noise * amp;
amp *= lerp(1.0, (noise + 1) * 0.5, weightedStrength);
x *= lacunarity;
y *= lacunarity;
z *= lacunarity;
amp *= gain;
}
return sum;
}
}

View File

@@ -0,0 +1,48 @@
package com.dfsek.terra.api.math.noise.samplers.noise.fractal;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.noise.samplers.noise.NoiseFunction;
public abstract class FractalNoiseFunction extends NoiseFunction {
protected final NoiseSampler input;
protected double fractalBounding = 1 / 1.75;
protected int octaves = 3;
protected double gain = 0.5;
protected double lacunarity = 2.0d;
protected double weightedStrength = 0.0d;
public FractalNoiseFunction(int seed, NoiseSampler input) {
super(seed);
this.input = input;
frequency = 1;
}
public void setWeightedStrength(double weightedStrength) {
this.weightedStrength = weightedStrength;
}
protected void calculateFractalBounding() {
double gain = fastAbs(this.gain);
double amp = gain;
double ampFractal = 1.0;
for(int i = 1; i < octaves; i++) {
ampFractal += amp;
amp *= gain;
}
fractalBounding = 1 / ampFractal;
}
public void setOctaves(int octaves) {
this.octaves = octaves;
calculateFractalBounding();
}
public void setGain(double gain) {
this.gain = gain;
calculateFractalBounding();
}
public void setLacunarity(double lacunarity) {
this.lacunarity = lacunarity;
}
}

View File

@@ -0,0 +1,58 @@
package com.dfsek.terra.api.math.noise.samplers.noise.fractal;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public class PingPongSampler extends FractalNoiseFunction {
private double pingPongStrength = 2.0;
public PingPongSampler(int seed, NoiseSampler input) {
super(seed, input);
}
private static double pingPong(double t) {
t -= (int) (t * 0.5f) << 1;
return t < 1 ? t : 2 - t;
}
public void setPingPongStrength(double strength) {
this.pingPongStrength = strength;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
double sum = 0;
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double noise = pingPong((input.getNoiseSeeded(seed++, x, y) + 1) * pingPongStrength);
sum += (noise - 0.5) * 2 * amp;
amp *= lerp(1.0, noise, weightedStrength);
x *= lacunarity;
y *= lacunarity;
amp *= gain;
}
return sum;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
double sum = 0;
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double noise = pingPong((input.getNoiseSeeded(seed++, x, y, z) + 1) * pingPongStrength);
sum += (noise - 0.5) * 2 * amp;
amp *= lerp(1.0, noise, weightedStrength);
x *= lacunarity;
y *= lacunarity;
z *= lacunarity;
amp *= gain;
}
return sum;
}
}

View File

@@ -0,0 +1,47 @@
package com.dfsek.terra.api.math.noise.samplers.noise.fractal;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public class RidgedFractalSampler extends FractalNoiseFunction {
public RidgedFractalSampler(int seed, NoiseSampler input) {
super(seed, input);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
double sum = 0;
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double noise = fastAbs(input.getNoiseSeeded(seed++, x, y));
sum += (noise * -2 + 1) * amp;
amp *= lerp(1.0, 1 - noise, weightedStrength);
x *= lacunarity;
y *= lacunarity;
amp *= gain;
}
return sum;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
double sum = 0;
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double noise = fastAbs(input.getNoiseSeeded(seed++, x, y, z));
sum += (noise * -2 + 1) * amp;
amp *= lerp(1.0, 1 - noise, weightedStrength);
x *= lacunarity;
y *= lacunarity;
z *= lacunarity;
amp *= gain;
}
return sum;
}
}

View File

@@ -0,0 +1,39 @@
package com.dfsek.terra.api.math.noise.samplers.noise.random;
import com.dfsek.terra.api.math.noise.samplers.noise.NoiseFunction;
/**
* NoiseSampler implementation to provide random, normally distributed (Gaussian) noise.
*/
public class GaussianNoiseSampler extends NoiseFunction {
private final WhiteNoiseSampler whiteNoiseSampler; // Back with a white noise sampler.
public GaussianNoiseSampler(int seed) {
super(seed);
whiteNoiseSampler = new WhiteNoiseSampler(seed);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
double v1, v2, s;
do {
v1 = whiteNoiseSampler.getNoiseSeeded(seed++, x, y);
v2 = whiteNoiseSampler.getNoiseSeeded(seed++, x, y);
s = v1 * v1 + v2 * v2;
} while(s >= 1 || s == 0);
double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
return v1 * multiplier;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
double v1, v2, s;
do {
v1 = whiteNoiseSampler.getNoiseSeeded(seed++, x, y, z);
v2 = whiteNoiseSampler.getNoiseSeeded(seed++, x, y, z);
s = v1 * v1 + v2 * v2;
} while(s >= 1 || s == 0);
double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
return v1 * multiplier;
}
}

View File

@@ -0,0 +1,52 @@
package com.dfsek.terra.api.math.noise.samplers.noise.random;
import com.dfsek.terra.api.math.noise.samplers.noise.NoiseFunction;
/**
* NoiseSampler implementation to produce random, uniformly distributed (white) noise.
*/
public class WhiteNoiseSampler extends NoiseFunction {
private static final long POSITIVE_POW1 = 0b01111111111L << 52; // Bits that when applied to the exponent/sign section of a double, produce a positive number with a power of 1.
public WhiteNoiseSampler(int seed) {
super(seed);
}
public double getNoiseRaw(long seed) {
return (Double.longBitsToDouble((murmur64(seed) & 0x000fffffffffffffL) | POSITIVE_POW1) - 1.5) * 2;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
return (getNoiseUnmapped(seed, x, y) - 1.5) * 2;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
return (getNoiseUnmapped(seed, x, y, z) - 1.5) * 2;
}
public double getNoiseUnmapped(int seed, double x, double y, double z) {
long base = ((randomBits(seed, x, y, z)) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
return Double.longBitsToDouble(base);
}
public double getNoiseUnmapped(int seed, double x, double y) {
long base = (randomBits(seed, x, y) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
return Double.longBitsToDouble(base);
}
public long randomBits(int seed, double x, double y, double z) {
long hashX = Double.doubleToRawLongBits(x) ^ seed;
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
return murmur64(hash);
}
public long randomBits(int seed, double x, double y) {
long hashX = Double.doubleToRawLongBits(x) ^ seed;
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
return murmur64(hash);
}
}

View File

@@ -0,0 +1,266 @@
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
/**
* NoiseSampler implementation to provide OpenSimplex2 (Smooth Variant) noise.
*/
public class OpenSimplex2SSampler extends SimplexStyleSampler {
public OpenSimplex2SSampler(int seed) {
super(seed);
}
@Override
@SuppressWarnings("NumericOverflow")
public double getNoiseRaw(int seed, double x, double y) {
// 2D OpenSimplex2S case is a modified 2D simplex noise.
final double SQRT3 = 1.7320508075688772935274463415059;
final double G2 = (3 - SQRT3) / 6;
final double F2 = 0.5f * (SQRT3 - 1);
double s = (x + y) * F2;
x += s;
y += s;
int i = fastFloor(x);
int j = fastFloor(y);
double xi = x - i;
double yi = y - j;
i *= PRIME_X;
j *= PRIME_Y;
int i1 = i + PRIME_X;
int j1 = j + PRIME_Y;
double t = (xi + yi) * G2;
double x0 = xi - t;
double y0 = yi - t;
double a0 = (2.0 / 3.0) - x0 * x0 - y0 * y0;
double value = (a0 * a0) * (a0 * a0) * gradCoord(seed, i, j, x0, y0);
double a1 = 2 * (1 - 2 * G2) * (1 / G2 - 2) * t + ((-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a0);
double x1 = x0 - (1 - 2 * G2);
double y1 = y0 - (1 - 2 * G2);
value += (a1 * a1) * (a1 * a1) * gradCoord(seed, i1, j1, x1, y1);
// Nested conditionals were faster than compact bit logic/arithmetic.
double xmyi = xi - yi;
if(t > G2) {
if(xi + xmyi > 1) {
double x2 = x0 + (3 * G2 - 2);
double y2 = y0 + (3 * G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i + (PRIME_X << 1), j + PRIME_Y, x2, y2);
}
} else {
double x2 = x0 + G2;
double y2 = y0 + (G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i, j + PRIME_Y, x2, y2);
}
}
if(yi - xmyi > 1) {
double x3 = x0 + (3 * G2 - 1);
double y3 = y0 + (3 * G2 - 2);
double a3 = (2.0 / 3.0) - x3 * x3 - y3 * y3;
if(a3 > 0) {
value += (a3 * a3) * (a3 * a3) * gradCoord(seed, i + PRIME_X, j + (PRIME_Y << 1), x3, y3);
}
} else {
double x3 = x0 + (G2 - 1);
double y3 = y0 + G2;
double a3 = (2.0 / 3.0) - x3 * x3 - y3 * y3;
if(a3 > 0) {
value += (a3 * a3) * (a3 * a3) * gradCoord(seed, i + PRIME_X, j, x3, y3);
}
}
} else {
if(xi + xmyi < 0) {
double x2 = x0 + (1 - G2);
double y2 = y0 - G2;
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i - PRIME_X, j, x2, y2);
}
} else {
double x2 = x0 + (G2 - 1);
double y2 = y0 + G2;
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i + PRIME_X, j, x2, y2);
}
}
if(yi < xmyi) {
double x2 = x0 - G2;
double y2 = y0 - (G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i, j - PRIME_Y, x2, y2);
}
} else {
double x2 = x0 + G2;
double y2 = y0 + (G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i, j + PRIME_Y, x2, y2);
}
}
}
return value * 18.24196194486065;
}
@Override
@SuppressWarnings("NumericOverflow")
public double getNoiseRaw(int seed, double x, double y, double z) {
// 3D OpenSimplex2S case uses two offset rotated cube grids.
final double R3 = (2.0 / 3.0);
double r = (x + y + z) * R3; // Rotation, not skew
x = r - x;
y = r - y;
z = r - z;
int i = fastFloor(x);
int j = fastFloor(y);
int k = fastFloor(z);
double xi = x - i;
double yi = y - j;
double zi = z - k;
i *= PRIME_X;
j *= PRIME_Y;
k *= PRIME_Z;
int seed2 = seed + 1293373;
int xNMask = (int) (-0.5 - xi);
int yNMask = (int) (-0.5 - yi);
int zNMask = (int) (-0.5 - zi);
double x0 = xi + xNMask;
double y0 = yi + yNMask;
double z0 = zi + zNMask;
double a0 = 0.75 - x0 * x0 - y0 * y0 - z0 * z0;
double value = (a0 * a0) * (a0 * a0) * gradCoord(seed, i + (xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (zNMask & PRIME_Z), x0, y0,
z0);
double x1 = xi - 0.5;
double y1 = yi - 0.5;
double z1 = zi - 0.5;
double a1 = 0.75 - x1 * x1 - y1 * y1 - z1 * z1;
value += (a1 * a1) * (a1 * a1) * gradCoord(seed2, i + PRIME_X, j + PRIME_Y, k + PRIME_Z, x1, y1, z1);
double xAFlipMask0 = ((xNMask | 1) << 1) * x1;
double yAFlipMask0 = ((yNMask | 1) << 1) * y1;
double zAFlipMask0 = ((zNMask | 1) << 1) * z1;
double xAFlipMask1 = (-2 - (xNMask << 2)) * x1 - 1.0;
double yAFlipMask1 = (-2 - (yNMask << 2)) * y1 - 1.0;
double zAFlipMask1 = (-2 - (zNMask << 2)) * z1 - 1.0;
boolean skip5 = false;
double a2 = xAFlipMask0 + a0;
if(a2 > 0) {
double x2 = x0 - (xNMask | 1);
value += (a2 * a2) * (a2 * a2) * gradCoord(seed, i + (~xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (zNMask & PRIME_Z), x2, y0,
z0);
} else {
double a3 = yAFlipMask0 + zAFlipMask0 + a0;
if(a3 > 0) {
double y3 = y0 - (yNMask | 1);
double z3 = z0 - (zNMask | 1);
value += (a3 * a3) * (a3 * a3) * gradCoord(seed, i + (xNMask & PRIME_X), j + (~yNMask & PRIME_Y), k + (~zNMask & PRIME_Z), x0,
y3, z3);
}
double a4 = xAFlipMask1 + a1;
if(a4 > 0) {
double x4 = (xNMask | 1) + x1;
value += (a4 * a4) * (a4 * a4) * gradCoord(seed2, i + (xNMask & (PRIME_X << 1)), j + PRIME_Y, k + PRIME_Z, x4, y1, z1);
skip5 = true;
}
}
boolean skip9 = false;
double a6 = yAFlipMask0 + a0;
if(a6 > 0) {
double y6 = y0 - (yNMask | 1);
value += (a6 * a6) * (a6 * a6) * gradCoord(seed, i + (xNMask & PRIME_X), j + (~yNMask & PRIME_Y), k + (zNMask & PRIME_Z), x0, y6,
z0);
} else {
double a7 = xAFlipMask0 + zAFlipMask0 + a0;
if(a7 > 0) {
double x7 = x0 - (xNMask | 1);
double z7 = z0 - (zNMask | 1);
value += (a7 * a7) * (a7 * a7) * gradCoord(seed, i + (~xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (~zNMask & PRIME_Z), x7,
y0, z7);
}
double a8 = yAFlipMask1 + a1;
if(a8 > 0) {
double y8 = (yNMask | 1) + y1;
value += (a8 * a8) * (a8 * a8) * gradCoord(seed2, i + PRIME_X, j + (yNMask & (PRIME_Y << 1)), k + PRIME_Z, x1, y8, z1);
skip9 = true;
}
}
boolean skipD = false;
double aA = zAFlipMask0 + a0;
if(aA > 0) {
double zA = z0 - (zNMask | 1);
value += (aA * aA) * (aA * aA) * gradCoord(seed, i + (xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (~zNMask & PRIME_Z), x0, y0,
zA);
} else {
double aB = xAFlipMask0 + yAFlipMask0 + a0;
if(aB > 0) {
double xB = x0 - (xNMask | 1);
double yB = y0 - (yNMask | 1);
value += (aB * aB) * (aB * aB) * gradCoord(seed, i + (~xNMask & PRIME_X), j + (~yNMask & PRIME_Y), k + (zNMask & PRIME_Z), xB,
yB, z0);
}
double aC = zAFlipMask1 + a1;
if(aC > 0) {
double zC = (zNMask | 1) + z1;
value += (aC * aC) * (aC * aC) * gradCoord(seed2, i + PRIME_X, j + PRIME_Y, k + (zNMask & (PRIME_Z << 1)), x1, y1, zC);
skipD = true;
}
}
if(!skip5) {
double a5 = yAFlipMask1 + zAFlipMask1 + a1;
if(a5 > 0) {
double y5 = (yNMask | 1) + y1;
double z5 = (zNMask | 1) + z1;
value += (a5 * a5) * (a5 * a5) * gradCoord(seed2, i + PRIME_X, j + (yNMask & (PRIME_Y << 1)), k + (zNMask & (PRIME_Z << 1)),
x1, y5, z5);
}
}
if(!skip9) {
double a9 = xAFlipMask1 + zAFlipMask1 + a1;
if(a9 > 0) {
double x9 = (xNMask | 1) + x1;
double z9 = (zNMask | 1) + z1;
value += (a9 * a9) * (a9 * a9) * gradCoord(seed2, i + (xNMask & (PRIME_X << 1)), j + PRIME_Y, k + (zNMask & (PRIME_Z << 1)), x9,
y1, z9);
}
}
if(!skipD) {
double aD = xAFlipMask1 + yAFlipMask1 + a1;
if(aD > 0) {
double xD = (xNMask | 1) + x1;
double yD = (yNMask | 1) + y1;
value += (aD * aD) * (aD * aD) * gradCoord(seed2, i + (xNMask & (PRIME_X << 1)), j + (yNMask & (PRIME_Y << 1)), k + PRIME_Z,
xD, yD, z1);
}
}
return value * 9.046026385208288;
}
}

View File

@@ -0,0 +1,155 @@
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
/**
* NoiseSampler implementation to provide OpenSimplex2 noise.
*/
public class OpenSimplex2Sampler extends SimplexStyleSampler {
private static final double SQRT3 = 1.7320508075688772935274463415059;
public OpenSimplex2Sampler(int seed) {
super(seed);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
// 2D OpenSimplex2 case uses the same algorithm as ordinary Simplex.
final double G2 = (3 - SQRT3) / 6;
final double F2 = 0.5f * (SQRT3 - 1);
double s = (x + y) * F2;
x += s;
y += s;
int i = fastFloor(x);
int j = fastFloor(y);
double xi = x - i;
double yi = y - j;
double t = (xi + yi) * G2;
double x0 = xi - t;
double y0 = yi - t;
i *= PRIME_X;
j *= PRIME_Y;
double n0, n1, n2;
double a = 0.5 - x0 * x0 - y0 * y0;
if(a <= 0) n0 = 0;
else {
n0 = (a * a) * (a * a) * gradCoord(seed, i, j, x0, y0);
}
double c = 2 * (1 - 2 * G2) * (1 / G2 - 2) * t + ((-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a);
if(c <= 0) n2 = 0;
else {
double x2 = x0 + (2 * G2 - 1);
double y2 = y0 + (2 * G2 - 1);
n2 = (c * c) * (c * c) * gradCoord(seed, i + PRIME_X, j + PRIME_Y, x2, y2);
}
if(y0 > x0) {
double x1 = x0 + G2;
double y1 = y0 + (G2 - 1);
double b = 0.5 - x1 * x1 - y1 * y1;
if(b <= 0) n1 = 0;
else {
n1 = (b * b) * (b * b) * gradCoord(seed, i, j + PRIME_Y, x1, y1);
}
} else {
double x1 = x0 + (G2 - 1);
double y1 = y0 + G2;
double b = 0.5 - x1 * x1 - y1 * y1;
if(b <= 0) n1 = 0;
else {
n1 = (b * b) * (b * b) * gradCoord(seed, i + PRIME_X, j, x1, y1);
}
}
return (n0 + n1 + n2) * 99.83685446303647f;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
// 3D OpenSimplex2Sampler case uses two offset rotated cube grids.
final double R3 = (2.0 / 3.0);
double r = (x + y + z) * R3; // Rotation, not skew
x = r - x;
y = r - y;
z = r - z;
int i = fastRound(x);
int j = fastRound(y);
int k = fastRound(z);
double x0 = x - i;
double y0 = y - j;
double z0 = z - k;
int xNSign = (int) (-1.0 - x0) | 1;
int yNSign = (int) (-1.0 - y0) | 1;
int zNSign = (int) (-1.0 - z0) | 1;
double ax0 = xNSign * -x0;
double ay0 = yNSign * -y0;
double az0 = zNSign * -z0;
i *= PRIME_X;
j *= PRIME_Y;
k *= PRIME_Z;
double value = 0;
double a = (0.6f - x0 * x0) - (y0 * y0 + z0 * z0);
for(int l = 0; ; l++) {
if(a > 0) {
value += (a * a) * (a * a) * gradCoord(seed, i, j, k, x0, y0, z0);
}
if(ax0 >= ay0 && ax0 >= az0) {
double b = a + ax0 + ax0;
if(b > 1) {
b -= 1;
value += (b * b) * (b * b) * gradCoord(seed, i - xNSign * PRIME_X, j, k, x0 + xNSign, y0, z0);
}
} else if(ay0 > ax0 && ay0 >= az0) {
double b = a + ay0 + ay0;
if(b > 1) {
b -= 1;
value += (b * b) * (b * b) * gradCoord(seed, i, j - yNSign * PRIME_Y, k, x0, y0 + yNSign, z0);
}
} else {
double b = a + az0 + az0;
if(b > 1) {
b -= 1;
value += (b * b) * (b * b) * gradCoord(seed, i, j, k - zNSign * PRIME_Z, x0, y0, z0 + zNSign);
}
}
if(l == 1) break;
ax0 = 0.5 - ax0;
ay0 = 0.5 - ay0;
az0 = 0.5 - az0;
x0 = xNSign * ax0;
y0 = yNSign * ay0;
z0 = zNSign * az0;
a += (0.75 - ax0) - (ay0 + az0);
i += (xNSign >> 1) & PRIME_X;
j += (yNSign >> 1) & PRIME_Y;
k += (zNSign >> 1) & PRIME_Z;
xNSign = -xNSign;
yNSign = -yNSign;
zNSign = -zNSign;
seed = ~seed;
}
return value * 32.69428253173828125;
}
}

View File

@@ -0,0 +1,69 @@
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
/**
* NoiseSampler implementation to provide Perlin Noise.
*/
public class PerlinSampler extends SimplexStyleSampler {
public PerlinSampler(int seed) {
super(seed);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
int x0 = fastFloor(x);
int y0 = fastFloor(y);
double xd0 = x - x0;
double yd0 = y - y0;
double xd1 = xd0 - 1;
double yd1 = yd0 - 1;
double xs = interpQuintic(xd0);
double ys = interpQuintic(yd0);
x0 *= PRIME_X;
y0 *= PRIME_Y;
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
double xf0 = lerp(gradCoord(seed, x0, y0, xd0, yd0), gradCoord(seed, x1, y0, xd1, yd0), xs);
double xf1 = lerp(gradCoord(seed, x0, y1, xd0, yd1), gradCoord(seed, x1, y1, xd1, yd1), xs);
return lerp(xf0, xf1, ys) * 1.4247691104677813;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
int x0 = fastFloor(x);
int y0 = fastFloor(y);
int z0 = fastFloor(z);
double xd0 = x - x0;
double yd0 = y - y0;
double zd0 = z - z0;
double xd1 = xd0 - 1;
double yd1 = yd0 - 1;
double zd1 = zd0 - 1;
double xs = interpQuintic(xd0);
double ys = interpQuintic(yd0);
double zs = interpQuintic(zd0);
x0 *= PRIME_X;
y0 *= PRIME_Y;
z0 *= PRIME_Z;
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
int z1 = z0 + PRIME_Z;
double xf00 = lerp(gradCoord(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord(seed, x1, y0, z0, xd1, yd0, zd0), xs);
double xf10 = lerp(gradCoord(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord(seed, x1, y1, z0, xd1, yd1, zd0), xs);
double xf01 = lerp(gradCoord(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord(seed, x1, y0, z1, xd1, yd0, zd1), xs);
double xf11 = lerp(gradCoord(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord(seed, x1, y1, z1, xd1, yd1, zd1), xs);
double yf0 = lerp(xf00, xf10, ys);
double yf1 = lerp(xf01, xf11, ys);
return lerp(yf0, yf1, zs) * 0.964921414852142333984375;
}
}

View File

@@ -0,0 +1,243 @@
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
public class SimplexSampler extends SimplexStyleSampler {
private static final Double2[] GRAD_2D = {
new Double2(-1, -1), new Double2(1, -1), new Double2(-1, 1), new Double2(1, 1),
new Double2(0, -1), new Double2(-1, 0), new Double2(0, 1), new Double2(1, 0),
};
private static final Double3[] GRAD_3D = {
new Double3(1, 1, 0), new Double3(-1, 1, 0), new Double3(1, -1, 0), new Double3(-1, -1, 0),
new Double3(1, 0, 1), new Double3(-1, 0, 1), new Double3(1, 0, -1), new Double3(-1, 0, -1),
new Double3(0, 1, 1), new Double3(0, -1, 1), new Double3(0, 1, -1), new Double3(0, -1, -1),
new Double3(1, 1, 0), new Double3(0, -1, 1), new Double3(-1, 1, 0), new Double3(0, -1, -1),
};
private static final double F2 = 1.0 / 2.0;
private static final double F3 = (1.0 / 3.0);
private static final double G2 = 1.0 / 4.0;
private static final double G3 = (1.0 / 6.0);
private static final double G33 = G3 * 3 - 1;
private static final int X_PRIME = 1619;
private static final int Y_PRIME = 31337;
private static final int Z_PRIME = 6971;
public SimplexSampler(int seed) {
super(seed);
}
private static double gradCoord3D(int seed, int x, int y, int z, double xd, double yd, double zd) {
int hash = seed;
hash ^= X_PRIME * x;
hash ^= Y_PRIME * y;
hash ^= Z_PRIME * z;
hash = hash * hash * hash * 60493;
hash = (hash >> 13) ^ hash;
Double3 g = GRAD_3D[hash & 15];
return xd * g.x + yd * g.y + zd * g.z;
}
private static double gradCoord2D(int seed, int x, int y, double xd, double yd) {
int hash = seed;
hash ^= X_PRIME * x;
hash ^= Y_PRIME * y;
hash = hash * hash * hash * 60493;
hash = (hash >> 13) ^ hash;
Double2 g = GRAD_2D[hash & 7];
return xd * g.x + yd * g.y;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
double t = (x + y) * F2;
int i = fastFloor(x + t);
int j = fastFloor(y + t);
t = (i + j) * G2;
double X0 = i - t;
double Y0 = j - t;
double x0 = x - X0;
double y0 = y - Y0;
int i1, j1;
if(x0 > y0) {
i1 = 1;
j1 = 0;
} else {
i1 = 0;
j1 = 1;
}
double x1 = x0 - i1 + G2;
double y1 = y0 - j1 + G2;
double x2 = x0 - 1 + F2;
double y2 = y0 - 1 + F2;
double n0, n1, n2;
t = 0.5 - x0 * x0 - y0 * y0;
if(t < 0) {
n0 = 0;
} else {
t *= t;
n0 = t * t * gradCoord2D(seed, i, j, x0, y0);
}
t = 0.5 - x1 * x1 - y1 * y1;
if(t < 0) {
n1 = 0;
} else {
t *= t;
n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1);
}
t = 0.5 - x2 * x2 - y2 * y2;
if(t < 0) {
n2 = 0;
} else {
t *= t;
n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2);
}
return 50 * (n0 + n1 + n2);
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
double t = (x + y + z) * F3;
int i = fastFloor(x + t);
int j = fastFloor(y + t);
int k = fastFloor(z + t);
t = (i + j + k) * G3;
double x0 = x - (i - t);
double y0 = y - (j - t);
double z0 = z - (k - t);
int i1, j1, k1;
int i2, j2, k2;
if(x0 >= y0) {
if(y0 >= z0) {
i1 = 1;
j1 = 0;
k1 = 0;
i2 = 1;
j2 = 1;
k2 = 0;
} else if(x0 >= z0) {
i1 = 1;
j1 = 0;
k1 = 0;
i2 = 1;
j2 = 0;
k2 = 1;
} else // x0 < z0
{
i1 = 0;
j1 = 0;
k1 = 1;
i2 = 1;
j2 = 0;
k2 = 1;
}
} else // x0 < y0
{
if(y0 < z0) {
i1 = 0;
j1 = 0;
k1 = 1;
i2 = 0;
j2 = 1;
k2 = 1;
} else if(x0 < z0) {
i1 = 0;
j1 = 1;
k1 = 0;
i2 = 0;
j2 = 1;
k2 = 1;
} else // x0 >= z0
{
i1 = 0;
j1 = 1;
k1 = 0;
i2 = 1;
j2 = 1;
k2 = 0;
}
}
double x1 = x0 - i1 + G3;
double y1 = y0 - j1 + G3;
double z1 = z0 - k1 + G3;
double x2 = x0 - i2 + F3;
double y2 = y0 - j2 + F3;
double z2 = z0 - k2 + F3;
double x3 = x0 + G33;
double y3 = y0 + G33;
double z3 = z0 + G33;
double n0, n1, n2, n3;
t = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
if(t < 0) n0 = 0;
else {
t *= t;
n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0);
}
t = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
if(t < 0) {
n1 = 0;
} else {
t *= t;
n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1);
}
t = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
if(t < 0) {
n2 = 0;
} else {
t *= t;
n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2);
}
t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
if(t < 0) {
n3 = 0;
} else {
t *= t;
n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3);
}
return 32 * (n0 + n1 + n2 + n3);
}
private static class Double2 {
public final double x, y;
public Double2(double x, double y) {
this.x = x;
this.y = y;
}
}
private static class Double3 {
public final double x, y, z;
public Double3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
}

View File

@@ -0,0 +1,100 @@
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
import com.dfsek.terra.api.math.noise.samplers.noise.NoiseFunction;
/**
* Abstract NoiseSampler implementation for simplex-style noise functions.
*/
public abstract class SimplexStyleSampler extends NoiseFunction {
protected static final double[] GRADIENTS_2_D = {
0.130526192220052d, 0.99144486137381d, 0.38268343236509d, 0.923879532511287d, 0.608761429008721d, 0.793353340291235d,
0.793353340291235d, 0.608761429008721d, 0.923879532511287d, 0.38268343236509d, 0.99144486137381d, 0.130526192220051d,
0.99144486137381d, -0.130526192220051d, 0.923879532511287d, -0.38268343236509d, 0.793353340291235d, -0.60876142900872d,
0.608761429008721d, -0.793353340291235d, 0.38268343236509d, -0.923879532511287d, 0.130526192220052d, -0.99144486137381d,
-0.130526192220052d, -0.99144486137381d, -0.38268343236509d, -0.923879532511287d, -0.608761429008721d, -0.793353340291235d,
-0.793353340291235d, -0.608761429008721d, -0.923879532511287d, -0.38268343236509d, -0.99144486137381d, -0.130526192220052d,
-0.99144486137381d, 0.130526192220051d, -0.923879532511287d, 0.38268343236509d, -0.793353340291235d, 0.608761429008721d,
-0.608761429008721d, 0.793353340291235d, -0.38268343236509d, 0.923879532511287d, -0.130526192220052d, 0.99144486137381d,
0.130526192220052d, 0.99144486137381d, 0.38268343236509d, 0.923879532511287d, 0.608761429008721d, 0.793353340291235d,
0.793353340291235d, 0.608761429008721d, 0.923879532511287d, 0.38268343236509d, 0.99144486137381d, 0.130526192220051d,
0.99144486137381d, -0.130526192220051d, 0.923879532511287d, -0.38268343236509d, 0.793353340291235d, -0.60876142900872d,
0.608761429008721d, -0.793353340291235d, 0.38268343236509d, -0.923879532511287d, 0.130526192220052d, -0.99144486137381d,
-0.130526192220052d, -0.99144486137381d, -0.38268343236509d, -0.923879532511287d, -0.608761429008721d, -0.793353340291235d,
-0.793353340291235d, -0.608761429008721d, -0.923879532511287d, -0.38268343236509d, -0.99144486137381d, -0.130526192220052d,
-0.99144486137381d, 0.130526192220051d, -0.923879532511287d, 0.38268343236509d, -0.793353340291235d, 0.608761429008721d,
-0.608761429008721d, 0.793353340291235d, -0.38268343236509d, 0.923879532511287d, -0.130526192220052d, 0.99144486137381d,
0.130526192220052d, 0.99144486137381d, 0.38268343236509d, 0.923879532511287d, 0.608761429008721d, 0.793353340291235d,
0.793353340291235d, 0.608761429008721d, 0.923879532511287d, 0.38268343236509d, 0.99144486137381d, 0.130526192220051d,
0.99144486137381d, -0.130526192220051d, 0.923879532511287d, -0.38268343236509d, 0.793353340291235d, -0.60876142900872d,
0.608761429008721d, -0.793353340291235d, 0.38268343236509d, -0.923879532511287d, 0.130526192220052d, -0.99144486137381d,
-0.130526192220052d, -0.99144486137381d, -0.38268343236509d, -0.923879532511287d, -0.608761429008721d, -0.793353340291235d,
-0.793353340291235d, -0.608761429008721d, -0.923879532511287d, -0.38268343236509d, -0.99144486137381d, -0.130526192220052d,
-0.99144486137381d, 0.130526192220051d, -0.923879532511287d, 0.38268343236509d, -0.793353340291235d, 0.608761429008721d,
-0.608761429008721d, 0.793353340291235d, -0.38268343236509d, 0.923879532511287d, -0.130526192220052d, 0.99144486137381d,
0.130526192220052d, 0.99144486137381d, 0.38268343236509d, 0.923879532511287d, 0.608761429008721d, 0.793353340291235d,
0.793353340291235d, 0.608761429008721d, 0.923879532511287d, 0.38268343236509d, 0.99144486137381d, 0.130526192220051d,
0.99144486137381d, -0.130526192220051d, 0.923879532511287d, -0.38268343236509d, 0.793353340291235d, -0.60876142900872d,
0.608761429008721d, -0.793353340291235d, 0.38268343236509d, -0.923879532511287d, 0.130526192220052d, -0.99144486137381d,
-0.130526192220052d, -0.99144486137381d, -0.38268343236509d, -0.923879532511287d, -0.608761429008721d, -0.793353340291235d,
-0.793353340291235d, -0.608761429008721d, -0.923879532511287d, -0.38268343236509d, -0.99144486137381d, -0.130526192220052d,
-0.99144486137381d, 0.130526192220051d, -0.923879532511287d, 0.38268343236509d, -0.793353340291235d, 0.608761429008721d,
-0.608761429008721d, 0.793353340291235d, -0.38268343236509d, 0.923879532511287d, -0.130526192220052d, 0.99144486137381d,
0.130526192220052d, 0.99144486137381d, 0.38268343236509d, 0.923879532511287d, 0.608761429008721d, 0.793353340291235d,
0.793353340291235d, 0.608761429008721d, 0.923879532511287d, 0.38268343236509d, 0.99144486137381d, 0.130526192220051d,
0.99144486137381d, -0.130526192220051d, 0.923879532511287d, -0.38268343236509d, 0.793353340291235d, -0.60876142900872d,
0.608761429008721d, -0.793353340291235d, 0.38268343236509d, -0.923879532511287d, 0.130526192220052d, -0.99144486137381d,
-0.130526192220052d, -0.99144486137381d, -0.38268343236509d, -0.923879532511287d, -0.608761429008721d, -0.793353340291235d,
-0.793353340291235d, -0.608761429008721d, -0.923879532511287d, -0.38268343236509d, -0.99144486137381d, -0.130526192220052d,
-0.99144486137381d, 0.130526192220051d, -0.923879532511287d, 0.38268343236509d, -0.793353340291235d, 0.608761429008721d,
-0.608761429008721d, 0.793353340291235d, -0.38268343236509d, 0.923879532511287d, -0.130526192220052d, 0.99144486137381d,
0.38268343236509d, 0.923879532511287d, 0.923879532511287d, 0.38268343236509d, 0.923879532511287d, -0.38268343236509d,
0.38268343236509d, -0.923879532511287d, -0.38268343236509d, -0.923879532511287d, -0.923879532511287d, -0.38268343236509d,
-0.923879532511287d, 0.38268343236509d, -0.38268343236509d, 0.923879532511287d,
};
protected static final double[] GRADIENTS_3D = {
0, 1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0,
1, 0, 1, 0, -1, 0, 1, 0, 1, 0, -1, 0, -1, 0, -1, 0,
1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0, 0,
0, 1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0,
1, 0, 1, 0, -1, 0, 1, 0, 1, 0, -1, 0, -1, 0, -1, 0,
1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0, 0,
0, 1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0,
1, 0, 1, 0, -1, 0, 1, 0, 1, 0, -1, 0, -1, 0, -1, 0,
1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0, 0,
0, 1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0,
1, 0, 1, 0, -1, 0, 1, 0, 1, 0, -1, 0, -1, 0, -1, 0,
1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0, 0,
0, 1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0,
1, 0, 1, 0, -1, 0, 1, 0, 1, 0, -1, 0, -1, 0, -1, 0,
1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, -1, -1, 0, 0,
1, 1, 0, 0, 0, -1, 1, 0, -1, 1, 0, 0, 0, -1, -1, 0
};
public SimplexStyleSampler(int seed) {
super(seed);
}
protected static double gradCoord(int seed, int xPrimed, int yPrimed, double xd, double yd) {
int hash = hash(seed, xPrimed, yPrimed);
hash ^= hash >> 15;
hash &= 127 << 1;
double xg = GRADIENTS_2_D[hash];
double yg = GRADIENTS_2_D[hash | 1];
return xd * xg + yd * yg;
}
protected static double gradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, double xd, double yd, double zd) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
hash ^= hash >> 15;
hash &= 63 << 2;
double xg = GRADIENTS_3D[hash];
double yg = GRADIENTS_3D[hash | 1];
double zg = GRADIENTS_3D[hash | 2];
return xd * xg + yd * yg + zd * zg;
}
}

View File

@@ -0,0 +1,104 @@
package com.dfsek.terra.api.math.noise.samplers.noise.value;
public class ValueCubicSampler extends ValueStyleNoise {
public ValueCubicSampler(int seed) {
super(seed);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
int x1 = fastFloor(x);
int y1 = fastFloor(y);
double xs = x - x1;
double ys = y - y1;
x1 *= PRIME_X;
y1 *= PRIME_Y;
int x0 = x1 - PRIME_X;
int y0 = y1 - PRIME_Y;
int x2 = x1 + PRIME_X;
int y2 = y1 + PRIME_Y;
int x3 = x1 + (PRIME_X << 1);
int y3 = y1 + (PRIME_Y << 1);
return cubicLerp(
cubicLerp(valCoord(seed, x0, y0), valCoord(seed, x1, y0), valCoord(seed, x2, y0), valCoord(seed, x3, y0),
xs),
cubicLerp(valCoord(seed, x0, y1), valCoord(seed, x1, y1), valCoord(seed, x2, y1), valCoord(seed, x3, y1),
xs),
cubicLerp(valCoord(seed, x0, y2), valCoord(seed, x1, y2), valCoord(seed, x2, y2), valCoord(seed, x3, y2),
xs),
cubicLerp(valCoord(seed, x0, y3), valCoord(seed, x1, y3), valCoord(seed, x2, y3), valCoord(seed, x3, y3),
xs),
ys) * (1 / (1.5 * 1.5));
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
int x1 = fastFloor(x);
int y1 = fastFloor(y);
int z1 = fastFloor(z);
double xs = x - x1;
double ys = y - y1;
double zs = z - z1;
x1 *= PRIME_X;
y1 *= PRIME_Y;
z1 *= PRIME_Z;
int x0 = x1 - PRIME_X;
int y0 = y1 - PRIME_Y;
int z0 = z1 - PRIME_Z;
int x2 = x1 + PRIME_X;
int y2 = y1 + PRIME_Y;
int z2 = z1 + PRIME_Z;
int x3 = x1 + (PRIME_X << 1);
int y3 = y1 + (PRIME_Y << 1);
int z3 = z1 + (PRIME_Z << 1);
return cubicLerp(
cubicLerp(
cubicLerp(valCoord(seed, x0, y0, z0), valCoord(seed, x1, y0, z0), valCoord(seed, x2, y0, z0),
valCoord(seed, x3, y0, z0), xs),
cubicLerp(valCoord(seed, x0, y1, z0), valCoord(seed, x1, y1, z0), valCoord(seed, x2, y1, z0),
valCoord(seed, x3, y1, z0), xs),
cubicLerp(valCoord(seed, x0, y2, z0), valCoord(seed, x1, y2, z0), valCoord(seed, x2, y2, z0),
valCoord(seed, x3, y2, z0), xs),
cubicLerp(valCoord(seed, x0, y3, z0), valCoord(seed, x1, y3, z0), valCoord(seed, x2, y3, z0),
valCoord(seed, x3, y3, z0), xs),
ys),
cubicLerp(
cubicLerp(valCoord(seed, x0, y0, z1), valCoord(seed, x1, y0, z1), valCoord(seed, x2, y0, z1),
valCoord(seed, x3, y0, z1), xs),
cubicLerp(valCoord(seed, x0, y1, z1), valCoord(seed, x1, y1, z1), valCoord(seed, x2, y1, z1),
valCoord(seed, x3, y1, z1), xs),
cubicLerp(valCoord(seed, x0, y2, z1), valCoord(seed, x1, y2, z1), valCoord(seed, x2, y2, z1),
valCoord(seed, x3, y2, z1), xs),
cubicLerp(valCoord(seed, x0, y3, z1), valCoord(seed, x1, y3, z1), valCoord(seed, x2, y3, z1),
valCoord(seed, x3, y3, z1), xs),
ys),
cubicLerp(
cubicLerp(valCoord(seed, x0, y0, z2), valCoord(seed, x1, y0, z2), valCoord(seed, x2, y0, z2),
valCoord(seed, x3, y0, z2), xs),
cubicLerp(valCoord(seed, x0, y1, z2), valCoord(seed, x1, y1, z2), valCoord(seed, x2, y1, z2),
valCoord(seed, x3, y1, z2), xs),
cubicLerp(valCoord(seed, x0, y2, z2), valCoord(seed, x1, y2, z2), valCoord(seed, x2, y2, z2),
valCoord(seed, x3, y2, z2), xs),
cubicLerp(valCoord(seed, x0, y3, z2), valCoord(seed, x1, y3, z2), valCoord(seed, x2, y3, z2),
valCoord(seed, x3, y3, z2), xs),
ys),
cubicLerp(
cubicLerp(valCoord(seed, x0, y0, z3), valCoord(seed, x1, y0, z3), valCoord(seed, x2, y0, z3),
valCoord(seed, x3, y0, z3), xs),
cubicLerp(valCoord(seed, x0, y1, z3), valCoord(seed, x1, y1, z3), valCoord(seed, x2, y1, z3),
valCoord(seed, x3, y1, z3), xs),
cubicLerp(valCoord(seed, x0, y2, z3), valCoord(seed, x1, y2, z3), valCoord(seed, x2, y2, z3),
valCoord(seed, x3, y2, z3), xs),
cubicLerp(valCoord(seed, x0, y3, z3), valCoord(seed, x1, y3, z3), valCoord(seed, x2, y3, z3),
valCoord(seed, x3, y3, z3), xs),
ys),
zs) * (1 / (1.5 * 1.5 * 1.5));
}
}

View File

@@ -0,0 +1,54 @@
package com.dfsek.terra.api.math.noise.samplers.noise.value;
public class ValueSampler extends ValueStyleNoise {
public ValueSampler(int seed) {
super(seed);
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
int x0 = fastFloor(x);
int y0 = fastFloor(y);
double xs = interpHermite(x - x0);
double ys = interpHermite(y - y0);
x0 *= PRIME_X;
y0 *= PRIME_Y;
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
double xf0 = lerp(valCoord(seed, x0, y0), valCoord(seed, x1, y0), xs);
double xf1 = lerp(valCoord(seed, x0, y1), valCoord(seed, x1, y1), xs);
return lerp(xf0, xf1, ys);
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
int x0 = fastFloor(x);
int y0 = fastFloor(y);
int z0 = fastFloor(z);
double xs = interpHermite(x - x0);
double ys = interpHermite(y - y0);
double zs = interpHermite(z - z0);
x0 *= PRIME_X;
y0 *= PRIME_Y;
z0 *= PRIME_Z;
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
int z1 = z0 + PRIME_Z;
double xf00 = lerp(valCoord(seed, x0, y0, z0), valCoord(seed, x1, y0, z0), xs);
double xf10 = lerp(valCoord(seed, x0, y1, z0), valCoord(seed, x1, y1, z0), xs);
double xf01 = lerp(valCoord(seed, x0, y0, z1), valCoord(seed, x1, y0, z1), xs);
double xf11 = lerp(valCoord(seed, x0, y1, z1), valCoord(seed, x1, y1, z1), xs);
double yf0 = lerp(xf00, xf10, ys);
double yf1 = lerp(xf01, xf11, ys);
return lerp(yf0, yf1, zs);
}
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra.api.math.noise.samplers.noise.value;
import com.dfsek.terra.api.math.noise.samplers.noise.NoiseFunction;
public abstract class ValueStyleNoise extends NoiseFunction {
public ValueStyleNoise(int seed) {
super(seed);
}
protected static double valCoord(int seed, int xPrimed, int yPrimed) {
int hash = hash(seed, xPrimed, yPrimed);
hash *= hash;
hash ^= hash << 19;
return hash * (1 / 2147483648.0);
}
protected static double valCoord(int seed, int xPrimed, int yPrimed, int zPrimed) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
hash *= hash;
hash ^= hash << 19;
return hash * (1 / 2147483648.0);
}
}

View File

@@ -0,0 +1,27 @@
package com.dfsek.terra.api.math.paralithic;
import com.dfsek.paralithic.functions.dynamic.DynamicFunction;
public class BlankFunction implements DynamicFunction {
private final int args;
public BlankFunction(int args) {
this.args = args;
}
@Override
public int getArgNumber() {
return args;
}
@Override
public double eval(double... d) {
return 0;
}
@Override
public boolean isStateless() {
return true;
}
}

View File

@@ -0,0 +1,44 @@
package com.dfsek.terra.api.math.paralithic.defined;
import com.dfsek.paralithic.Expression;
import com.dfsek.paralithic.eval.parser.Parser;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.dynamic.DynamicFunction;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
public class UserDefinedFunction implements DynamicFunction {
private final Expression expression;
private final int args;
protected UserDefinedFunction(Expression expression, int args) {
this.expression = expression;
this.args = args;
}
@Override
public double eval(double... args) {
return expression.evaluate(args);
}
@Override
public boolean isStateless() {
return true;
}
@Override
public int getArgNumber() {
return args;
}
public static UserDefinedFunction newInstance(FunctionTemplate template, Parser parser, Scope parent) throws ParseException {
Scope functionScope = new Scope().withParent(parent);
template.getArgs().forEach(functionScope::addInvocationVariable);
return new UserDefinedFunction(parser.parse(template.getFunction(), functionScope), template.getArgs().size());
}
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.math.paralithic.noise;
import com.dfsek.paralithic.functions.dynamic.DynamicFunction;
public interface NoiseFunction extends DynamicFunction {
}

View File

@@ -0,0 +1,55 @@
package com.dfsek.terra.api.math.paralithic.noise;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.util.hash.HashMapDoubleDouble;
public class NoiseFunction2 implements NoiseFunction {
private final NoiseSampler gen;
private final Cache cache = new Cache();
public NoiseFunction2(NoiseSampler gen) {
this.gen = gen;
}
@Override
public int getArgNumber() {
return 2;
}
@Override
public double eval(double... args) {
return cache.get(gen, args[0], args[1]);
}
@Override
public boolean isStateless() {
return true;
}
private static class Cache extends HashMapDoubleDouble {
private static final long serialVersionUID = 8915092734723467010L;
private static final int cacheSize = 384;
public Cache() {
super(cacheSize);
}
public double get(NoiseSampler noise, double x, double z) {
double xx = x >= 0 ? x * 2 : x * -2 - 1;
double zz = z >= 0 ? z * 2 : z * -2 - 1;
double key = (xx >= zz) ? (xx * xx + xx + zz) : (zz * zz + xx);
double value = this.get(key);
if(this.size() > cacheSize) {
this.clear();
}
return (value == 4.9E-324D ? addAndReturn(noise.getNoise(x, z), key) : value);
}
private synchronized double addAndReturn(double value, double key) {
this.put(key, value);
return value;
}
}
}

View File

@@ -0,0 +1,26 @@
package com.dfsek.terra.api.math.paralithic.noise;
import com.dfsek.terra.api.math.noise.NoiseSampler;
public class NoiseFunction3 implements NoiseFunction {
private final NoiseSampler gen;
public NoiseFunction3(NoiseSampler gen) {
this.gen = gen;
}
@Override
public int getArgNumber() {
return 3;
}
@Override
public double eval(double... args) {
return gen.getNoise(args[0], args[1], args[2]);
}
@Override
public boolean isStateless() {
return true;
}
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.math.pixel;
public class Distribution {
public Distribution(Rectangle bound, int numPoints, double minRad) {
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.math.pixel;
import com.dfsek.terra.api.math.vector.Vector2;
import java.util.Set;
@SuppressWarnings("unused")
public abstract class Polygon {
public abstract Set<Vector2> getContainedPixels();
}

View File

@@ -0,0 +1,35 @@
package com.dfsek.terra.api.math.pixel;
import com.dfsek.terra.api.math.vector.Vector2;
import net.jafama.FastMath;
import java.util.HashSet;
import java.util.Set;
@SuppressWarnings("unused")
public class Rectangle extends Polygon {
private final Vector2 min;
private final Vector2 max;
public Rectangle(Vector2 min, Vector2 max) {
this.max = new Vector2(FastMath.min(min.getX(), max.getX()), FastMath.min(min.getZ(), max.getZ()));
this.min = new Vector2(FastMath.max(min.getX(), max.getX()), FastMath.max(min.getZ(), max.getZ()));
}
public Rectangle(Vector2 center, double xRadius, double zRadius) {
Vector2 rad = new Vector2(xRadius, zRadius);
this.min = center.clone().subtract(rad);
this.max = center.clone().add(rad);
}
@Override
public Set<Vector2> getContainedPixels() {
Set<Vector2> pixels = new HashSet<>();
for(int x = (int) min.getX(); x <= max.getX(); x++) {
for(int z = (int) min.getZ(); z <= max.getZ(); z++) {
pixels.add(new Vector2(x, z));
}
}
return pixels;
}
}

View File

@@ -0,0 +1,178 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.world.World;
import java.util.Objects;
public class Location implements Cloneable {
private World world;
private Vector3 vector;
private double pitch;
private double yaw;
public Location(World w, double x, double y, double z) {
this.world = w;
this.vector = new Vector3(x, y, z);
}
public Location(World w, Vector3 vector) {
this.world = w;
this.vector = vector;
}
public void setWorld(World world) {
this.world = world;
}
public Vector3 getVector() {
return vector;
}
public void setVector(Vector3 vector) {
this.vector = vector;
}
@Override
public Location clone() {
try {
Location other = (Location) super.clone();
other.setVector(other.getVector().clone());
return other;
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
public int getBlockX() {
return vector.getBlockX();
}
public int getBlockY() {
return vector.getBlockY();
}
public int getBlockZ() {
return vector.getBlockZ();
}
public double getY() {
return vector.getY();
}
public Location setY(double y) {
vector.setY(y);
return this;
}
public double getX() {
return vector.getX();
}
public Location setX(double x) {
vector.setX(x);
return this;
}
public double getZ() {
return vector.getZ();
}
public Location setZ(double z) {
vector.setZ(z);
return this;
}
public World getWorld() {
return world;
}
public Location add(double x, double y, double z) {
vector.add(x, y, z);
return this;
}
public Block getBlock() {
return world.getBlockAt(this);
}
public Location subtract(int x, int y, int z) {
vector.subtract(x, y, z);
return this;
}
public Location add(Vector3 add) {
vector.add(add);
return this;
}
public Location add(Location add) {
vector.add(add.toVector());
return this;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Location)) {
return false;
}
final Location other = (Location) obj;
World world = this.world;
World otherWorld = other.world;
if(!Objects.equals(world, otherWorld)) {
return false;
}
if(Double.doubleToLongBits(this.vector.getX()) != Double.doubleToLongBits(other.vector.getX())) {
return false;
}
if(Double.doubleToLongBits(this.vector.getY()) != Double.doubleToLongBits(other.vector.getY())) {
return false;
}
return Double.doubleToLongBits(this.vector.getZ()) == Double.doubleToLongBits(other.vector.getZ());
}
public double getPitch() {
return pitch;
}
public void setPitch(double pitch) {
this.pitch = pitch;
}
public double getYaw() {
return yaw;
}
public void setYaw(double yaw) {
this.yaw = yaw;
}
@Override
public int hashCode() {
int hash = 3;
World world = (this.world == null) ? null : this.world;
hash = 19 * hash + (world != null ? world.hashCode() : 0);
hash = 19 * hash + (int) (Double.doubleToLongBits(this.vector.getX()) ^ (Double.doubleToLongBits(this.vector.getX()) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.vector.getY()) ^ (Double.doubleToLongBits(this.vector.getY()) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.vector.getZ()) ^ (Double.doubleToLongBits(this.vector.getZ()) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.pitch) ^ Double.doubleToLongBits(this.pitch) >>> 32);
hash = 19 * hash + (int) (Double.doubleToLongBits(this.yaw) ^ Double.doubleToLongBits(this.yaw) >>> 32);
return hash;
}
public Vector3 toVector() {
return vector.clone();
}
@Override
public String toString() {
return "[" + world + ": (" + getX() + ", " + getY() + ", " + getZ() + ")]";
}
public Location multiply(double v) {
vector.multiply(v);
return this;
}
}

View File

@@ -0,0 +1,192 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.math.MathUtil;
import net.jafama.FastMath;
/**
* oh yeah
*/
public class Vector2 implements Cloneable {
private double x;
private double z;
/**
* Create a vector with a given X and Z component
*
* @param x X component
* @param z Z component
*/
public Vector2(double x, double z) {
this.x = x;
this.z = z;
}
/**
* Get X component
*
* @return X component
*/
public double getX() {
return x;
}
public Vector2 setX(double x) {
this.x = x;
return this;
}
/**
* Get Z component
*
* @return Z component
*/
public double getZ() {
return z;
}
public Vector2 setZ(double z) {
this.z = z;
return this;
}
/**
* Multiply X and Z components by a value.
*
* @param m Value to multiply
* @return Mutated vector, for chaining.
*/
public Vector2 multiply(double m) {
x *= m;
z *= m;
return this;
}
/**
* Add this vector to another.
*
* @param other Vector to add
* @return Mutated vector, for chaining.
*/
public Vector2 add(Vector2 other) {
x += other.x;
z += other.z;
return this;
}
/**
* Subtract a vector from this vector,
*
* @param other Vector to subtract
* @return Mutated vector, for chaining.
*/
public Vector2 subtract(Vector2 other) {
x -= other.x;
z -= other.z;
return this;
}
/**
* Normalize this vector to length 1
*
* @return Mutated vector, for chaining.
*/
public Vector2 normalize() {
divide(length());
return this;
}
/**
* Divide X and Z components by a value.
*
* @param d Divisor
* @return Mutated vector, for chaining.
*/
public Vector2 divide(double d) {
x /= d;
z /= d;
return this;
}
/**
* Get the length of this Vector
*
* @return length
*/
public double length() {
return FastMath.sqrt(lengthSquared());
}
/**
* Get the squared length of this Vector
*
* @return squared length
*/
public double lengthSquared() {
return x * x + z * z;
}
/**
* Get the distance from this vector to another.
*
* @param other Another vector
* @return Distance between vectors
*/
public double distance(Vector2 other) {
return FastMath.sqrt(distanceSquared(other));
}
/**
* Get the squared distance between 2 vectors.
*
* @param other Another vector
* @return Squared distance
*/
public double distanceSquared(Vector2 other) {
double dx = other.x - x;
double dz = other.z - z;
return dx * dx + dz * dz;
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + Double.hashCode(x);
hash = 31 * hash + Double.hashCode(z);
return hash;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector2)) return false;
Vector2 other = (Vector2) obj;
return MathUtil.equals(this.x, other.x) && MathUtil.equals(this.z, other.z);
}
@Override
public Vector2 clone() {
try {
return (Vector2) super.clone();
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
public Vector2 add(double x, double z) {
this.x += x;
this.z += z;
return this;
}
public int getBlockX() {
return FastMath.floorToInt(x);
}
public int getBlockZ() {
return FastMath.floorToInt(z);
}
@Override
public String toString() {
return "(" + x + ", " + z + ")";
}
}

View File

@@ -0,0 +1,341 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.platform.world.World;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
/**
* 3D Mutable Vector
*/
public class Vector3 implements Cloneable {
private double x;
private double y;
private double z;
public Vector3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public double getZ() {
return z;
}
public Vector3 setZ(double z) {
this.z = z;
return this;
}
public double getX() {
return x;
}
public Vector3 setX(double x) {
this.x = x;
return this;
}
public double getY() {
return y;
}
public Vector3 setY(double y) {
this.y = y;
return this;
}
public int getBlockX() {
return FastMath.floorToInt(x);
}
public int getBlockY() {
return FastMath.floorToInt(y);
}
public int getBlockZ() {
return FastMath.floorToInt(z);
}
public Vector3 multiply(double m) {
x *= m;
y *= m;
z *= m;
return this;
}
public Vector3 add(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
public Vector3 add(Vector3 other) {
this.x += other.getX();
this.y += other.getY();
this.z += other.getZ();
return this;
}
public Vector3 add(Vector2 other) {
this.x += other.getX();
this.z += other.getZ();
return this;
}
public double lengthSquared() {
return x * x + y * y + z * z;
}
@Override
public Vector3 clone() {
try {
return (Vector3) super.clone();
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
public double length() {
return FastMath.sqrt(lengthSquared());
}
public double inverseLength() {
return FastMath.invSqrtQuick(lengthSquared());
}
/**
* Returns if a vector is normalized
*
* @return whether the vector is normalised
*/
public boolean isNormalized() {
return MathUtil.equals(this.lengthSquared(), 1);
}
/**
* Rotates the vector around the x axis.
* <p>
* This piece of math is based on the standard rotation matrix for vectors
* in three dimensional space. This matrix can be found here:
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
* Matrix</a>.
*
* @param angle the angle to rotate the vector about. This angle is passed
* in radians
* @return the same vector
*/
@NotNull
public Vector3 rotateAroundX(double angle) {
double angleCos = Math.cos(angle);
double angleSin = Math.sin(angle);
double y = angleCos * getY() - angleSin * getZ();
double z = angleSin * getY() + angleCos * getZ();
return setY(y).setZ(z);
}
/**
* Rotates the vector around the y axis.
* <p>
* This piece of math is based on the standard rotation matrix for vectors
* in three dimensional space. This matrix can be found here:
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
* Matrix</a>.
*
* @param angle the angle to rotate the vector about. This angle is passed
* in radians
* @return the same vector
*/
@NotNull
public Vector3 rotateAroundY(double angle) {
double angleCos = Math.cos(angle);
double angleSin = Math.sin(angle);
double x = angleCos * getX() + angleSin * getZ();
double z = -angleSin * getX() + angleCos * getZ();
return setX(x).setZ(z);
}
/**
* Rotates the vector around the z axis
* <p>
* This piece of math is based on the standard rotation matrix for vectors
* in three dimensional space. This matrix can be found here:
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
* Matrix</a>.
*
* @param angle the angle to rotate the vector about. This angle is passed
* in radians
* @return the same vector
*/
@NotNull
public Vector3 rotateAroundZ(double angle) {
double angleCos = Math.cos(angle);
double angleSin = Math.sin(angle);
double x = angleCos * getX() - angleSin * getY();
double y = angleSin * getX() + angleCos * getY();
return setX(x).setY(y);
}
/**
* Get the distance between this vector and another. The value of this
* method is not cached and uses a costly square-root function, so do not
* repeatedly call this method to get the vector's magnitude. NaN will be
* returned if the inner result of the sqrt() function overflows, which
* will be caused if the distance is too long.
*
* @param o The other vector
* @return the distance
*/
public double distance(@NotNull Vector3 o) {
return FastMath.sqrt(FastMath.pow2(x - o.x) + FastMath.pow2(y - o.y) + FastMath.pow2(z - o.z));
}
/**
* Get the squared distance between this vector and another.
*
* @param o The other vector
* @return the distance
*/
public double distanceSquared(@NotNull Vector3 o) {
return FastMath.pow2(x - o.x) + FastMath.pow2(y - o.y) + FastMath.pow2(z - o.z);
}
/**
* Rotates the vector around a given arbitrary axis in 3 dimensional space.
*
* <p>
* Rotation will follow the general Right-Hand-Rule, which means rotation
* will be counterclockwise when the axis is pointing towards the observer.
* <p>
* This method will always make sure the provided axis is a unit vector, to
* not modify the length of the vector when rotating.
*
* @param axis the axis to rotate the vector around. If the passed vector is
* not of length 1, it gets copied and normalized before using it for the
* rotation. Please use {@link Vector3#normalize()} on the instance before
* passing it to this method
* @param angle the angle to rotate the vector around the axis
* @return the same vector
* @throws IllegalArgumentException if the provided axis vector instance is
* null
*/
@NotNull
public Vector3 rotateAroundAxis(@NotNull Vector3 axis, double angle) throws IllegalArgumentException {
return rotateAroundNonUnitAxis(axis.isNormalized() ? axis : axis.clone().normalize(), angle);
}
/**
* Rotates the vector around a given arbitrary axis in 3 dimensional space.
*
* <p>
* Rotation will follow the general Right-Hand-Rule, which means rotation
* will be counterclockwise when the axis is pointing towards the observer.
* <p>
* Note that the vector length will change accordingly to the axis vector
* length. If the provided axis is not a unit vector, the rotated vector
* will not have its previous length. The scaled length of the resulting
* vector will be related to the axis vector.
*
* @param axis the axis to rotate the vector around.
* @param angle the angle to rotate the vector around the axis
* @return the same vector
* @throws IllegalArgumentException if the provided axis vector instance is
* null
*/
@NotNull
public Vector3 rotateAroundNonUnitAxis(@NotNull Vector3 axis, double angle) throws IllegalArgumentException {
double x = getX(), y = getY(), z = getZ();
double x2 = axis.getX(), y2 = axis.getY(), z2 = axis.getZ();
double cosTheta = Math.cos(angle);
double sinTheta = Math.sin(angle);
double dotProduct = this.dot(axis);
double xPrime = x2 * dotProduct * (1d - cosTheta)
+ x * cosTheta
+ (-z2 * y + y2 * z) * sinTheta;
double yPrime = y2 * dotProduct * (1d - cosTheta)
+ y * cosTheta
+ (z2 * x - x2 * z) * sinTheta;
double zPrime = z2 * dotProduct * (1d - cosTheta)
+ z * cosTheta
+ (-y2 * x + x2 * y) * sinTheta;
return setX(xPrime).setY(yPrime).setZ(zPrime);
}
/**
* Calculates the dot product of this vector with another. The dot product
* is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar.
*
* @param other The other vector
* @return dot product
*/
public double dot(@NotNull Vector3 other) {
return x * other.x + y * other.y + z * other.z;
}
public Location toLocation(World world) {
return new Location(world, this.clone());
}
public Vector3 normalize() {
return this.multiply(this.inverseLength());
}
public Vector3 subtract(int x, int y, int z) {
this.x -= x;
this.y -= y;
this.z -= z;
return this;
}
public Vector3 subtract(Vector3 end) {
x -= end.x;
y -= end.y;
z -= end.z;
return this;
}
/**
* Returns a hash code for this vector
*
* @return hash code
*/
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
hash = 79 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
return hash;
}
/**
* Checks to see if two objects are equal.
* <p>
* Only two Vectors can ever return true. This method uses a fuzzy match
* to account for floating point errors. The epsilon can be retrieved
* with epsilon.
*/
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector3)) return false;
Vector3 other = (Vector3) obj;
return MathUtil.equals(x, other.x) && MathUtil.equals(y, other.y) && MathUtil.equals(x, other.z);
}
@Override
public String toString() {
return "(" + getX() + ", " + getY() + ", " + getZ() + ")";
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Cylinder extends VoxelGeometry {
public Cylinder(Vector3 start, int rad, int height) {
}
}

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.vector.Vector3;
public class DeformedSphere extends VoxelGeometry {
public DeformedSphere(Vector3 start, int rad, double deform, NoiseSampler noise) {
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector3 c = new Vector3(x, y, z);
if(c.length() < (rad + 0.5) * ((noise.getNoise(x, y, z) + 1) * deform)) {
addVector(c.add(start));
}
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Sphere extends VoxelGeometry {
public Sphere(Vector3 start, int rad) {
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector3 c = new Vector3(x, y, z);
if(c.length() < rad + 0.5) {
addVector(c.add(start));
}
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More