diff --git a/gradle.properties b/gradle.properties index 784899a..3b31e2f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ # Sets default memory used for gradle commands. Can be overridden by user or command line properties. # This is required to provide enough memory for the Minecraft decompilation process. org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false +org.gradle.daemon=true diff --git a/src/main/java/t/j/dual/FiniteSampledGrid.java b/src/main/java/t/j/dual/FiniteSampledGrid.java new file mode 100644 index 0000000..0ee746f --- /dev/null +++ b/src/main/java/t/j/dual/FiniteSampledGrid.java @@ -0,0 +1,12 @@ +package t.j.dual; + +import net.minecraft.util.math.*; + +import t.j.dual.Utilities; + +import t.j.dual.SlicedFiniteSampledGrid; + +public interface FiniteSampledGrid extends SampledGrid { + public IntGrid getGrid(); + public T get(Vec3i cell); +} diff --git a/src/main/java/t/j/dual/GridCache.java b/src/main/java/t/j/dual/GridCache.java index bff5a9c..1d2bdc4 100644 --- a/src/main/java/t/j/dual/GridCache.java +++ b/src/main/java/t/j/dual/GridCache.java @@ -8,9 +8,9 @@ import net.minecraft.util.math.*; import t.j.dual.Utilities; -public class GridCache { - Function source; - WeakHashMap>> cacheMap; +public class GridCache implements SampledGrid { + private Function source; + private WeakHashMap>> cacheMap; class CacheEntry { public Vec3i keyRef; diff --git a/src/main/java/t/j/dual/IntGrid.java b/src/main/java/t/j/dual/IntGrid.java new file mode 100644 index 0000000..b2ca67d --- /dev/null +++ b/src/main/java/t/j/dual/IntGrid.java @@ -0,0 +1,96 @@ +package t.j.dual; + +import java.util.Iterator; + +import net.minecraft.util.math.*; + +import t.j.dual.Utilities; + +public class IntGrid implements Iterable { + public Vec3i origin; + public Vec3i extent; + + public class Iter implements Iterator { + public IntGrid grid; + int x, y, z; + + public Iter(IntGrid grid) { + this.x = 0; + this.y = 0; + this.z = 0; + this.grid = grid; + } + + public boolean hasNext() { + return z < grid.extent.getZ(); + } + + public Vec3i next() { + Vec3i result = new Vec3i(x + grid.origin.getX(), y + grid.origin.getY(), z + grid.origin.getZ()); + x += 1; + if (x == grid.extent.getX()) { + x = 0; + y += 1; + if (y == grid.extent.getY()) { + y = 0; + z += 1; + } + } + return result; + } + } + + public IntGrid(Vec3i origin, Vec3i extent) { + this.origin = origin; + this.extent = extent; + } + + public int numPoints() { + return extent.getX() * extent.getY() * extent.getZ(); + } + + public Vec3i translateToFrame(Vec3i in) { + return Utilities.viSub(in, origin); + } + + public Vec3i translateFromFrame(Vec3i in) { + return Utilities.viAdd(in, origin); + } + + public Vec3i nToGridCoordsInFrame(int idx) { + int x = idx / (extent.getY() * extent.getZ()); + int remX = idx % (extent.getY() * extent.getZ()); + int y = remX / extent.getZ(); + int z = remX % extent.getZ(); + return new Vec3i(x, y, z); + } + + public Vec3i nToGridCoords(int idx) { + return translateFromFrame(nToGridCoordsInFrame(idx)); + } + + public int gridCoordsToN(Vec3i ucoords) { + Vec3i coords = translateToFrame(ucoords); + return (coords.getX() * extent.getY() + coords.getY()) * extent.getZ() + coords.getZ(); + } + + public Iterator iterator() { + return new Iter(this); + } + + public Vec3i origin() { + return origin; + } + + // Not actually in the grid, since the grid is bounded strictly less than this + public Vec3i remote() { + return new Vec3i(origin.getX() + extent.getX(), origin.getY() + extent.getY(), origin.getZ() + extent.getZ()); + } + + public IntGrid intersect(IntGrid other) { + Vec3i newOrigin = Utilities.ewiMax(origin, other.origin); + Vec3i newRemote = Utilities.ewiMin(remote(), other.remote()); + Vec3i newExtent = Utilities.viSub(newRemote, newOrigin); + return new IntGrid(newOrigin, newExtent); + } +} diff --git a/src/main/java/t/j/dual/PointGrid.java b/src/main/java/t/j/dual/PointGrid.java index 587cdfb..88c3c0c 100644 --- a/src/main/java/t/j/dual/PointGrid.java +++ b/src/main/java/t/j/dual/PointGrid.java @@ -4,67 +4,58 @@ import java.util.Iterator; import net.minecraft.util.math.*; -import t.j.dual.PointList; - -public class PointGrid implements PointList { +public class PointGrid implements Iterable { + public IntGrid domain; public Vec3d origin; public Vec3d step; - public Vec3i extent; public class Iter implements Iterator { + public Iterator subIter; public PointGrid grid; - int x, y, z; public Iter(PointGrid grid) { - this.x = 0; - this.y = 0; - this.z = 0; this.grid = grid; + this.subIter = grid.domain.iterator(); } public boolean hasNext() { - return z < grid.extent.getZ(); + return subIter.hasNext(); } public Vec3d next() { - Vec3d result = new Vec3d(origin.x + x * step.x, origin.y + y * step.y, origin.z + z * step.z); - x += 1; - if (x == grid.extent.getX()) { - x = 0; - y += 1; - if (y == grid.extent.getY()) { - y = 0; - z += 1; - } - } - return result; + return grid.getCoords(subIter.next()); } } - public PointGrid(Vec3d origin, Vec3i extent, Vec3d step) { + public PointGrid(Vec3d origin, IntGrid intGrid, Vec3d step) { + this.domain = intGrid; this.origin = origin; - this.extent = extent; this.step = step; } - public PointGrid(Vec3d origin, Vec3d remote, Vec3i extent) { + public PointGrid(Vec3d origin, Vec3d remote, IntGrid intGrid) { + this.domain = intGrid; this.origin = origin; - this.extent = extent; Vec3d size = remote.subtract(origin); - Vec3d divisor = new Vec3d(extent.getX() - 1, extent.getY() - 1, extent.getZ() - 1); + Vec3d divisor = new Vec3d(domain.extent.getX() - 1, domain.extent.getY() - 1, domain.extent.getZ() - 1); this.step = new Vec3d(size.x / divisor.x, size.y / divisor.y, size.z / divisor.z); } public int numPoints() { - return extent.getX() * extent.getY() * extent.getZ(); + return domain.numPoints(); + } + + public Vec3d getCoords(Vec3i untranslated) { + return getCoordsInFrame(domain.translateToFrame(untranslated)); + } + + public Vec3d getCoordsInFrame(Vec3i at) { + return new Vec3d(origin.x + at.getX() * step.x, origin.y + at.getY() * step.y, origin.z + at.getZ() * step.z); } public Vec3d nth(int idx) { - int coordX = idx / (extent.getY() * extent.getZ()); - int remX = idx % (extent.getY() * extent.getZ()); - int coordY = remX / extent.getZ(); - int coordZ = remX % extent.getZ(); - return new Vec3d(origin.x + coordX * step.x, origin.y + coordY * step.y, origin.z + coordZ * step.z); + Vec3i coords = domain.nToGridCoordsInFrame(idx); + return new Vec3d(origin.x + coords.getX() * step.x, origin.y + coords.getY() * step.y, origin.z + coords.getZ() * step.z); } public Iterator iterator() { @@ -72,7 +63,7 @@ public class PointGrid implements PointList { } public Vec3d center() { - return new Vec3d(origin.x + (extent.getX() - 1) * step.x / 2.0, origin.y + (extent.getY() - 1) * step.y / 2.0, origin.z + (extent.getZ() - 1) * step.z / 2.0); + return new Vec3d(origin.x + (domain.extent.getX() - 1) * step.x / 2.0, origin.y + (domain.extent.getY() - 1) * step.y / 2.0, origin.z + (domain.extent.getZ() - 1) * step.z / 2.0); } public Vec3d origin() { @@ -80,7 +71,18 @@ public class PointGrid implements PointList { } public Vec3d remote() { - return new Vec3d(origin.x + (extent.getX() - 1) * step.x, origin.y + (extent.getY() - 1) * step.y, origin.z + (extent.getZ() - 1) * step.z); + return new Vec3d(origin.x + (domain.extent.getX() - 1) * step.x, origin.y + (domain.extent.getY() - 1) * step.y, origin.z + (domain.extent.getZ() - 1) * step.z); + } + + public PointGrid changeDomain(IntGrid newDomain) { + this.origin = getCoords(newDomain.origin()); + this.domain = newDomain; + return this; + } + + // intGrids will still share a reference though + public PointGrid clone() { + return new PointGrid(origin, domain, step); } } diff --git a/src/main/java/t/j/dual/PointList.java b/src/main/java/t/j/dual/PointList.java deleted file mode 100644 index 7407d15..0000000 --- a/src/main/java/t/j/dual/PointList.java +++ /dev/null @@ -1,16 +0,0 @@ -package t.j.dual; - -import java.util.Iterator; - -import net.minecraft.util.math.*; - -public interface PointList { - int numPoints(); - Vec3d nth(int idx); - Vec3d center(); - public Iterator iterator(); - // bounding box: - Vec3d origin(); - Vec3d remote(); -} - diff --git a/src/main/java/t/j/dual/PointWorld.java b/src/main/java/t/j/dual/PointWorld.java index b5b963e..8491132 100644 --- a/src/main/java/t/j/dual/PointWorld.java +++ b/src/main/java/t/j/dual/PointWorld.java @@ -8,7 +8,7 @@ import net.minecraft.util.math.*; import t.j.dual.Utilities; -interface PointWorld { +interface PointWorld extends SampledGrid> { public ArrayList pointsInCell(Vec3i cell); public float getMeanPointsPerCell(); @@ -30,4 +30,8 @@ interface PointWorld { } return points; } + + default public ArrayList get(Vec3i cell) { + return pointsInCell(cell); + } } diff --git a/src/main/java/t/j/dual/SampledGrid.java b/src/main/java/t/j/dual/SampledGrid.java new file mode 100644 index 0000000..2ce3799 --- /dev/null +++ b/src/main/java/t/j/dual/SampledGrid.java @@ -0,0 +1,13 @@ +package t.j.dual; + +import net.minecraft.util.math.*; + +import t.j.dual.Utilities; + +public interface SampledGrid { + public T get(Vec3i cell); + default public FiniteSampledGrid sampleOnGrid(IntGrid grid) { + return new SlicedFiniteSampledGrid(this, grid); + } +} + diff --git a/src/main/java/t/j/dual/SlicedFiniteSampledGrid.java b/src/main/java/t/j/dual/SlicedFiniteSampledGrid.java new file mode 100644 index 0000000..b85faf0 --- /dev/null +++ b/src/main/java/t/j/dual/SlicedFiniteSampledGrid.java @@ -0,0 +1,33 @@ +package t.j.dual; + +import net.minecraft.util.math.*; + +import t.j.dual.Utilities; + +public class SlicedFiniteSampledGrid implements FiniteSampledGrid { + private IntGrid grid; + private SampledGrid source; + + public SlicedFiniteSampledGrid(SampledGrid source, IntGrid intGrid) { + if (source instanceof SlicedFiniteSampledGrid) { + SlicedFiniteSampledGrid sliced = (SlicedFiniteSampledGrid) source; + this.source = sliced.source; + this.grid = sliced.grid.intersect(intGrid); + } else if (source instanceof FiniteSampledGrid) { + FiniteSampledGrid finite = (FiniteSampledGrid) source; + this.source = source; + this.grid = finite.getGrid().intersect(intGrid); + } else { + this.source = source; + this.grid = intGrid; + } + } + + public IntGrid getGrid() { + return grid; + } + + public T get(Vec3i cell) { + return source.get(cell); + } +} diff --git a/src/main/java/t/j/dual/StoredFiniteSampledGrid.java b/src/main/java/t/j/dual/StoredFiniteSampledGrid.java new file mode 100644 index 0000000..2a9affe --- /dev/null +++ b/src/main/java/t/j/dual/StoredFiniteSampledGrid.java @@ -0,0 +1,45 @@ +package t.j.dual; + +import java.util.ArrayList; +import java.util.function.Function; + +import net.minecraft.util.math.*; + +import t.j.dual.Utilities; + +public class StoredFiniteSampledGrid implements FiniteSampledGrid { + private ArrayList samples; + private IntGrid grid; + + public StoredFiniteSampledGrid(IntGrid grid, ArrayList samples) { + this.grid = grid; + this.samples = samples; + if (samples.size() != grid.numPoints()) { + Utilities.logger.warn("[StoredFiniteSampledGrid constructor] Size of grid does not match size of samples array!"); + } + } + + public StoredFiniteSampledGrid(PointGrid grid, Function f) { + this.grid = grid.domain; + samples = new ArrayList(grid.numPoints()); + for (Vec3d coords: grid) { + samples.add(f.apply(coords)); + } + } + + public StoredFiniteSampledGrid(FiniteSampledGrid source) { + this.grid = source.getGrid(); + samples = new ArrayList(grid.numPoints()); + for (Vec3i coords: grid) { + samples.add(source.get(coords)); + } + } + + public IntGrid getGrid() { + return grid; + } + + public T get(Vec3i coords) { + return samples.get(grid.gridCoordsToN(coords)); + } +} diff --git a/src/main/java/t/j/dual/Utilities.java b/src/main/java/t/j/dual/Utilities.java index 23d9e76..67b0ce3 100644 --- a/src/main/java/t/j/dual/Utilities.java +++ b/src/main/java/t/j/dual/Utilities.java @@ -8,6 +8,24 @@ import org.apache.logging.log4j.Logger; class Utilities { public static Logger logger; + // Modulus m, not remainder r. For all b > 0, a: 0 ≤ m < b + public static int iMod(int a, int b) { + if (a >= 0) { + return a % b; + } else { + return b + a % b; + } + } + + // Euclidean division, q. For all a, b: qb + m = a + public static int iDiv(int a, int b) { + if (a >= 0) { + return a / b; + } else { + return a / b - 1; + } + } + public static int drawPoisson(Random random, float mean) { float target = (float) Math.exp(-mean); float l = 1.0f; @@ -37,6 +55,22 @@ class Utilities { return new Vec3d(1.0 / b.x, 1.0 / b.y, 1.0 / b.z); } + public static Vec3i viSub(Vec3i a, Vec3i b) { + return new Vec3i(a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ()); + } + + public static Vec3i viAdd(Vec3i a, Vec3i b) { + return new Vec3i(a.getX() + b.getX(), a.getY() + b.getY(), a.getZ() + b.getZ()); + } + + public static Vec3i ewiMax(Vec3i a, Vec3i b) { + return new Vec3i(Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY()), Math.max(a.getZ(), b.getZ())); + } + + public static Vec3i ewiMin(Vec3i a, Vec3i b) { + return new Vec3i(Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY()), Math.min(a.getZ(), b.getZ())); + } + public static class LFSRRandom extends Random { private long seed; @@ -49,7 +83,7 @@ class Utilities { // 63 zeros is extended to 64 zeroes. This is to (a) prevent degeneraccy // when the LFSR's seed becomes zero; and (b) balance the number of zeroes // and ones exactly. - public long shiftAndFeedback(long in) { + public static long shiftAndFeedback(long in) { if ((in & 0x7fffffff) == 0) { if (in == 0) { return 1; @@ -81,5 +115,34 @@ class Utilities { return trimmed; } } + + // See arXiv:1704.00358v5, "Middle Square Weyl Sequence RNG", Bernard Widynski + public static class MiddleSquareWeylRandom extends Random { + private long seed; + private long oddConstant; + private long sequenceElement; + + public MiddleSquareWeylRandom(long seed, long oddConstant) { + this.setOddConstant(oddConstant); + this.setSeed(seed); + } + + public void setSeed(long seed) { + this.seed = seed; + this.sequenceElement = seed + oddConstant; + } + + public void setOddConstant(long oddConstant) { + this.oddConstant = oddConstant | 1; + } + + // I'm not bothering to mask out the upper bits in the hope that it will be satistactory. + protected int next(int bits) { + sequenceElement += oddConstant; + seed = seed * seed + sequenceElement; + seed = (seed >>> 32) | (seed << 32); + return (int) seed; + } + } }