geforkt von Mirrors/FastAsyncWorldEdit
Merge pull request #568 from HazelTheWitch/fix-spline-sweep
Fixed NullPointerException in Spline and Sweep brush
Dieser Commit ist enthalten in:
Commit
75cbae8261
@ -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) {
|
||||
|
@ -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));
|
||||
|
@ -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))
|
||||
);
|
||||
|
||||
return pasteBlocks(target, offset, angle);
|
||||
angle = ((angle % 360) + 360) % 360; // Wrap to 360 degrees
|
||||
|
||||
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 {
|
||||
|
@ -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,38 +85,27 @@ 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;
|
||||
double length = interpol.arcLength(0, 1);
|
||||
double step = 1 / (length * quality);
|
||||
for (double pos = 0; pos <= 1; pos += step) {
|
||||
spline.pastePosition(pos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -775,6 +775,10 @@ public abstract class BlockVector3 {
|
||||
}
|
||||
|
||||
public final boolean equals(BlockVector3 other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return other.getX() == this.getX() && other.getY() == this.getY() && other.getZ() == this.getZ();
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.math.MutableBlockVector3;
|
||||
import com.sk89q.worldedit.math.MutableVector3;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -43,7 +44,7 @@ public class KochanekBartelsInterpolation implements Interpolation {
|
||||
private Vector3[] coeffC;
|
||||
private Vector3[] coeffD;
|
||||
private double scaling;
|
||||
private final MutableBlockVector3 mutable = new MutableBlockVector3();
|
||||
private final MutableVector3 mutable = new MutableVector3();
|
||||
|
||||
public KochanekBartelsInterpolation() {
|
||||
setNodes(Collections.emptyList());
|
||||
@ -166,7 +167,7 @@ public class KochanekBartelsInterpolation implements Interpolation {
|
||||
mutable.mutX((a.getX() * r3 + b.getX() * r2 + c.getX() * remainder + d.getX()));
|
||||
mutable.mutY((a.getY() * r3 + b.getY() * r2 + c.getY() * remainder + d.getY()));
|
||||
mutable.mutZ((a.getZ() * r3 + b.getZ() * r2 + c.getZ() * remainder + d.getZ()));
|
||||
return mutable.toVector3();
|
||||
return Vector3.at(mutable.getX(), mutable.getY(), mutable.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,8 +17,7 @@ public class RoundedTransform implements Transform {
|
||||
@Override
|
||||
public Vector3 apply(Vector3 input) {
|
||||
Vector3 val = transform.apply(input);
|
||||
return Vector3.at(Math.floor(val.getX() + 0.5), Math.floor(val.getY() + 0.5), Math
|
||||
.floor(val.getY() + 0.5));
|
||||
return val.round();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,7 +128,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
|
||||
public boolean selectPrimary(BlockVector3 position, SelectorLimits limits) {
|
||||
checkNotNull(position);
|
||||
|
||||
if (position1 != null && position.equals(position1)) {
|
||||
if (position.equals(position1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
|
||||
public boolean selectSecondary(BlockVector3 position, SelectorLimits limits) {
|
||||
checkNotNull(position);
|
||||
|
||||
if (position2 != null && position.equals(position2)) {
|
||||
if (position.equals(position2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ public class ExtendingCuboidRegionSelector extends CuboidRegionSelector {
|
||||
|
||||
@Override
|
||||
public boolean selectPrimary(BlockVector3 position, SelectorLimits limits) {
|
||||
if (position1 != null && position2 != null && position.equals(position1) && position.equals(position2)) {
|
||||
if (position.equals(position1) && position.equals(position2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren