Advanced mantle editing

This commit is contained in:
cyberpwn
2021-08-19 16:36:45 -04:00
parent e7727afbe8
commit fc73e0e482
4 changed files with 865 additions and 13 deletions
@@ -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);
}
}