implement UserDefinedFunction

This commit is contained in:
dfsek
2021-01-31 01:41:05 -07:00
parent e600b50a11
commit 1863ba29fc
10 changed files with 148 additions and 38 deletions

View File

@@ -1,17 +1,20 @@
package com.dfsek.terra.api.math.noise.samplers;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.parsii.RandomFunction;
import com.dfsek.terra.api.math.parsii.defined.UserDefinedFunction;
import com.dfsek.terra.api.math.parsii.noise.NoiseFunction2;
import com.dfsek.terra.api.math.parsii.noise.NoiseFunction3;
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import parsii.eval.Expression;
import parsii.eval.Parser;
import parsii.eval.Scope;
import parsii.eval.Variable;
import parsii.tokenizer.ParseException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Sampler implementation using parsii expression
@@ -22,7 +25,7 @@ public class ExpressionSampler implements NoiseSampler {
private final Variable y;
private final Variable z;
public ExpressionSampler(String equation, Scope parent, long seed, Map<String, NoiseSeeded> functions) throws ParseException {
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);
@@ -41,7 +44,15 @@ public class ExpressionSampler implements NoiseSampler {
}
});
parser.registerFunction("rand", new RandomFunction());
for(Map.Entry<String, FunctionTemplate> entry : definedFunctions.entrySet()) {
String id = entry.getKey();
FunctionTemplate fun = entry.getValue();
Scope functionScope = new Scope().withParent(parent);
List<Variable> variables = fun.getArgs().stream().map(functionScope::create).collect(Collectors.toList());
parser.registerFunction(id, new UserDefinedFunction(parser.parse(fun.getFunction(), functionScope), variables));
}
this.expression = parser.parse(equation, scope);
}

View File

@@ -1,30 +0,0 @@
package com.dfsek.terra.api.math.parsii;
import com.dfsek.terra.api.util.FastRandom;
import parsii.eval.Expression;
import parsii.eval.Function;
import java.util.List;
/**
* Provides access to a PRNG ({@link com.dfsek.terra.api.util.FastRandom})
* <p>
* Takes 1 argument, which sets the seed
*/
public class RandomFunction implements Function {
@Override
public int getNumberOfArguments() {
return 1;
}
@Override
public double eval(List<Expression> list) {
long seed = (long) list.get(0).evaluate();
return new FastRandom(seed).nextDouble();
}
@Override
public boolean isNaturalFunction() {
return true;
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.math.parsii.defined;
import com.dfsek.terra.api.util.seeded.SeededBuilder;
public class DefinedFunctionTemplate implements SeededBuilder<UserDefinedFunction> {
@Override
public UserDefinedFunction apply(Long aLong) {
return null;
}
}

View File

@@ -0,0 +1,35 @@
package com.dfsek.terra.api.math.parsii.defined;
import parsii.eval.Expression;
import parsii.eval.Function;
import parsii.eval.Variable;
import java.util.List;
public class UserDefinedFunction implements Function {
private final Expression expression;
private final List<Variable> variables;
public UserDefinedFunction(Expression expression, List<Variable> variables) {
this.expression = expression;
this.variables = variables;
}
@Override
public int getNumberOfArguments() {
return variables.size();
}
@Override
public synchronized double eval(List<Expression> args) {
for(int i = 0; i < variables.size(); i++) {
variables.get(i).setValue(args.get(i).evaluate());
}
return expression.evaluate();
}
@Override
public boolean isNaturalFunction() {
return true;
}
}

View File

@@ -28,6 +28,7 @@ import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.Borde
import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.ReplaceListMutatorTemplate;
import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.ReplaceMutatorTemplate;
import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.SmoothMutatorTemplate;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader;
import com.dfsek.terra.config.loaders.config.sampler.templates.DomainWarpTemplate;
import com.dfsek.terra.config.loaders.config.sampler.templates.FastNoiseTemplate;
@@ -77,6 +78,7 @@ public class GenericLoaders implements LoaderRegistrar {
.registerLoader(ReplaceListMutatorTemplate.class, ReplaceListMutatorTemplate::new)
.registerLoader(BorderMutatorTemplate.class, BorderMutatorTemplate::new)
.registerLoader(BorderListMutatorTemplate.class, BorderListMutatorTemplate::new)
.registerLoader(FunctionTemplate.class, FunctionTemplate::new)
.registerLoader(ImageSampler.Channel.class, (t, object, cf) -> ImageSampler.Channel.valueOf((String) object))
.registerLoader(ExpanderStage.Type.class, (t, object, cf) -> ExpanderStage.Type.valueOf((String) object))
.registerLoader(MutatorStage.Type.class, (t, object, cf) -> MutatorStage.Type.valueOf((String) object))

View File

@@ -5,6 +5,7 @@ import com.dfsek.terra.api.math.noise.samplers.ConstantSampler;
import com.dfsek.terra.api.math.noise.samplers.ExpressionSampler;
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
import com.dfsek.terra.api.world.palette.holder.PaletteHolder;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import com.dfsek.terra.world.generation.WorldGenerator;
import parsii.eval.Scope;
import parsii.tokenizer.ParseException;
@@ -26,6 +27,8 @@ public class GeneratorBuilder {
private Map<String, NoiseSeeded> noiseBuilderMap;
private Map<String, FunctionTemplate> functionTemplateMap;
private PaletteHolder palettes;
private PaletteHolder slantPalettes;
@@ -51,9 +54,9 @@ public class GeneratorBuilder {
NoiseSampler elevation;
NoiseSampler carving;
try {
noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap);
elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap);
carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap);
noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap, functionTemplateMap);
elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap, functionTemplateMap);
carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap, functionTemplateMap);
} catch(ParseException e) {
throw new RuntimeException(e);
}
@@ -66,6 +69,10 @@ public class GeneratorBuilder {
this.blendWeight = blendWeight;
}
public void setFunctionTemplateMap(Map<String, FunctionTemplate> functionTemplateMap) {
this.functionTemplateMap = functionTemplateMap;
}
public void setBlendStep(int blendStep) {
this.blendStep = blendStep;
}

View File

@@ -4,10 +4,14 @@ import com.dfsek.terra.api.core.TerraPlugin;
import com.dfsek.terra.biome.TerraBiome;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.config.builder.GeneratorBuilder;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.BiomeTemplate;
import parsii.eval.Scope;
import java.util.HashMap;
import java.util.Map;
public class BiomeFactory implements TerraFactory<BiomeTemplate, TerraBiome> {
private final ConfigPack pack;
@@ -21,7 +25,12 @@ public class BiomeFactory implements TerraFactory<BiomeTemplate, TerraBiome> {
generatorBuilder.setElevationEquation(template.getElevationEquation());
generatorBuilder.setNoiseEquation(template.getNoiseEquation());
generatorBuilder.setCarvingEquation(template.getCarvingEquation());
generatorBuilder.setNoiseBuilderMap(template.getPack().getTemplate().getNoiseBuilderMap());
generatorBuilder.setNoiseBuilderMap(pack.getTemplate().getNoiseBuilderMap());
Map<String, FunctionTemplate> functions = new HashMap<>(pack.getTemplate().getFunctions());
functions.putAll(template.getFunctions());
generatorBuilder.setFunctionTemplateMap(functions);
generatorBuilder.setPalettes(template.getPalette());
generatorBuilder.setSlantPalettes(template.getSlantPalette());

View File

@@ -0,0 +1,28 @@
package com.dfsek.terra.config.loaders.config.function;
import com.dfsek.tectonic.annotations.Value;
import com.dfsek.tectonic.loading.object.ObjectTemplate;
import java.util.List;
@SuppressWarnings("unused")
public class FunctionTemplate implements ObjectTemplate<FunctionTemplate> {
@Value("arguments")
private List<String> args;
@Value("function")
private String function;
public List<String> getArgs() {
return args;
}
public String getFunction() {
return function;
}
@Override
public FunctionTemplate get() {
return this;
}
}

View File

@@ -5,6 +5,7 @@ import com.dfsek.tectonic.annotations.Value;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
import com.dfsek.terra.biome.provider.BiomeProvider;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import java.util.HashMap;
import java.util.Map;
@@ -21,6 +22,10 @@ public class ConfigPackTemplate implements ConfigTemplate {
@Default
private Map<String, Double> variables = new HashMap<>();
@Value("functions")
@Default
private Map<String, FunctionTemplate> functions = new HashMap<>();
@Value("structures.locatable")
@Default
private Map<String, String> locatable = new HashMap<>();
@@ -80,6 +85,10 @@ public class ConfigPackTemplate implements ConfigTemplate {
return providerBuilder;
}
public Map<String, FunctionTemplate> getFunctions() {
return functions;
}
public String getVersion() {
return version;
}

View File

@@ -10,6 +10,7 @@ import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.math.parsii.BlankFunction;
import com.dfsek.terra.api.math.parsii.defined.UserDefinedFunction;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.world.Biome;
@@ -18,6 +19,7 @@ import com.dfsek.terra.api.util.seeded.NoiseSeeded;
import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.api.world.palette.SinglePalette;
import com.dfsek.terra.api.world.palette.holder.PaletteHolder;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import com.dfsek.terra.config.loaders.config.sampler.templates.FastNoiseTemplate;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.world.population.items.TerraStructure;
@@ -26,12 +28,14 @@ import com.dfsek.terra.world.population.items.ores.OreHolder;
import com.dfsek.terra.world.population.items.tree.TreeLayer;
import parsii.eval.Parser;
import parsii.eval.Scope;
import parsii.eval.Variable;
import parsii.tokenizer.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@SuppressWarnings({"FieldMayBeFinal", "unused"})
public class BiomeTemplate extends AbstractableTemplate implements ValidatedConfigTemplate {
@@ -50,6 +54,11 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
@Default
private Map<String, Double> variables = new HashMap<>();
@Value("functions")
@Default
@Abstractable
private Map<String, FunctionTemplate> functions = new HashMap<>();
@Value("carving.equation")
@Abstractable
@Default
@@ -180,6 +189,10 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
return tags;
}
public Map<String, FunctionTemplate> getFunctions() {
return functions;
}
public double getBlendWeight() {
return blendWeight;
}
@@ -316,7 +329,7 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
@Override
public boolean validate() throws ValidationException {
color |= 0x1fe00000; // Alpha adjustment
color |= 0xff000000; // Alpha adjustment
Parser tester = new Parser();
Scope testScope = new Scope().withParent(pack.getVarScope());
@@ -329,6 +342,22 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
pack.getTemplate().getNoiseBuilderMap().forEach((id, builder) -> tester.registerFunction(id, new BlankFunction(builder.getDimensions()))); // Register dummy functions
Map<String, FunctionTemplate> testFunctions = new HashMap<>(pack.getTemplate().getFunctions());
testFunctions.putAll(functions);
for(Map.Entry<String, FunctionTemplate> entry : testFunctions.entrySet()) {
String id = entry.getKey();
FunctionTemplate fun = entry.getValue();
Scope functionScope = new Scope().withParent(testScope);
List<Variable> variables = fun.getArgs().stream().map(functionScope::create).collect(Collectors.toList());
try {
tester.registerFunction(id, new UserDefinedFunction(tester.parse(fun.getFunction(), functionScope), variables));
} catch(ParseException e) {
throw new ValidationException("Invalid function: ", e);
}
}
try {
tester.parse(noiseEquation, testScope);
} catch(ParseException e) {