diff --git a/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs b/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs
index 778f730..1516b69 100644
--- a/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs
+++ b/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs
@@ -13,11 +13,17 @@ namespace OSCADSharp.ConsoleTests
{
static void Main(string[] args)
{
- var cube = new Cube();
- var sphere = new Sphere().Translate(0, 0, 2);
- var hull = cube.Hull(sphere);
+ var obj = new Cube(5, 10, 20).Mirror(0, 0, 1).Mirror(0, 1, 0)
+ .Rotate(15, -45, 120).Translate(-20, 10, 15).Rotate(90, 15, 25)
+ .Translate(-10, -20, -20).Rotate(-90, -90, -45);
- string script = hull.ToString();
+ var pos = obj.Position();
+ var cyl1 = new Cylinder(1, 100, true).Translate(pos);
+ var cyl2 = new Cylinder(1, 100, true).Rotate(0, 90, 0).Translate(pos);
+ var cyl3 = new Cylinder(1, 100, true).Rotate(90, 0, 0).Translate(pos);
+ var axisHelper = cyl1.Union(cyl2, cyl3).Color("Red");
+
+ string script = obj.Union(axisHelper).ToString();
File.WriteAllLines("test.scad", new string[] { script.ToString() });
//Console.ReadKey();
diff --git a/OSCADSharp/OSCADSharp.UnitTests/CubeTests.cs b/OSCADSharp/OSCADSharp.UnitTests/CubeTests.cs
index 9af5d5f..2968f8a 100644
--- a/OSCADSharp/OSCADSharp.UnitTests/CubeTests.cs
+++ b/OSCADSharp/OSCADSharp.UnitTests/CubeTests.cs
@@ -53,5 +53,37 @@ namespace OSCADSharp.UnitTests
Assert.IsTrue(script.StartsWith("cube("));
Assert.IsTrue(script.EndsWith(");"));
}
+
+ [TestMethod]
+ public void Cube_InitialPositionForNonCenteredCubeIsHalfLengthWidthAndHeight()
+ {
+ var cube = new Cube(10, 10, 10);
+
+ Assert.IsTrue(cube.Position() == new Vector3(5, 5, 5));
+ }
+
+ [TestMethod]
+ public void Cube_InitialPositionIfCenteredIsOrigin()
+ {
+ var cube = new Cube(25, 25, 25, true);
+
+ Assert.AreEqual(new Vector3(), cube.Position());
+ }
+
+ [TestMethod]
+ public void Cube_PositionMovesWithCubeOnTranslate()
+ {
+ var cube = new Cube(50, 50, 50).Translate(10, 10, 0);
+
+ Assert.AreEqual(new Vector3(35, 35, 25), cube.Position());
+ }
+
+ [TestMethod]
+ public void Cube_PositionMovesWithCubeOnNegativeTranslate()
+ {
+ var cube = new Cube(50, 50, 50, true).Translate(-5, 0, -15);
+
+ Assert.AreEqual(new Vector3(-5, 0, -15), cube.Position());
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/CylinderTests.cs b/OSCADSharp/OSCADSharp.UnitTests/CylinderTests.cs
index ff3f83b..78396c5 100644
--- a/OSCADSharp/OSCADSharp.UnitTests/CylinderTests.cs
+++ b/OSCADSharp/OSCADSharp.UnitTests/CylinderTests.cs
@@ -23,5 +23,21 @@ namespace OSCADSharp.UnitTests
Assert.IsTrue(script.Contains("h = 12.1"));
Assert.IsTrue(script.Contains("center = true"));
}
+
+ [TestMethod]
+ public void Cylinder_UncenteredPositionZValueIsHalfTheHeight()
+ {
+ var cylinder = new Cylinder(3, 40);
+
+ Assert.AreEqual(new Vector3(0, 0, 20), cylinder.Position());
+ }
+
+ [TestMethod]
+ public void Cylinder_CenteredCylinderPositionIsZero()
+ {
+ var cylinder = new Cylinder(5, 20, true);
+
+ Assert.AreEqual(new Vector3(), cylinder.Position());
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/DifferenceTests.cs b/OSCADSharp/OSCADSharp.UnitTests/DifferenceTests.cs
new file mode 100644
index 0000000..16b5c31
--- /dev/null
+++ b/OSCADSharp/OSCADSharp.UnitTests/DifferenceTests.cs
@@ -0,0 +1,19 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OSCADSharp.Solids;
+
+namespace OSCADSharp.UnitTests
+{
+ [TestClass]
+ public class DifferenceTests
+ {
+ [TestMethod]
+ [ExpectedException(typeof(NotSupportedException))]
+ public void Difference_PositionThrowsNotSupportedException()
+ {
+ var diff = new Sphere().Difference(new Cube());
+
+ var pos = diff.Position();
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/InterpolationTests.cs b/OSCADSharp/OSCADSharp.UnitTests/InterpolationTests.cs
new file mode 100644
index 0000000..f64e3d8
--- /dev/null
+++ b/OSCADSharp/OSCADSharp.UnitTests/InterpolationTests.cs
@@ -0,0 +1,130 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OSCADSharp.Solids;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSCADSharp.UnitTests
+{
+ [TestClass]
+ public class InterpolationTests
+ {
+ //Positive X rotation
+ [TestMethod]
+ public void Interpolation_RotateOnXAxis()
+ {
+ var cube = new Cube(9, 9, 9).Rotate(90, 0, 0);
+
+ //Rotating on X axis by 90 should shift the center of the cube on the negative Y quadrant
+ Assert.AreEqual(new Vector3(4.5, -4.5, 4.5), cube.Position());
+ }
+
+ //Negative X rotation
+ [TestMethod]
+ public void Interpolation_NegativeRotationOnXAxis()
+ {
+ var cube = new Cube(11, 11, 11).Rotate(-90, 0, 0);
+
+ //Rotating on X axis by -90 should shift the center of the cube on the negative Z quadrant
+ Assert.AreEqual(new Vector3(5.5, 5.5, -5.5), cube.Position());
+ }
+
+ //Y Rotation
+ [TestMethod]
+ public void Interpolation_PositiveYRotationWithTallCube()
+ {
+ var cube = new Cube(10, 12, 23).Rotate(0, 90, 0);
+
+ //Rotating on Y axis by 90 should shift the center of the cube on the negative Z quadrant
+ Assert.AreEqual(new Vector3(11.5, 6, -5), cube.Position());
+ }
+
+ //Negative Y rotation
+ [TestMethod]
+ public void Interpolation_NegativeYRotationWithWideCube()
+ {
+ var cube = new Cube(10, 30, 15).Rotate(0, -90, 0);
+
+ //Rotating on Y axis by -90 should shift the center of the cube on the negative X quadrant
+ Assert.AreEqual(new Vector3(-7.5, 15, 5), cube.Position());
+ }
+
+ //Z Rotation
+ [TestMethod]
+ public void Interpolation_ZRotationWithLongCube()
+ {
+ var cube = new Cube(10, 5, 2).Rotate(0, 0, 115);
+
+ //Rotating on Z axis by 90 should shift the center of the cube on the negative X quadrant
+ Assert.AreEqual(new Vector3(-4.37886077629512, 3.4749932808315, 1), cube.Position());
+ }
+
+ //Negative Z rotation
+ [TestMethod]
+ public void Interpolation_NegativeZRotation()
+ {
+ var cube = new Cube(10, 5, 2).Rotate(0, 0, -95);
+
+ //Rotating on Z axis by 90 should shift the center of the cube on the negative Y quadrant
+ Assert.AreEqual(new Vector3(2.05470803149107, -5.19886284732787, 1), cube.Position());
+ }
+
+ //Centered rotation (no change)
+ [TestMethod]
+ public void Interpolation_CenteredCubePositionNotUpdatedWhenRotated()
+ {
+ var cube = new Cube(5, 20, 20, true).Rotate(15, -120, 270);
+
+ Assert.AreEqual(new Vector3(), cube.Position());
+ }
+
+ //X and Y rotation
+ [TestMethod]
+ public void Interpolation_XAndYRotation()
+ {
+ var cube = new Cube(5, 5, 5).Rotate(120, 45, 0);
+
+ Assert.AreEqual(new Vector3(2.41481456572267, -3.4150635094611, -1.12071934021007), cube.Position());
+ }
+
+ //Y and Z rotation
+ [TestMethod]
+ public void Interpolation_YandZRotation()
+ {
+ var cube = new Cube(13, 13, 13).Rotate(0, 270, -35);
+
+ Assert.AreEqual(new Vector3(-1.59624145159665, 9.05273512416025, 6.5), cube.Position());
+ }
+
+ //X and Z rotation
+ [TestMethod]
+ public void Interpolation_XandZRotation()
+ {
+ var cube = new Cube(13, 13, 13).Rotate(-145, 0, 190);
+
+ Assert.AreEqual(new Vector3(-6.67843481376553, 0.443277802376793, -9.05273512416025), cube.Position());
+ }
+
+ //X, Y and Z rotation
+ [TestMethod]
+ public void Interpolation_XYZRotation()
+ {
+ var cube = new Cube(13, 13, 13).Rotate(90, 37.5, -180);
+
+ Assert.AreEqual(new Vector3(-9.11374600044971, 6.5, 1.19984742333634), cube.Position());
+ }
+
+ [TestMethod]
+ public void Interpolation_PositionAfterLotsOfOperations()
+ {
+ var obj = new Cube(5, 10, 20).Mirror(0, 0, 1).Mirror(0, 1, 0)
+ .Rotate(15, -45, 120).Translate(-20, 10, 15).Rotate(90, 15, 25)
+ .Translate(-10, -20, -20).Rotate(-90, -90, -45);
+
+ var position = obj.Position();
+ Assert.AreEqual(new Vector3(-21.7567866493247, 28.2686425980997, -21.6189570529939), position);
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/IntersectionTests.cs b/OSCADSharp/OSCADSharp.UnitTests/IntersectionTests.cs
new file mode 100644
index 0000000..fadd682
--- /dev/null
+++ b/OSCADSharp/OSCADSharp.UnitTests/IntersectionTests.cs
@@ -0,0 +1,19 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OSCADSharp.Solids;
+
+namespace OSCADSharp.UnitTests
+{
+ [TestClass]
+ public class IntersectionTests
+ {
+ [TestMethod]
+ [ExpectedException(typeof(NotSupportedException))]
+ public void Intersection_PositionThrowsNotSupportedException()
+ {
+ var obj = new Sphere().Intersection(new Text3D("Sup"));
+
+ var pos = obj.Position();
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/MinkowskiTests.cs b/OSCADSharp/OSCADSharp.UnitTests/MinkowskiTests.cs
new file mode 100644
index 0000000..abbc9c7
--- /dev/null
+++ b/OSCADSharp/OSCADSharp.UnitTests/MinkowskiTests.cs
@@ -0,0 +1,19 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OSCADSharp.Solids;
+
+namespace OSCADSharp.UnitTests
+{
+ [TestClass]
+ public class MinkowskiTests
+ {
+ [TestMethod]
+ [ExpectedException(typeof(NotSupportedException))]
+ public void Minkowski_PositionThrowsNotSupportedException()
+ {
+ var obj = new Cylinder().Intersection(new Sphere()).Translate(0, 5, 5);
+
+ var pos = obj.Position();
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/MirrorTests.cs b/OSCADSharp/OSCADSharp.UnitTests/MirrorTests.cs
new file mode 100644
index 0000000..dc2747b
--- /dev/null
+++ b/OSCADSharp/OSCADSharp.UnitTests/MirrorTests.cs
@@ -0,0 +1,44 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OSCADSharp.Solids;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSCADSharp.UnitTests
+{
+ [TestClass]
+ public class MirrorTests
+ {
+ [TestMethod]
+ public void Mirror_SingleAxisMirrorInvertsPosition()
+ {
+ var cube = new Cube(5, 10, 20);
+ var xMirror = cube.Clone().Mirror(1, 0, 0);
+ var yMirror = cube.Clone().Mirror(0, 1, 0);
+ var zMirror = cube.Clone().Mirror(0, 0, 1);
+
+ var pos = cube.Position().Clone();
+ pos.X = -pos.X;
+ Assert.AreEqual(pos, xMirror.Position());
+
+ pos = cube.Position().Clone();
+ pos.Y = -pos.Y;
+ Assert.AreEqual(pos, yMirror.Position());
+
+ pos = cube.Position().Clone();
+ pos.Z = -pos.Z;
+ Assert.AreEqual(pos, zMirror.Position());
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(NotSupportedException))]
+ public void Mirror_MultiAxisPositionThrowsNotSupportedException()
+ {
+ var cube = new Cube(5, 10, 20);
+
+ var pos = cube.Mirror(1, 1, 0).Position();
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/OSCADSharp.UnitTests.csproj b/OSCADSharp/OSCADSharp.UnitTests/OSCADSharp.UnitTests.csproj
index 31cac6c..f66616f 100644
--- a/OSCADSharp/OSCADSharp.UnitTests/OSCADSharp.UnitTests.csproj
+++ b/OSCADSharp/OSCADSharp.UnitTests/OSCADSharp.UnitTests.csproj
@@ -55,10 +55,16 @@
+
+
+
+
+
+
diff --git a/OSCADSharp/OSCADSharp.UnitTests/SphereTests.cs b/OSCADSharp/OSCADSharp.UnitTests/SphereTests.cs
index 292fe69..1e87047 100644
--- a/OSCADSharp/OSCADSharp.UnitTests/SphereTests.cs
+++ b/OSCADSharp/OSCADSharp.UnitTests/SphereTests.cs
@@ -69,5 +69,13 @@ namespace OSCADSharp.UnitTests
Assert.IsTrue(sphere.IsSameAs(clone));
}
+
+ [TestMethod]
+ public void Sphere_PositionIsAtZero()
+ {
+ var sphere = new Sphere();
+
+ Assert.AreEqual(new Vector3(), sphere.Position());
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp.UnitTests/Text3DTests.cs b/OSCADSharp/OSCADSharp.UnitTests/Text3DTests.cs
new file mode 100644
index 0000000..e8ebcf3
--- /dev/null
+++ b/OSCADSharp/OSCADSharp.UnitTests/Text3DTests.cs
@@ -0,0 +1,22 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OSCADSharp.Solids;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSCADSharp.UnitTests
+{
+ [TestClass]
+ public class Text3DTests
+ {
+ [TestMethod]
+ public void Text_PositionIsCentered()
+ {
+ var text = new Text3D("Bom chicka bow wow");
+
+ Assert.AreEqual(new Vector3(), text.Position());
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp/Booleans/Difference.cs b/OSCADSharp/OSCADSharp/Booleans/Difference.cs
index 3877265..bd66924 100644
--- a/OSCADSharp/OSCADSharp/Booleans/Difference.cs
+++ b/OSCADSharp/OSCADSharp/Booleans/Difference.cs
@@ -18,6 +18,11 @@ namespace OSCADSharp.Booleans
///
public Difference(IEnumerable children) : base("difference()", children)
{
- }
+ }
+
+ public override Vector3 Position()
+ {
+ throw new NotSupportedException("Position is not supported on Differenced objects.");
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Booleans/Intersection.cs b/OSCADSharp/OSCADSharp/Booleans/Intersection.cs
index a67f010..f5c0f72 100644
--- a/OSCADSharp/OSCADSharp/Booleans/Intersection.cs
+++ b/OSCADSharp/OSCADSharp/Booleans/Intersection.cs
@@ -19,5 +19,10 @@ namespace OSCADSharp.Booleans
public Intersection(IEnumerable children) : base("intersection()", children)
{
}
+
+ public override Vector3 Position()
+ {
+ throw new NotSupportedException("Position is not supported on Intersected objects.");
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/OSCADObject.cs b/OSCADSharp/OSCADSharp/OSCADObject.cs
index d3034a0..4ee907e 100644
--- a/OSCADSharp/OSCADSharp/OSCADObject.cs
+++ b/OSCADSharp/OSCADSharp/OSCADObject.cs
@@ -204,6 +204,16 @@ namespace OSCADSharp
#endregion
#region Utility Methods
+ ///
+ /// Returns the computed position of this object.
+ ///
+ /// For some objects that are the aggregate of many operations or
+ /// multiple children, this may be an approximation or average
+ /// of the position.
+ ///
+ ///
+ public abstract Vector3 Position();
+
///
/// Creates a copy of this object and all of its children
///
diff --git a/OSCADSharp/OSCADSharp/OSCADSharp.csproj b/OSCADSharp/OSCADSharp/OSCADSharp.csproj
index edbee13..16dc350 100644
--- a/OSCADSharp/OSCADSharp/OSCADSharp.csproj
+++ b/OSCADSharp/OSCADSharp/OSCADSharp.csproj
@@ -41,6 +41,7 @@
+
@@ -62,7 +63,7 @@
-
+
diff --git a/OSCADSharp/OSCADSharp/Scripting/MultiBlockStatementObject.cs b/OSCADSharp/OSCADSharp/Scripting/MultiBlockStatementObject.cs
index 0abf646..c3a4cea 100644
--- a/OSCADSharp/OSCADSharp/Scripting/MultiBlockStatementObject.cs
+++ b/OSCADSharp/OSCADSharp/Scripting/MultiBlockStatementObject.cs
@@ -43,5 +43,11 @@ namespace OSCADSharp.Scripting
return new MultiBlockStatementObject(this.outerStatement, childClones);
}
+
+ public override Vector3 Position()
+ {
+ var positions = this.children.Select(child => child.Position());
+ return Vector3.Average(positions.ToArray());
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Solids/Cube.cs b/OSCADSharp/OSCADSharp/Solids/Cube.cs
index 2264b2c..6945e1a 100644
--- a/OSCADSharp/OSCADSharp/Solids/Cube.cs
+++ b/OSCADSharp/OSCADSharp/Solids/Cube.cs
@@ -76,6 +76,21 @@ namespace OSCADSharp.Solids
Center = this.Center
};
}
+
+ public override Vector3 Position()
+ {
+ Vector3 position;
+ if(this.Center == false)
+ {
+ position = new Vector3(this.Size.X / 2, this.Size.Y / 2, this.Size.Z / 2);
+ }
+ else
+ {
+ position = new Vector3();
+ }
+
+ return position;
+ }
#endregion
}
}
diff --git a/OSCADSharp/OSCADSharp/Solids/Cylinder.cs b/OSCADSharp/OSCADSharp/Solids/Cylinder.cs
index 22f6bbc..aaf6899 100644
--- a/OSCADSharp/OSCADSharp/Solids/Cylinder.cs
+++ b/OSCADSharp/OSCADSharp/Solids/Cylinder.cs
@@ -137,6 +137,21 @@ namespace OSCADSharp.Solids
Center = this.Center
};
}
+
+ public override Vector3 Position()
+ {
+ Vector3 position;
+ if (this.Center == false)
+ {
+ position = new Vector3(0, 0, this.Height / 2);
+ }
+ else
+ {
+ position = new Vector3();
+ }
+
+ return position;
+ }
#endregion
}
}
diff --git a/OSCADSharp/OSCADSharp/Solids/Sphere.cs b/OSCADSharp/OSCADSharp/Solids/Sphere.cs
index e3c16c2..6316efc 100644
--- a/OSCADSharp/OSCADSharp/Solids/Sphere.cs
+++ b/OSCADSharp/OSCADSharp/Solids/Sphere.cs
@@ -80,6 +80,11 @@ namespace OSCADSharp.Solids
Radius = this.Radius
};
}
+
+ public override Vector3 Position()
+ {
+ return new Vector3();
+ }
#endregion
}
}
diff --git a/OSCADSharp/OSCADSharp/Solids/Text3D.cs b/OSCADSharp/OSCADSharp/Solids/Text3D.cs
index 2a69ba0..1468921 100644
--- a/OSCADSharp/OSCADSharp/Solids/Text3D.cs
+++ b/OSCADSharp/OSCADSharp/Solids/Text3D.cs
@@ -59,6 +59,7 @@ namespace OSCADSharp.Solids
public string Language { get; set; }
#endregion
+
#region Constructors
///
/// Creates 3d text with the default parameters
@@ -115,6 +116,11 @@ namespace OSCADSharp.Solids
sb.Append("\"");
appendIfValueNotNullOrEmpty("size", this.Size?.ToString(), sb);
+ // Text is always centered in OSCADSharp to ensure correctness of
+ // position interpolation
+ appendIfValueNotNullOrEmpty("halign", "\"center\"", sb);
+ appendIfValueNotNullOrEmpty("valign", "\"center\"", sb);
+
appendIfValueNotNullOrEmpty("font", this.Font, sb);
appendIfValueNotNullOrEmpty("spacing", this.Spacing?.ToString(), sb);
appendIfValueNotNullOrEmpty("direction", this.TextDirection?.ToString(), sb);
@@ -125,6 +131,18 @@ namespace OSCADSharp.Solids
var formatter = new SingleBlockFormatter(String.Format("linear_extrude(height = {0})", 1), sb.ToString());
return formatter.ToString();
}
+
+ ///
+ /// In reaction to the need for this value to be correct, halign and valign will always
+ /// be "center" by default, since non-centered text would vary dramatically in position based upon
+ /// the font of the text
+ /// - MLS 2/15/2016
+ ///
+ ///
+ public override Vector3 Position()
+ {
+ return new Vector3();
+ }
#endregion
}
}
diff --git a/OSCADSharp/OSCADSharp/Spatial/Matrix.cs b/OSCADSharp/OSCADSharp/Spatial/Matrix.cs
new file mode 100644
index 0000000..981acd8
--- /dev/null
+++ b/OSCADSharp/OSCADSharp/Spatial/Matrix.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSCADSharp.Spatial
+{
+ ///
+ /// An n-dimensional Matrix for performing operations on doubles that
+ /// represent spatial positions
+ ///
+ internal class Matrix
+ {
+ #region Fields/Properties
+ private double[] values;
+
+ internal int ColumnCount { get; private set; }
+ internal int RowCount { get; private set; }
+
+ internal double[] GetValues()
+ {
+ return this.values;
+ }
+ #endregion
+
+ #region Constructors
+ internal Matrix(double[] values, int numRows, int numColumns)
+ {
+ this.values = values;
+ this.RowCount = numRows;
+ this.ColumnCount = numColumns;
+ }
+
+ internal Matrix(List values, int numColumns)
+ {
+ this.values = values.ToArray();
+ this.ColumnCount = numColumns;
+ this.RowCount = values.Count / numColumns;
+ }
+ #endregion
+
+ #region Public API
+ internal Matrix Multiply(Matrix other)
+ {
+ double[] otherValues = other.GetValues();
+ List result = new List();
+ int currentRowInResult = 0;
+
+ //Iterate over each row in this matrix
+ for (int row = 0; row < this.RowCount; row++)
+ {
+ //And iterate over each column in the other matrix
+ for (int column = 0; column < other.ColumnCount; column++)
+ {
+ // Multiply item in the current row on the left, by each item in column right
+ // and add it to the result in the corresponding row/column
+ for (int leftMatrixColumn = 0; leftMatrixColumn < this.ColumnCount; leftMatrixColumn++)
+ {
+ result.Add(0);
+ result[currentRowInResult * other.ColumnCount + column] +=
+ this.values[row * this.ColumnCount + leftMatrixColumn] *
+ otherValues[leftMatrixColumn * other.ColumnCount + column];
+ }
+ }
+
+ currentRowInResult++;
+ }
+
+ return new Matrix(result, other.ColumnCount);
+ }
+ #endregion
+
+ #region Static Transformation Matrices
+ private static readonly double piOver180 = Math.PI / 180;
+ private static double toRadians(double degrees)
+ {
+ return piOver180 * degrees;
+ }
+
+ private static readonly Matrix identity = new Matrix(new double[] {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ }, 4, 4);
+ internal static Matrix Identity { get { return identity; } }
+
+ ///
+ /// Gets a transformation matrix for performing rotations on the X-Axis
+ /// (Assuming you are in a right-handed 3D world)
+ ///
+ /// Degrees of rotation
+ /// Transformation matrix to perform the rotation
+ internal static Matrix XRotation(double angle)
+ {
+ if (angle == 0)
+ return Identity;
+
+ double radAngle = toRadians(angle);
+ double[] rotationArr = new double[] {
+ 1 , 0, 0, 0,
+ 0, Math.Cos(radAngle), Math.Sin(radAngle), 0,
+ 0, -Math.Sin(radAngle), Math.Cos(radAngle), 0,
+ 0, 0, 0, 1
+ };
+
+ return new Matrix(rotationArr, 4, 4);
+ }
+
+ ///
+ /// Gets a transformation matrix for performing rotations on the Y-Axis
+ /// (Assuming you are in a right-handed 3D world)
+ ///
+ /// Degrees of rotation
+ /// Transformation matrix to perform the rotation
+ internal static Matrix YRotation(double angle)
+ {
+ if (angle == 0)
+ return Identity;
+
+ double radAngle = toRadians(angle);
+ double[] rotationArr = new double[] {
+ Math.Cos(radAngle), 0, -Math.Sin(radAngle), 0,
+ 0, 1, 0, 0,
+ Math.Sin(radAngle), 0, Math.Cos(radAngle), 0,
+ 0, 0, 0, 1
+ };
+
+ return new Matrix(rotationArr, 4, 4);
+ }
+
+ ///
+ /// Gets a transformation matrix for performing rotations on the Z-Axis
+ /// (Assuming you are in a right-handed 3D world)
+ ///
+ /// Degrees of rotation
+ /// Transformation matrix to perform the rotation
+ internal static Matrix ZRotation(double angle)
+ {
+ if (angle == 0)
+ return Identity;
+
+ double radAngle = toRadians(angle);
+ double[] rotationArr = new double[] {
+ Math.Cos(radAngle), Math.Sin(radAngle), 0, 0,
+ -Math.Sin(radAngle), Math.Cos(radAngle), 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ };
+
+ return new Matrix(rotationArr, 4, 4);
+ }
+
+ ///
+ /// Gets a point's position after rotations have been applied
+ ///
+ /// Point to rotate
+ ///
+ ///
+ ///
+ /// Point after rotation
+ internal static Vector3 GetRotatedPoint(Vector3 point, double xAngle, double yAngle, double zAngle)
+ {
+ var x = XRotation(-xAngle).Multiply(point.ToMatrix());
+ var y = YRotation(-yAngle).Multiply(x);
+ var z = ZRotation(-zAngle).Multiply(y).GetValues();
+ return new Vector3(z[0], z[1], z[2]);
+ }
+ #endregion
+ }
+}
diff --git a/OSCADSharp/OSCADSharp/Spatial/Vector3.cs b/OSCADSharp/OSCADSharp/Spatial/Vector3.cs
new file mode 100644
index 0000000..82f4e65
--- /dev/null
+++ b/OSCADSharp/OSCADSharp/Spatial/Vector3.cs
@@ -0,0 +1,157 @@
+using OSCADSharp.Spatial;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSCADSharp
+{
+ ///
+ /// A Three-Dimensional vector
+ ///
+ /// Can be used to represent a direction, or a point in space
+ ///
+ public class Vector3
+ {
+ #region Attributes
+ public double X { get; set; }
+ public double Y { get; set; }
+ public double Z { get; set; }
+ #endregion
+
+ public Vector3(double x = 0, double y = 0, double z = 0)
+ {
+ this.X = x;
+ this.Y = y;
+ this.Z = z;
+ }
+
+ ///
+ /// Negates the values of this vector, returning an inverse of it
+ ///
+ /// A negated vector
+ public Vector3 Negate()
+ {
+ return new Vector3(-this.X, -this.Y, -this.Z);
+ }
+
+ ///
+ /// Creates a copy of this vector that's a new instance
+ /// with the same values
+ ///
+ /// A clone of this vector
+ public Vector3 Clone()
+ {
+ return new Vector3(this.X, this.Y, this.Z);
+ }
+
+ ///
+ /// Returns the average position of the provided positions
+ ///
+ ///
+ ///
+ public static Vector3 Average(params Vector3[] positions)
+ {
+ if(positions == null || positions.Length == 0)
+ {
+ return null;
+ }
+ else if (positions.Length == 1)
+ {
+ return positions[0];
+ }
+
+ var sum = new Vector3();
+
+ foreach (var pos in positions)
+ {
+ sum += pos;
+ }
+
+ return new Vector3(sum.X / positions.Length, sum.Y / positions.Length, sum.Z / positions.Length);
+ }
+
+ ///
+ /// Returns the unit vector for this vector
+ ///
+ ///
+ public Vector3 Normalize()
+ {
+ if(this.X == 0 && this.Y == 0 && this.Z == 0)
+ {
+ return this;
+ }
+
+ double sum = Math.Abs(this.X) + Math.Abs(this.Y) + Math.Abs(this.Z);
+ return new Vector3(this.X / sum, this.Y / sum, this.Z / sum);
+ }
+
+ public double Dot(Vector3 other)
+ {
+ return this.X * other.X + this.Y * other.Y + this.Z * other.Z;
+ }
+
+ #region Operators/Overrides
+ public override bool Equals(object obj)
+ {
+ return this.GetHashCode() == obj.GetHashCode();
+ }
+
+ public override int GetHashCode()
+ {
+ return this.ToString().GetHashCode();
+ }
+
+ public static bool operator ==(Vector3 left, Vector3 right)
+ {
+ return left.X == right.X &&
+ left.Y == right.Y &&
+ left.Z == right.Z;
+ }
+
+ public static bool operator !=(Vector3 left, Vector3 right)
+ {
+ return !(left.X == right.X &&
+ left.Y == right.Y &&
+ left.Z == right.Z);
+ }
+
+ public static Vector3 operator +(Vector3 left, Vector3 right)
+ {
+ return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
+ }
+
+ public static Vector3 operator -(Vector3 left, Vector3 right)
+ {
+ return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
+ }
+
+ public static Vector3 operator *(Vector3 left, Vector3 right)
+ {
+ return new Vector3(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
+ }
+
+ public static Vector3 operator *(Vector3 left, double right)
+ {
+ return new Vector3(left.X * right, left.Y * right, left.Z * right);
+ }
+
+ public static Vector3 operator *(double left, Vector3 right)
+ {
+ return new Vector3(left * right.X, left * right.Y, left * right.Z);
+ }
+ #endregion
+
+ internal Matrix ToMatrix()
+ {
+ double[] coords = { this.X, this.Y, this.Z, 0 };
+ return new Matrix(coords, 4, 1);
+ }
+
+ public override string ToString()
+ {
+ return String.Format("[X: {0}, Y: {1}, Z: {2}]", this.X.ToString(), this.Y.ToString(), this.Z.ToString());
+ }
+ }
+}
diff --git a/OSCADSharp/OSCADSharp/Transforms/ColoredObject.cs b/OSCADSharp/OSCADSharp/Transforms/ColoredObject.cs
index af988aa..6c6a047 100644
--- a/OSCADSharp/OSCADSharp/Transforms/ColoredObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/ColoredObject.cs
@@ -50,5 +50,10 @@ namespace OSCADSharp.Transforms
{
return new ColoredObject(obj, this.ColorName, this.Opacity);
}
+
+ public override Vector3 Position()
+ {
+ return this.obj.Position();
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/LinearExtrudedObject.cs b/OSCADSharp/OSCADSharp/Transforms/LinearExtrudedObject.cs
index a6744e2..d1506b2 100644
--- a/OSCADSharp/OSCADSharp/Transforms/LinearExtrudedObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/LinearExtrudedObject.cs
@@ -36,7 +36,6 @@ namespace OSCADSharp.Transforms
this.children.Add(obj);
}
-
public override OSCADObject Clone()
{
return new LinearExtrudedObject(this.obj.Clone(), this.Height);
@@ -48,5 +47,10 @@ namespace OSCADSharp.Transforms
var formatter = new SingleBlockFormatter(extrudeCommand, this.obj.ToString());
return formatter.ToString();
}
+
+ public override Vector3 Position()
+ {
+ throw new NotSupportedException();
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/MinkowskiedObject.cs b/OSCADSharp/OSCADSharp/Transforms/MinkowskiedObject.cs
index 1af5640..3b4693c 100644
--- a/OSCADSharp/OSCADSharp/Transforms/MinkowskiedObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/MinkowskiedObject.cs
@@ -15,6 +15,11 @@ namespace OSCADSharp.Transforms
public MinkowskiedObject(IEnumerable children) : base("minkowski()", children)
{
- }
+ }
+
+ public override Vector3 Position()
+ {
+ throw new NotSupportedException("Position is not supported on Minkowskied objects.");
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/MirroredObject.cs b/OSCADSharp/OSCADSharp/Transforms/MirroredObject.cs
index 7d7f22d..05f4dc7 100644
--- a/OSCADSharp/OSCADSharp/Transforms/MirroredObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/MirroredObject.cs
@@ -1,4 +1,5 @@
using OSCADSharp.Scripting;
+using OSCADSharp.Spatial;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -49,5 +50,29 @@ namespace OSCADSharp.Transforms
{
return new MirroredObject(obj, this.Normal);
}
+
+ // TODO: This will yield incorrect positions if mirroring on multiple axes
+ // fix mirrored positions for multiple-axis mirroring
+ public override Vector3 Position()
+ {
+ if (this.isMoreThanOneAxis())
+ {
+ throw new NotSupportedException("Getting the position of an object that's been mirrored on more than one axis is not currently supported.");
+ }
+
+ var pos = obj.Position();
+
+ double x = this.Normal.X != 0 ? pos.X * -1 : pos.X;
+ double y = this.Normal.Y != 0 ? pos.Y * -1 : pos.Y;
+ double z = this.Normal.Z != 0 ? pos.Z * -1 : pos.Z;
+
+ return new Vector3(x, y, z);
+ }
+
+ private bool isMoreThanOneAxis()
+ {
+ return (this.Normal.X != 0 && (this.Normal.Y != 0 || this.Normal.Z != 0)) ||
+ (this.Normal.Y != 0 && (this.Normal.X != 0 || this.Normal.Z != 0));
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/ResizedObject.cs b/OSCADSharp/OSCADSharp/Transforms/ResizedObject.cs
index c058ea8..506bdb3 100644
--- a/OSCADSharp/OSCADSharp/Transforms/ResizedObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/ResizedObject.cs
@@ -48,5 +48,10 @@ namespace OSCADSharp.Transforms
{
return new ResizedObject(obj, this.Size);
}
+
+ public override Vector3 Position()
+ {
+ return obj.Position();
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/RotatedObject.cs b/OSCADSharp/OSCADSharp/Transforms/RotatedObject.cs
index e21f975..c83aa95 100644
--- a/OSCADSharp/OSCADSharp/Transforms/RotatedObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/RotatedObject.cs
@@ -1,4 +1,5 @@
using OSCADSharp.Scripting;
+using OSCADSharp.Spatial;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -48,5 +49,10 @@ namespace OSCADSharp.Transforms
{
return new RotatedObject(obj, this.Angle);
}
+
+ public override Vector3 Position()
+ {
+ return Matrix.GetRotatedPoint(this.obj.Position(), this.Angle.X, this.Angle.Y, this.Angle.Z);
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/ScaledObject.cs b/OSCADSharp/OSCADSharp/Transforms/ScaledObject.cs
index aadeade..ed30e60 100644
--- a/OSCADSharp/OSCADSharp/Transforms/ScaledObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/ScaledObject.cs
@@ -48,5 +48,10 @@ namespace OSCADSharp.Transforms
{
return new ScaledObject(obj, this.ScaleFactor);
}
+
+ public override Vector3 Position()
+ {
+ return obj.Position();
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Transforms/TranslatedObject.cs b/OSCADSharp/OSCADSharp/Transforms/TranslatedObject.cs
index 636319c..e9f0c81 100644
--- a/OSCADSharp/OSCADSharp/Transforms/TranslatedObject.cs
+++ b/OSCADSharp/OSCADSharp/Transforms/TranslatedObject.cs
@@ -45,5 +45,10 @@ namespace OSCADSharp.Transforms
{
return new TranslatedObject(obj, this.Vector);
}
+
+ public override Vector3 Position()
+ {
+ return this.obj.Position() + this.Vector;
+ }
}
}
diff --git a/OSCADSharp/OSCADSharp/Vector3.cs b/OSCADSharp/OSCADSharp/Vector3.cs
deleted file mode 100644
index 72a9ae1..0000000
--- a/OSCADSharp/OSCADSharp/Vector3.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace OSCADSharp
-{
- ///
- /// A Three-Dimensional vector
- ///
- /// Can be used to represent a direction, or a point in space
- ///
- public class Vector3
- {
- #region Attributes
- public double X { get; set; }
- public double Y { get; set; }
- public double Z { get; set; }
- #endregion
-
- public Vector3(double x = 0, double y = 0, double z = 0)
- {
- this.X = x;
- this.Y = y;
- this.Z = z;
- }
-
- ///
- /// Negates the values of this vector, returning an inverse of it
- ///
- /// A negated vector
- public Vector3 Negate()
- {
- return new Vector3(-this.X, -this.Y, -this.Z);
- }
-
- ///
- /// Creates a copy of this vector that's a new instance
- /// with the same values
- ///
- /// A clone of this vector
- public Vector3 Clone()
- {
- return new Vector3(this.X, this.Y, this.Z);
- }
-
- #region Operators
- public static Vector3 operator +(Vector3 left, Vector3 right)
- {
- return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
- }
-
- public static Vector3 operator -(Vector3 left, Vector3 right)
- {
- return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
- }
- #endregion
-
- public override string ToString()
- {
- return String.Format("[X: {0}, Y: {1}, Z: {2}]", this.X.ToString(), this.Y.ToString(), this.Z.ToString());
- }
- }
-}