diff --git a/build.gradle b/build.gradle index ce39e224d..b1fd3587d 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { id "de.undercouch.download" version "5.0.1" } -version '2.2.0-1.19' // Needs to be version specific +version '2.2.1-1.19' // Needs to be version specific def nmsVersion = "1.19" def apiVersion = '1.19' def spigotJarVersion = '1.19-R0.1-SNAPSHOT' diff --git a/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java b/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java index 1ebb2f203..123a1d247 100644 --- a/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java +++ b/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java @@ -35,6 +35,8 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import java.util.Objects; + @Snippet("style") @Accessors(chain = true) @NoArgsConstructor @@ -70,6 +72,10 @@ public class IrisGeneratorStyle { @MaxNumber(64) @Desc("The exponent") private double exponent = 1; + @MinNumber(0) + @MaxNumber(8192) + @Desc("If the cache size is set above 0, this generator will be cached") + private int cacheSize = 0; public IrisGeneratorStyle(NoiseStyle s) { this.style = s; @@ -81,6 +87,18 @@ public class IrisGeneratorStyle { } public CNG createNoCache(RNG rng, IrisData data) { + return createNoCache(rng, data, false); + } + + + private int hash() + { + return Objects.hash(expression, imageMap, multiplier, axialFracturing, fracture != null ? fracture.hash() : 0, exponent, cacheSize, zoom, cellularZoom, cellularFrequency, style); + } + + public CNG createNoCache(RNG rng, IrisData data, boolean actuallyCached) { + String cacheKey = hash() + ""; + if(getExpression() != null) { IrisExpression e = data.getExpressionLoader().load(getExpression()); @@ -134,7 +152,7 @@ public class IrisGeneratorStyle { } public CNG create(RNG rng, IrisData data) { - return cng.aquire(() -> createNoCache(rng, data)); + return cng.aquire(() -> createNoCache(rng, data, true)); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") diff --git a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index e9216efb9..d12028f25 100644 --- a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -31,11 +31,15 @@ import com.volmit.iris.engine.object.StudioMode; import com.volmit.iris.engine.platform.studio.StudioGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.data.IrisBiomeStorage; +import com.volmit.iris.util.format.Form; import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder; +import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder; import com.volmit.iris.util.io.ReactiveFolder; import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Looper; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Setter; @@ -278,9 +282,11 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun if(studioGenerator != null) { studioGenerator.generateChunk(getEngine(), tc, x, z); } else { - Hunk blocks = Hunk.view(tc); - Hunk biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()); + ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); + BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight()); getEngine().generate(x << 4, z << 4, blocks, biomes, true); + blocks.apply(); + biomes.apply(); } ChunkData c = tc.getRaw(); diff --git a/src/main/java/com/volmit/iris/util/cache/ArrayCache.java b/src/main/java/com/volmit/iris/util/cache/ArrayCache.java new file mode 100644 index 000000000..8b578898e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/ArrayCache.java @@ -0,0 +1,82 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import com.volmit.iris.engine.data.cache.Cache; +import com.volmit.iris.util.hunk.bits.Writable; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public interface ArrayCache extends Writable { + T get(int i); + + void set(int i, T t); + + void iset(int i, int v); + + int getWidth(); + + int getHeight(); + + void writeCache(DataOutputStream dos) throws IOException; + + static int zigZag(int coord, int size) + { + if(coord < 0) + { + coord = Math.abs(coord); + } + + if(coord % (size * 2) >= size) + { + return (size) - (coord % size) - 1; + } + + else { + return coord % size; + } + } + + default void set(int x, int y, T v) + { + set((zigZag(y, getHeight()) * getWidth()) + zigZag(x, getWidth()), v); + } + + default T get(int x, int y) + { + try + { + return get((zigZag(y, getHeight()) * getWidth()) + zigZag(x, getWidth())); + } + + catch(Throwable e) + { + e.printStackTrace(); + throw e; + } + } + + default void iset(int x, int y, int v) + { + iset((zigZag(y, getHeight()) * getWidth()) + zigZag(x, getWidth()), v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/ByteBitCache.java b/src/main/java/com/volmit/iris/util/cache/ByteBitCache.java new file mode 100644 index 000000000..eb701973f --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/ByteBitCache.java @@ -0,0 +1,44 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class ByteBitCache extends DataBitCache { + public ByteBitCache(int width, int height) { + super(width, height); + } + + @Override + public Integer readNodeData(DataInputStream din) throws IOException { + return (int) din.readByte(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Integer integer) throws IOException { + dos.writeByte(integer); + } + + @Override + public void iset(int i, int v) { + set(i, v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/ByteCache.java b/src/main/java/com/volmit/iris/util/cache/ByteCache.java new file mode 100644 index 000000000..7c9421b3a --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/ByteCache.java @@ -0,0 +1,76 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import lombok.Getter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class ByteCache implements ArrayCache { + @Getter + private final int width; + @Getter + private final int height; + private final byte[] cache; + + public ByteCache(int width, int height) + { + this.width = width; + this.height = height; + cache = new byte[width * height]; + } + + public void set(int i, Integer v) + { + cache[i] = v.byteValue(); + } + + public Integer get(int i) + { + return (int)cache[i]; + } + + @Override + public void writeCache(DataOutputStream dos) throws IOException { + dos.writeInt(width); + dos.writeInt(height); + + for(int i = 0; i < width * height; i++) + { + dos.writeByte(get(i)); + } + } + + @Override + public Integer readNodeData(DataInputStream din) throws IOException { + return (int) din.readByte(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Integer integer) throws IOException { + dos.writeByte(integer); + } + + @Override + public void iset(int i, int v) { + set(i, v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/DataBitCache.java b/src/main/java/com/volmit/iris/util/cache/DataBitCache.java new file mode 100644 index 000000000..ba8ff7ebf --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/DataBitCache.java @@ -0,0 +1,58 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import com.volmit.iris.util.hunk.bits.DataContainer; +import lombok.Getter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public abstract class DataBitCache implements ArrayCache { + @Getter + private final int width; + @Getter + private final int height; + private final DataContainer cache; + + public DataBitCache(int width, int height) + { + this.width = width; + this.height = height; + cache = new DataContainer<>(this, width * height); + } + + public void set(int i, T v) + { + cache.set(i, v); + } + + public T get(int i) + { + return cache.get(i); + } + + @Override + public void writeCache(DataOutputStream dos) throws IOException { + dos.writeInt(width); + dos.writeInt(height); + cache.writeDos(dos); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/FloatBitCache.java b/src/main/java/com/volmit/iris/util/cache/FloatBitCache.java new file mode 100644 index 000000000..4d0473dfc --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/FloatBitCache.java @@ -0,0 +1,44 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class FloatBitCache extends DataBitCache { + public FloatBitCache(int width, int height) { + super(width, height); + } + + @Override + public Float readNodeData(DataInputStream din) throws IOException { + return din.readFloat(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Float integer) throws IOException { + dos.writeFloat(integer); + } + + @Override + public void iset(int i, int v) { + set(i, (float)v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/FloatCache.java b/src/main/java/com/volmit/iris/util/cache/FloatCache.java new file mode 100644 index 000000000..334922a52 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/FloatCache.java @@ -0,0 +1,93 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import lombok.Getter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +public class FloatCache implements ArrayCache { + @Getter + private final int width; + @Getter + private final int height; + private final float[] cache; + + + public FloatCache(File file) throws IOException { + this(new DataInputStream(new FileInputStream(file))); + } + + public FloatCache(DataInputStream din) throws IOException { + this(din.readInt(), din.readInt()); + for(int i = 0; i < width * height; i++) + { + cache[i] = din.readFloat(); + } + din.close(); + } + + public FloatCache(int width, int height) + { + this.width = width; + this.height = height; + cache = new float[width * height]; + } + + public void set(int i, Float v) + { + cache[i] = v; + } + + public Float get(int i) + { + return cache[i]; + } + + @Override + public void writeCache(DataOutputStream dos) throws IOException { + dos.writeInt(width); + dos.writeInt(height); + + for(int i = 0; i < width * height; i++) + { + dos.writeFloat(get(i)); + } + } + + @Override + public Float readNodeData(DataInputStream din) throws IOException { + return din.readFloat(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Float integer) throws IOException { + dos.writeFloat(integer); + } + + @Override + public void iset(int i, int v) { + set(i, (float) v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/IntBitCache.java b/src/main/java/com/volmit/iris/util/cache/IntBitCache.java new file mode 100644 index 000000000..c06e62682 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/IntBitCache.java @@ -0,0 +1,44 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class IntBitCache extends DataBitCache { + public IntBitCache(int width, int height) { + super(width, height); + } + + @Override + public Integer readNodeData(DataInputStream din) throws IOException { + return din.readInt(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Integer integer) throws IOException { + dos.writeInt(integer); + } + + @Override + public void iset(int i, int v) { + set(i, v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/IntCache.java b/src/main/java/com/volmit/iris/util/cache/IntCache.java new file mode 100644 index 000000000..63509466e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/IntCache.java @@ -0,0 +1,78 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import com.volmit.iris.util.hunk.bits.DataContainer; +import com.volmit.iris.util.hunk.bits.Writable; +import lombok.Getter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class IntCache implements ArrayCache { + @Getter + private final int width; + @Getter + private final int height; + private final int[] cache; + + public IntCache(int width, int height) + { + this.width = width; + this.height = height; + cache = new int[width * height]; + } + + public void set(int i, Integer v) + { + cache[i] = v; + } + + public Integer get(int i) + { + return cache[i]; + } + + @Override + public void writeCache(DataOutputStream dos) throws IOException { + dos.writeInt(width); + dos.writeInt(height); + + for(int i = 0; i < width * height; i++) + { + dos.writeInt(get(i)); + } + } + + @Override + public Integer readNodeData(DataInputStream din) throws IOException { + return din.readInt(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Integer integer) throws IOException { + dos.writeInt(integer); + } + + @Override + public void iset(int i, int v) { + set(i, v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/ShortBitCache.java b/src/main/java/com/volmit/iris/util/cache/ShortBitCache.java new file mode 100644 index 000000000..4bb01d053 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/ShortBitCache.java @@ -0,0 +1,44 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class ShortBitCache extends DataBitCache { + public ShortBitCache(int width, int height) { + super(width, height); + } + + @Override + public Short readNodeData(DataInputStream din) throws IOException { + return din.readShort(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Short integer) throws IOException { + dos.writeShort(integer); + } + + @Override + public void iset(int i, int v) { + set(i, (short) v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/ShortCache.java b/src/main/java/com/volmit/iris/util/cache/ShortCache.java new file mode 100644 index 000000000..b25f3981d --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/ShortCache.java @@ -0,0 +1,76 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +import lombok.Getter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class ShortCache implements ArrayCache { + @Getter + private final int width; + @Getter + private final int height; + private final short[] cache; + + public ShortCache(int width, int height) + { + this.width = width; + this.height = height; + cache = new short[width * height]; + } + + public void set(int i, Short v) + { + cache[i] = v; + } + + public Short get(int i) + { + return cache[i]; + } + + @Override + public void writeCache(DataOutputStream dos) throws IOException { + dos.writeInt(width); + dos.writeInt(height); + + for(int i = 0; i < width * height; i++) + { + dos.writeShort(get(i)); + } + } + + @Override + public Short readNodeData(DataInputStream din) throws IOException { + return din.readShort(); + } + + @Override + public void writeNodeData(DataOutputStream dos, Short integer) throws IOException { + dos.writeShort(integer); + } + + @Override + public void iset(int i, int v) { + set(i, (short) v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/UByteBitCache.java b/src/main/java/com/volmit/iris/util/cache/UByteBitCache.java new file mode 100644 index 000000000..4a1309f7b --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/UByteBitCache.java @@ -0,0 +1,40 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +public class UByteBitCache extends ByteBitCache { + public UByteBitCache(int width, int height) { + super(width, height); + } + + @Override + public void set(int i, Integer v) { + super.set(i, v + Byte.MIN_VALUE); + } + + @Override + public Integer get(int i) { + return super.get(i) - Byte.MIN_VALUE; + } + + @Override + public void iset(int i, int v) { + set(i, v); + } +} diff --git a/src/main/java/com/volmit/iris/util/cache/UByteCache.java b/src/main/java/com/volmit/iris/util/cache/UByteCache.java new file mode 100644 index 000000000..81f5c23cc --- /dev/null +++ b/src/main/java/com/volmit/iris/util/cache/UByteCache.java @@ -0,0 +1,40 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.cache; + +public class UByteCache extends ByteCache { + public UByteCache(int width, int height) { + super(width, height); + } + + @Override + public void set(int i, Integer v) { + super.set(i, v + Byte.MIN_VALUE); + } + + @Override + public Integer get(int i) { + return super.get(i) - Byte.MIN_VALUE; + } + + @Override + public void iset(int i, int v) { + set(i, v); + } +} diff --git a/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkHolder.java b/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkHolder.java new file mode 100644 index 000000000..a7b066526 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkHolder.java @@ -0,0 +1,86 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.hunk.view; + +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.engine.data.chunk.LinkedTerrainChunk; +import com.volmit.iris.util.hunk.storage.AtomicHunk; +import lombok.Getter; +import org.bukkit.block.Biome; +import org.bukkit.generator.ChunkGenerator.BiomeGrid; + +@SuppressWarnings("ClassCanBeRecord") +public class BiomeGridHunkHolder extends AtomicHunk { + @Getter + private final BiomeGrid chunk; + private final int minHeight; + private final int maxHeight; + + public BiomeGridHunkHolder(BiomeGrid chunk, int minHeight, int maxHeight) { + super(16, maxHeight - minHeight, 16); + this.chunk = chunk; + this.minHeight = minHeight; + this.maxHeight = maxHeight; + } + + @Override + public int getWidth() { + return 16; + } + + @Override + public int getDepth() { + return 16; + } + + @Override + public int getHeight() { + return maxHeight - minHeight; + } + + public void apply() { + for(int i = 0; i < getHeight(); i++) { + for(int j = 0; j < getWidth(); j++) { + for(int k = 0; k < getDepth(); k++) { + Biome b = super.getRaw(j, i, k); + + if(b != null) + { + chunk.setBiome(j, i + minHeight, k, b); + } + } + } + } + } + + @Override + public Biome getRaw(int x, int y, int z) { + Biome b = super.getRaw(x, y, z); + + return b != null ? b : Biome.PLAINS; + } + + public void forceBiomeBaseInto(int x, int y, int z, Object somethingVeryDirty) { + if(chunk instanceof LinkedTerrainChunk) { + INMS.get().forceBiomeInto(x, y + minHeight, z, somethingVeryDirty, ((LinkedTerrainChunk) chunk).getRawBiome()); + return; + } + INMS.get().forceBiomeInto(x, y + minHeight, z, somethingVeryDirty, chunk); + } +} diff --git a/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkView.java b/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkView.java index c205162ee..ce1654acd 100644 --- a/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkView.java +++ b/src/main/java/com/volmit/iris/util/hunk/view/BiomeGridHunkView.java @@ -18,6 +18,7 @@ package com.volmit.iris.util.hunk.view; +import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.chunk.LinkedTerrainChunk; import com.volmit.iris.util.hunk.Hunk; @@ -31,6 +32,7 @@ public class BiomeGridHunkView implements Hunk { private final BiomeGrid chunk; private final int minHeight; private final int maxHeight; + private int highest = -1000; public BiomeGridHunkView(BiomeGrid chunk, int minHeight, int maxHeight) { this.chunk = chunk; @@ -56,6 +58,12 @@ public class BiomeGridHunkView implements Hunk { @Override public void setRaw(int x, int y, int z, Biome t) { chunk.setBiome(x, y + minHeight, z, t); + + if(y > highest) + { + highest = y; + Iris.info("Highest = " + highest); + } } @Override diff --git a/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkHolder.java b/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkHolder.java new file mode 100644 index 000000000..cec67eb41 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkHolder.java @@ -0,0 +1,76 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.hunk.view; + +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.hunk.storage.AtomicHunk; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.generator.ChunkGenerator.ChunkData; + +import java.util.concurrent.atomic.AtomicReferenceArray; + +@SuppressWarnings("ClassCanBeRecord") +public class ChunkDataHunkHolder extends AtomicHunk { + private static final BlockData AIR = Material.AIR.createBlockData(); + private final ChunkData chunk; + + public ChunkDataHunkHolder(ChunkData chunk) { + super(16, chunk.getMaxHeight() - chunk.getMinHeight(), 16); + this.chunk = chunk; + } + + @Override + public int getWidth() { + return 16; + } + + @Override + public int getDepth() { + return 16; + } + + @Override + public int getHeight() { + return chunk.getMaxHeight() - chunk.getMinHeight(); + } + + @Override + public BlockData getRaw(int x, int y, int z) { + BlockData b = super.getRaw(x, y, z); + + return b != null ? b : AIR; + } + + public void apply() { + for(int i = 0; i < getHeight(); i++) { + for(int j = 0; j < getWidth(); j++) { + for(int k = 0; k < getDepth(); k++) { + BlockData b = super.getRaw(j, i, k); + + if(b != null) + { + chunk.setBlock(j, i + chunk.getMinHeight(), k, b); + } + } + } + } + } +} diff --git a/src/main/java/com/volmit/iris/util/noise/CNG.java b/src/main/java/com/volmit/iris/util/noise/CNG.java index 44d052e53..4d3ab4b6b 100644 --- a/src/main/java/com/volmit/iris/util/noise/CNG.java +++ b/src/main/java/com/volmit/iris/util/noise/CNG.java @@ -22,6 +22,8 @@ import com.volmit.iris.Iris; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.IRare; import com.volmit.iris.engine.object.NoiseStyle; +import com.volmit.iris.util.cache.FloatBitCache; +import com.volmit.iris.util.cache.FloatCache; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.function.NoiseInjector; @@ -33,6 +35,11 @@ import com.volmit.iris.util.stream.arithmetic.FittedStream; import com.volmit.iris.util.stream.sources.CNGStream; import lombok.Data; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.List; @Data @@ -56,6 +63,7 @@ public class CNG { private boolean trueFracturing = false; private KList children; private CNG fracture; + private FloatCache cache; private NoiseGenerator generator; private NoiseInjector injector; private RNG rng; @@ -133,14 +141,55 @@ public class CNG { }, 1D, 1); } - public CNG cached(int size) + public CNG cached(int size, String key, File cacheFolder) { if(size <= 0) { return this; } - generator = new CachedNoise(generator, size); + cache = null; + + File f = new File(new File(cacheFolder, ".cache"), key + ".cnm"); + FloatCache fbc; + boolean cached = false; + if(f.exists()) + { + try { + fbc = new FloatCache(f); + cached = true; + } catch(IOException e) { + fbc = new FloatCache(size, size); + } + } + + else { + fbc = new FloatCache(size, size); + } + + if(!cached) + { + for(int i = 0; i < size; i++) + { + for(int j = 0; j < size; j++) + { + fbc.set(i, j, (float) noise(i, j)); + } + } + + try { + f.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(f); + DataOutputStream dos = new DataOutputStream(fos); + fbc.writeCache(dos); + dos.close(); + Iris.info("Saved Noise Cache " + f.getName()); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + cache = fbc; return this; } @@ -435,6 +484,11 @@ public class CNG { } public double noise(double... dim) { + if(cache != null && dim.length == 2) + { + return cache.get((int)dim[0], (int)dim[1]); + } + double n = getNoise(dim); n = power != 1D ? (n < 0 ? -Math.pow(Math.abs(n), power) : Math.pow(n, power)) : n; double m = 1; diff --git a/src/main/java/com/volmit/iris/util/uniques/U.java b/src/main/java/com/volmit/iris/util/uniques/U.java new file mode 100644 index 000000000..97f559104 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/U.java @@ -0,0 +1,14 @@ +package com.volmit.iris.util.uniques; + +import java.io.File; + +public class U { + public static void main(String[] a) + { + UniqueRenderer r = new UniqueRenderer("helloworld", 2560 , 1440); + + r.writeCollectionFrames(new File("collection"), 1, 1024); + + System.exit(0); + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/UBufferedImage.java b/src/main/java/com/volmit/iris/util/uniques/UBufferedImage.java new file mode 100644 index 000000000..3211d49a2 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/UBufferedImage.java @@ -0,0 +1,51 @@ +package com.volmit.iris.util.uniques; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; + +public class UBufferedImage implements UImage { + private final BufferedImage buf; + + public UBufferedImage(BufferedImage buf) + { + this.buf = buf; + } + + @Override + public int getWidth() { + return buf.getWidth(); + } + + @Override + public int getHeight() { + return buf.getHeight(); + } + + @Override + public UImage copy() { + ColorModel cm = buf.getColorModel(); + boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); + WritableRaster raster = buf.copyData(null); + return new UBufferedImage(new BufferedImage(cm, raster, isAlphaPremultiplied, null)); + } + + @Override + public Color get(int x, int y) { + return new Color(buf.getRGB(x, y)); + } + + @Override + public void set(int x, int y, Color color) { + try + { + buf.setRGB(x, y, color.getRGB()); + } + + catch(Throwable e) + { + + } + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/UFeature.java b/src/main/java/com/volmit/iris/util/uniques/UFeature.java new file mode 100644 index 000000000..a06ccd00b --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/UFeature.java @@ -0,0 +1,77 @@ +package com.volmit.iris.util.uniques; + +import com.volmit.iris.engine.object.NoiseStyle; +import com.volmit.iris.util.function.Function2; +import com.volmit.iris.util.function.NoiseInjector; +import com.volmit.iris.util.interpolation.InterpolationMethod; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.stream.ProceduralStream; +import com.volmit.iris.util.stream.interpolation.Interpolated; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.List; +import java.util.function.Consumer; + +public interface UFeature { + List injectors = List.of( + CNG.ADD, + CNG.DST_MOD, + CNG.DST_POW, + CNG.DST_SUBTRACT, + CNG.MAX, + CNG.MIN, + CNG.SRC_MOD, + CNG.SRC_POW, + CNG.SRC_SUBTRACT, + CNG.MULTIPLY + ); + + void render(UImage image, RNG rng, double time, Consumer progressor, UFeatureMeta meta); + + default Color color(CNG hue, CNG saturation, CNG brightness, double x, double y, double t) + { + return Color.getHSBColor((float)hue.fitDouble(0, 1, x + t, y + t), + (float)saturation.fitDouble(0, 1, x + t, y + t), + (float)brightness.fitDouble(0, 1, x + t, y + t)); + } + + default InterpolationMethod interpolator(RNG rng) + { + return rng.pick( + UniqueRenderer.renderer.getInterpolators() + ); + } + + default CNG generator(String key, RNG rng, double scaleMod, long salt, UFeatureMeta meta) + { + return generator(key, rng, scaleMod, rng.i(1, 3), rng.i(1, 5), salt, meta); + } + + default CNG generator(String key, RNG rng, double scaleMod, int fractures, int composites, long salt, UFeatureMeta meta) + { + RNG rngg = rng.nextParallelRNG(salt); + CNG cng = rng.pick(UniqueRenderer.renderer.getStyles()).create(rngg).oct(rng.i(1, 5)); + RNG rngf = rngg.nextParallelRNG(-salt); + cng.scale(rngf.d(0.33 * scaleMod, 1.66 * scaleMod)); + + if(fractures > 0) + { + cng.fractureWith(generator(null, rngf.nextParallelRNG(salt + fractures), scaleMod / rng.d(4, 17), fractures-1, composites, salt + fractures + 55, null), scaleMod * rngf.nextDouble(16, 256)); + } + + for(int i = 0; i < composites; i++) + { + CNG sub = generator(null, rngf.nextParallelRNG(salt + fractures), scaleMod * rngf.d(0.4, 3.3), fractures / 3, 0, salt + fractures + composites + 78, null); + sub.setInjector(rng.pick(injectors)); + cng.child(sub); + } + + if(key != null && meta != null) + { + meta.registerGenerator(key, cng); + } + return cng; + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/UFeatureMeta.java b/src/main/java/com/volmit/iris/util/uniques/UFeatureMeta.java new file mode 100644 index 000000000..faa87fef1 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/UFeatureMeta.java @@ -0,0 +1,102 @@ +package com.volmit.iris.util.uniques; + +import com.volmit.iris.engine.object.NoiseStyle; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.function.NoiseInjector; +import com.volmit.iris.util.interpolation.InterpolationMethod; +import com.volmit.iris.util.interpolation.IrisInterpolation; +import com.volmit.iris.util.noise.CNG; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +public class UFeatureMeta { + private KMap interpolators; + private KMap generators; + private String feature; + + public void registerInterpolator(String key, InterpolationMethod method, double radius) + { + if(interpolators == null) + { + interpolators = new KMap<>(); + } + interpolators.put(key, new UFeatureMetaInterpolator(method, radius)); + } + + public void registerGenerator(String key, CNG cng) + { + if(generators == null) + { + generators = new KMap<>(); + } + generators.put(key, buildGenerator(cng)); + } + + public UFeatureMetaGenerator buildGenerator(CNG cng) + { + UFeatureMetaGenerator g = new UFeatureMetaGenerator(); + g.setScale(cng.getScale()); + g.setOctaves(cng.getOct()); + + if(cng.getFracture() != null) + { + g.setFracture(buildGenerator(cng.getFracture())); + g.setFractureMultiplier(cng.getFscale()); + } + + if(cng.getChildren() != null && cng.getChildren().isNotEmpty()) + { + g.setChildren(new KList<>()); + + for(CNG i : cng.getChildren()) + { + g.getChildren().add(buildGenerator(i)); + } + } + + if(cng.getInjector() == CNG.ADD){g.setParentInject("add");} + else if(cng.getInjector() == CNG.SRC_SUBTRACT){g.setParentInject("src_subtract");} + else if(cng.getInjector() == CNG.DST_SUBTRACT){g.setParentInject("dst_subtract");} + else if(cng.getInjector() == CNG.MULTIPLY ){g.setParentInject("multiply");} + else if(cng.getInjector() == CNG.MAX){g.setParentInject("max");} + else if(cng.getInjector() == CNG.MIN){g.setParentInject("min");} + else if(cng.getInjector() == CNG.SRC_MOD ){g.setParentInject("src_mod");} + else if(cng.getInjector() == CNG.SRC_POW ){g.setParentInject("src_pow");} + else if(cng.getInjector() == CNG.DST_MOD ){g.setParentInject("dst_mod");} + else if(cng.getInjector() == CNG.DST_POW){g.setParentInject("dst_pow");} + + return g; + } + + public boolean isEmpty() { + return (interpolators == null || interpolators.isEmpty()) && (generators == null || generators.isEmpty()); + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + static class UFeatureMetaInterpolator + { + private InterpolationMethod interpolator; + private double radius; + } + + @Data + @NoArgsConstructor + static class UFeatureMetaGenerator + { + private NoiseStyle style; + private int octaves = 1; + private double scale = 1; + private String parentInject; + private UFeatureMetaGenerator fracture; + private Double fractureMultiplier; + private List children; + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/UImage.java b/src/main/java/com/volmit/iris/util/uniques/UImage.java new file mode 100644 index 000000000..821584a64 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/UImage.java @@ -0,0 +1,20 @@ +package com.volmit.iris.util.uniques; + +import java.awt.*; + +public interface UImage { + int getWidth(); + + int getHeight(); + + default boolean isInBounds(int x, int y) + { + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); + } + + UImage copy(); + + Color get(int x, int y); + + void set(int x, int y, Color color); +} diff --git a/src/main/java/com/volmit/iris/util/uniques/UMeta.java b/src/main/java/com/volmit/iris/util/uniques/UMeta.java new file mode 100644 index 000000000..f5ba9aae6 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/UMeta.java @@ -0,0 +1,56 @@ +package com.volmit.iris.util.uniques; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.volmit.iris.engine.object.NoiseStyle; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.interpolation.InterpolationMethod; +import com.volmit.iris.util.io.IO; +import com.volmit.iris.util.noise.CNG; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.List; + +@Data +@NoArgsConstructor +public class UMeta { + private transient BufferedImage image; + private KMap features; + private long id; + private double time; + private int width; + private int height; + + public void registerFeature(String key, UFeatureMeta feature) + { + if(features == null) + { + features = new KMap<>(); + } + + features.put(key, feature); + } + + public void export(File destination) throws IOException { + + for(String i : features.k()) + { + if(features.get(i).isEmpty()) + { + features.remove(i); + } + } + + width = image.getWidth(); + height = image.getHeight(); + ImageIO.write(image, "PNG", destination); + IO.writeAll(new File(destination.getParentFile(), destination.getName() + ".json"), new GsonBuilder().setPrettyPrinting().create().toJson(this)); + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/UniqueRenderer.java b/src/main/java/com/volmit/iris/util/uniques/UniqueRenderer.java new file mode 100644 index 000000000..a8b80e3fe --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/UniqueRenderer.java @@ -0,0 +1,338 @@ +package com.volmit.iris.util.uniques; + +import com.volmit.iris.Iris; +import com.volmit.iris.engine.object.NoiseStyle; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.function.NoiseProvider; +import com.volmit.iris.util.interpolation.InterpolationMethod; +import com.volmit.iris.util.interpolation.IrisInterpolation; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.parallel.BurstExecutor; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.scheduling.ChronoLatch; +import com.volmit.iris.util.scheduling.J; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import com.volmit.iris.util.stream.ProceduralStream; +import com.volmit.iris.util.uniques.features.*; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +public class UniqueRenderer { + static UniqueRenderer renderer; + + static final List backgrounds = List.of(new UFWarpedBackground()); + static final List interpolators = List.of(new UFInterpolator(), new UFNOOP()); + static final List features = List.of(new UFWarpedLines(), new UFWarpedDisc(), new UFWarpedDots(), new UFWarpedCircle()); + private final String seed; + private final ProceduralStream spatialSeed; + private final int width; + private final int height; + private final KMap writing = new KMap<>(); + private KList sortedStyles = new KList(); + private KList sortedInterpolators = new KList(); + int cores = Runtime.getRuntime().availableProcessors(); + private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); + + public UniqueRenderer(String seed, int width, int height) + { + renderer = this; + computeNoiseStyles(3000, 2); + computeInterpolationMethods(3000, 2); + this.seed = seed; + this.width = width; + this.height = height; + spatialSeed = NoiseStyle.FRACTAL_WATER.stream(new RNG(seed)).convert((d) -> new RNG(Math.round(seed.hashCode() + (d * 934321234D)))); + new Thread(() -> { + while(true) + { + J.sleep(5000); + + if(!writing.isEmpty()) + { + System.out.println(Form.repeat("\n", 60)); + System.out.println(Form.memSize(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(), 2) + " of " + Form.memSize(Runtime.getRuntime().totalMemory(), 2)); + KMap c = writing.copy(); + + for(String i : writing.k().sort()) + { + String prog = ""; + String f = writing.get(i); + + if(f.contains("%")) + { + String v = f.split("\\Q%\\E")[0]; + try + { + prog = drawProgress(Double.valueOf(Integer.parseInt(v.substring(v.length() - 2))) / 100D, 30); + } + + catch(Throwable e) + { + try + { + prog = drawProgress(Double.valueOf(Integer.parseInt(v.substring(v.length() - 1))) / 100D, 30); + } + + catch(Throwable ee) + { + try + { + prog = drawProgress(Double.valueOf(Integer.parseInt(v.substring(v.length() - 3))) / 100D, 30); + } + + catch(Throwable eee) + { + + } + } + } + } + + System.out.println(prog + " " + i + " => " + f); + } + } + } + }).start(); + } + + public UMeta renderFrameBuffer(long id, double t) + { + UMeta meta = new UMeta(); + meta.setId(id); + meta.setTime(t); + RNG rng = spatialSeed.get(id, id + ((id * id) % (id / 3D))); + RNG rngbg = spatialSeed.get(id, -id + ((id * id) % (id / 4D))); + BufferedImage buf = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + BufferedImage bufFG = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + UImage image = new UBufferedImage(buf); + UImage imageFG = new UBufferedImage(bufFG); + ChronoLatch cl = new ChronoLatch(250); + UFeature background = rng.pick(backgrounds); + UFeature interpolator = rng.pick(interpolators); + UFeature foreground = rng.pick(features); + UFeature foregroundInterpolator = rng.pick(interpolators); + UFeatureMeta backgroundMeta = new UFeatureMeta(); + UFeatureMeta foregroundMeta = new UFeatureMeta(); + UFeatureMeta backgroundInterpolatorMeta = new UFeatureMeta(); + UFeatureMeta foregroundInterpolatorMeta = new UFeatureMeta(); + background.render(image, rngbg, t, (p) -> { + if(cl.flip()) + { + writing.put("#" + id + ":" + t, Form.pc(p / 4D) + " [" + background.getClass().getSimpleName() + " " + Form.pc(p) + "]"); + } + }, backgroundMeta); + backgroundMeta.setFeature(background.getClass().getSimpleName()); + meta.registerFeature("background", backgroundMeta); + interpolator.render(image, rng, t, (p) -> { + if(cl.flip()) + { + writing.put("#" + id + ":" + t, Form.pc(0.25 + (p / 4d)) + " [" + interpolator.getClass().getSimpleName() + " " + Form.pc(p) + "]"); + } + }, backgroundInterpolatorMeta); + backgroundInterpolatorMeta.setFeature(interpolator.getClass().getSimpleName()); + meta.registerFeature("backgroundInterpolator", backgroundInterpolatorMeta); + foreground.render(imageFG, rng, t, (p) -> { + if(cl.flip()) + { + writing.put("#" + id + ":" + t, Form.pc(0.5 + (p / 4d)) + " [" + foreground.getClass().getSimpleName() + " " + Form.pc(p) + "]"); + } + }, foregroundMeta); + foregroundMeta.setFeature(foreground.getClass().getSimpleName()); + meta.registerFeature("foreground", foregroundMeta); + overlay(imageFG, bufFG, image); + foregroundInterpolator.render(image, rng, t, (p) -> { + if(cl.flip()) + { + writing.put("#" + id + ":" + t, Form.pc(0.75 + (p / 4d)) + " [" + interpolator.getClass().getSimpleName() + " " + Form.pc(p) + "]"); + } + }, foregroundInterpolatorMeta); + foregroundInterpolatorMeta.setFeature(foregroundInterpolator.getClass().getSimpleName()); + meta.registerFeature("foregroundInterpolator", foregroundInterpolatorMeta); + overlay(imageFG, bufFG, image); + meta.setImage(buf); + writing.remove("#" + id + ":" + t); + return meta; + } + +private void overlay(UImage layer, BufferedImage layerBuf, UImage onto) +{ + for(int i = 0; i < onto.getWidth(); i++) + { + for(int j = 0; j < onto.getHeight(); j++) + { + if(layerBuf.getRGB(i, j) != 0) + { + onto.set(i, j, layer.get(i, j)); + } + } + } +} + + private String drawProgress(double progress, int len) + { + int max = len; + int in = (int) Math.round(progress * max); + max -= in; + + return "[" + Form.repeat("=", in) + Form.repeat(" ", max)+ "]"; + } + + private void computeNoiseStyles(double time, double scope) { + List allowedStyles = new KList<>(NoiseStyle.values()); + allowedStyles.remove(NoiseStyle.FLAT); + KMap speeds = new KMap<>(); + double allocateMS = time; + double maxTestDuration = allocateMS / allowedStyles.size(); + System.out.println("Running Noise Style Benchmark for " + Form.duration(allocateMS, 0) + "."); + System.out.println("Benchmarking " + allowedStyles.size() + " + Noise Styles for " + Form.duration(maxTestDuration, 1) + " each."); + System.out.println(); + + for(NoiseStyle i : allowedStyles) + { + int score = 0; + CNG cng = i.create(new RNG("renderspeedtest")); + PrecisionStopwatch p = PrecisionStopwatch.start(); + double g = 0; + while(p.getMilliseconds() < maxTestDuration) + { + cng.noise(g, -g * 2); + g+= 0.1; + g *= 1.25; + score++; + } + + speeds.put(i, score); + } + + for(NoiseStyle i : speeds.sortKNumber()) + { + System.out.println(Form.capitalizeWords(i.name().toLowerCase(Locale.ROOT).replaceAll("\\Q_\\E", " ")) + " => " + Form.f(speeds.get(i))); + } + System.out.println(); + int takeUpTo = (int) Math.max(1, scope * speeds.size()); + System.out.println("Choosing the fastest " + Form.pc(scope) + " styles (" + takeUpTo + ")"); + + for(NoiseStyle i : speeds.sortKNumber().reverse()) + { + if(takeUpTo-- <= 0) + { + break; + } + + sortedStyles.add(i); + System.out.println("- " + Form.capitalizeWords(i.name().toLowerCase(Locale.ROOT).replaceAll("\\Q_\\E", " "))); + } + } + + private void computeInterpolationMethods(double time, double scope) { + List allowedStyles = new KList<>(InterpolationMethod.values()); + allowedStyles.remove(InterpolationMethod.NONE); + KMap speeds = new KMap<>(); + double allocateMS = time; + double maxTestDuration = allocateMS / allowedStyles.size(); + System.out.println("Running Interpolation Method Benchmark for " + Form.duration(allocateMS, 0) + "."); + System.out.println("Benchmarking " + allowedStyles.size() + " + Interpolation Methods for " + Form.duration(maxTestDuration, 1) + " each."); + System.out.println(); + + RNG r = new RNG("renderspeedtestinterpolation"); + CNG cng = NoiseStyle.SIMPLEX.create(r); + NoiseProvider np = (x, z) -> cng.noise(x, z); + + for(InterpolationMethod i : allowedStyles) + { + int score = 0; + + PrecisionStopwatch p = PrecisionStopwatch.start(); + double g = 0; + while(p.getMilliseconds() < maxTestDuration) + { + IrisInterpolation.getNoise(i, (int) g, (int) (-g * 2.225), r.d(4, 64), np); + cng.noise(g, -g * 2); + g+= 1.1; + g *= 1.25; + score++; + } + + speeds.put(i, score); + } + + for(InterpolationMethod i : speeds.sortKNumber()) + { + System.out.println(Form.capitalizeWords(i.name().toLowerCase(Locale.ROOT).replaceAll("\\Q_\\E", " ")) + " => " + Form.f(speeds.get(i))); + } + System.out.println(); + int takeUpTo = (int) Math.max(1, scope * speeds.size()); + System.out.println("Choosing the fastest " + Form.pc(scope) + " interpolators (" + takeUpTo + ")"); + + for(InterpolationMethod i : speeds.sortKNumber().reverse()) + { + if(takeUpTo-- <= 0) + { + break; + } + + sortedInterpolators.add(i); + System.out.println("- " + Form.capitalizeWords(i.name().toLowerCase(Locale.ROOT).replaceAll("\\Q_\\E", " "))); + } + } + + public void writeCollectionFrames(File folder, int fromId, int toId) + { + folder.mkdirs(); + BurstExecutor burst = new BurstExecutor(executor, Math.min(toId - fromId, 1000)); + burst.setMulticore(true); + AtomicInteger ai = new AtomicInteger(0); + int max = toId - fromId; + + for(int i = fromId; i <= toId; i++) + { + int ii = i; + burst.queue(() -> { + + writing.put("!#[" + fromId + "-" + toId + "] Collection", ai.get() + " of " + max + " (" + Form.pc(ai.get() / (double)max, 0) + ")"); + writeFrame(new File(folder, ii + ".png"), ii, 0); + ai.incrementAndGet(); + writing.put("!#[" + fromId + "-" + toId + "] Collection", ai.get() + " of " + max + " (" + Form.pc(ai.get() /(double) max, 0) + ")"); + }); + } + + burst.complete(); + writing.remove("!#[" + fromId + "-" + toId + "] Collection"); + } + + public void writeFrame(File destination, long id, double t) { + try + { + renderFrameBuffer(id, t).export(destination); + } + + catch(Throwable e) + { + e.printStackTrace(); + } + } + + public void report(String s) + { + System.out.println(s); + } + + public KList getStyles() { + return sortedStyles; + } + + public List getInterpolators() { + return sortedInterpolators; + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFInterpolator.java b/src/main/java/com/volmit/iris/util/uniques/features/UFInterpolator.java new file mode 100644 index 000000000..e2cfecf8f --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFInterpolator.java @@ -0,0 +1,64 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.function.NoiseProvider; +import com.volmit.iris.util.interpolation.InterpolationMethod; +import com.volmit.iris.util.interpolation.IrisInterpolation; +import com.volmit.iris.util.math.M; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; +import com.volmit.iris.util.uniques.UniqueRenderer; + +import java.awt.*; +import java.util.function.Consumer; + +public class UFInterpolator implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + UImage ref = image.copy(); + CNG rmod = generator("interpolator_radius", rng, 1, 33004, meta); + + NoiseProvider nHue = (x, y) -> { + int ix = Math.abs(((int)x)%ref.getWidth()); + int iy = Math.abs(((int)y)%ref.getHeight()); + Color color = ref.get(ix, iy); + float[] hsv = new float[3]; + Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getGreen(), hsv); + return hsv[0]; + }; + NoiseProvider nSat = (x, y) -> { + int ix = Math.abs(((int)x)%ref.getWidth()); + int iy = Math.abs(((int)y)%ref.getHeight()); + Color color = ref.get(ix, iy); + float[] hsv = new float[3]; + Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getGreen(), hsv); + return hsv[1]; + }; + NoiseProvider nBri = (x, y) -> { + int ix = Math.abs(((int)x)%ref.getWidth()); + int iy = Math.abs(((int)y)%ref.getHeight()); + Color color = ref.get(ix, iy); + float[] hsv = new float[3]; + Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getGreen(), hsv); + return hsv[2]; + }; + InterpolationMethod method = interpolator(rng); + int sizeMin = Math.min(image.getWidth(), image.getHeight()); + double radius = Math.max(4, rmod.fit(sizeMin / 256, sizeMin / 4, t * rng.d(0.03, 1.25), t * rng.d(0.01, 2.225))); + for(int i = 0; i < image.getWidth(); i++) + { + for(int j = 0; j < image.getHeight(); j++) + { + image.set(i, j, Color.getHSBColor( + (float)Math.max(Math.min(1D, IrisInterpolation.getNoise(method, i, j, radius, nHue)), 0D), + (float)Math.max(Math.min(1D, IrisInterpolation.getNoise(method, i, j, radius, nSat)), 0D), + (float)Math.max(Math.min(1D, IrisInterpolation.getNoise(method, i, j, radius, nBri)), 0D) + )); + } + + progressor.accept(i / (double)image.getWidth()); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFNOOP.java b/src/main/java/com/volmit/iris/util/uniques/features/UFNOOP.java new file mode 100644 index 000000000..76c4db56b --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFNOOP.java @@ -0,0 +1,16 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; + +import java.awt.*; +import java.util.function.Consumer; + +public class UFNOOP implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedBackground.java b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedBackground.java new file mode 100644 index 000000000..da5f0e411 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedBackground.java @@ -0,0 +1,30 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; + +import java.awt.*; +import java.util.function.Consumer; + +public class UFWarpedBackground implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + CNG hue = generator("color_hue", rng, rng.d(0.001, rng.d(2, 5)), rng.i(0, 3) ,rng.i(0, 3), 31007, meta); + CNG sat = generator("color_sat", rng, rng.d(0.001, rng.d(2, 5)), rng.i(0, 2) ,rng.i(0, 2), 33004, meta); + CNG bri = generator("color_bri", rng, rng.d(0.001, rng.d(2, 5)), rng.i(0, 1) ,rng.i(0, 1), 32005, meta).patch(0.145); + double tcf = rng.d(0.15, 0.55); + + for(int i = 0; i < image.getWidth(); i++) + { + for(int j = 0; j < image.getHeight(); j++) + { + image.set(i, j, color(hue, sat, bri, i, j, tcf * t)); + } + + progressor.accept(i / (double)image.getWidth()); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedCircle.java b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedCircle.java new file mode 100644 index 000000000..2d73ee01b --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedCircle.java @@ -0,0 +1,50 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.function.Consumer; + +public class UFWarpedCircle implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + double r = Math.min(image.getWidth(), image.getHeight()) / 2.5D; + double i, angle, x1, y1; + CNG xShift = generator("x_warp", rng, 0.6, 1001, meta); + CNG yShift = generator("y_warp", rng, 0.6, 1002, meta); + CNG hue = generator("color_hue", rng, rng.d(0.25, 2.5), 1003, meta); + CNG sat = generator("color_sat",rng, rng.d(0.25, 2.5), 1004, meta); + CNG bri = generator("color_bri",rng, rng.d(0.25, 2.5), 1005, meta); + double tcf = rng.d(0.75, 11.25); + double rcf = rng.d(7.75, 16.25); + int x = image.getWidth()/2; + int y = image.getHeight()/2; + + for(int d = 0; d < 256; d++) + { + r -= Math.min(image.getWidth(), image.getHeight()) / 300D; + + if(r <= 0) + { + return; + } + + for(i = 0; i < 360; i += 0.1) + { + angle = i; + x1 = r * Math.cos(angle * Math.PI / 180); + y1 = r * Math.sin(angle * Math.PI / 180); + image.set((int)Math.round(x + x1 + xShift.fit(-r/2, r/2, x1 + (t+ (d * 8)), -y1 + (t+ (d * 8)))), + (int)Math.round(y + y1 + yShift.fit(-r/2, r/2, y1 + (t+ (d * 8)), -x1 + (t+ (d * 8)))), + color(hue, sat, bri, x1, y1, (t * tcf) + (d * rcf))); + } + + progressor.accept(d / 256D); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedDisc.java b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedDisc.java new file mode 100644 index 000000000..b394f2c7c --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedDisc.java @@ -0,0 +1,39 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; + +import java.util.function.Consumer; + +public class UFWarpedDisc implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + double r = Math.min(image.getWidth(), image.getHeight()) / 3D; + CNG xShift = generator("x_warp", rng, 0.6, 1001, meta); + CNG yShift = generator("y_warp",rng, 0.6, 1002, meta); + CNG hue = generator("color_hue",rng, rng.d(0.25, 2.5), 1003, meta); + CNG sat = generator("color_sat",rng, rng.d(0.25, 2.5), 1004, meta); + CNG bri = generator("color_bri",rng, rng.d(0.25, 2.5), 1005, meta); + double tcf = rng.d(0.75, 11.25); + int x = image.getWidth()/2; + int y = image.getHeight()/2; + + for(int i = (int)( x - r); i < x+r; i++) + { + for(int j =(int)( y - r); j < y+r; j++) + { + if(image.isInBounds(i, j) && Math.pow(x - i, 2) + Math.pow(y - j, 2) <= r*r) + { + image.set(Math.round(i + xShift.fit(-r/2, r/2, i+t, -j+t)), + Math.round(j + yShift.fit(-r/2, r/2, j+t, -i+t)), + color(hue, sat, bri, i, j, tcf * t)); + } + } + + progressor.accept(i / (x + r)); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedDots.java b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedDots.java new file mode 100644 index 000000000..a9670600e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedDots.java @@ -0,0 +1,48 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; + +import java.awt.*; +import java.util.function.Consumer; + +public class UFWarpedDots implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + CNG genX = generator("x_pos", rng, 4, 2000, meta); + CNG genY = generator("y_pos", rng, 4, 2001, meta); + + double tcf = rng.d(0.75, 11.25); + + for(int i = 1; i <= 8; i++) + { + CNG xShift = generator("x_warp_" + i, rng, 2, 2006+i, meta); + CNG yShift = generator("y_warp_" + i,rng, 2, 2007+i, meta); + CNG hue = generator("color_hue_" + i,rng, rng.d(0.55, 3.5), 2003+i, meta); + CNG sat = generator("color_sat_" + i,rng, rng.d(0.55, 3.5), 2004+i, meta); + CNG bri = generator("color_bri_" + i,rng, rng.d(0.55, 3.5), 2005+i, meta); + int x = genX.fit(0, image.getWidth(), i * 128, i * 5855); + int y = genY.fit(0, image.getHeight(), i * 128, i * 5855); + Color color = color(hue, sat, bri, x, y, t); + double r = Math.max(genX.fit(image.getWidth() / 10, image.getWidth() / 6, x, y), genY.fit(image.getHeight() / 10, image.getHeight() / 6, x, y)); + + for(int j = (int)(x - r); j < x + r; j++) + { + for(int k = (int)(y - r); k < y + r; k++) + { + if(image.isInBounds(j, k) && Math.pow(x - j, 2) + Math.pow(y - k, 2) <= r*r) + { + image.set(Math.round(j + xShift.fit(-r/2, r/2, j+t, -k+t)), + Math.round(k + yShift.fit(-r/2, r/2, k+t, -j+t)), + color(hue, sat, bri, j, k, tcf * t)); + } + } + } + + progressor.accept(i / 32D); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedLines.java b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedLines.java new file mode 100644 index 000000000..badfdb79e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/uniques/features/UFWarpedLines.java @@ -0,0 +1,47 @@ +package com.volmit.iris.util.uniques.features; + +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.uniques.UFeature; +import com.volmit.iris.util.uniques.UFeatureMeta; +import com.volmit.iris.util.uniques.UImage; + +import java.util.function.Consumer; + +public class UFWarpedLines implements UFeature { + @Override + public void render(UImage image, RNG rng, double t, Consumer progressor, UFeatureMeta meta) { + for(int g = 1; g < 5; g++) + { + CNG xShift = generator("x_warp_"+g, rng, 0.6, 1001 * g, meta); + CNG yShift = generator("y_warp_"+g, rng, 0.6, 1002 * g, meta); + CNG cX = generator("x_clip_"+g, rng, rng.d(0.035, 0.6), 77001 * g, meta); + CNG cY = generator("y_clip"+g, rng, rng.d(0.035, 0.6), 77002, meta); + CNG hue = generator("color_hue_"+g, rng, rng.d(0.25, 2.5), 1003 * g, meta); + CNG sat = generator("color_sat_"+g, rng, rng.d(0.25, 2.5), 1004 * g, meta); + CNG bri = generator("color_bri_"+g, rng, rng.d(0.25, 2.5), 1005 * g, meta); + double tcf = rng.d(0.75, 11.25 + (g * 4)); + double rcf = rng.d(7.75, 16.25 + (g * 5)); + double xcf = rng.d(0.15, 0.55 + (g * 0.645)); + double w = rng.d(64, 186 + (g * 8)); + double ww = image.getWidth() / rng.d(3, 9); + double hh = image.getHeight() / rng.d(3, 9); + boolean wh = rng.nextBoolean(); + double sa = rng.d(0.35, 0.66); + double sb = rng.d(0.35, 0.66); + + for(int i = 0; i < image.getWidth(); i+= (wh ? image.getWidth() / w : 1)) + { + for(int j = 0; j < image.getHeight(); j+= (!wh ? image.getHeight() / w : 1)) + { + if(cX.fitDouble(0, 1, i, -j, t * xcf) > sa && cY.fitDouble(0, 1, -j, i, t * xcf) > sb) + { + image.set(Math.round(i + xShift.fit(-ww, ww, i, j, (t * rcf))), + Math.round(j + yShift.fit(-hh, hh, -j, i, (t * rcf))), + color(hue, sat, bri, i, j, (t * tcf))); + } + } + } + } + } +}