mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-03 00:17:15 +00:00
Advanced mantle editing
This commit is contained in:
parent
e7727afbe8
commit
fc73e0e482
@ -18,8 +18,10 @@
|
||||
|
||||
package com.volmit.iris.util.mantle;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.object.basic.IrisPosition;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.documentation.BlockCoordinates;
|
||||
@ -28,17 +30,20 @@ import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import com.volmit.iris.util.math.INode;
|
||||
import com.volmit.iris.util.math.KochanekBartelsInterpolation;
|
||||
import com.volmit.iris.util.math.M;
|
||||
import com.volmit.iris.util.math.PathInterpolation;
|
||||
import com.volmit.iris.util.matter.Matter;
|
||||
import com.volmit.iris.util.parallel.BurstExecutor;
|
||||
import com.volmit.iris.util.parallel.HyperLock;
|
||||
import com.volmit.iris.util.parallel.MultiBurst;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -79,6 +84,13 @@ public class Mantle {
|
||||
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Raise a flag if it is lowered currently, If the flag was raised, execute the runnable
|
||||
* @param x the chunk x
|
||||
* @param z the chunk z
|
||||
* @param flag the flag to raise
|
||||
* @param r the runnable to fire if the flag is now raised (and was previously lowered)
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public void raiseFlag(int x, int z, MantleFlag flag, Runnable r) {
|
||||
if (!hasFlag(x, z, flag)) {
|
||||
@ -87,6 +99,13 @@ public class Mantle {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower a flag if it is raised. If the flag was lowered (meaning it was previously raised), execute the runnable
|
||||
* @param x the chunk x
|
||||
* @param z the chunk z
|
||||
* @param flag the flag to lower
|
||||
* @param r the runnable that is fired if the flag was raised but is now lowered
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public void lowerFlag(int x, int z, MantleFlag flag, Runnable r) {
|
||||
if (hasFlag(x, z, flag)) {
|
||||
@ -95,11 +114,27 @@ public class Mantle {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag or unflag a chunk
|
||||
* @param x the chunk x
|
||||
* @param z the chunk z
|
||||
* @param flag the flag
|
||||
* @param flagged should it be set to flagged or not
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public void flag(int x, int z, MantleFlag flag, boolean flagged) {
|
||||
get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31).flag(flag, flagged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate data in a chunk
|
||||
* @param x the chunk x
|
||||
* @param z the chunk z
|
||||
* @param type the type of data to iterate
|
||||
* @param iterator the iterator (x,y,z,data) -> do stuff
|
||||
* @param requiredFlags any required flags that must be met for this chunk to be iterated
|
||||
* @param <T> the type of data to iterate
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public <T> void iterateChunk(int x, int z, Class<T> type, Consumer4<Integer, Integer, Integer, T> iterator, MantleFlag... requiredFlags) {
|
||||
for (MantleFlag i : requiredFlags) {
|
||||
@ -111,17 +146,13 @@ public class Mantle {
|
||||
get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31).iterate(type, iterator);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
public <T> void iterateChunk(int x, int z, Class<T> type, Consumer4<Integer, Integer, Integer, T> iterator, BurstExecutor e, MantleFlag... requiredFlags) {
|
||||
for (MantleFlag i : requiredFlags) {
|
||||
if (!hasFlag(x, z, i)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31).iterate(type, iterator, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this chunk have a flag on it?
|
||||
* @param x the x
|
||||
* @param z the z
|
||||
* @param flag the flag to test
|
||||
* @return true if it's flagged
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public boolean hasFlag(int x, int z, MantleFlag flag) {
|
||||
return get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31).isFlagged(flag);
|
||||
@ -190,6 +221,10 @@ public class Mantle {
|
||||
.get(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this mantle closed
|
||||
* @return true if it is
|
||||
*/
|
||||
public boolean isClosed()
|
||||
{
|
||||
return closed.get();
|
||||
@ -367,10 +402,23 @@ public class Mantle {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file for a region
|
||||
* @param folder the folder
|
||||
* @param x the x coord
|
||||
* @param z the z coord
|
||||
* @return the file
|
||||
*/
|
||||
public static File fileForRegion(File folder, int x, int z) {
|
||||
return fileForRegion(folder, key(x, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file for the given region
|
||||
* @param folder the data folder
|
||||
* @param key the region key
|
||||
* @return the file
|
||||
*/
|
||||
public static File fileForRegion(File folder, Long key) {
|
||||
String id = UUID.nameUUIDFromBytes(("TectonicPlate:" + key).getBytes(StandardCharsets.UTF_8)).toString();
|
||||
File f = new File(folder, id.substring(0, 2) + "/" + id.split("\\Q-\\E")[3] + "/" + id + ".ttp");
|
||||
@ -378,6 +426,12 @@ public class Mantle {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the long value representing a chunk or region coordinate
|
||||
* @param x the x
|
||||
* @param z the z
|
||||
* @return the value
|
||||
*/
|
||||
public static Long key(int x, int z) {
|
||||
return Cache.key(x, z);
|
||||
}
|
||||
@ -385,4 +439,437 @@ public class Mantle {
|
||||
public void saveAll() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a sphere into the mantle
|
||||
* @param cx the center x
|
||||
* @param cy the center y
|
||||
* @param cz the center z
|
||||
* @param radius the radius of this sphere
|
||||
* @param fill should it be filled? or just the outer shell?
|
||||
* @param data the data to set
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
public <T> void setSphere(int cx, int cy, int cz, double radius, boolean fill, T data)
|
||||
{
|
||||
setElipsoid(cx, cy, cz, radius, radius, radius, fill, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an elipsoid into the mantle
|
||||
* @param cx the center x
|
||||
* @param cy the center y
|
||||
* @param cz the center z
|
||||
* @param rx the x radius
|
||||
* @param ry the y radius
|
||||
* @param rz the z radius
|
||||
* @param fill should it be filled or just the outer shell?
|
||||
* @param data the data to set
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
public <T> void setElipsoid(int cx, int cy, int cz, double rx, double ry, double rz, boolean fill, T data)
|
||||
{
|
||||
rx += 0.5;
|
||||
ry += 0.5;
|
||||
rz += 0.5;
|
||||
final double invRadiusX = 1 / rx;
|
||||
final double invRadiusY = 1 / ry;
|
||||
final double invRadiusZ = 1 / rz;
|
||||
final int ceilRadiusX = (int) Math.ceil(rx);
|
||||
final int ceilRadiusY = (int) Math.ceil(ry);
|
||||
final int ceilRadiusZ = (int) Math.ceil(rz);
|
||||
double nextXn = 0;
|
||||
|
||||
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
final double xn = nextXn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
double nextYn = 0;
|
||||
forY: for (int y = 0; y <= ceilRadiusY; ++y) {
|
||||
final double yn = nextYn;
|
||||
nextYn = (y + 1) * invRadiusY;
|
||||
double nextZn = 0;
|
||||
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
final double zn = nextZn;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
|
||||
double distanceSq = lengthSq(xn, yn, zn);
|
||||
if (distanceSq > 1) {
|
||||
if (z == 0) {
|
||||
if (y == 0) {
|
||||
break forX;
|
||||
}
|
||||
break forY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fill) {
|
||||
if (lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
set(x + cx,y + cy,z + cz, data);
|
||||
set(-x + cx,y + cy,z + cz, data);
|
||||
set(x + cx,-y + cy,z + cz, data);
|
||||
set(x + cx,y + cy,-z + cz, data);
|
||||
set(-x + cx,y + cy,-z + cz, data);
|
||||
set(-x + cx,-y + cy,z + cz, data);
|
||||
set(x + cx,-y + cy,-z + cz, data);
|
||||
set(-x + cx,y + cy,-z + cz, data);
|
||||
set(-x + cx,-y + cy,-z + cz, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cuboid of data in the mantle
|
||||
* @param x1 the min x
|
||||
* @param y1 the min y
|
||||
* @param z1 the min z
|
||||
* @param x2 the max x
|
||||
* @param y2 the max y
|
||||
* @param z2 the max z
|
||||
* @param data the data to set
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
public <T> void setCuboid(int x1, int y1, int z1, int x2, int y2, int z2, T data)
|
||||
{
|
||||
int j,k;
|
||||
|
||||
for(int i = x1; i <= x2; i++)
|
||||
{
|
||||
for(j = x1; j <= x2; j++)
|
||||
{
|
||||
for(k = x1; k <= x2; k++)
|
||||
{
|
||||
set(i,j,k,data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a pyramid of data in the mantle
|
||||
* @param cx the center x
|
||||
* @param cy the base y
|
||||
* @param cz the center z
|
||||
* @param data the data to set
|
||||
* @param size the size of the pyramid (width of base & height)
|
||||
* @param filled should it be filled or hollow
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public <T> void setPyramid(int cx, int cy, int cz, T data, int size, boolean filled) {
|
||||
int height = size;
|
||||
|
||||
for (int y = 0; y <= height; ++y) {
|
||||
size--;
|
||||
for (int x = 0; x <= size; ++x) {
|
||||
for (int z = 0; z <= size; ++z) {
|
||||
if ((filled && z <= size && x <= size) || z == size || x == size) {
|
||||
set(x + cx, y + cy, z + cz, data);
|
||||
set(-x + cx, y + cy, z + cz, data);
|
||||
set(x + cx, y + cy, -z + cz, data);
|
||||
set(-x + cx, y + cy, -z + cz, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a 3d tube spline interpolated with Kochanek Bartels
|
||||
* @param nodevectors the vector points
|
||||
* @param radius the radius
|
||||
* @param filled if it should be filled or hollow
|
||||
* @param data the data to set
|
||||
*/
|
||||
public <T> void setSpline(List<Vector> nodevectors, double radius, boolean filled, T data) {
|
||||
setSpline(nodevectors, 0, 0, 0, 10, radius, filled, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a 3d tube spline interpolated with Kochanek Bartels
|
||||
* @param nodevectors the spline points
|
||||
* @param tension the tension 0
|
||||
* @param bias the bias 0
|
||||
* @param continuity the continuity 0
|
||||
* @param quality the quality 10
|
||||
* @param radius the radius
|
||||
* @param filled filled or hollow
|
||||
* @param data the data to set
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
public <T> void setSpline(List<Vector> nodevectors, double tension, double bias, double continuity, double quality, double radius, boolean filled, T data) {
|
||||
Set<IrisPosition> vset = new KSet<>();
|
||||
List<INode> nodes = new ArrayList<>(nodevectors.size());
|
||||
PathInterpolation interpol = new KochanekBartelsInterpolation();
|
||||
|
||||
for (Vector nodevector : nodevectors) {
|
||||
INode n = new INode(nodevector);
|
||||
n.setTension(tension);
|
||||
n.setBias(bias);
|
||||
n.setContinuity(continuity);
|
||||
nodes.add(n);
|
||||
}
|
||||
|
||||
interpol.setNodes(nodes);
|
||||
double splinelength = interpol.arcLength(0, 1);
|
||||
for (double loop = 0; loop <= 1; loop += 1D / splinelength / quality) {
|
||||
Vector tipv = interpol.getPosition(loop);
|
||||
vset.add(new IrisPosition(tipv.toBlockVector()));
|
||||
}
|
||||
|
||||
vset = getBallooned(vset, radius);
|
||||
if (!filled) {
|
||||
vset = getHollowed(vset);
|
||||
}
|
||||
|
||||
set(vset, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a 3d line
|
||||
* @param a the first point
|
||||
* @param b the second point
|
||||
* @param radius the radius
|
||||
* @param filled hollow or filled?
|
||||
* @param data the data
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
public <T> void setLine(IrisPosition a, IrisPosition b, double radius, boolean filled, T data)
|
||||
{
|
||||
setLine(ImmutableList.of(a, b), radius, filled, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lines for points
|
||||
* @param vectors the points
|
||||
* @param radius the radius
|
||||
* @param filled hollow or filled?
|
||||
* @param data the data to set
|
||||
* @param <T> the type of data to apply to the mantle
|
||||
*/
|
||||
public <T> void setLine(List<IrisPosition> vectors, double radius, boolean filled, T data) {
|
||||
Set<IrisPosition> vset = new KSet<>();
|
||||
|
||||
for (int i = 0; vectors.size() != 0 && i < vectors.size() - 1; i++) {
|
||||
IrisPosition pos1 = vectors.get(i);
|
||||
IrisPosition pos2 = vectors.get(i + 1);
|
||||
int x1 = pos1.getX();
|
||||
int y1 = pos1.getY();
|
||||
int z1 = pos1.getZ();
|
||||
int x2 = pos2.getX();
|
||||
int y2 = pos2.getY();
|
||||
int z2 = pos2.getZ();
|
||||
int tipx = x1;
|
||||
int tipy = y1;
|
||||
int tipz = z1;
|
||||
int dx = Math.abs(x2 - x1);
|
||||
int dy = Math.abs(y2 - y1);
|
||||
int dz = Math.abs(z2 - z1);
|
||||
|
||||
if (dx + dy + dz == 0) {
|
||||
vset.add(new IrisPosition(tipx, tipy, tipz));
|
||||
continue;
|
||||
}
|
||||
|
||||
int dMax = Math.max(Math.max(dx, dy), dz);
|
||||
if (dMax == dx) {
|
||||
for (int domstep = 0; domstep <= dx; domstep++) {
|
||||
tipx = x1 + domstep * (x2 - x1 > 0 ? 1 : -1);
|
||||
tipy = (int) Math.round(y1 + domstep * ((double) dy) / ((double) dx) * (y2 - y1 > 0 ? 1 : -1));
|
||||
tipz = (int) Math.round(z1 + domstep * ((double) dz) / ((double) dx) * (z2 - z1 > 0 ? 1 : -1));
|
||||
|
||||
vset.add(new IrisPosition(tipx, tipy, tipz));
|
||||
}
|
||||
} else if (dMax == dy) {
|
||||
for (int domstep = 0; domstep <= dy; domstep++) {
|
||||
tipy = y1 + domstep * (y2 - y1 > 0 ? 1 : -1);
|
||||
tipx = (int) Math.round(x1 + domstep * ((double) dx) / ((double) dy) * (x2 - x1 > 0 ? 1 : -1));
|
||||
tipz = (int) Math.round(z1 + domstep * ((double) dz) / ((double) dy) * (z2 - z1 > 0 ? 1 : -1));
|
||||
|
||||
vset.add(new IrisPosition(tipx, tipy, tipz));
|
||||
}
|
||||
} else /* if (dMax == dz) */ {
|
||||
for (int domstep = 0; domstep <= dz; domstep++) {
|
||||
tipz = z1 + domstep * (z2 - z1 > 0 ? 1 : -1);
|
||||
tipy = (int) Math.round(y1 + domstep * ((double) dy) / ((double) dz) * (y2 - y1 > 0 ? 1 : -1));
|
||||
tipx = (int) Math.round(x1 + domstep * ((double) dx) / ((double) dz) * (x2 - x1 > 0 ? 1 : -1));
|
||||
|
||||
vset.add(new IrisPosition(tipx, tipy, tipz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vset = getBallooned(vset, radius);
|
||||
|
||||
if (!filled) {
|
||||
vset = getHollowed(vset);
|
||||
}
|
||||
|
||||
set(vset, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cylinder in the mantle
|
||||
* @param cx the center x
|
||||
* @param cy the base y
|
||||
* @param cz the center z
|
||||
* @param data the data to set
|
||||
* @param radius the radius
|
||||
* @param height the height of the cyl
|
||||
* @param filled filled or not
|
||||
*/
|
||||
public <T> void setCylinder(int cx, int cy, int cz, T data, double radius, int height, boolean filled){
|
||||
setCylinder(cx, cy, cz, data, radius, radius, height, filled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cylinder in the mantle
|
||||
* @param cx the center x
|
||||
* @param cy the base y
|
||||
* @param cz the center z
|
||||
* @param data the data to set
|
||||
* @param radiusX the x radius
|
||||
* @param radiusZ the z radius
|
||||
* @param height the height of this cyl
|
||||
* @param filled filled or hollow?
|
||||
*/
|
||||
public <T> void setCylinder(int cx, int cy, int cz, T data, double radiusX, double radiusZ, int height, boolean filled) {
|
||||
int affected = 0;
|
||||
radiusX += 0.5;
|
||||
radiusZ += 0.5;
|
||||
|
||||
if (height == 0) {
|
||||
return;
|
||||
} else if (height < 0) {
|
||||
height = -height;
|
||||
cy = cy - height;
|
||||
}
|
||||
|
||||
if (cy < 0) {
|
||||
cy = 0;
|
||||
} else if (cy + height - 1 > worldHeight) {
|
||||
height = worldHeight - cy + 1;
|
||||
}
|
||||
|
||||
final double invRadiusX = 1 / radiusX;
|
||||
final double invRadiusZ = 1 / radiusZ;
|
||||
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
||||
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
||||
double nextXn = 0;
|
||||
|
||||
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
final double xn = nextXn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
double nextZn = 0;
|
||||
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
final double zn = nextZn;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
double distanceSq = lengthSq(xn, zn);
|
||||
|
||||
if (distanceSq > 1) {
|
||||
if (z == 0) {
|
||||
break forX;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filled) {
|
||||
if (lengthSq(nextXn, zn) <= 1 && lengthSq(xn, nextZn) <= 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
set(cx + x, cy + y, cz + z, data);
|
||||
set(cx + -x, cy + y, cz + z, data);
|
||||
set(cx + x, cy + y, cz + -z, data);
|
||||
set(cx + -x, cy + y, cz + -z, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <T> void set(IrisPosition pos, T data)
|
||||
{
|
||||
set(pos.getX(), pos.getY(), pos.getZ(), data);
|
||||
}
|
||||
|
||||
public <T> void set(List<IrisPosition> positions, T data)
|
||||
{
|
||||
for(IrisPosition i : positions)
|
||||
{
|
||||
set(i, data);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void set(Set<IrisPosition> positions, T data)
|
||||
{
|
||||
for(IrisPosition i : positions)
|
||||
{
|
||||
set(i, data);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<IrisPosition> getBallooned(Set<IrisPosition> vset, double radius) {
|
||||
Set<IrisPosition> returnset = new HashSet<>();
|
||||
int ceilrad = (int) Math.ceil(radius);
|
||||
|
||||
for (IrisPosition v : vset) {
|
||||
int tipx = v.getX();
|
||||
int tipy = v.getY();
|
||||
int tipz = v.getZ();
|
||||
|
||||
for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) {
|
||||
for (int loopy = tipy - ceilrad; loopy <= tipy + ceilrad; loopy++) {
|
||||
for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) {
|
||||
if (hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= radius) {
|
||||
returnset.add(new IrisPosition(loopx, loopy, loopz));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnset;
|
||||
}
|
||||
|
||||
private static Set<IrisPosition> getHollowed(Set<IrisPosition> vset) {
|
||||
Set<IrisPosition> returnset = new KSet<>();
|
||||
for (IrisPosition v : vset) {
|
||||
double x = v.getX();
|
||||
double y = v.getY();
|
||||
double z = v.getZ();
|
||||
if (!(vset.contains(new IrisPosition(x + 1, y, z))
|
||||
&& vset.contains(new IrisPosition(x - 1, y, z))
|
||||
&& vset.contains(new IrisPosition(x, y + 1, z))
|
||||
&& vset.contains(new IrisPosition(x, y - 1, z))
|
||||
&& vset.contains(new IrisPosition(x, y, z + 1))
|
||||
&& vset.contains(new IrisPosition(x, y, z - 1)))) {
|
||||
returnset.add(v);
|
||||
}
|
||||
}
|
||||
return returnset;
|
||||
}
|
||||
|
||||
private static double hypot(double... pars) {
|
||||
double sum = 0;
|
||||
for (double d : pars) {
|
||||
sum += Math.pow(d, 2);
|
||||
}
|
||||
return Math.sqrt(sum);
|
||||
}
|
||||
|
||||
private static double lengthSq(double x, double y, double z) {
|
||||
return (x * x) + (y * y) + (z * z);
|
||||
}
|
||||
|
||||
private static double lengthSq(double x, double z) {
|
||||
return (x * x) + (z * z);
|
||||
}
|
||||
}
|
||||
|
47
src/main/java/com/volmit/iris/util/math/INode.java
Normal file
47
src/main/java/com/volmit/iris/util/math/INode.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.math;
|
||||
|
||||
import lombok.Data;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@Data
|
||||
public class INode {
|
||||
|
||||
private Vector position;
|
||||
private double tension;
|
||||
private double bias;
|
||||
private double continuity;
|
||||
|
||||
public INode() {
|
||||
this(new Vector(0,0,0));
|
||||
}
|
||||
|
||||
public INode(INode other) {
|
||||
this.position = other.position;
|
||||
|
||||
this.tension = other.tension;
|
||||
this.bias = other.bias;
|
||||
this.continuity = other.continuity;
|
||||
}
|
||||
|
||||
public INode(Vector position) {
|
||||
this.position = position;
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.math;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class KochanekBartelsInterpolation implements PathInterpolation {
|
||||
|
||||
private List<INode> nodes;
|
||||
private Vector[] coeffA;
|
||||
private Vector[] coeffB;
|
||||
private Vector[] coeffC;
|
||||
private Vector[] coeffD;
|
||||
private double scaling;
|
||||
|
||||
public KochanekBartelsInterpolation() {
|
||||
setNodes(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodes(List<INode> nodes) {
|
||||
this.nodes = nodes;
|
||||
recalc();
|
||||
}
|
||||
|
||||
private void recalc() {
|
||||
final int nNodes = nodes.size();
|
||||
coeffA = new Vector[nNodes];
|
||||
coeffB = new Vector[nNodes];
|
||||
coeffC = new Vector[nNodes];
|
||||
coeffD = new Vector[nNodes];
|
||||
|
||||
if (nNodes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
INode nodeB = nodes.get(0);
|
||||
double tensionB = nodeB.getTension();
|
||||
double biasB = nodeB.getBias();
|
||||
double continuityB = nodeB.getContinuity();
|
||||
for (int i = 0; i < nNodes; ++i) {
|
||||
final double tensionA = tensionB;
|
||||
final double biasA = biasB;
|
||||
final double continuityA = continuityB;
|
||||
|
||||
if (i + 1 < nNodes) {
|
||||
nodeB = nodes.get(i + 1);
|
||||
tensionB = nodeB.getTension();
|
||||
biasB = nodeB.getBias();
|
||||
continuityB = nodeB.getContinuity();
|
||||
}
|
||||
|
||||
// Kochanek-Bartels tangent coefficients
|
||||
final double ta = (1 - tensionA) * (1 + biasA) * (1 + continuityA) / 2; // Factor for lhs of d[i]
|
||||
final double tb = (1 - tensionA) * (1 - biasA) * (1 - continuityA) / 2; // Factor for rhs of d[i]
|
||||
final double tc = (1 - tensionB) * (1 + biasB) * (1 - continuityB) / 2; // Factor for lhs of d[i+1]
|
||||
final double td = (1 - tensionB) * (1 - biasB) * (1 + continuityB) / 2; // Factor for rhs of d[i+1]
|
||||
|
||||
coeffA[i] = linearCombination(i, -ta, ta - tb - tc + 2, tb + tc - td - 2, td);
|
||||
coeffB[i] = linearCombination(i, 2 * ta, -2 * ta + 2 * tb + tc - 3, -2 * tb - tc + td + 3, -td);
|
||||
coeffC[i] = linearCombination(i, -ta, ta - tb, tb, 0);
|
||||
//coeffD[i] = linearCombination(i, 0, 1, 0, 0);
|
||||
coeffD[i] = retrieve(i); // this is an optimization
|
||||
}
|
||||
|
||||
scaling = nodes.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the linear combination of the given coefficients with the nodes adjacent to baseIndex.
|
||||
*
|
||||
* @param baseIndex node index
|
||||
* @param f1 coefficient for baseIndex-1
|
||||
* @param f2 coefficient for baseIndex
|
||||
* @param f3 coefficient for baseIndex+1
|
||||
* @param f4 coefficient for baseIndex+2
|
||||
* @return linear combination of nodes[n-1..n+2] with f1..4
|
||||
*/
|
||||
private Vector linearCombination(int baseIndex, double f1, double f2, double f3, double f4) {
|
||||
final Vector r1 = retrieve(baseIndex - 1).multiply(f1);
|
||||
final Vector r2 = retrieve(baseIndex ).multiply(f2);
|
||||
final Vector r3 = retrieve(baseIndex + 1).multiply(f3);
|
||||
final Vector r4 = retrieve(baseIndex + 2).multiply(f4);
|
||||
|
||||
return r1.add(r2).add(r3).add(r4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a node. Indexes are clamped to the valid range.
|
||||
*
|
||||
* @param index node index to retrieve
|
||||
* @return nodes[clamp(0, nodes.length-1)]
|
||||
*/
|
||||
private Vector retrieve(int index) {
|
||||
if (index < 0) {
|
||||
return fastRetrieve(0);
|
||||
}
|
||||
|
||||
if (index >= nodes.size()) {
|
||||
return fastRetrieve(nodes.size() - 1);
|
||||
}
|
||||
|
||||
return fastRetrieve(index);
|
||||
}
|
||||
|
||||
private Vector fastRetrieve(int index) {
|
||||
return nodes.get(index).getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getPosition(double position) {
|
||||
if (coeffA == null) {
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
}
|
||||
|
||||
if (position > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
position *= scaling;
|
||||
|
||||
final int index = (int) Math.floor(position);
|
||||
final double remainder = position - index;
|
||||
|
||||
final Vector a = coeffA[index];
|
||||
final Vector b = coeffB[index];
|
||||
final Vector c = coeffC[index];
|
||||
final Vector d = coeffD[index];
|
||||
|
||||
return a.multiply(remainder).add(b).multiply(remainder).add(c).multiply(remainder).add(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector get1stDerivative(double position) {
|
||||
if (coeffA == null) {
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
}
|
||||
|
||||
if (position > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
position *= scaling;
|
||||
|
||||
final int index = (int) Math.floor(position);
|
||||
//final double remainder = position - index;
|
||||
|
||||
final Vector a = coeffA[index];
|
||||
final Vector b = coeffB[index];
|
||||
final Vector c = coeffC[index];
|
||||
|
||||
return a.multiply(1.5 * position - 3.0 * index).add(b).multiply(2.0 * position).add(a.multiply(1.5 * index).subtract(b).multiply(2.0 * index)).add(c).multiply(scaling);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double arcLength(double positionA, double positionB) {
|
||||
if (coeffA == null) {
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
}
|
||||
|
||||
if (positionA > positionB) {
|
||||
return arcLength(positionB, positionA);
|
||||
}
|
||||
|
||||
positionA *= scaling;
|
||||
positionB *= scaling;
|
||||
|
||||
final int indexA = (int) Math.floor(positionA);
|
||||
final double remainderA = positionA - indexA;
|
||||
|
||||
final int indexB = (int) Math.floor(positionB);
|
||||
final double remainderB = positionB - indexB;
|
||||
|
||||
return arcLengthRecursive(indexA, remainderA, indexB, remainderB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a < b.
|
||||
*/
|
||||
private double arcLengthRecursive(int indexLeft, double remainderLeft, int indexRight, double remainderRight) {
|
||||
switch (indexRight - indexLeft) {
|
||||
case 0:
|
||||
return arcLengthRecursive(indexLeft, remainderLeft, remainderRight);
|
||||
|
||||
case 1:
|
||||
// This case is merely a speed-up for a very common case
|
||||
return arcLengthRecursive(indexLeft, remainderLeft, 1.0)
|
||||
+ arcLengthRecursive(indexRight, 0.0, remainderRight);
|
||||
|
||||
default:
|
||||
return arcLengthRecursive(indexLeft, remainderLeft, indexRight - 1, 1.0)
|
||||
+ arcLengthRecursive(indexRight, 0.0, remainderRight);
|
||||
}
|
||||
}
|
||||
|
||||
private double arcLengthRecursive(int index, double remainderLeft, double remainderRight) {
|
||||
final Vector a = coeffA[index].multiply(3.0);
|
||||
final Vector b = coeffB[index].multiply(2.0);
|
||||
final Vector c = coeffC[index];
|
||||
|
||||
final int nPoints = 8;
|
||||
|
||||
double accum = a.multiply(remainderLeft).add(b).multiply(remainderLeft).add(c).length() / 2.0;
|
||||
for (int i = 1; i < nPoints - 1; ++i) {
|
||||
double t = ((double) i) / nPoints;
|
||||
t = (remainderRight - remainderLeft) * t + remainderLeft;
|
||||
accum += a.multiply(t).add(b).multiply(t).add(c).length();
|
||||
}
|
||||
|
||||
accum += a.multiply(remainderRight).add(b).multiply(remainderRight).add(c).length() / 2.0;
|
||||
return accum * (remainderRight - remainderLeft) / nPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSegment(double position) {
|
||||
if (coeffA == null) {
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
}
|
||||
|
||||
if (position > 1) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
position *= scaling;
|
||||
|
||||
return (int) Math.floor(position);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.math;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PathInterpolation {
|
||||
|
||||
/**
|
||||
* Sets nodes to be used by subsequent calls to
|
||||
* {@link #getPosition(double)} and the other methods.
|
||||
*
|
||||
* @param nodes the nodes
|
||||
*/
|
||||
void setNodes(List<INode> nodes);
|
||||
|
||||
/**
|
||||
* Gets the result of f(position).
|
||||
*
|
||||
* @param position the position to interpolate
|
||||
* @return the result
|
||||
*/
|
||||
Vector getPosition(double position);
|
||||
|
||||
/**
|
||||
* Gets the result of f'(position).
|
||||
*
|
||||
* @param position the position to interpolate
|
||||
* @return the result
|
||||
*/
|
||||
Vector get1stDerivative(double position);
|
||||
|
||||
/**
|
||||
* Gets the result of ∫<sub>a</sub><sup style="position: relative; left: -1ex">b</sup>|f'(t)| dt.<br />
|
||||
* That means it calculates the arc length (in meters) between positionA
|
||||
* and positionB.
|
||||
*
|
||||
* @param positionA lower limit
|
||||
* @param positionB upper limit
|
||||
* @return the arc length
|
||||
*/
|
||||
double arcLength(double positionA, double positionB);
|
||||
|
||||
/**
|
||||
* Get the segment position.
|
||||
*
|
||||
* @param position the position
|
||||
* @return the segment position
|
||||
*/
|
||||
int getSegment(double position);
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user