diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index 66957025..e46e0c9a 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -128,6 +128,8 @@ public enum BBC { BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"), BRUSH_SMOOTH("Note: Use the blend brush if you want to smooth overhangs or caves.", "WorldEdit.Brush"), BRUSH_SPLINE("Click to add a point, click the same spot to finish", "WorldEdit.Brush"), + BRUSH_LINE_PRIMARY("Added point %s0, click another position to create the line", "WorldEdit.Brush"), + BRUSH_LINE_SECONDARY("Created pline", "WorldEdit.Brush"), BRUSH_SPLINE_PRIMARY_2("Added position, Click the same spot to join!", "WorldEdit.Brush"), BRUSH_SPLINE_SECONDARY_ERROR("Not enough positions set!", "WorldEdit.Brush"), BRUSH_SPLINE_SECONDARY("Created spline", "WorldEdit.Brush"), diff --git a/core/src/main/java/com/boydti/fawe/object/brush/CatenaryBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/CatenaryBrush.java new file mode 100644 index 00000000..e6b8a711 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/CatenaryBrush.java @@ -0,0 +1,74 @@ +package com.boydti.fawe.object.brush; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.brush.visualization.VisualExtent; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.function.pattern.Pattern; +import java.util.Arrays; +import java.util.List; + +public class CatenaryBrush implements Brush, ResettableTool { + + private final boolean shell, select; + private final double slack; + private Vector pos1; + + public CatenaryBrush(boolean shell, boolean select, double lengthFactor) { + this.shell = shell; + this.select = select; + this.slack = lengthFactor; + } + + @Override + public void build(EditSession editSession, Vector pos2, final Pattern pattern, double size) throws MaxChangedBlocksException { + boolean visual = (editSession.getExtent() instanceof VisualExtent); + if (pos1 == null || pos2.equals(pos1)) { + if (!visual) { + pos1 = pos2; + BBC.BRUSH_LINE_PRIMARY.send(editSession.getPlayer(), pos2); + } + return; + } + Vector vertex = getVertex(pos1, pos2, slack); + List nodes = Arrays.asList(pos1, vertex, pos2); + editSession.drawSpline(pattern, nodes, 0, 0, 0, 10, size, !shell); + if (!visual) { + BBC.BRUSH_LINE_SECONDARY.send(editSession.getPlayer()); + if (!select) { + pos1 = null; + return; + } else { + pos1 = pos2; + } + } + } + + @Override + public boolean reset() { + pos1 = null; + return true; + } + +public Vector getVertex(Vector pos1, Vector pos2, double lenPercent) { + double len = pos1.distance(pos2) * lenPercent; + + double dy = pos2.getY() - pos1.getY(); + double dx = pos2.getX() - pos1.getX(); + double dz = pos2.getZ() - pos1.getZ(); + double h = Math.sqrt(dx * dx + dz * dz); + + double t = Math.sqrt(len * len - dy * dy) / h; + double z = 0.001; + for (; Math.sinh(z) < t*z; z += 0.001); // close enough + + double a = (h / 2) / z; + double p = (h - a * Math.log((len + dy) / (len - dy)))/2; + double q = (dy - len * Math.cosh(z) / Math.sinh(z)) / 2; + double y = a * 1 + q; + + return pos1.add(pos2.subtract(pos1).multiply(p / h).add(0, y, 0)).round(); +} +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/LineBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/LineBrush.java index c6baad64..cdf6d0dc 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/LineBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/LineBrush.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.brush; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.brush.visualization.VisualExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -22,11 +23,15 @@ public class LineBrush implements Brush, ResettableTool { public void build(EditSession editSession, Vector position, final Pattern pattern, double size) throws MaxChangedBlocksException { boolean visual = (editSession.getExtent() instanceof VisualExtent); if (pos1 == null) { - if (!visual) pos1 = position; + if (!visual) { + pos1 = position; + BBC.BRUSH_LINE_PRIMARY.send(editSession.getPlayer(), position); + } return; } editSession.drawLine(pattern, pos1, position, size, !shell, flat); if (!visual) { + BBC.BRUSH_LINE_SECONDARY.send(editSession.getPlayer()); if (!select) { pos1 = null; return; diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index aac7940c..5c017df8 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -27,6 +27,7 @@ import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.brush.BlendBall; import com.boydti.fawe.object.brush.BrushSettings; +import com.boydti.fawe.object.brush.CatenaryBrush; import com.boydti.fawe.object.brush.CircleBrush; import com.boydti.fawe.object.brush.CommandBrush; import com.boydti.fawe.object.brush.CopyPastaBrush; @@ -84,6 +85,7 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.command.InvalidUsageException; +import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; import java.awt.Color; @@ -245,7 +247,25 @@ public class BrushCommands extends MethodCommands { .setFill(fill); } - // final double tension, final double bias, final double continuity, final double quality + @Command( + aliases = {"catenary", "cat", "gravityline", "saggedline"}, + usage = " [length-factor=1.2] [size=0]", + desc = "Create a hanging line between two points", + help = "Create a hanging line between two points.\n" + + "The length-factor controls how long the line is\n" + + "The -h flag creates only a shell\n" + + "The -s flag selects the clicked point after drawing\n", + min = 1, + max = 3 + ) + @CommandPermissions("worldedit.brush.spline") + public BrushSettings catenaryBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("1.2") @Range(min=1) double lengthFactor, @Optional("0") double radius, @Switch('h') boolean shell, @Switch('s') boolean select, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + return get(context) + .setBrush(new CatenaryBrush(shell, select, lengthFactor)) + .setSize(radius) + .setFill(fill); + } @Command( aliases = {"sspl", "sspline", "surfacespline"}, @@ -254,7 +274,7 @@ public class BrushCommands extends MethodCommands { help = "Create a spline on the surface\n" + "Video: https://www.youtube.com/watch?v=zSN-2jJxXlM", min = 0, - max = 2 + max = 6 ) @CommandPermissions("worldedit.brush.surfacespline") // 0, 0, 0, 10, 0, public BrushSettings surfaceSpline(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("0") double radius, @Optional("0") double tension, @Optional("0") double bias, @Optional("0") double continuity, @Optional("10") double quality, CommandContext context) throws WorldEditException {