mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-04 20:16:41 +00:00
Switch to Gradle. Use git log --follow for history.
This converts the project into a multi-module Gradle build. By default, Git does not show history past a rename, so use git log --follow to see further history.
This commit is contained in:
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math;
|
||||
|
||||
/**
|
||||
* Various math utility methods.
|
||||
*/
|
||||
public final class MathUtils {
|
||||
|
||||
/**
|
||||
* Safe minimum, such that 1 / SAFE_MIN does not overflow.
|
||||
*
|
||||
* <p>In IEEE 754 arithmetic, this is also the smallest normalized number
|
||||
* 2<sup>-1022</sup>. The value of this constant is from Apache Commons
|
||||
* Math 2.2.</p>
|
||||
*/
|
||||
public static final double SAFE_MIN = 0x1.0p-1022;
|
||||
|
||||
private MathUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Modulus, divisor-style.
|
||||
*
|
||||
* @param a a
|
||||
* @param n n
|
||||
* @return the modulus
|
||||
*/
|
||||
public static int divisorMod(int a, int n) {
|
||||
return (int) (a - n * Math.floor(Math.floor(a) / n));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.convolution;
|
||||
|
||||
import java.awt.image.Kernel;
|
||||
|
||||
/**
|
||||
* A Gaussian Kernel generator (2D bellcurve).
|
||||
*/
|
||||
public class GaussianKernel extends Kernel {
|
||||
|
||||
/**
|
||||
* Constructor of the kernel
|
||||
*
|
||||
* @param radius the resulting diameter will be radius * 2 + 1
|
||||
* @param sigma controls 'flatness'
|
||||
*/
|
||||
public GaussianKernel(int radius, double sigma) {
|
||||
super(radius * 2 + 1, radius * 2 + 1, createKernel(radius, sigma));
|
||||
}
|
||||
|
||||
private static float[] createKernel(int radius, double sigma) {
|
||||
int diameter = radius * 2 + 1;
|
||||
float[] data = new float[diameter * diameter];
|
||||
|
||||
double sigma22 = 2 * sigma * sigma;
|
||||
double constant = Math.PI * sigma22;
|
||||
for (int y = -radius; y <= radius; ++y) {
|
||||
for (int x = -radius; x <= radius; ++x) {
|
||||
data[(y + radius) * diameter + x + radius] = (float) (Math.exp(-(x * x + y * y) / sigma22) / constant);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.convolution;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Allows applications of Kernels onto the region's height map.
|
||||
*
|
||||
* <p>Currently only used for smoothing (with a GaussianKernel)</p>.
|
||||
*/
|
||||
public class HeightMap {
|
||||
|
||||
private int[] data;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
private Region region;
|
||||
private EditSession session;
|
||||
|
||||
/**
|
||||
* Constructs the HeightMap
|
||||
*
|
||||
* @param session an edit session
|
||||
* @param region the region
|
||||
*/
|
||||
public HeightMap(EditSession session, Region region) {
|
||||
this(session, region, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the HeightMap
|
||||
*
|
||||
* @param session an edit session
|
||||
* @param region the region
|
||||
* @param naturalOnly ignore non-natural blocks
|
||||
*/
|
||||
public HeightMap(EditSession session, Region region, boolean naturalOnly) {
|
||||
checkNotNull(session);
|
||||
checkNotNull(region);
|
||||
|
||||
this.session = session;
|
||||
this.region = region;
|
||||
|
||||
this.width = region.getWidth();
|
||||
this.height = region.getLength();
|
||||
|
||||
int minX = region.getMinimumPoint().getBlockX();
|
||||
int minY = region.getMinimumPoint().getBlockY();
|
||||
int minZ = region.getMinimumPoint().getBlockZ();
|
||||
int maxY = region.getMaximumPoint().getBlockY();
|
||||
|
||||
// Store current heightmap data
|
||||
data = new int[width * height];
|
||||
for (int z = 0; z < height; ++z) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY, naturalOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter 'iterations' amount times.
|
||||
*
|
||||
* @param filter the filter
|
||||
* @param iterations the number of iterations
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException
|
||||
*/
|
||||
|
||||
public int applyFilter(HeightMapFilter filter, int iterations) throws MaxChangedBlocksException {
|
||||
checkNotNull(filter);
|
||||
|
||||
int[] newData = new int[data.length];
|
||||
System.arraycopy(data, 0, newData, 0, data.length);
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
newData = filter.filter(newData, width, height);
|
||||
}
|
||||
|
||||
return apply(newData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a raw heightmap to the region
|
||||
*
|
||||
* @param data the data
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException
|
||||
*/
|
||||
|
||||
public int apply(int[] data) throws MaxChangedBlocksException {
|
||||
checkNotNull(data);
|
||||
|
||||
Vector minY = region.getMinimumPoint();
|
||||
int originX = minY.getBlockX();
|
||||
int originY = minY.getBlockY();
|
||||
int originZ = minY.getBlockZ();
|
||||
|
||||
int maxY = region.getMaximumPoint().getBlockY();
|
||||
BaseBlock fillerAir = new BaseBlock(BlockID.AIR);
|
||||
|
||||
int blocksChanged = 0;
|
||||
|
||||
// Apply heightmap
|
||||
for (int z = 0; z < height; ++z) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
int index = z * width + x;
|
||||
int curHeight = this.data[index];
|
||||
|
||||
// Clamp newHeight within the selection area
|
||||
int newHeight = Math.min(maxY, data[index]);
|
||||
|
||||
// Offset x,z to be 'real' coordinates
|
||||
int xr = x + originX;
|
||||
int zr = z + originZ;
|
||||
|
||||
// We are keeping the topmost blocks so take that in account for the scale
|
||||
double scale = (double) (curHeight - originY) / (double) (newHeight - originY);
|
||||
|
||||
// Depending on growing or shrinking we need to start at the bottom or top
|
||||
if (newHeight > curHeight) {
|
||||
// Set the top block of the column to be the same type (this might go wrong with rounding)
|
||||
BaseBlock existing = session.getBlock(new Vector(xr, curHeight, zr));
|
||||
|
||||
// Skip water/lava
|
||||
if (existing.getType() != BlockID.WATER && existing.getType() != BlockID.STATIONARY_WATER
|
||||
&& existing.getType() != BlockID.LAVA && existing.getType() != BlockID.STATIONARY_LAVA) {
|
||||
session.setBlock(new Vector(xr, newHeight, zr), existing);
|
||||
++blocksChanged;
|
||||
|
||||
// Grow -- start from 1 below top replacing airblocks
|
||||
for (int y = newHeight - 1 - originY; y >= 0; --y) {
|
||||
int copyFrom = (int) (y * scale);
|
||||
session.setBlock(new Vector(xr, originY + y, zr), session.getBlock(new Vector(xr, originY + copyFrom, zr)));
|
||||
++blocksChanged;
|
||||
}
|
||||
}
|
||||
} else if (curHeight > newHeight) {
|
||||
// Shrink -- start from bottom
|
||||
for (int y = 0; y < newHeight - originY; ++y) {
|
||||
int copyFrom = (int) (y * scale);
|
||||
session.setBlock(new Vector(xr, originY + y, zr), session.getBlock(new Vector(xr, originY + copyFrom, zr)));
|
||||
++blocksChanged;
|
||||
}
|
||||
|
||||
// Set the top block of the column to be the same type
|
||||
// (this could otherwise go wrong with rounding)
|
||||
session.setBlock(new Vector(xr, newHeight, zr), session.getBlock(new Vector(xr, curHeight, zr)));
|
||||
++blocksChanged;
|
||||
|
||||
// Fill rest with air
|
||||
for (int y = newHeight + 1; y <= curHeight; ++y) {
|
||||
session.setBlock(new Vector(xr, y, zr), fillerAir);
|
||||
++blocksChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop trees to the floor -- TODO
|
||||
|
||||
return blocksChanged;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.convolution;
|
||||
|
||||
import java.awt.image.Kernel;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Allows applications of Kernels onto the region's height map.
|
||||
*
|
||||
* <p>Only used for smoothing (with a GaussianKernel).</p>
|
||||
*/
|
||||
public class HeightMapFilter {
|
||||
|
||||
private Kernel kernel;
|
||||
|
||||
/**
|
||||
* Construct the HeightMapFilter object.
|
||||
*
|
||||
* @param kernel the kernel
|
||||
*/
|
||||
public HeightMapFilter(Kernel kernel) {
|
||||
checkNotNull(kernel);
|
||||
this.kernel = kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the HeightMapFilter object.
|
||||
*
|
||||
* @param kernelWidth the width
|
||||
* @param kernelHeight the height
|
||||
* @param kernelData the data
|
||||
*/
|
||||
public HeightMapFilter(int kernelWidth, int kernelHeight, float[] kernelData) {
|
||||
checkNotNull(kernelData);
|
||||
this.kernel = new Kernel(kernelWidth, kernelHeight, kernelData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the kernel
|
||||
*/
|
||||
public Kernel getKernel() {
|
||||
return kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Kernel
|
||||
*
|
||||
* @param kernel the kernel
|
||||
*/
|
||||
public void setKernel(Kernel kernel) {
|
||||
checkNotNull(kernel);
|
||||
|
||||
this.kernel = kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter with a 2D kernel
|
||||
*
|
||||
* @param inData the data
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
*
|
||||
* @return the modified height map
|
||||
*/
|
||||
public int[] filter(int[] inData, int width, int height) {
|
||||
checkNotNull(inData);
|
||||
|
||||
int index = 0;
|
||||
float[] matrix = kernel.getKernelData(null);
|
||||
int[] outData = new int[inData.length];
|
||||
|
||||
int kh = kernel.getHeight();
|
||||
int kw = kernel.getWidth();
|
||||
int kox = kernel.getXOrigin();
|
||||
int koy = kernel.getYOrigin();
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
float z = 0;
|
||||
|
||||
for (int ky = 0; ky < kh; ++ky) {
|
||||
int offsetY = y + ky - koy;
|
||||
// Clamp coordinates inside data
|
||||
if (offsetY < 0 || offsetY >= height) {
|
||||
offsetY = y;
|
||||
}
|
||||
|
||||
offsetY *= width;
|
||||
|
||||
int matrixOffset = ky * kw;
|
||||
for (int kx = 0; kx < kw; ++kx) {
|
||||
float f = matrix[matrixOffset + kx];
|
||||
if (f == 0) continue;
|
||||
|
||||
int offsetX = x + kx - kox;
|
||||
// Clamp coordinates inside data
|
||||
if (offsetX < 0 || offsetX >= width) {
|
||||
offsetX = x;
|
||||
}
|
||||
|
||||
z += f * inData[offsetY + offsetX];
|
||||
}
|
||||
}
|
||||
outData[index++] = (int) (z + 0.5);
|
||||
}
|
||||
}
|
||||
return outData;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.convolution;
|
||||
|
||||
import java.awt.image.Kernel;
|
||||
|
||||
/**
|
||||
* A linear Kernel generator (all cells weight the same)
|
||||
*/
|
||||
public class LinearKernel extends Kernel {
|
||||
|
||||
public LinearKernel(int radius) {
|
||||
super(radius * 2 + 1, radius * 2 + 1, createKernel(radius));
|
||||
}
|
||||
|
||||
private static float[] createKernel(int radius) {
|
||||
int diameter = radius * 2 + 1;
|
||||
float[] data = new float[diameter * diameter];
|
||||
|
||||
for (int i = 0; i < data.length; data[i++] = 1.0f / data.length);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.geom;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper method for anything related to polygons.
|
||||
*/
|
||||
public final class Polygons {
|
||||
|
||||
private Polygons() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the polygon shape of a cylinder which can then be used for e.g. intersection detection.
|
||||
*
|
||||
* @param center the center point of the cylinder
|
||||
* @param radius the radius of the cylinder
|
||||
* @param maxPoints max points to be used for the calculation
|
||||
* @return a list of {@link BlockVector2D} which resemble the shape as a polygon
|
||||
*/
|
||||
public static List<BlockVector2D> polygonizeCylinder(Vector2D center, Vector2D radius, int maxPoints) {
|
||||
int nPoints = (int) Math.ceil(Math.PI*radius.length());
|
||||
|
||||
// These strange semantics for maxPoints are copied from the selectSecondary method.
|
||||
if (maxPoints >= 0 && nPoints >= maxPoints) {
|
||||
nPoints = maxPoints - 1;
|
||||
}
|
||||
|
||||
final List<BlockVector2D> points = new ArrayList<BlockVector2D>(nPoints);
|
||||
for (int i = 0; i < nPoints; ++i) {
|
||||
double angle = i * (2.0 * Math.PI) / nPoints;
|
||||
final Vector2D pos = new Vector2D(Math.cos(angle), Math.sin(angle));
|
||||
final BlockVector2D blockVector2D = pos.multiply(radius).add(center).toBlockVector2D();
|
||||
points.add(blockVector2D);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents an arbitrary function in ℝ → ℝ<sup>3</sup>
|
||||
*/
|
||||
public interface Interpolation {
|
||||
|
||||
/**
|
||||
* Sets nodes to be used by subsequent calls to
|
||||
* {@link #getPosition(double)} and the other methods.
|
||||
*
|
||||
* @param nodes the nodes
|
||||
*/
|
||||
public void setNodes(List<Node> nodes);
|
||||
|
||||
/**
|
||||
* Gets the result of f(position)
|
||||
*
|
||||
* @param position the position to interpolate
|
||||
* @return the result
|
||||
*/
|
||||
public Vector getPosition(double position);
|
||||
|
||||
/**
|
||||
* Gets the result of f'(position).
|
||||
*
|
||||
* @param position the position to interpolate
|
||||
* @return the result
|
||||
*/
|
||||
public Vector get1stDerivative(double position);
|
||||
|
||||
/**
|
||||
* Gets the result of ∫<sub>a</sub><sup style="position: relative; left: -1ex">b</sup>|f'(t)| dt.<br />
|
||||
* That means it calculates the arc length (in meters) between positionA
|
||||
* and positionB.
|
||||
*
|
||||
* @param positionA lower limit
|
||||
* @param positionB upper limit
|
||||
* @return the arc length
|
||||
*/
|
||||
double arcLength(double positionA, double positionB);
|
||||
|
||||
/**
|
||||
* Get the segment position.
|
||||
*
|
||||
* @param position the position
|
||||
* @return the segment position
|
||||
*/
|
||||
int getSegment(double position);
|
||||
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A Kochanek-Bartels interpolation; continuous in the 2nd derivative.
|
||||
*
|
||||
* <p>Supports {@link Node#tension tension}, {@link Node#bias bias} and
|
||||
* {@link Node#continuity continuity} parameters per {@link Node}.</p>
|
||||
*/
|
||||
public class KochanekBartelsInterpolation implements Interpolation {
|
||||
|
||||
private List<Node> nodes;
|
||||
private Vector[] coeffA;
|
||||
private Vector[] coeffB;
|
||||
private Vector[] coeffC;
|
||||
private Vector[] coeffD;
|
||||
private double scaling;
|
||||
|
||||
public KochanekBartelsInterpolation() {
|
||||
setNodes(Collections.<Node>emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodes(List<Node> nodes) {
|
||||
checkNotNull(nodes);
|
||||
|
||||
this.nodes = nodes;
|
||||
recalc();
|
||||
}
|
||||
|
||||
private void recalc() {
|
||||
final int nNodes = nodes.size();
|
||||
coeffA = new Vector[nNodes];
|
||||
coeffB = new Vector[nNodes];
|
||||
coeffC = new Vector[nNodes];
|
||||
coeffD = new Vector[nNodes];
|
||||
|
||||
if (nNodes == 0)
|
||||
return;
|
||||
|
||||
Node nodeB = nodes.get(0);
|
||||
double tensionB = nodeB.getTension();
|
||||
double biasB = nodeB.getBias();
|
||||
double continuityB = nodeB.getContinuity();
|
||||
for (int i = 0; i < nNodes; ++i) {
|
||||
final double tensionA = tensionB;
|
||||
final double biasA = biasB;
|
||||
final double continuityA = continuityB;
|
||||
|
||||
if (i + 1 < nNodes) {
|
||||
nodeB = nodes.get(i + 1);
|
||||
tensionB = nodeB.getTension();
|
||||
biasB = nodeB.getBias();
|
||||
continuityB = nodeB.getContinuity();
|
||||
}
|
||||
|
||||
// Kochanek-Bartels tangent coefficients
|
||||
final double ta = (1-tensionA)*(1+biasA)*(1+continuityA)/2; // Factor for lhs of d[i]
|
||||
final double tb = (1-tensionA)*(1-biasA)*(1-continuityA)/2; // Factor for rhs of d[i]
|
||||
final double tc = (1-tensionB)*(1+biasB)*(1-continuityB)/2; // Factor for lhs of d[i+1]
|
||||
final double td = (1-tensionB)*(1-biasB)*(1+continuityB)/2; // Factor for rhs of d[i+1]
|
||||
|
||||
coeffA[i] = linearCombination(i, -ta, ta- tb-tc+2, tb+tc-td-2, td);
|
||||
coeffB[i] = linearCombination(i, 2*ta, -2*ta+2*tb+tc-3, -2*tb-tc+td+3, -td);
|
||||
coeffC[i] = linearCombination(i, -ta, ta- tb , tb , 0);
|
||||
//coeffD[i] = linearCombination(i, 0, 1, 0, 0);
|
||||
coeffD[i] = retrieve(i); // this is an optimization
|
||||
}
|
||||
|
||||
scaling = nodes.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the linear combination of the given coefficients with the nodes adjacent to baseIndex.
|
||||
*
|
||||
* @param baseIndex node index
|
||||
* @param f1 coefficient for baseIndex-1
|
||||
* @param f2 coefficient for baseIndex
|
||||
* @param f3 coefficient for baseIndex+1
|
||||
* @param f4 coefficient for baseIndex+2
|
||||
* @return linear combination of nodes[n-1..n+2] with f1..4
|
||||
*/
|
||||
private Vector linearCombination(int baseIndex, double f1, double f2, double f3, double f4) {
|
||||
final Vector r1 = retrieve(baseIndex - 1).multiply(f1);
|
||||
final Vector r2 = retrieve(baseIndex ).multiply(f2);
|
||||
final Vector r3 = retrieve(baseIndex + 1).multiply(f3);
|
||||
final Vector r4 = retrieve(baseIndex + 2).multiply(f4);
|
||||
|
||||
return r1.add(r2).add(r3).add(r4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a node. Indexes are clamped to the valid range.
|
||||
*
|
||||
* @param index node index to retrieve
|
||||
* @return nodes[clamp(0, nodes.length-1)]
|
||||
*/
|
||||
private Vector retrieve(int index) {
|
||||
if (index < 0)
|
||||
return fastRetrieve(0);
|
||||
|
||||
if (index >= nodes.size())
|
||||
return fastRetrieve(nodes.size()-1);
|
||||
|
||||
return fastRetrieve(index);
|
||||
}
|
||||
|
||||
private Vector fastRetrieve(int index) {
|
||||
return nodes.get(index).getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getPosition(double position) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
position *= scaling;
|
||||
|
||||
final int index = (int) Math.floor(position);
|
||||
final double remainder = position - index;
|
||||
|
||||
final Vector a = coeffA[index];
|
||||
final Vector b = coeffB[index];
|
||||
final Vector c = coeffC[index];
|
||||
final Vector d = coeffD[index];
|
||||
|
||||
return a.multiply(remainder).add(b).multiply(remainder).add(c).multiply(remainder).add(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector get1stDerivative(double position) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
position *= scaling;
|
||||
|
||||
final int index = (int) Math.floor(position);
|
||||
//final double remainder = position - index;
|
||||
|
||||
final Vector a = coeffA[index];
|
||||
final Vector b = coeffB[index];
|
||||
final Vector c = coeffC[index];
|
||||
|
||||
return a.multiply(1.5*position - 3.0*index).add(b).multiply(2.0*position).add(a.multiply(1.5*index).subtract(b).multiply(2.0*index)).add(c).multiply(scaling);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double arcLength(double positionA, double positionB) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (positionA > positionB)
|
||||
return arcLength(positionB, positionA);
|
||||
|
||||
positionA *= scaling;
|
||||
positionB *= scaling;
|
||||
|
||||
final int indexA = (int) Math.floor(positionA);
|
||||
final double remainderA = positionA - indexA;
|
||||
|
||||
final int indexB = (int) Math.floor(positionB);
|
||||
final double remainderB = positionB - indexB;
|
||||
|
||||
return arcLengthRecursive(indexA, remainderA, indexB, remainderB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a < b
|
||||
*/
|
||||
private double arcLengthRecursive(int indexLeft, double remainderLeft, int indexRight, double remainderRight) {
|
||||
switch (indexRight - indexLeft) {
|
||||
case 0:
|
||||
return arcLengthRecursive(indexLeft, remainderLeft, remainderRight);
|
||||
|
||||
case 1:
|
||||
// This case is merely a speed-up for a very common case
|
||||
return
|
||||
arcLengthRecursive(indexLeft, remainderLeft, 1.0) +
|
||||
arcLengthRecursive(indexRight, 0.0, remainderRight);
|
||||
|
||||
default:
|
||||
return
|
||||
arcLengthRecursive(indexLeft, remainderLeft, indexRight - 1, 1.0) +
|
||||
arcLengthRecursive(indexRight, 0.0, remainderRight);
|
||||
}
|
||||
}
|
||||
|
||||
private double arcLengthRecursive(int index, double remainderLeft, double remainderRight) {
|
||||
final Vector a = coeffA[index].multiply(3.0);
|
||||
final Vector b = coeffB[index].multiply(2.0);
|
||||
final Vector c = coeffC[index];
|
||||
|
||||
final int nPoints = 8;
|
||||
|
||||
double accum = a.multiply(remainderLeft).add(b).multiply(remainderLeft).add(c).length() / 2.0;
|
||||
for (int i = 1; i < nPoints-1; ++i) {
|
||||
double t = ((double) i) / nPoints;
|
||||
t = (remainderRight-remainderLeft)*t + remainderLeft;
|
||||
accum += a.multiply(t).add(b).multiply(t).add(c).length();
|
||||
}
|
||||
|
||||
accum += a.multiply(remainderRight).add(b).multiply(remainderRight).add(c).length() / 2.0;
|
||||
return accum * (remainderRight - remainderLeft) / nPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSegment(double position) {
|
||||
if (coeffA == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return Integer.MAX_VALUE;
|
||||
|
||||
position *= scaling;
|
||||
|
||||
return (int) Math.floor(position);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Simple linear interpolation. Mainly used for testing.
|
||||
*/
|
||||
public class LinearInterpolation implements Interpolation {
|
||||
|
||||
private List<Node> nodes;
|
||||
|
||||
@Override
|
||||
public void setNodes(List<Node> nodes) {
|
||||
checkNotNull(nodes);
|
||||
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getPosition(double position) {
|
||||
if (nodes == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
position *= nodes.size() - 1;
|
||||
|
||||
final int index1 = (int) Math.floor(position);
|
||||
final double remainder = position - index1;
|
||||
|
||||
final Vector position1 = nodes.get(index1).getPosition();
|
||||
final Vector position2 = nodes.get(index1 + 1).getPosition();
|
||||
|
||||
return position1.multiply(1.0 - remainder).add(position2.multiply(remainder));
|
||||
}
|
||||
|
||||
/*
|
||||
Formula for position:
|
||||
p1*(1-t) + p2*t
|
||||
Formula for position in Horner/monomial form:
|
||||
(p2-p1)*t + p1
|
||||
1st Derivative:
|
||||
p2-p1
|
||||
2nd Derivative:
|
||||
0
|
||||
Integral:
|
||||
(p2-p1)/2*t^2 + p1*t + constant
|
||||
Integral in Horner form:
|
||||
((p2-p1)/2*t + p1)*t + constant
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Vector get1stDerivative(double position) {
|
||||
if (nodes == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
position *= nodes.size() - 1;
|
||||
|
||||
final int index1 = (int) Math.floor(position);
|
||||
|
||||
final Vector position1 = nodes.get(index1).getPosition();
|
||||
final Vector position2 = nodes.get(index1 + 1).getPosition();
|
||||
|
||||
return position2.subtract(position1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double arcLength(double positionA, double positionB) {
|
||||
if (nodes == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (positionA > positionB)
|
||||
return arcLength(positionB, positionA);
|
||||
|
||||
positionA *= nodes.size() - 1;
|
||||
positionB *= nodes.size() - 1;
|
||||
|
||||
final int indexA = (int) Math.floor(positionA);
|
||||
final double remainderA = positionA - indexA;
|
||||
|
||||
final int indexB = (int) Math.floor(positionB);
|
||||
final double remainderB = positionB - indexB;
|
||||
|
||||
return arcLengthRecursive(indexA, remainderA, indexB, remainderB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a < b.
|
||||
*/
|
||||
private double arcLengthRecursive(int indexA, double remainderA, int indexB, double remainderB) {
|
||||
switch (indexB - indexA) {
|
||||
case 0:
|
||||
return arcLengthRecursive(indexA, remainderA, remainderB);
|
||||
|
||||
case 1:
|
||||
// This case is merely a speed-up for a very common case
|
||||
return
|
||||
arcLengthRecursive(indexA, remainderA, 1.0) +
|
||||
arcLengthRecursive(indexB, 0.0, remainderB);
|
||||
|
||||
default:
|
||||
return
|
||||
arcLengthRecursive(indexA, remainderA, indexB - 1, 1.0) +
|
||||
arcLengthRecursive(indexB, 0.0, remainderB);
|
||||
}
|
||||
}
|
||||
|
||||
private double arcLengthRecursive(int index, double remainderA, double remainderB) {
|
||||
final Vector position1 = nodes.get(index).getPosition();
|
||||
final Vector position2 = nodes.get(index + 1).getPosition();
|
||||
|
||||
return position1.distance(position2) * (remainderB - remainderA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSegment(double position) {
|
||||
if (nodes == null)
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (position > 1)
|
||||
return Integer.MAX_VALUE;
|
||||
|
||||
position *= nodes.size() - 1;
|
||||
|
||||
return (int) Math.floor(position);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
/**
|
||||
* Represents a node for interpolation.
|
||||
*
|
||||
* <p>The {@link #tension}, {@link #bias} and {@link #continuity} fields
|
||||
* are parameters for the Kochanek-Bartels interpolation algorithm.</p>
|
||||
*/
|
||||
public class Node {
|
||||
|
||||
private Vector position;
|
||||
|
||||
private double tension;
|
||||
private double bias;
|
||||
private double continuity;
|
||||
|
||||
public Node() {
|
||||
this(new Vector(0, 0, 0));
|
||||
}
|
||||
|
||||
public Node(Node other) {
|
||||
this.position = other.position;
|
||||
|
||||
this.tension = other.tension;
|
||||
this.bias = other.bias;
|
||||
this.continuity = other.continuity;
|
||||
}
|
||||
|
||||
public Node(Vector position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
|
||||
public Vector getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Vector position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public double getTension() {
|
||||
return tension;
|
||||
}
|
||||
|
||||
public void setTension(double tension) {
|
||||
this.tension = tension;
|
||||
}
|
||||
|
||||
public double getBias() {
|
||||
return bias;
|
||||
}
|
||||
|
||||
public void setBias(double bias) {
|
||||
this.bias = bias;
|
||||
}
|
||||
|
||||
public double getContinuity() {
|
||||
return continuity;
|
||||
}
|
||||
|
||||
public void setContinuity(double continuity) {
|
||||
this.continuity = continuity;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.math.interpolation;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Reparametrises another interpolation function by arc length.
|
||||
*
|
||||
* <p>This is done so entities travel at roughly the same speed across
|
||||
* the whole route.</p>
|
||||
*/
|
||||
public class ReparametrisingInterpolation implements Interpolation {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ReparametrisingInterpolation.class.getCanonicalName());
|
||||
|
||||
private final Interpolation baseInterpolation;
|
||||
private double totalArcLength;
|
||||
private final TreeMap<Double, Double> cache = new TreeMap<Double, Double>();
|
||||
|
||||
public ReparametrisingInterpolation(Interpolation baseInterpolation) {
|
||||
checkNotNull(baseInterpolation);
|
||||
|
||||
this.baseInterpolation = baseInterpolation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodes(List<Node> nodes) {
|
||||
checkNotNull(nodes);
|
||||
|
||||
baseInterpolation.setNodes(nodes);
|
||||
cache.clear();
|
||||
cache.put(0.0, 0.0);
|
||||
cache.put(totalArcLength = baseInterpolation.arcLength(0.0, 1.0), 1.0);
|
||||
}
|
||||
|
||||
public Interpolation getBaseInterpolation() {
|
||||
return baseInterpolation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getPosition(double position) {
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
return baseInterpolation.getPosition(arcToParameter(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector get1stDerivative(double position) {
|
||||
if (position > 1)
|
||||
return null;
|
||||
|
||||
return baseInterpolation.get1stDerivative(arcToParameter(position)).normalize().multiply(totalArcLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double arcLength(double positionA, double positionB) {
|
||||
return baseInterpolation.arcLength(arcToParameter(positionA), arcToParameter(positionB));
|
||||
}
|
||||
|
||||
private double arcToParameter(double arc) {
|
||||
if (cache.isEmpty())
|
||||
throw new IllegalStateException("Must call setNodes first.");
|
||||
|
||||
if (arc > 1) arc = 1;
|
||||
arc *= totalArcLength;
|
||||
|
||||
Entry<Double, Double> floorEntry = cache.floorEntry(arc);
|
||||
final double leftArc = floorEntry.getKey();
|
||||
final double leftParameter = floorEntry.getValue();
|
||||
|
||||
if (leftArc == arc) {
|
||||
return leftParameter;
|
||||
}
|
||||
|
||||
Entry<Double, Double> ceilingEntry = cache.ceilingEntry(arc);
|
||||
if (ceilingEntry == null) {
|
||||
log.warning("Error in arcToParameter: no ceiling entry for " + arc + " found!");
|
||||
return 0;
|
||||
}
|
||||
final double rightArc = ceilingEntry.getKey();
|
||||
final double rightParameter = ceilingEntry.getValue();
|
||||
|
||||
if (rightArc == arc) {
|
||||
return rightParameter;
|
||||
}
|
||||
|
||||
return evaluate(arc, leftArc, leftParameter, rightArc, rightParameter);
|
||||
}
|
||||
|
||||
private double evaluate(double arc, double leftArc, double leftParameter, double rightArc, double rightParameter) {
|
||||
double midParameter = 0;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
midParameter = (leftParameter + rightParameter) * 0.5;
|
||||
//final double midArc = leftArc + baseInterpolation.arcLength(leftParameter, midParameter);
|
||||
final double midArc = baseInterpolation.arcLength(0, midParameter);
|
||||
cache.put(midArc, midParameter);
|
||||
|
||||
if (midArc < leftArc) {
|
||||
return leftParameter;
|
||||
}
|
||||
|
||||
if (midArc > rightArc) {
|
||||
return rightParameter;
|
||||
}
|
||||
|
||||
if (Math.abs(midArc - arc) < 0.01) {
|
||||
return midParameter;
|
||||
}
|
||||
|
||||
if (arc < midArc) {
|
||||
// search between left and mid
|
||||
rightArc = midArc;
|
||||
rightParameter = midParameter;
|
||||
}
|
||||
else {
|
||||
// search between mid and right
|
||||
leftArc = midArc;
|
||||
leftParameter = midParameter;
|
||||
}
|
||||
}
|
||||
return midParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSegment(double position) {
|
||||
if (position > 1)
|
||||
return Integer.MAX_VALUE;
|
||||
|
||||
return baseInterpolation.getSegment(arcToParameter(position));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.noise;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import net.royawesome.jlibnoise.module.Module;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
abstract class JLibNoiseGenerator<V extends Module> implements NoiseGenerator {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private final V module;
|
||||
|
||||
JLibNoiseGenerator() {
|
||||
module = createModule();
|
||||
setSeed(RANDOM.nextInt());
|
||||
}
|
||||
|
||||
protected abstract V createModule();
|
||||
|
||||
protected V getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public abstract void setSeed(int seed);
|
||||
|
||||
public abstract int getSeed();
|
||||
|
||||
@Override
|
||||
public float noise(Vector2D position) {
|
||||
return forceRange(module.GetValue(position.getX(), 0, position.getZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float noise(Vector position) {
|
||||
return forceRange(module.GetValue(position.getX(), position.getY(), position.getZ()));
|
||||
}
|
||||
|
||||
private float forceRange(double value) {
|
||||
return (float) Math.max(0, Math.min(1, value / 2.0 + 0.5));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.noise;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
|
||||
/**
|
||||
* Generates noise in a deterministic or non-deterministic manner.
|
||||
*/
|
||||
public interface NoiseGenerator {
|
||||
|
||||
/**
|
||||
* Get the noise value for the given position. The returned value may
|
||||
* change on every future call for the same position.
|
||||
*
|
||||
* @param position the position
|
||||
* @return a noise value between 0 (inclusive) and 1 (inclusive)
|
||||
*/
|
||||
float noise(Vector2D position);
|
||||
|
||||
/**
|
||||
* Get the noise value for the given position. The returned value may
|
||||
* change on every future call for the same position.
|
||||
*
|
||||
* @param position the position
|
||||
* @return a noise value between 0 (inclusive) and 1 (inclusive)
|
||||
*/
|
||||
float noise(Vector position);
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.noise;
|
||||
|
||||
import net.royawesome.jlibnoise.module.source.Perlin;
|
||||
|
||||
/**
|
||||
* Generates Perlin noise.
|
||||
*/
|
||||
public class PerlinNoise extends JLibNoiseGenerator<Perlin> {
|
||||
|
||||
@Override
|
||||
protected Perlin createModule() {
|
||||
return new Perlin();
|
||||
}
|
||||
|
||||
public double getFrequency() {
|
||||
return getModule().getFrequency();
|
||||
}
|
||||
|
||||
public void setFrequency(double frequency) {
|
||||
getModule().setFrequency(frequency);
|
||||
}
|
||||
|
||||
public double getLacunarity() {
|
||||
return getModule().getLacunarity();
|
||||
}
|
||||
|
||||
public void setLacunarity(double lacunarity) {
|
||||
getModule().setLacunarity(lacunarity);
|
||||
}
|
||||
|
||||
public int getOctaveCount() {
|
||||
return getModule().getOctaveCount();
|
||||
}
|
||||
|
||||
public void setOctaveCount(int octaveCount) {
|
||||
getModule().setOctaveCount(octaveCount);
|
||||
}
|
||||
|
||||
public void setPersistence(double persistence) {
|
||||
getModule().setPersistence(persistence);
|
||||
}
|
||||
|
||||
public double getPersistence() {
|
||||
return getModule().getPersistence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(int seed) {
|
||||
getModule().setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeed() {
|
||||
return getModule().getSeed();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.noise;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Generates noise using {@link java.util.Random}. Every time a noise
|
||||
* generating function is called, a new value will be returned.
|
||||
*/
|
||||
public class RandomNoise implements NoiseGenerator {
|
||||
|
||||
private final Random random;
|
||||
|
||||
/**
|
||||
* Create a new noise generator using the given {@code Random}.
|
||||
*
|
||||
* @param random the random instance
|
||||
*/
|
||||
public RandomNoise(Random random) {
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new noise generator with a newly constructed {@code Random}
|
||||
* instance.
|
||||
*/
|
||||
public RandomNoise() {
|
||||
this(new Random());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float noise(Vector2D position) {
|
||||
return random.nextFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float noise(Vector position) {
|
||||
return random.nextFloat();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.noise;
|
||||
|
||||
import net.royawesome.jlibnoise.module.source.RidgedMulti;
|
||||
|
||||
/**
|
||||
* Generates ridged multi-fractal noise.
|
||||
*/
|
||||
public class RidgedMultiFractalNoise extends JLibNoiseGenerator<RidgedMulti> {
|
||||
|
||||
@Override
|
||||
protected RidgedMulti createModule() {
|
||||
return new RidgedMulti();
|
||||
}
|
||||
|
||||
public double getFrequency() {
|
||||
return getModule().getFrequency();
|
||||
}
|
||||
|
||||
public void setFrequency(double frequency) {
|
||||
getModule().setFrequency(frequency);
|
||||
}
|
||||
|
||||
public double getLacunarity() {
|
||||
return getModule().getLacunarity();
|
||||
}
|
||||
|
||||
public void setLacunarity(double lacunarity) {
|
||||
getModule().setLacunarity(lacunarity);
|
||||
}
|
||||
|
||||
public int getOctaveCount() {
|
||||
return getModule().getOctaveCount();
|
||||
}
|
||||
|
||||
public void setOctaveCount(int octaveCount) {
|
||||
getModule().setOctaveCount(octaveCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(int seed) {
|
||||
getModule().setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeed() {
|
||||
return getModule().getSeed();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.noise;
|
||||
|
||||
import net.royawesome.jlibnoise.module.source.Voronoi;
|
||||
|
||||
/**
|
||||
* Generates Voronoi noise.
|
||||
*/
|
||||
public class VoronoiNoise extends JLibNoiseGenerator<Voronoi> {
|
||||
|
||||
@Override
|
||||
protected Voronoi createModule() {
|
||||
return new Voronoi();
|
||||
}
|
||||
|
||||
public double getFrequency() {
|
||||
return getModule().getFrequency();
|
||||
}
|
||||
|
||||
public void setFrequency(double frequency) {
|
||||
getModule().setFrequency(frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(int seed) {
|
||||
getModule().setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeed() {
|
||||
return getModule().getSeed();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.transform;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
/**
|
||||
* An affine transform.
|
||||
*
|
||||
* <p>This class is from the
|
||||
* <a href="http://geom-java.sourceforge.net/index.html>JavaGeom project</a>,
|
||||
* which is licensed under LGPL v2.1.</p>
|
||||
*/
|
||||
public class AffineTransform implements Transform {
|
||||
|
||||
/**
|
||||
* coefficients for x coordinate.
|
||||
*/
|
||||
private double m00, m01, m02, m03;
|
||||
|
||||
/**
|
||||
* coefficients for y coordinate.
|
||||
*/
|
||||
private double m10, m11, m12, m13;
|
||||
|
||||
/**
|
||||
* coefficients for z coordinate.
|
||||
*/
|
||||
private double m20, m21, m22, m23;
|
||||
|
||||
// ===================================================================
|
||||
// constructors
|
||||
|
||||
/**
|
||||
* Creates a new affine transform3D set to identity
|
||||
*/
|
||||
public AffineTransform() {
|
||||
// init to identity matrix
|
||||
m00 = m11 = m22 = 1;
|
||||
m01 = m02 = m03 = 0;
|
||||
m10 = m12 = m13 = 0;
|
||||
m20 = m21 = m23 = 0;
|
||||
}
|
||||
|
||||
public AffineTransform(double[] coefs) {
|
||||
if (coefs.length == 9) {
|
||||
m00 = coefs[0];
|
||||
m01 = coefs[1];
|
||||
m02 = coefs[2];
|
||||
m10 = coefs[3];
|
||||
m11 = coefs[4];
|
||||
m12 = coefs[5];
|
||||
m20 = coefs[6];
|
||||
m21 = coefs[7];
|
||||
m22 = coefs[8];
|
||||
} else if (coefs.length == 12) {
|
||||
m00 = coefs[0];
|
||||
m01 = coefs[1];
|
||||
m02 = coefs[2];
|
||||
m03 = coefs[3];
|
||||
m10 = coefs[4];
|
||||
m11 = coefs[5];
|
||||
m12 = coefs[6];
|
||||
m13 = coefs[7];
|
||||
m20 = coefs[8];
|
||||
m21 = coefs[9];
|
||||
m22 = coefs[10];
|
||||
m23 = coefs[11];
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Input array must have 9 or 12 elements");
|
||||
}
|
||||
}
|
||||
|
||||
public AffineTransform(double xx, double yx, double zx, double tx,
|
||||
double xy, double yy, double zy, double ty, double xz, double yz,
|
||||
double zz, double tz) {
|
||||
m00 = xx;
|
||||
m01 = yx;
|
||||
m02 = zx;
|
||||
m03 = tx;
|
||||
m10 = xy;
|
||||
m11 = yy;
|
||||
m12 = zy;
|
||||
m13 = ty;
|
||||
m20 = xz;
|
||||
m21 = yz;
|
||||
m22 = zz;
|
||||
m23 = tz;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// accessors
|
||||
|
||||
@Override
|
||||
public boolean isIdentity() {
|
||||
if (m00 != 1)
|
||||
return false;
|
||||
if (m11 != 1)
|
||||
return false;
|
||||
if (m22 != 0)
|
||||
return false;
|
||||
if (m01 != 0)
|
||||
return false;
|
||||
if (m02 != 0)
|
||||
return false;
|
||||
if (m03 != 0)
|
||||
return false;
|
||||
if (m10 != 0)
|
||||
return false;
|
||||
if (m12 != 0)
|
||||
return false;
|
||||
if (m13 != 0)
|
||||
return false;
|
||||
if (m20 != 0)
|
||||
return false;
|
||||
if (m21 != 0)
|
||||
return false;
|
||||
if (m23 != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the affine coefficients of the transform. Result is an array of
|
||||
* 12 double.
|
||||
*/
|
||||
public double[] coefficients() {
|
||||
return new double[]{m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the determinant of this transform. Can be zero.
|
||||
*
|
||||
* @return the determinant of the transform.
|
||||
*/
|
||||
private double determinant() {
|
||||
return m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m20 * m12)
|
||||
+ m02 * (m10 * m21 - m20 * m11);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the inverse affine transform.
|
||||
*/
|
||||
@Override
|
||||
public AffineTransform inverse() {
|
||||
double det = this.determinant();
|
||||
return new AffineTransform(
|
||||
(m11 * m22 - m21 * m12) / det,
|
||||
(m21 * m01 - m01 * m22) / det,
|
||||
(m01 * m12 - m11 * m02) / det,
|
||||
(m01 * (m22 * m13 - m12 * m23) + m02 * (m11 * m23 - m21 * m13)
|
||||
- m03 * (m11 * m22 - m21 * m12)) / det,
|
||||
(m20 * m12 - m10 * m22) / det,
|
||||
(m00 * m22 - m20 * m02) / det,
|
||||
(m10 * m02 - m00 * m12) / det,
|
||||
(m00 * (m12 * m23 - m22 * m13) - m02 * (m10 * m23 - m20 * m13)
|
||||
+ m03 * (m10 * m22 - m20 * m12)) / det,
|
||||
(m10 * m21 - m20 * m11) / det,
|
||||
(m20 * m01 - m00 * m21) / det,
|
||||
(m00 * m11 - m10 * m01) / det,
|
||||
(m00 * (m21 * m13 - m11 * m23) + m01 * (m10 * m23 - m20 * m13)
|
||||
- m03 * (m10 * m21 - m20 * m11)) / det);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// general methods
|
||||
|
||||
/**
|
||||
* Returns the affine transform created by applying first the affine
|
||||
* transform given by {@code that}, then this affine transform.
|
||||
*
|
||||
* @param that the transform to apply first
|
||||
* @return the composition this * that
|
||||
*/
|
||||
public AffineTransform concatenate(AffineTransform that) {
|
||||
double n00 = m00 * that.m00 + m01 * that.m10 + m02 * that.m20;
|
||||
double n01 = m00 * that.m01 + m01 * that.m11 + m02 * that.m21;
|
||||
double n02 = m00 * that.m02 + m01 * that.m12 + m02 * that.m22;
|
||||
double n03 = m00 * that.m03 + m01 * that.m13 + m02 * that.m23 + m03;
|
||||
double n10 = m10 * that.m00 + m11 * that.m10 + m12 * that.m20;
|
||||
double n11 = m10 * that.m01 + m11 * that.m11 + m12 * that.m21;
|
||||
double n12 = m10 * that.m02 + m11 * that.m12 + m12 * that.m22;
|
||||
double n13 = m10 * that.m03 + m11 * that.m13 + m12 * that.m23 + m13;
|
||||
double n20 = m20 * that.m00 + m21 * that.m10 + m22 * that.m20;
|
||||
double n21 = m20 * that.m01 + m21 * that.m11 + m22 * that.m21;
|
||||
double n22 = m20 * that.m02 + m21 * that.m12 + m22 * that.m22;
|
||||
double n23 = m20 * that.m03 + m21 * that.m13 + m22 * that.m23 + m23;
|
||||
return new AffineTransform(
|
||||
n00, n01, n02, n03,
|
||||
n10, n11, n12, n13,
|
||||
n20, n21, n22, n23);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the affine transform created by applying first this affine
|
||||
* transform, then the affine transform given by {@code that}.
|
||||
*
|
||||
* @param that the transform to apply in a second step
|
||||
* @return the composition that * this
|
||||
*/
|
||||
public AffineTransform preConcatenate(AffineTransform that) {
|
||||
double n00 = that.m00 * m00 + that.m01 * m10 + that.m02 * m20;
|
||||
double n01 = that.m00 * m01 + that.m01 * m11 + that.m02 * m21;
|
||||
double n02 = that.m00 * m02 + that.m01 * m12 + that.m02 * m22;
|
||||
double n03 = that.m00 * m03 + that.m01 * m13 + that.m02 * m23 + that.m03;
|
||||
double n10 = that.m10 * m00 + that.m11 * m10 + that.m12 * m20;
|
||||
double n11 = that.m10 * m01 + that.m11 * m11 + that.m12 * m21;
|
||||
double n12 = that.m10 * m02 + that.m11 * m12 + that.m12 * m22;
|
||||
double n13 = that.m10 * m03 + that.m11 * m13 + that.m12 * m23 + that.m13;
|
||||
double n20 = that.m20 * m00 + that.m21 * m10 + that.m22 * m20;
|
||||
double n21 = that.m20 * m01 + that.m21 * m11 + that.m22 * m21;
|
||||
double n22 = that.m20 * m02 + that.m21 * m12 + that.m22 * m22;
|
||||
double n23 = that.m20 * m03 + that.m21 * m13 + that.m22 * m23 + that.m23;
|
||||
return new AffineTransform(
|
||||
n00, n01, n02, n03,
|
||||
n10, n11, n12, n13,
|
||||
n20, n21, n22, n23);
|
||||
}
|
||||
|
||||
public AffineTransform translate(Vector vec) {
|
||||
return translate(vec.getX(), vec.getY(), vec.getZ());
|
||||
}
|
||||
|
||||
public AffineTransform translate(double x, double y, double z) {
|
||||
return concatenate(new AffineTransform(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z));
|
||||
}
|
||||
|
||||
public AffineTransform rotateX(double theta) {
|
||||
theta = Math.toRadians(theta);
|
||||
double cot = Math.cos(theta);
|
||||
double sit = Math.sin(theta);
|
||||
return concatenate(
|
||||
new AffineTransform(
|
||||
1, 0, 0, 0,
|
||||
0, cot, -sit, 0,
|
||||
0, sit, cot, 0));
|
||||
}
|
||||
|
||||
public AffineTransform rotateY(double theta) {
|
||||
theta = Math.toRadians(theta);
|
||||
double cot = Math.cos(theta);
|
||||
double sit = Math.sin(theta);
|
||||
return concatenate(
|
||||
new AffineTransform(
|
||||
cot, 0, sit, 0,
|
||||
0, 1, 0, 0,
|
||||
-sit, 0, cot, 0));
|
||||
}
|
||||
|
||||
public AffineTransform rotateZ(double theta) {
|
||||
theta = Math.toRadians(theta);
|
||||
double cot = Math.cos(theta);
|
||||
double sit = Math.sin(theta);
|
||||
return concatenate(
|
||||
new AffineTransform(
|
||||
cot, -sit, 0, 0,
|
||||
sit, cot, 0, 0,
|
||||
0, 0, 1, 0));
|
||||
}
|
||||
|
||||
public AffineTransform scale(double s) {
|
||||
return scale(s, s, s);
|
||||
}
|
||||
|
||||
public AffineTransform scale(double sx, double sy, double sz) {
|
||||
return concatenate(new AffineTransform(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0));
|
||||
}
|
||||
|
||||
public AffineTransform scale(Vector vec) {
|
||||
return scale(vec.getX(), vec.getY(), vec.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector apply(Vector vector) {
|
||||
return new Vector(
|
||||
vector.getX() * m00 + vector.getY() * m01 + vector.getZ() * m02 + m03,
|
||||
vector.getX() * m10 + vector.getY() * m11 + vector.getZ() * m12 + m13,
|
||||
vector.getX() * m20 + vector.getY() * m21 + vector.getZ() * m22 + m23);
|
||||
}
|
||||
|
||||
public AffineTransform combine(AffineTransform other) {
|
||||
return concatenate(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform combine(Transform other) {
|
||||
if (other instanceof AffineTransform) {
|
||||
return concatenate((AffineTransform) other);
|
||||
} else {
|
||||
return new CombinedTransform(this, other);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Affine[%g %g %g %g, %g %g %g %g, %g %g %g %g]}", m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.transform;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Combines several transforms in order.
|
||||
*/
|
||||
public class CombinedTransform implements Transform {
|
||||
|
||||
private final Transform[] transforms;
|
||||
|
||||
/**
|
||||
* Create a new combined transformation.
|
||||
*
|
||||
* @param transforms a list of transformations
|
||||
*/
|
||||
public CombinedTransform(Transform... transforms) {
|
||||
checkNotNull(transforms);
|
||||
this.transforms = Arrays.copyOf(transforms, transforms.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new combined transformation.
|
||||
*
|
||||
* @param transforms a list of transformations
|
||||
*/
|
||||
public CombinedTransform(Collection<Transform> transforms) {
|
||||
this(transforms.toArray(new Transform[checkNotNull(transforms).size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdentity() {
|
||||
for (Transform transform : transforms) {
|
||||
if (!transform.isIdentity()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector apply(Vector vector) {
|
||||
for (Transform transform : transforms) {
|
||||
vector = transform.apply(vector);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform inverse() {
|
||||
List<Transform> list = new ArrayList<Transform>();
|
||||
for (int i = transforms.length - 1; i >= 0; i--) {
|
||||
list.add(transforms[i].inverse());
|
||||
}
|
||||
return new CombinedTransform(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform combine(Transform other) {
|
||||
checkNotNull(other);
|
||||
if (other instanceof CombinedTransform) {
|
||||
CombinedTransform combinedOther = (CombinedTransform) other;
|
||||
Transform[] newTransforms = new Transform[transforms.length + combinedOther.transforms.length];
|
||||
System.arraycopy(transforms, 0, newTransforms, 0, transforms.length);
|
||||
System.arraycopy(combinedOther.transforms, 0, newTransforms, transforms.length, combinedOther.transforms.length);
|
||||
return new CombinedTransform(newTransforms);
|
||||
} else {
|
||||
return new CombinedTransform(this, other);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.transform;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
/**
|
||||
* Makes no transformation to given vectors.
|
||||
*/
|
||||
public class Identity implements Transform {
|
||||
|
||||
@Override
|
||||
public boolean isIdentity() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector apply(Vector vector) {
|
||||
return vector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform inverse() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform combine(Transform other) {
|
||||
if (other instanceof Identity) {
|
||||
return this;
|
||||
} else {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.transform;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
/**
|
||||
* Makes a transformation of {@link Vector}s.
|
||||
*/
|
||||
public interface Transform {
|
||||
|
||||
/**
|
||||
* Return whether this transform is an identity.
|
||||
*
|
||||
* <p>If it is not known, then {@code false} must be returned.</p>
|
||||
*
|
||||
* @return true if identity
|
||||
*/
|
||||
boolean isIdentity();
|
||||
|
||||
/**
|
||||
* Returns the result of applying the function to the input.
|
||||
*
|
||||
* @param input the input
|
||||
* @return the result
|
||||
*/
|
||||
Vector apply(Vector input);
|
||||
|
||||
/**
|
||||
* Create a new inverse transform.
|
||||
*
|
||||
* @return a new inverse transform
|
||||
*/
|
||||
Transform inverse();
|
||||
|
||||
/**
|
||||
* Create a new {@link Transform} that combines this transform with another.
|
||||
*
|
||||
* @param other the other transform to occur second
|
||||
* @return a new transform
|
||||
*/
|
||||
Transform combine(Transform other);
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.math.transform;
|
||||
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Various utility methods related to {@link Transform}s.
|
||||
*/
|
||||
public final class Transforms {
|
||||
|
||||
private Transforms() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a location's position with a given transform.
|
||||
*
|
||||
* <p>Direction is unaffected.</p>
|
||||
*
|
||||
* @param location the location
|
||||
* @param transform the transform
|
||||
* @return the transformed location
|
||||
*/
|
||||
public static Location transform(Location location, Transform transform) {
|
||||
checkNotNull(location);
|
||||
checkNotNull(transform);
|
||||
return new Location(location.getExtent(), transform.apply(location.toVector()), location.getDirection());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user