Merge pull request #568 from HazelTheWitch/fix-spline-sweep

Fixed NullPointerException in Spline and Sweep brush
This commit is contained in:
NotMyFault
2020-08-21 18:25:09 +02:00
committed by GitHub
10 changed files with 61 additions and 43 deletions

View File

@ -59,7 +59,7 @@ public class SplineBrush implements Brush, ResettableTool {
return;
}
int originalSize = numSplines;
boolean newPos = this.position == null || !position.equals(this.position);
boolean newPos = !position.equals(this.position);
this.position = position;
if (newPos) {
if (positionSets.size() >= MAX_POINTS) {

View File

@ -8,6 +8,7 @@ import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.interpolation.Interpolation;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.RoundedTransform;
@ -80,7 +81,7 @@ public class ClipboardSpline extends Spline {
}
@Override
protected int pasteBlocks(BlockVector3 target, BlockVector3 offset, double angle) throws MaxChangedBlocksException {
protected int pasteBlocks(BlockVector3 target, Vector3 offset, double angle) throws MaxChangedBlocksException {
RoundedTransform transform = new RoundedTransform(new AffineTransform()
.translate(offset)
.rotateY(angle));

View File

@ -21,7 +21,7 @@ import java.util.List;
*/
public abstract class Spline {
private BlockVector2 direction = BlockVector2.at(1, 0);
private Vector2 direction = Vector2.at(1, 0);
private final int nodeCount;
protected EditSession editSession;
@ -77,10 +77,10 @@ public abstract class Spline {
* is rotated by that angle to follow the curve slope.
* <p>
* The default direction is a (1;0) vector (pointing in the positive x-direction).
* @param direction A normalized vector representing the horizontal forward direction of the clipboard content
* @param direction A vector representing the horizontal forward direction of the clipboard content
*/
public void setDirection(BlockVector2 direction) {
this.direction = direction;
public void setDirection(Vector2 direction) {
this.direction = direction.normalize();
}
/**
@ -93,7 +93,7 @@ public abstract class Spline {
* The default direction is a (1;0) vector (pointing in the positive x-direction).
* @return A vector representing the horizontal forward direction of the clipboard content
*/
public BlockVector2 getDirection() {
public Vector2 getDirection() {
return direction;
}
@ -115,6 +115,13 @@ public abstract class Spline {
}
}
/**
* 2 dimensional "cross" product. cross2D(v1, v2) = |v1|*|v2|*sin(theta) or v1 X v2 taking Y to be 0
*/
private double cross2D(Vector2 v1, Vector2 v2) {
return v1.getX() * v2.getZ() - v2.getX() * v1.getZ();
}
/**
* Paste structure at the provided position on the curve. The position will not be position-corrected
* but will be passed directly to the interpolation algorithm.
@ -127,22 +134,24 @@ public abstract class Spline {
Preconditions.checkArgument(position <= 1);
// Calculate position from spline
BlockVector3 target = interpolation.getPosition(position).toBlockPoint();
BlockVector3 offset = target.subtract(target.round());
target = target.subtract(offset);
Vector3 target = interpolation.getPosition(position);
BlockVector3 blockTarget = target.toBlockPoint();
Vector3 offset = target.subtract(target.floor());
// Calculate rotation from spline
Vector3 deriv = interpolation.get1stDerivative(position);
Vector2 deriv2D = Vector2.at(deriv.getX(), deriv.getZ()).normalize();
double angle = Math.toDegrees(
Math.atan2(direction.getZ(), direction.getX()) - Math.atan2(deriv2D.getZ(), deriv2D.getX())
-Math.atan2(cross2D(direction, deriv2D), direction.dot(deriv2D))
);
angle = ((angle % 360) + 360) % 360; // Wrap to 360 degrees
return pasteBlocks(target, offset, angle);
return pasteBlocks(blockTarget, offset, angle);
}
protected abstract int pasteBlocks(BlockVector3 target, BlockVector3 offset, double angle) throws MaxChangedBlocksException;
protected abstract int pasteBlocks(BlockVector3 target, Vector3 offset, double angle) throws MaxChangedBlocksException;
private void initSections() {
int sectionCount = nodeCount - 1;
@ -176,7 +185,15 @@ public abstract class Spline {
double flexOffset = flexPosition - previousSection.flexStart;
double uniOffset = flexOffset / previousSection.flexLength * previousSection.uniLength;
return previousSection.uniStart + uniOffset;
double finalPosition = previousSection.uniStart + uniOffset;
//Really rough fix, but fixes a bug with no visual artifacts so it's probably ok?
//flexPosition very close to 1 causes outputs very slightly higher than 1 on rare occasions
if (finalPosition > 1) {
return 1;
}
return finalPosition;
}
private class Section {

View File

@ -13,11 +13,14 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.MutableVector3;
import com.sk89q.worldedit.math.Vector2;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.interpolation.Interpolation;
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
import com.sk89q.worldedit.math.interpolation.Node;
import com.sk89q.worldedit.math.interpolation.ReparametrisingInterpolation;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
@ -64,7 +67,7 @@ public class SweepBrush implements Brush, ResettableTool {
return;
}
Interpolation interpol = new KochanekBartelsInterpolation();
Interpolation interpol = new ReparametrisingInterpolation(new KochanekBartelsInterpolation());
List<Node> nodes = positions.stream().map(v -> {
Node n = new Node(v.toVector3());
n.setTension(tension);
@ -82,37 +85,26 @@ public class SweepBrush implements Brush, ResettableTool {
Clipboard clipboard = holder.getClipboard();
BlockVector3 dimensions = clipboard.getDimensions();
AffineTransform transform = new AffineTransform();
if (dimensions.getBlockX() > dimensions.getBlockZ()) {
transform = transform.rotateY(90);
}
double quality = Math.max(dimensions.getBlockX(), dimensions.getBlockZ());
AffineTransform transform = new AffineTransform();
ClipboardSpline spline = new ClipboardSpline(editSession, holder, interpol, transform, nodes.size());
if (dimensions.getBlockX() > dimensions.getBlockZ()) {
spline.setDirection(Vector2.at(0, 1));
}
switch (copies) {
case 1: {
spline.pastePosition(0D);
break;
}
case -1: {
double splineLength = interpol.arcLength(0D, 1D);
double blockDistance = 1d / splineLength;
double step = blockDistance / quality;
double accumulation = 0;
MutableVector3 last = new MutableVector3(0, 0, 0);
for (double pos = 0D; pos <= 1D; pos += step) {
Vector3 gradient = interpol.get1stDerivative(pos);
double dist = MathMan.sqrtApprox(last.distanceSq(gradient));
last.mutX(gradient.getX());
last.mutY(gradient.getY());
last.mutZ(gradient.getZ());
double change = dist * step;
// Accumulation is arbitrary, but much faster than calculation overlapping regions
if ((accumulation += change + step * 2) > blockDistance) {
accumulation -= blockDistance;
spline.pastePosition(pos);
}
double length = interpol.arcLength(0, 1);
double step = 1 / (length * quality);
for (double pos = 0; pos <= 1; pos += step) {
spline.pastePosition(pos);
}
break;
}

View File

@ -200,6 +200,10 @@ public abstract class DFSVisitor implements Operation {
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
Node other = (Node) obj;
return other.x == x && other.z == z && other.y == y;
}