diff --git a/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java b/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java
new file mode 100644
index 000000000..4fef9b606
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java
@@ -0,0 +1,298 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.math.transform;
+
+import com.sk89q.worldedit.Vector;
+
+/**
+ * An affine transform.
+ *
+ * This class is from the 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 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) {
+ 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) {
+ 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) {
+ 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));
+ }
+
+ @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);
+ }
+
+ @Override
+ public Transform combine(Transform other) {
+ if (other instanceof AffineTransform) {
+ return concatenate((AffineTransform) other);
+ } else {
+ return new CombinedTransform(this, other);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java b/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java
new file mode 100644
index 000000000..893e3c384
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java
@@ -0,0 +1,77 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.math.transform;
+
+import com.sk89q.worldedit.Vector;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+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 transforms) {
+ this(transforms.toArray(new Transform[checkNotNull(transforms).size()]));
+ }
+
+ @Override
+ public Vector apply(Vector vector) {
+ for (Transform transform : transforms) {
+ vector = transform.apply(vector);
+ }
+ return vector;
+ }
+
+ @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);
+ }
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Identity.java b/src/main/java/com/sk89q/worldedit/math/transform/Identity.java
new file mode 100644
index 000000000..e13547de1
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/math/transform/Identity.java
@@ -0,0 +1,42 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.math.transform;
+
+import com.sk89q.worldedit.Vector;
+
+/**
+ * Makes no transformation to given vectors.
+ */
+public class Identity implements Transform {
+
+ @Override
+ public Vector apply(Vector vector) {
+ return vector;
+ }
+
+ @Override
+ public Transform combine(Transform other) {
+ if (other instanceof Identity) {
+ return this;
+ } else {
+ return new CombinedTransform(this, other);
+ }
+ }
+}
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Transform.java b/src/main/java/com/sk89q/worldedit/math/transform/Transform.java
new file mode 100644
index 000000000..38aba52e9
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/math/transform/Transform.java
@@ -0,0 +1,38 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.math.transform;
+
+import com.google.common.base.Function;
+import com.sk89q.worldedit.Vector;
+
+/**
+ * Makes a transformation of {@link Vector}s.
+ */
+public interface Transform extends Function {
+
+ /**
+ * 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);
+
+}