Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-12-25 18:40:05 +01:00
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;
|
return;
|
||||||
}
|
}
|
||||||
int originalSize = numSplines;
|
int originalSize = numSplines;
|
||||||
boolean newPos = this.position == null || !position.equals(this.position);
|
boolean newPos = !position.equals(this.position);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
if (newPos) {
|
if (newPos) {
|
||||||
if (positionSets.size() >= MAX_POINTS) {
|
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.Operation;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
import com.sk89q.worldedit.math.interpolation.Interpolation;
|
import com.sk89q.worldedit.math.interpolation.Interpolation;
|
||||||
import com.sk89q.worldedit.math.transform.AffineTransform;
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||||
import com.sk89q.worldedit.math.transform.RoundedTransform;
|
import com.sk89q.worldedit.math.transform.RoundedTransform;
|
||||||
@ -80,7 +81,7 @@ public class ClipboardSpline extends Spline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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()
|
RoundedTransform transform = new RoundedTransform(new AffineTransform()
|
||||||
.translate(offset)
|
.translate(offset)
|
||||||
.rotateY(angle));
|
.rotateY(angle));
|
||||||
|
@ -21,7 +21,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public abstract class Spline {
|
public abstract class Spline {
|
||||||
|
|
||||||
private BlockVector2 direction = BlockVector2.at(1, 0);
|
private Vector2 direction = Vector2.at(1, 0);
|
||||||
private final int nodeCount;
|
private final int nodeCount;
|
||||||
|
|
||||||
protected EditSession editSession;
|
protected EditSession editSession;
|
||||||
@ -77,10 +77,10 @@ public abstract class Spline {
|
|||||||
* is rotated by that angle to follow the curve slope.
|
* is rotated by that angle to follow the curve slope.
|
||||||
* <p>
|
* <p>
|
||||||
* The default direction is a (1;0) vector (pointing in the positive x-direction).
|
* 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) {
|
public void setDirection(Vector2 direction) {
|
||||||
this.direction = 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).
|
* 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
|
* @return A vector representing the horizontal forward direction of the clipboard content
|
||||||
*/
|
*/
|
||||||
public BlockVector2 getDirection() {
|
public Vector2 getDirection() {
|
||||||
return direction;
|
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
|
* 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.
|
* but will be passed directly to the interpolation algorithm.
|
||||||
@ -127,22 +134,24 @@ public abstract class Spline {
|
|||||||
Preconditions.checkArgument(position <= 1);
|
Preconditions.checkArgument(position <= 1);
|
||||||
|
|
||||||
// Calculate position from spline
|
// Calculate position from spline
|
||||||
BlockVector3 target = interpolation.getPosition(position).toBlockPoint();
|
Vector3 target = interpolation.getPosition(position);
|
||||||
BlockVector3 offset = target.subtract(target.round());
|
BlockVector3 blockTarget = target.toBlockPoint();
|
||||||
target = target.subtract(offset);
|
Vector3 offset = target.subtract(target.floor());
|
||||||
|
|
||||||
// Calculate rotation from spline
|
// Calculate rotation from spline
|
||||||
|
|
||||||
Vector3 deriv = interpolation.get1stDerivative(position);
|
Vector3 deriv = interpolation.get1stDerivative(position);
|
||||||
Vector2 deriv2D = Vector2.at(deriv.getX(), deriv.getZ()).normalize();
|
Vector2 deriv2D = Vector2.at(deriv.getX(), deriv.getZ()).normalize();
|
||||||
double angle = Math.toDegrees(
|
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() {
|
private void initSections() {
|
||||||
int sectionCount = nodeCount - 1;
|
int sectionCount = nodeCount - 1;
|
||||||
@ -176,7 +185,15 @@ public abstract class Spline {
|
|||||||
double flexOffset = flexPosition - previousSection.flexStart;
|
double flexOffset = flexPosition - previousSection.flexStart;
|
||||||
double uniOffset = flexOffset / previousSection.flexLength * previousSection.uniLength;
|
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 {
|
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.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.MutableVector3;
|
import com.sk89q.worldedit.math.MutableVector3;
|
||||||
|
import com.sk89q.worldedit.math.Vector2;
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
import com.sk89q.worldedit.math.interpolation.Interpolation;
|
import com.sk89q.worldedit.math.interpolation.Interpolation;
|
||||||
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
|
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
|
||||||
import com.sk89q.worldedit.math.interpolation.Node;
|
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.math.transform.AffineTransform;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||||
|
|
||||||
@ -64,7 +67,7 @@ public class SweepBrush implements Brush, ResettableTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interpolation interpol = new KochanekBartelsInterpolation();
|
Interpolation interpol = new ReparametrisingInterpolation(new KochanekBartelsInterpolation());
|
||||||
List<Node> nodes = positions.stream().map(v -> {
|
List<Node> nodes = positions.stream().map(v -> {
|
||||||
Node n = new Node(v.toVector3());
|
Node n = new Node(v.toVector3());
|
||||||
n.setTension(tension);
|
n.setTension(tension);
|
||||||
@ -82,38 +85,27 @@ public class SweepBrush implements Brush, ResettableTool {
|
|||||||
Clipboard clipboard = holder.getClipboard();
|
Clipboard clipboard = holder.getClipboard();
|
||||||
|
|
||||||
BlockVector3 dimensions = clipboard.getDimensions();
|
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());
|
double quality = Math.max(dimensions.getBlockX(), dimensions.getBlockZ());
|
||||||
|
|
||||||
|
AffineTransform transform = new AffineTransform();
|
||||||
|
|
||||||
ClipboardSpline spline = new ClipboardSpline(editSession, holder, interpol, transform, nodes.size());
|
ClipboardSpline spline = new ClipboardSpline(editSession, holder, interpol, transform, nodes.size());
|
||||||
|
|
||||||
|
if (dimensions.getBlockX() > dimensions.getBlockZ()) {
|
||||||
|
spline.setDirection(Vector2.at(0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
switch (copies) {
|
switch (copies) {
|
||||||
case 1: {
|
case 1: {
|
||||||
spline.pastePosition(0D);
|
spline.pastePosition(0D);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case -1: {
|
case -1: {
|
||||||
double splineLength = interpol.arcLength(0D, 1D);
|
double length = interpol.arcLength(0, 1);
|
||||||
double blockDistance = 1d / splineLength;
|
double step = 1 / (length * quality);
|
||||||
double step = blockDistance / quality;
|
for (double pos = 0; pos <= 1; pos += step) {
|
||||||
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);
|
spline.pastePosition(pos);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -200,6 +200,10 @@ public abstract class DFSVisitor implements Operation {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Node other = (Node) obj;
|
Node other = (Node) obj;
|
||||||
return other.x == x && other.z == z && other.y == y;
|
return other.x == x && other.z == z && other.y == y;
|
||||||
}
|
}
|
||||||
|
@ -775,6 +775,10 @@ public abstract class BlockVector3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final boolean equals(BlockVector3 other) {
|
public final boolean equals(BlockVector3 other) {
|
||||||
|
if (other == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return other.getX() == this.getX() && other.getY() == this.getY() && other.getZ() == this.getZ();
|
return other.getX() == this.getX() && other.getY() == this.getY() && other.getZ() == this.getZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
package com.sk89q.worldedit.math.interpolation;
|
package com.sk89q.worldedit.math.interpolation;
|
||||||
|
|
||||||
import com.sk89q.worldedit.math.MutableBlockVector3;
|
import com.sk89q.worldedit.math.MutableBlockVector3;
|
||||||
|
import com.sk89q.worldedit.math.MutableVector3;
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -43,7 +44,7 @@ public class KochanekBartelsInterpolation implements Interpolation {
|
|||||||
private Vector3[] coeffC;
|
private Vector3[] coeffC;
|
||||||
private Vector3[] coeffD;
|
private Vector3[] coeffD;
|
||||||
private double scaling;
|
private double scaling;
|
||||||
private final MutableBlockVector3 mutable = new MutableBlockVector3();
|
private final MutableVector3 mutable = new MutableVector3();
|
||||||
|
|
||||||
public KochanekBartelsInterpolation() {
|
public KochanekBartelsInterpolation() {
|
||||||
setNodes(Collections.emptyList());
|
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.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.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()));
|
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
|
@Override
|
||||||
|
@ -17,8 +17,7 @@ public class RoundedTransform implements Transform {
|
|||||||
@Override
|
@Override
|
||||||
public Vector3 apply(Vector3 input) {
|
public Vector3 apply(Vector3 input) {
|
||||||
Vector3 val = transform.apply(input);
|
Vector3 val = transform.apply(input);
|
||||||
return Vector3.at(Math.floor(val.getX() + 0.5), Math.floor(val.getY() + 0.5), Math
|
return val.round();
|
||||||
.floor(val.getY() + 0.5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -128,7 +128,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
|
|||||||
public boolean selectPrimary(BlockVector3 position, SelectorLimits limits) {
|
public boolean selectPrimary(BlockVector3 position, SelectorLimits limits) {
|
||||||
checkNotNull(position);
|
checkNotNull(position);
|
||||||
|
|
||||||
if (position1 != null && position.equals(position1)) {
|
if (position.equals(position1)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
|
|||||||
public boolean selectSecondary(BlockVector3 position, SelectorLimits limits) {
|
public boolean selectSecondary(BlockVector3 position, SelectorLimits limits) {
|
||||||
checkNotNull(position);
|
checkNotNull(position);
|
||||||
|
|
||||||
if (position2 != null && position.equals(position2)) {
|
if (position.equals(position2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ public class ExtendingCuboidRegionSelector extends CuboidRegionSelector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean selectPrimary(BlockVector3 position, SelectorLimits limits) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren