using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Threading; using MatterHackers.Agg; using MatterHackers.Agg.Font; using MatterHackers.Agg.Platform; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.MatterControl.MatterCad; using MatterHackers.MatterControl.PartPreviewWindow.View3D; using MatterHackers.PolygonMesh; using MatterHackers.RenderOpenGl; using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.MeshObjects { public enum Alignment { X, Y, Z, negX, negY, negZ }; [Flags] public enum Face { Left = 0x01, Right = 0x02, Front = 0x04, Back = 0x08, Bottom = 0x10, Top = 0x20, }; [Flags] public enum Edge { LeftFront = Face.Left | Face.Front, LeftBack = Face.Left | Face.Back, LeftBottom = Face.Left | Face.Bottom, LeftTop = Face.Left | Face.Top, RightFront = Face.Right | Face.Front, RightBack = Face.Right | Face.Back, RightBottom = Face.Right | Face.Bottom, RightTop = Face.Right | Face.Top, FrontBottom = Face.Front | Face.Bottom, FrontTop = Face.Front | Face.Top, BackBottom = Face.Back | Face.Bottom, BackTop = Face.Back | Face.Top } public class BadSubtract : MatterCadObject3D { public BadSubtract() { RebuildMeshes(); } public double Sides { get; set; } = 4; public override void RebuildMeshes() { int sides = 3; IObject3D keep = new Cylinder(20, 20, sides); IObject3D subtract = new Cylinder(10, 21, sides); subtract = new SetCenter(subtract, keep.GetCenter()); IObject3D result = keep.Minus(subtract); this.SetChildren(result); } } public class SetCenter : Object3D { public SetCenter() { } public SetCenter(IObject3D item, Vector3 position) { Matrix *= Matrix4X4.CreateTranslation(position - item.GetCenter()); Children.Add(item.Clone()); } public SetCenter(IObject3D item, double x, double y, double z) : this(item, new Vector3(x, y, z)) { } public SetCenter(IObject3D item, Vector3 offset, bool onX = true, bool onY = true, bool onZ = true) { var center = item.GetAxisAlignedBoundingBox(Matrix4X4.Identity).Center; Vector3 consideredOffset = Vector3.Zero; // zero out anything we don't want if (onX) { consideredOffset.X = offset.X - center.X; } if (onY) { consideredOffset.Y = offset.Y - center.Y; } if (onZ) { consideredOffset.Z = offset.Z - center.Z; } Matrix *= Matrix4X4.CreateTranslation(consideredOffset); Children.Add(item.Clone()); } } public class Cylinder : Object3D { public Cylinder() { } public Cylinder(double radius, double height, int sides, Alignment alignment = Alignment.Z) : this(radius, radius, height, sides, alignment) { } public Cylinder(double radiusBottom, double radiusTop, double height, int sides, Alignment alignment = Alignment.Z) { var path = new VertexStorage(); path.MoveTo(0, -height/2); path.LineTo(radiusBottom, -height/2); path.LineTo(radiusTop, height/2); path.LineTo(0, height/2); Mesh = VertexSourceToMesh.Revolve(path, sides); switch (alignment) { case Alignment.X: Matrix = Matrix4X4.CreateRotationY(MathHelper.Tau / 4); break; case Alignment.Y: Matrix = Matrix4X4.CreateRotationX(MathHelper.Tau / 4); break; case Alignment.Z: // This is the natural case (how it was modled) break; case Alignment.negX: Matrix = Matrix4X4.CreateRotationY(-MathHelper.Tau / 4); break; case Alignment.negY: Matrix = Matrix4X4.CreateRotationX(-MathHelper.Tau / 4); break; case Alignment.negZ: Matrix = Matrix4X4.CreateRotationX(MathHelper.Tau / 2); break; } } } public class CardHolder : MatterCadObject3D { public CardHolder() { RebuildMeshes(); } [DisplayName("Name")] public string NameToWrite { get; set; } = "MatterHackers"; public override void RebuildMeshes() { IObject3D plainCardHolder = Object3D.Load("C:/Temp/CardHolder.stl"); //TypeFace typeFace = TypeFace.LoadSVG("Viking_n.svg"); var letterPrinter = new TypeFacePrinter(NameToWrite);//, new StyledTypeFace(typeFace, 12)); IObject3D nameMesh = new Object3D() { Mesh = VertexSourceToMesh.Extrude(letterPrinter, 5) }; AxisAlignedBoundingBox textBounds = nameMesh.GetAxisAlignedBoundingBox(Matrix4X4.Identity); var textArea = new Vector2(90, 20); // test the area that the names will go to // nameMesh = new Box(textArea.X, textArea.Y, 5); double scale = Math.Min(textArea.X / textBounds.XSize, textArea.Y / textBounds.YSize); nameMesh = new Scale(nameMesh, scale, scale, 1); nameMesh = new Align(nameMesh, Face.Bottom | Face.Front, plainCardHolder, Face.Bottom | Face.Front); nameMesh = new SetCenter(nameMesh, plainCardHolder.GetCenter(), true, false, false); nameMesh = new Rotate(nameMesh, MathHelper.DegreesToRadians(-16)); nameMesh = new Translate(nameMesh, 0, 4, 2); // output two meshes for card holder and text this.Children.Modify(list => { list.Clear(); list.Add(plainCardHolder); list.Add(nameMesh); }); } } /* public class ChairFoot2 : MatterCadObject3D { public ChairFoot() { RebuildMeshes(); } [DisplayName("Angle")] public double AngleDegrees { get; set; } = 3; // these are the public variables that would be edited [DisplayName("Final")] public bool FinalPart { get; set; } = true; [DisplayName("Height")] public double HeightFromFloorToBottomOfLeg { get; set; } = 10; [DisplayName("Inner Size")] public double InnerSize { get; set; } = 20; [DisplayName("Reach")] public double InsideReach { get; set; } = 10; [DisplayName("Outer Size")] public double OuterSize { get; set; } = 22; public override void RebuildMeshes() { // This would be better expressed as the desired offset height (height from ground to bottom of chair leg). double angleRadians = MathHelper.DegreesToRadians(AngleDegrees); double extraHeightForRotation = Math.Sinh(angleRadians) * OuterSize; // get the distance to clip off the extra bottom double unclippedFootHeight = HeightFromFloorToBottomOfLeg + extraHeightForRotation; if (FinalPart) { Box chairFootBox = new Box(OuterSize, OuterSize, unclippedFootHeight); //chairFootBox.BevelEdge(Edge.LeftBack, 2); //chairFootBox.BevelEdge(Edge.LeftFront, 2); //chairFootBox.BevelEdge(Edge.RightBack, 2); //chairFootBox.BevelEdge(Edge.RightFront, 2); IObject3D chairFoot = chairFootBox; IObject3D ring = new Cylinder(InnerSize / 2 - 1, InsideReach, 30); ring -= new Cylinder(ring.XSize / 2 - 2, ring.ZSize + 1, 30); IObject3D fins = new Box(3, 1, ring.ZSize); fins = new Translate(fins, 0, 1) + new Translate(fins, 0, -1); fins -= new Align(new Rotate(new Box(5, 5, 5), 0, MathHelper.DegreesToRadians(45)), Face.Bottom | Face.Left, fins, Face.Top | Face.Left, 0, 0, -fins.XSize); fins = new Translate(fins, InnerSize / 2 - .1); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45)); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45 + 90)); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45 + 180)); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45 - 90)); chairFoot += new Align(ring, Face.Bottom, chairFoot, Face.Top, 0, 0, -.1); chairFoot = new Rotate(chairFoot, 0, angleRadians, 0); IObject3D clipBox = new Align(new Box(OuterSize * 2, OuterSize * 2, unclippedFootHeight), Face.Top, chairFoot, Face.Bottom, 0, 0, extraHeightForRotation); chairFoot -= clipBox; chairFoot = new Translate(chairFoot, 0, 0, clipBox.GetAxisAlignedBoundingBox().maxXYZ.Z); this.Mesh = CsgToMesh.Convert(chairFoot); } else // fit part { double baseHeight = 3; double insideHeight = 4; Box chairFootBox = new Box(OuterSize, OuterSize, baseHeight); chairFootBox.BevelEdge(Edge.LeftBack, 2); chairFootBox.BevelEdge(Edge.LeftFront, 2); chairFootBox.BevelEdge(Edge.RightBack, 2); chairFootBox.BevelEdge(Edge.RightFront, 2); IObject3D chairFoot = chairFootBox; IObject3D ring = new Cylinder(InnerSize / 2 - 1, insideHeight, 30); ring -= new Cylinder(ring.XSize / 2 - 2, ring.ZSize + 1, 30); IObject3D fins = new Box(3, 1, ring.ZSize); fins = new Translate(fins, 0, 1) + new Translate(fins, 0, -1); fins -= new Align(new Rotate(new Box(5, 5, 5), 0, MathHelper.DegreesToRadians(45)), Face.Bottom | Face.Left, fins, Face.Top | Face.Left, 0, 0, -fins.XSize); fins = new Translate(fins, InnerSize / 2 - .1); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45)); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45 + 90)); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45 + 180)); ring += new Rotate(fins, 0, 0, MathHelper.DegreesToRadians(45 - 90)); chairFoot += new Align(ring, Face.Bottom, chairFoot, Face.Top, 0, 0, -.1); this.Mesh = CsgToMesh.Convert(chairFoot); } } } */ public class CubePrimitive : MatterCadObject3D { public CubePrimitive() { RebuildMeshes(); } public double Width { get; set; } = 20; public double Depth { get; set; } = 20; public double Height { get; set; } = 20; public override void RebuildMeshes() { var aabb = AxisAlignedBoundingBox.Zero; if (Mesh != null) { this.GetAxisAlignedBoundingBox(); } Mesh = PlatonicSolids.CreateCube(Width, Depth, Height); Mesh.CleanAndMergMesh(CancellationToken.None); PlatingHelper.PlaceMeshAtHeight(this, aabb.minXYZ.Z); } } public class CylinderPrimitive : MatterCadObject3D { public CylinderPrimitive() { RebuildMeshes(); } public double Diameter { get; set; } = 20; public double Height { get; set; } = 20; public int Sides { get; set; } = 30; public override void RebuildMeshes() { var aabb = AxisAlignedBoundingBox.Zero; if (Mesh != null) { this.GetAxisAlignedBoundingBox(); } var path = new VertexStorage(); path.MoveTo(0, 0); path.LineTo(Diameter / 2, 0); path.LineTo(Diameter / 2, Height); path.LineTo(0, Height); Mesh = VertexSourceToMesh.Revolve(path, Sides); Mesh.CleanAndMergMesh(CancellationToken.None); PlatingHelper.PlaceMeshAtHeight(this, aabb.minXYZ.Z); } } public class ConePrimitive : MatterCadObject3D { public ConePrimitive() { RebuildMeshes(); } [DisplayName("Diameter")] public double Diameter { get; set; } = 20; //[DisplayName("Top")] //public double TopDiameter { get; set; } = 0; public double Height { get; set; } = 20; public int Sides { get; set; } = 30; public override void RebuildMeshes() { var aabb = AxisAlignedBoundingBox.Zero; if (Mesh != null) { this.GetAxisAlignedBoundingBox(); } var path = new VertexStorage(); path.MoveTo(0, 0); path.LineTo(Diameter / 2, 0); path.LineTo(0, Height); Mesh = VertexSourceToMesh.Revolve(path, Sides); Mesh.CleanAndMergMesh(CancellationToken.None); PlatingHelper.PlaceMeshAtHeight(this, aabb.minXYZ.Z); } } public class TorusPrimitive : MatterCadObject3D { public TorusPrimitive() { RebuildMeshes(); } [DisplayName("Inner Diameter")] public double InnerDiameter { get; set; } = 10; [DisplayName("Outer Diameter")] public double OuterDiameter { get; set; } = 20; [DisplayName("Toroid Sides")] public int ToroidSides { get; set; } = 20; [DisplayName("Ring Sides")] public int PoleSides { get; set; } = 16; public override void RebuildMeshes() { var aabb = AxisAlignedBoundingBox.Zero; if (Mesh != null) { this.GetAxisAlignedBoundingBox(); } var poleRadius = (OuterDiameter / 2 - InnerDiameter / 2) / 2; var toroidRadius = InnerDiameter / 2 + poleRadius; var path = new VertexStorage(); var angleDelta = MathHelper.Tau / PoleSides; var angle = 0.0; var circleCenter = new Vector2(toroidRadius, 0); path.MoveTo(circleCenter + new Vector2(poleRadius * Math.Cos(angle), poleRadius * Math.Sin(angle))); for (int i = 0; i < PoleSides; i++) { angle += angleDelta; path.LineTo(circleCenter + new Vector2(poleRadius * Math.Cos(angle), poleRadius * Math.Sin(angle))); } Mesh = VertexSourceToMesh.Revolve(path, ToroidSides); Mesh.CleanAndMergMesh(CancellationToken.None); PlatingHelper.PlaceMeshAtHeight(this, aabb.minXYZ.Z); } } public class SpherePrimitive : MatterCadObject3D { public SpherePrimitive() { RebuildMeshes(); } public double Diameter { get; set; } = 20; [DisplayName("Longitude Sides")] public int LongitudeSides { get; set; } = 30; [DisplayName("Latitude Sides")] public int LatitudeSides { get; set; } = 20; public override void RebuildMeshes() { var aabb = AxisAlignedBoundingBox.Zero; if (Mesh != null) { this.GetAxisAlignedBoundingBox(); } var path = new VertexStorage(); var angleDelta = MathHelper.Tau / 2 / LatitudeSides; var angle = -MathHelper.Tau / 4; var radius = Diameter / 2; path.MoveTo(new Vector2(radius * Math.Cos(angle), radius * Math.Sin(angle))); for (int i = 0; i < LatitudeSides; i++) { angle += angleDelta; path.LineTo(new Vector2(radius * Math.Cos(angle), radius * Math.Sin(angle))); } Mesh = VertexSourceToMesh.Revolve(path, LongitudeSides); PlatingHelper.PlaceMeshAtHeight(this, aabb.minXYZ.Z); } } public class CurveTest : MatterCadObject3D { private PolygonMesh.Mesh inputMesh; private PolygonMesh.Mesh transformedMesh; public CurveTest() { var letterPrinter = new TypeFacePrinter("MatterHackers"); inputMesh = VertexSourceToMesh.Extrude(letterPrinter, 5); transformedMesh = PolygonMesh.Mesh.Copy(inputMesh, CancellationToken.None); RebuildMeshes(); } [DisplayName("Angle")] public double AngleDegrees { get; set; } = 0; [DisplayName("Bend Up")] public bool BendCW { get; set; } = true; public override void RebuildMeshes() { if (AngleDegrees > 0) { var aabb = inputMesh.GetAxisAlignedBoundingBox(); // find the radius that will make the x-size sweep out the requested angle // c = Tr ; r = c/T var angleRadians = MathHelper.DegreesToRadians(AngleDegrees); var circumference = aabb.XSize * MathHelper.Tau / angleRadians; var radius = circumference / MathHelper.Tau; var rotateXyPos = new Vector2(aabb.minXYZ.X, BendCW ? aabb.maxXYZ.Y : aabb.minXYZ.Y); if (!BendCW) { angleRadians = -angleRadians; } for (int i = 0; i < transformedMesh.Vertices.Count; i++) { var pos = inputMesh.Vertices[i].Position; var pos2D = new Vector2(pos); Vector2 rotateSpace = pos2D - rotateXyPos; var rotateRatio = rotateSpace.X / aabb.XSize; rotateSpace.X = 0; rotateSpace.Y += BendCW ? -radius : radius; rotateSpace.Rotate(angleRadians * rotateRatio); rotateSpace.Y += BendCW ? radius : -radius; ; rotateSpace += rotateXyPos; transformedMesh.Vertices[i].Position = new Vector3(rotateSpace.X, rotateSpace.Y, pos.Z); } } else { for (int i = 0; i < transformedMesh.Vertices.Count; i++) { transformedMesh.Vertices[i].Position = inputMesh.Vertices[i].Position; } } transformedMesh.MarkAsChanged(); transformedMesh.CalculateNormals(); this.Mesh = transformedMesh; } } public class PinchTest : MatterCadObject3D { private PolygonMesh.Mesh inputMesh; private PolygonMesh.Mesh transformedMesh; public PinchTest() { var letterPrinter = new TypeFacePrinter("MatterHackers"); inputMesh = VertexSourceToMesh.Extrude(letterPrinter, 5); transformedMesh = PolygonMesh.Mesh.Copy(inputMesh, CancellationToken.None); RebuildMeshes(); } [DisplayName("Back Ratio")] public double PinchRatio { get; set; } = 1; public override void RebuildMeshes() { var aabb = inputMesh.GetAxisAlignedBoundingBox(); for (int i = 0; i < transformedMesh.Vertices.Count; i++) { var pos = inputMesh.Vertices[i].Position; var ratioToApply = PinchRatio; var distFromCenter = pos.X - aabb.Center.X; var distanceToPinch = distFromCenter * (1 - PinchRatio); var delta = (aabb.Center.X + distFromCenter * ratioToApply) - pos.X; // find out how much to pinch based on y position var amountOfRatio = (pos.Y - aabb.minXYZ.Y) / aabb.YSize; transformedMesh.Vertices[i].Position = new Vector3(pos.X + delta * amountOfRatio, pos.Y, pos.Z); } transformedMesh.MarkAsChanged(); transformedMesh.CalculateNormals(); this.Mesh = transformedMesh; } } public class PvcT : MatterCadObject3D { private int sides = 50; public PvcT() { RebuildMeshes(); } [DisplayName("Inner Radius")] public double InnerDiameter { get; set; } = 15; [DisplayName("Outer Radius")] public double OuterDiameter { get; set; } = 20; public double BottomReach { get; set; } = 30; public double FrontReach { get; set; } = 25; public double TopReach { get; set; } = 30; public override void RebuildMeshes() { IObject3D topBottomConnect = new Cylinder(OuterDiameter / 2, OuterDiameter, sides, Alignment.Y); IObject3D frontConnect = new Cylinder(OuterDiameter / 2, OuterDiameter / 2, sides, Alignment.X); frontConnect = new Align(frontConnect, Face.Right, topBottomConnect, Face.Right); IObject3D bottomReach = new Rotate(CreateReach(BottomReach), -MathHelper.Tau / 4); bottomReach = new Align(bottomReach, Face.Back, topBottomConnect, Face.Front, 0, .1); IObject3D topReach = new Rotate(CreateReach(TopReach), MathHelper.Tau / 4); topReach = new Align(topReach, Face.Front, topBottomConnect, Face.Back, 0, -.1); IObject3D frontReach = new Rotate(CreateReach(FrontReach), 0, -MathHelper.Tau / 4); frontReach = new Align(frontReach, Face.Left, topBottomConnect, Face.Right, -.1); // output multiple meshes for pipe connector this.Children.Modify(list => { list.Clear(); list.Add(topBottomConnect); list.Add(frontConnect); list.Add(bottomReach); list.Add(topReach); list.Add(frontReach); }); this.Color = Color.Transparent; this.Mesh = null; } private IObject3D CreateReach(double reach) { var finWidth = 4.0; var finLength = InnerDiameter; var pattern = new VertexStorage(); pattern.MoveTo(0, 0); pattern.LineTo(finLength/2, 0); pattern.LineTo(finLength/2, reach - finLength / 8); pattern.LineTo(finLength/2 - finLength / 8, reach); pattern.LineTo(-finLength/2 + finLength / 8, reach); pattern.LineTo(-finLength/2, reach - finLength / 8); pattern.LineTo(-finLength/2, 0); var fin1 = new Object3D() { Mesh = VertexSourceToMesh.Extrude(pattern, finWidth) }; fin1 = new Translate(fin1, 0, 0, -finWidth / 2); //fin1.ChamferEdge(Face.Top | Face.Back, finLength / 8); //fin1.ChamferEdge(Face.Top | Face.Front, finLength / 8); fin1 = new Rotate(fin1, -MathHelper.Tau / 4); var fin2 = new SetCenter(new Rotate(fin1, 0, 0, MathHelper.Tau / 4), fin1.GetCenter()); return new Object3D().SetChildren(new List() { fin1, fin2 }); } } public class RibonWithName : MatterCadObject3D { private static TypeFace typeFace = null; public RibonWithName() { RebuildMeshes(); } [DisplayName("Name")] public string NameToWrite { get; set; } = "MatterHackers"; public override void RebuildMeshes() { IObject3D cancerRibonStl = Object3D.Load("Cancer_Ribbon.stl", CancellationToken.None); cancerRibonStl = new Rotate(cancerRibonStl, MathHelper.DegreesToRadians(90)); if (typeFace == null) { typeFace = TypeFace.LoadFrom(AggContext.StaticData.ReadAllText(Path.Combine("Fonts", "TitilliumWeb-Black.svg"))); } var letterPrinter = new TypeFacePrinter(NameToWrite.ToUpper(), new StyledTypeFace(typeFace, 12)); IObject3D nameMesh = new Object3D() { Mesh = VertexSourceToMesh.Extrude(letterPrinter, 5) }; AxisAlignedBoundingBox textBounds = nameMesh.GetAxisAlignedBoundingBox(); var textArea = new Vector2(25, 6); double scale = Math.Min(textArea.X / textBounds.XSize, textArea.Y / textBounds.YSize); nameMesh = new Scale(nameMesh, scale, scale, 2 / textBounds.ZSize); nameMesh = new Align(nameMesh, Face.Bottom | Face.Front, cancerRibonStl, Face.Top | Face.Front, 0, 0, -1); nameMesh = new SetCenter(nameMesh, cancerRibonStl.GetCenter(), true, false, false); nameMesh = new Rotate(nameMesh, 0, 0, MathHelper.DegreesToRadians(50)); nameMesh = new Translate(nameMesh, -37, -14, -1); // output two meshes for card holder and text this.Children.Modify(list => { list.Clear(); list.Add(cancerRibonStl); list.Add(nameMesh); }); this.Mesh = null; } } public class Box : Object3D { public Box(double x, double y, double z) { Mesh = PlatonicSolids.CreateCube(x, y, z); Mesh.CleanAndMergMesh(CancellationToken.None); } } public static class Object3DExtensions { public static IObject3D Translate(this IObject3D objectToTranslate, double x = 0, double y = 0, double z = 0, string name = "") { return objectToTranslate.Translate(new Vector3(x, y, z), name); } public static IObject3D Translate(this IObject3D objectToTranslate, Vector3 translation, string name = "") { objectToTranslate.Matrix *= Matrix4X4.CreateTranslation(translation); return objectToTranslate; } public static IObject3D Minus(this IObject3D a, IObject3D b) { var resultsA = a.Clone(); SubtractEditor.Subtract(resultsA.VisibleMeshes().ToList(), b.VisibleMeshes().ToList()); return resultsA; } public static Vector3 GetCenter(this IObject3D item) { return item.GetAxisAlignedBoundingBox(Matrix4X4.Identity).Center; } public static IObject3D SetChildren(this IObject3D parent, IEnumerable newChildren) { parent.Children.Modify((list) => { list.Clear(); list.AddRange(newChildren); }); return parent; } public static void SetChildren(this IObject3D parent, IObject3D newChild) { parent.Children.Modify((list) => { list.Clear(); list.Add(newChild); }); } } public class Translate : Object3D { public Translate() { } public Translate(IObject3D item, double x = 0, double y = 0, double z = 0) : this(item, new Vector3(x, y, z)) { } public Translate(IObject3D item, Vector3 translation) { Matrix *= Matrix4X4.CreateTranslation(translation); Children.Add(item.Clone()); } } public class Align : Object3D { public Align() { } public Align(IObject3D objectToAlign, Face boundingFacesToAlign, IObject3D objectToAlignTo, Face boundingFacesToAlignTo, double offsetX = 0, double offsetY = 0, double offsetZ = 0, string name = "") : this(objectToAlign, boundingFacesToAlign, GetPositionToAlignTo(objectToAlignTo, boundingFacesToAlignTo, new Vector3(offsetX, offsetY, offsetZ)), name) { if (objectToAlign == objectToAlignTo) { throw new Exception("You cannot align an object ot itself."); } } public Align(IObject3D objectToAlign, Face boundingFacesToAlign, double offsetX = 0, double offsetY = 0, double offsetZ = 0, string name = "") : this(objectToAlign, boundingFacesToAlign, new Vector3(offsetX, offsetY, offsetZ), name) { } public Align(IObject3D objectToAlign, Face boundingFacesToAlign, Vector3 positionToAlignTo, double offsetX, double offsetY, double offsetZ, string name = "") : this(objectToAlign, boundingFacesToAlign, positionToAlignTo + new Vector3(offsetX, offsetY, offsetZ), name) { } public Align(IObject3D item, Face boundingFacesToAlign, Vector3 positionToAlignTo, string name = "") { AxisAlignedBoundingBox bounds = item.GetAxisAlignedBoundingBox(); if (IsSet(boundingFacesToAlign, Face.Left, Face.Right)) { positionToAlignTo.X = positionToAlignTo.X - bounds.minXYZ.X; } if (IsSet(boundingFacesToAlign, Face.Right, Face.Left)) { positionToAlignTo.X = positionToAlignTo.X - bounds.minXYZ.X - (bounds.maxXYZ.X - bounds.minXYZ.X); } if (IsSet(boundingFacesToAlign, Face.Front, Face.Back)) { positionToAlignTo.Y = positionToAlignTo.Y - bounds.minXYZ.Y; } if (IsSet(boundingFacesToAlign, Face.Back, Face.Front)) { positionToAlignTo.Y = positionToAlignTo.Y - bounds.minXYZ.Y - (bounds.maxXYZ.Y - bounds.minXYZ.Y); } if (IsSet(boundingFacesToAlign, Face.Bottom, Face.Top)) { positionToAlignTo.Z = positionToAlignTo.Z - bounds.minXYZ.Z; } if (IsSet(boundingFacesToAlign, Face.Top, Face.Bottom)) { positionToAlignTo.Z = positionToAlignTo.Z - bounds.minXYZ.Z - (bounds.maxXYZ.Z - bounds.minXYZ.Z); } Matrix *= Matrix4X4.CreateTranslation(positionToAlignTo); Children.Add(item.Clone()); } public static Vector3 GetPositionToAlignTo(IObject3D objectToAlignTo, Face boundingFacesToAlignTo, Vector3 extraOffset) { Vector3 positionToAlignTo = new Vector3(); if (IsSet(boundingFacesToAlignTo, Face.Left, Face.Right)) { positionToAlignTo.X = objectToAlignTo.GetAxisAlignedBoundingBox().minXYZ.X; } if (IsSet(boundingFacesToAlignTo, Face.Right, Face.Left)) { positionToAlignTo.X = objectToAlignTo.GetAxisAlignedBoundingBox().maxXYZ.X; } if (IsSet(boundingFacesToAlignTo, Face.Front, Face.Back)) { positionToAlignTo.Y = objectToAlignTo.GetAxisAlignedBoundingBox().minXYZ.Y; } if (IsSet(boundingFacesToAlignTo, Face.Back, Face.Front)) { positionToAlignTo.Y = objectToAlignTo.GetAxisAlignedBoundingBox().maxXYZ.Y; } if (IsSet(boundingFacesToAlignTo, Face.Bottom, Face.Top)) { positionToAlignTo.Z = objectToAlignTo.GetAxisAlignedBoundingBox().minXYZ.Z; } if (IsSet(boundingFacesToAlignTo, Face.Top, Face.Bottom)) { positionToAlignTo.Z = objectToAlignTo.GetAxisAlignedBoundingBox().maxXYZ.Z; } return positionToAlignTo + extraOffset; } private static bool IsSet(Face variableToCheck, Face faceToCheckFor, Face faceToAssertNot) { if ((variableToCheck & faceToCheckFor) != 0) { if ((variableToCheck & faceToAssertNot) != 0) { throw new Exception("You cannot have both " + faceToCheckFor.ToString() + " and " + faceToAssertNot.ToString() + " set when calling Align. The are mutually exclusive."); } return true; } return false; } } public class Rotate : Object3D { public Rotate() { } public Rotate(IObject3D item, double x = 0, double y = 0, double z = 0, string name = "") : this(item, new Vector3(x, y, z), name) { } public Rotate(IObject3D item, Vector3 translation, string name = "") { Matrix *= Matrix4X4.CreateRotation(translation); Children.Add(item.Clone()); } } public class Scale : Object3D { public Scale() { } public Scale(IObject3D item, double x = 0, double y = 0, double z = 0, string name = "") : this(item, new Vector3(x, y, z), name) { } public Scale(IObject3D item, Vector3 translation, string name = "") { Matrix *= Matrix4X4.CreateScale(translation); Children.Add(item.Clone()); } } public class TestPart : MatterCadObject3D { public TestPart() { RebuildMeshes(); } public double XOffset { get; set; } = -.4; public override void RebuildMeshes() { IObject3D boxCombine = new Box(10, 10, 10); boxCombine = boxCombine.Minus(new Translate(new Box(10, 10, 10), XOffset, -3, 2)); this.SetChildren(boxCombine); } } }