diff --git a/MatterControl.csproj b/MatterControl.csproj index 80dd2f301..1783d0a78 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -549,10 +549,6 @@ {9B062971-A88E-4A3D-B3C9-12B78D15FA66} clipper_library - - {7E61A5BD-E78F-4B80-88C9-3821B4FA062E} - Csg - {04667764-DC7B-4B95-AEF6-B4E6C87A54E9} DataConverters3D diff --git a/PartPreviewWindow/View3D/Actions/SubtractEditor.cs b/PartPreviewWindow/View3D/Actions/SubtractEditor.cs index bbbeca726..09b686284 100644 --- a/PartPreviewWindow/View3D/Actions/SubtractEditor.cs +++ b/PartPreviewWindow/View3D/Actions/SubtractEditor.cs @@ -184,55 +184,66 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D var removeObjects = participants.Where((obj) => obj.OutputType == PrintOutputTypes.Hole).ToList(); var keepObjects = participants.Where((obj) => obj.OutputType != PrintOutputTypes.Hole).ToList(); - if (removeObjects.Any() - && keepObjects.Any()) - { - var totalOperations = removeObjects.Count * keepObjects.Count; - double amountPerOperation = 1.0 / totalOperations; - double percentCompleted = 0; - - foreach (var remove in removeObjects) - { - foreach (var keep in keepObjects) - { - progressStatus.Status = "Copy Remove"; - reporter.Report(progressStatus); - var transformedRemove = Mesh.Copy(remove.Mesh, CancellationToken.None); - transformedRemove.Transform(remove.WorldMatrix(null)); - - progressStatus.Status = "Copy Keep"; - reporter.Report(progressStatus); - var transformedKeep = Mesh.Copy(keep.Mesh, CancellationToken.None); - transformedKeep.Transform(keep.WorldMatrix(null)); - - progressStatus.Status = "Do CSG"; - reporter.Report(progressStatus); - transformedKeep = PolygonMesh.Csg.CsgOperations.Subtract(transformedKeep, transformedRemove, (status, progress0To1) => - { - // Abort if flagged - cancellationToken.ThrowIfCancellationRequested(); - - progressStatus.Status = status; - progressStatus.Progress0To1 = percentCompleted + amountPerOperation * progress0To1; - reporter.Report(progressStatus); - }, cancellationToken); - var inverse = keep.WorldMatrix(null); - inverse.Invert(); - transformedKeep.Transform(inverse); - - keep.Mesh = transformedKeep; - view3DWidget.Invalidate(); - - percentCompleted += amountPerOperation; - progressStatus.Progress0To1 = percentCompleted; - reporter.Report(progressStatus); - } - - remove.Visible = false; - } - } + Subtract(keepObjects, removeObjects, cancellationToken, reporter); return Task.CompletedTask; }); } + + public static void Subtract(List keepObjects, List removeObjects) + { + Subtract(keepObjects, removeObjects, CancellationToken.None, null); + } + + public static void Subtract(List keepObjects, List removeObjects, CancellationToken cancellationToken, IProgress reporter) + { + if (removeObjects.Any() + && keepObjects.Any()) + { + var totalOperations = removeObjects.Count * keepObjects.Count; + double amountPerOperation = 1.0 / totalOperations; + double percentCompleted = 0; + + ProgressStatus progressStatus = new ProgressStatus(); + foreach (var remove in removeObjects) + { + foreach (var keep in keepObjects) + { + progressStatus.Status = "Copy Remove"; + reporter?.Report(progressStatus); + var transformedRemove = Mesh.Copy(remove.Mesh, CancellationToken.None); + transformedRemove.Transform(remove.WorldMatrix(null)); + + progressStatus.Status = "Copy Keep"; + reporter?.Report(progressStatus); + var transformedKeep = Mesh.Copy(keep.Mesh, CancellationToken.None); + transformedKeep.Transform(keep.WorldMatrix(null)); + + progressStatus.Status = "Do CSG"; + reporter?.Report(progressStatus); + transformedKeep = PolygonMesh.Csg.CsgOperations.Subtract(transformedKeep, transformedRemove, (status, progress0To1) => + { + // Abort if flagged + cancellationToken.ThrowIfCancellationRequested(); + + progressStatus.Status = status; + progressStatus.Progress0To1 = percentCompleted + amountPerOperation * progress0To1; + reporter?.Report(progressStatus); + }, cancellationToken); + var inverse = keep.WorldMatrix(null); + inverse.Invert(); + transformedKeep.Transform(inverse); + + keep.Mesh = transformedKeep; + keep.Invalidate(); + + percentCompleted += amountPerOperation; + progressStatus.Progress0To1 = percentCompleted; + reporter?.Report(progressStatus); + } + + remove.Visible = false; + } + } + } } } \ No newline at end of file diff --git a/PartPreviewWindow/View3D/View3DWidget.cs b/PartPreviewWindow/View3D/View3DWidget.cs index e8a821dbc..601e98ecb 100644 --- a/PartPreviewWindow/View3D/View3DWidget.cs +++ b/PartPreviewWindow/View3D/View3DWidget.cs @@ -854,6 +854,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; viewControls3D.ActiveButton = ViewControls3DButtons.Translate; } + else if(Keyboard.IsKeyDown(Keys.Alt)) + { + activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; + viewControls3D.ActiveButton = ViewControls3DButtons.Scale; + } else { activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index d826d9291..8f747f763 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit d826d929111ca15c045bedb64b9dee560824ea3f +Subproject commit 8f747f76300429070c357dc37c5f0b3f0fe79703 diff --git a/Tests/MatterControl.Tests/MatterControl/ReleaseBuildTests.cs b/Tests/MatterControl.Tests/MatterControl/ReleaseBuildTests.cs index 3064e65bc..e704fc573 100644 --- a/Tests/MatterControl.Tests/MatterControl/ReleaseBuildTests.cs +++ b/Tests/MatterControl.Tests/MatterControl/ReleaseBuildTests.cs @@ -41,7 +41,6 @@ namespace MatterControl.Tests string knownAssemblies = @"MatterHackers.VectorMath.dll AGG.dll MatterHackers.PolygonMesh.dll - MatterHackers.Csg.dll clipper_library.dll MatterHackers.Agg.UI.dll Tesselate.dll diff --git a/TextCreator/CardHolderTool/CardHolder.cs b/TextCreator/CardHolderTool/CardHolder.cs deleted file mode 100644 index a208d5c75..000000000 --- a/TextCreator/CardHolderTool/CardHolder.cs +++ /dev/null @@ -1,621 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; -using MatterHackers.Agg; -using MatterHackers.Agg.Font; -using MatterHackers.Agg.Platform; -using MatterHackers.Agg.UI; -using MatterHackers.Csg; -using MatterHackers.Csg.Solids; -using MatterHackers.Csg.Transform; -using MatterHackers.DataConverters3D; -using MatterHackers.Localizations; -using MatterHackers.MatterControl.CustomWidgets; -using MatterHackers.MatterControl.PartPreviewWindow; -using MatterHackers.RenderOpenGl; -using MatterHackers.VectorMath; - -namespace MatterHackers.MatterControl.SimplePartScripting -{ - public abstract class MatterCadObject3D : Object3D - { - public override string ActiveEditor { get; set; } = "MatterCadEditor"; - - public abstract void RebuildMeshes(); - } - - public class MatterCadEditor : IObject3DEditor - { - private View3DWidget view3DWidget; - private IObject3D item; - - public string Name => "MatterCad"; - - public bool Unlocked { get; } = true; - - public IEnumerable SupportedTypes() => new Type[] - { - typeof(MatterCadObject3D), - }; - - public GuiWidget Create(IObject3D item, View3DWidget view3DWidget, ThemeConfig theme) - { - this.view3DWidget = view3DWidget; - this.item = item; - - var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) - { - HAnchor = HAnchor.Stretch - }; - - if (item is MatterCadObject3D) - { - ModifyCadObject(view3DWidget, mainContainer, theme); - } - - mainContainer.MinimumSize = new Vector2(250, 0); - return mainContainer; - } - - private void ModifyCadObject(View3DWidget view3DWidget, FlowLayoutWidget tabContainer, ThemeConfig theme) - { - var allowedTypes = new Type[] { typeof(double), typeof(string), typeof(bool) }; - - var ownedPropertiesOnly = System.Reflection.BindingFlags.Public - | System.Reflection.BindingFlags.Instance - | System.Reflection.BindingFlags.DeclaredOnly; - - var editableProperties = this.item.GetType().GetProperties(ownedPropertiesOnly) - .Where(pi => allowedTypes.Contains(pi.PropertyType) - && pi.GetGetMethod() != null) - .Select(p => new - { - Value = p.GetGetMethod().Invoke(this.item, null), - DisplayName = GetDisplayName(p), - PropertyInfo = p - }); - - foreach (var property in editableProperties) - { - // create a double editor - if (property.Value is double doubleValue) - { - FlowLayoutWidget rowContainer = CreateSettingsRow(property.DisplayName.Localize()); - var doubleEditWidget = new MHNumberEdit(doubleValue, pixelWidth: 50 * GuiWidget.DeviceScale, allowNegatives: true, allowDecimals: true, increment: .05) - { - SelectAllOnFocus = true, - VAnchor = VAnchor.Center - }; - doubleEditWidget.ActuallNumberEdit.EditComplete += (s, e) => - { - double editValue; - if (double.TryParse(doubleEditWidget.Text, out editValue)) - { - property.PropertyInfo.GetSetMethod().Invoke(this.item, new Object[] { editValue }); - } - ((MatterCadObject3D)item).RebuildMeshes(); - }; - rowContainer.AddChild(doubleEditWidget); - tabContainer.AddChild(rowContainer); - } - // create a bool editor - else if (property.Value is bool boolValue) - { - FlowLayoutWidget rowContainer = CreateSettingsRow(property.DisplayName.Localize()); - - var doubleEditWidget = new CheckBox(""); - doubleEditWidget.Checked = boolValue; - doubleEditWidget.CheckedStateChanged += (s, e) => - { - property.PropertyInfo.GetSetMethod().Invoke(this.item, new Object[] { doubleEditWidget.Checked }); - ((MatterCadObject3D)item).RebuildMeshes(); - }; - rowContainer.AddChild(doubleEditWidget); - tabContainer.AddChild(rowContainer); - } - // create a bool editor - else if (property.Value is string stringValue) - { - FlowLayoutWidget rowContainer = CreateSettingsRow(property.DisplayName.Localize()); - var textEditWidget = new MHTextEditWidget(stringValue, pixelWidth: 150 * GuiWidget.DeviceScale) - { - SelectAllOnFocus = true, - VAnchor = VAnchor.Center - }; - textEditWidget.ActualTextEditWidget.EditComplete += (s, e) => - { - property.PropertyInfo.GetSetMethod().Invoke(this.item, new Object[] { textEditWidget.Text }); - ((MatterCadObject3D)item).RebuildMeshes(); - }; - rowContainer.AddChild(textEditWidget); - tabContainer.AddChild(rowContainer); - } - } - - var updateButton = theme.ButtonFactory.Generate("Update".Localize()); - updateButton.Margin = new BorderDouble(5); - updateButton.HAnchor = HAnchor.Right; - updateButton.Click += (s, e) => - { - ((MatterCadObject3D)item).RebuildMeshes(); - }; - tabContainer.AddChild(updateButton); - } - - private string GetDisplayName(PropertyInfo prop) - { - var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); - return nameAttribute?.DisplayName ?? prop.Name; - } - - private static FlowLayoutWidget CreateSettingsRow(string labelText) - { - var rowContainer = new FlowLayoutWidget(FlowDirection.LeftToRight) - { - HAnchor = HAnchor.Stretch, - Padding = new BorderDouble(5) - }; - - var label = new TextWidget(labelText + ":", textColor: ActiveTheme.Instance.PrimaryTextColor) - { - Margin = new BorderDouble(0, 0, 3, 0), - VAnchor = VAnchor.Center - }; - rowContainer.AddChild(label); - - rowContainer.AddChild(new HorizontalSpacer()); - - return rowContainer; - } - } - - public class TestPart : MatterCadObject3D - { - public double XOffset { get; set; } = -.4; - - public TestPart() - { - RebuildMeshes(); - } - - public override void RebuildMeshes() - { - CsgObject boxCombine = new Box(10, 10, 10); - boxCombine -= new Translate(new Box(10, 10, 10), XOffset, -3, 2); - this.Mesh = CsgToMesh.Convert(boxCombine); - } - } - - public class BadSubtract : MatterCadObject3D - { - public double Sides { get; set; } = 4; - - public BadSubtract() - { - RebuildMeshes(); - } - - public override void RebuildMeshes() - { - int sides = 3; - CsgObject keep = new Cylinder(20, 20, sides); - CsgObject subtract = new Cylinder(10, 21, sides); - subtract = new SetCenter(subtract, keep.GetCenter()); - CsgObject result = keep - subtract; - this.Mesh = CsgToMesh.Convert(result); - } - } - - public class CardHolder : MatterCadObject3D - { - [DisplayName("Name")] - public string NameToWrite { get; set; } = "MatterHackers"; - - public CardHolder() - { - RebuildMeshes(); - } - - public override void RebuildMeshes() - { - CsgObject plainCardHolder = new MeshContainer("PlainBusinessCardHolder.stl"); - - //TypeFace typeFace = TypeFace.LoadSVG("Viking_n.svg"); - - var letterPrinter = new TypeFacePrinter(NameToWrite);//, new StyledTypeFace(typeFace, 12)); - PolygonMesh.Mesh textMesh = VertexSourceToMesh.Extrude(letterPrinter, 5); - - CsgObject nameMesh = new MeshContainer(textMesh); - - AxisAlignedBoundingBox textBounds = textMesh.GetAxisAlignedBoundingBox(); - var textArea = new Vector2(85, 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.Top | Face.Front, plainCardHolder, Face.Bottom | Face.Front); - nameMesh = new SetCenter(nameMesh, plainCardHolder.GetCenter(), true, false, false); - - nameMesh = new Rotate(nameMesh, MathHelper.DegreesToRadians(18)); - nameMesh = new Translate(nameMesh, 0, 2, 16); - - // output one combined mesh - //plainCardHolder += nameMesh; - //SetAndInvalidateMesh(CsgToMesh.Convert(plainCardHolder)); - - // output two meshes for card holder and text - this.Children.Modify(list => - { - list.Clear(); - list.AddRange(new[] - { - new Object3D() - { - Mesh = CsgToMesh.Convert(plainCardHolder) - }, - new Object3D() - { - Mesh = CsgToMesh.Convert(nameMesh) - } - }); - }); - - this.Mesh = null; - } - } - - public class PvcT : MatterCadObject3D - { - [DisplayName("Outer Radius")] - public double OuterDiameter { get; set; } = 20; - - [DisplayName("Inner Radius")] - public double InnerDiameter { get; set; } = 15; - - public double BottomReach { get; set; } = 30; - public double TopReach { get; set; } = 30; - public double FrontReach { get; set; } = 25; - - private int sides = 50; - - public PvcT() - { - RebuildMeshes(); - } - - public override void RebuildMeshes() - { - CsgObject topBottomConnect = new Cylinder(OuterDiameter/2, OuterDiameter, sides, Alignment.y); - CsgObject frontConnect = new Cylinder(OuterDiameter/2, OuterDiameter/2, sides, Alignment.x); - frontConnect = new Align(frontConnect, Face.Right, topBottomConnect, Face.Right); - - CsgObject bottomReach = new Rotate(CreateReach(BottomReach), -MathHelper.Tau / 4); - bottomReach = new Align(bottomReach, Face.Back, topBottomConnect, Face.Front, 0, 1); - - CsgObject topReach = new Rotate(CreateReach(TopReach), MathHelper.Tau / 4); - topReach = new Align(topReach, Face.Front, topBottomConnect, Face.Back, 0, -1); - - CsgObject 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.AddRange(new[] - { - new Object3D() - { - Mesh = CsgToMesh.Convert(topBottomConnect), - Color = Color.LightGray - }, - new Object3D() - { - Mesh = CsgToMesh.Convert(frontConnect), - Color = Color.LightGray - }, - new Object3D() - { - Mesh = CsgToMesh.Convert(bottomReach), - Color = Color.White - }, - new Object3D() - { - Mesh = CsgToMesh.Convert(topReach), - Color = Color.White - }, - new Object3D() - { - Mesh = CsgToMesh.Convert(frontReach), - Color = Color.White - } - }); - }); - - this.Color = Color.Transparent; - this.Mesh = null; - } - - private CsgObject CreateReach(double reach) - { - var finWidth = 4.0; - var finLength = InnerDiameter; - var fin1 = new Box(finWidth, finLength, reach); - fin1.ChamferEdge(Face.Top | Face.Back, finLength / 8); - fin1.ChamferEdge(Face.Top | Face.Front, finLength / 8); - CsgObject fin2 = new Rotate(fin1, 0, 0, MathHelper.Tau / 4); - - return fin1 + fin2; - } - } - - public class RibonWithName : MatterCadObject3D - { - [DisplayName("Name")] - public string NameToWrite { get; set; } = "MatterHackers"; - - static TypeFace typeFace = null; - - public RibonWithName() - { - RebuildMeshes(); - } - - public override void RebuildMeshes() - { - CsgObject cancerRibonStl = new MeshContainer("Cancer_Ribbon.stl"); - - 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)); - PolygonMesh.Mesh textMesh = VertexSourceToMesh.Extrude(letterPrinter, 5); - - CsgObject nameMesh = new MeshContainer(textMesh); - - AxisAlignedBoundingBox textBounds = textMesh.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 - - this.Children.Modify(list => - { - list.Clear(); - list.AddRange(new[] - { - new Object3D() - { - Mesh = CsgToMesh.Convert(cancerRibonStl) - }, - new Object3D() - { - Mesh = CsgToMesh.Convert(nameMesh) - } - }); - }); - - this.Mesh = null; - } - } - - public class PinchTest : MatterCadObject3D - { - [DisplayName("Back Ratio")] - public double PinchRatio { get; set; } = 1; - PolygonMesh.Mesh inputMesh; - PolygonMesh.Mesh transformedMesh; - - public PinchTest() - { - var letterPrinter = new TypeFacePrinter("MatterHackers"); - inputMesh = VertexSourceToMesh.Extrude(letterPrinter, 5); - transformedMesh = PolygonMesh.Mesh.Copy(inputMesh, CancellationToken.None); - - RebuildMeshes(); - } - - 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 CurveTest : MatterCadObject3D - { - [DisplayName("Bend Up")] - public bool BendCW { get; set; } = true; - - [DisplayName("Angle")] - public double AngleDegrees { get; set; } = 0; - PolygonMesh.Mesh inputMesh; - PolygonMesh.Mesh transformedMesh; - - public CurveTest() - { - var letterPrinter = new TypeFacePrinter("MatterHackers"); - inputMesh = VertexSourceToMesh.Extrude(letterPrinter, 5); - transformedMesh = PolygonMesh.Mesh.Copy(inputMesh, CancellationToken.None); - - RebuildMeshes(); - } - - 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 ChairFoot : MatterCadObject3D - { - // 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("Outer Size")] - public double OuterSize { get; set; } = 22; - - [DisplayName("Inner Size")] - public double InnerSize { get; set; } = 20; - [DisplayName("Reach")] - public double InsideReach { get; set; } = 10; - - [DisplayName("Angle")] - public double AngleDegrees { get; set; } = 3; - - public ChairFoot() - { - RebuildMeshes(); - } - - 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); - CsgObject chairFoot = chairFootBox; - - CsgObject ring = new Cylinder(InnerSize / 2 - 1, InsideReach, 30); - ring -= new Cylinder(ring.XSize / 2 - 2, ring.ZSize + 1, 30); - - CsgObject 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); - CsgObject 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); - CsgObject chairFoot = chairFootBox; - - CsgObject ring = new Cylinder(InnerSize / 2 - 1, insideHeight, 30); - ring -= new Cylinder(ring.XSize / 2 - 2, ring.ZSize + 1, 30); - - CsgObject 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); - } - } - } -} diff --git a/TextCreator/CardHolderTool/MatterCadEditor.cs b/TextCreator/CardHolderTool/MatterCadEditor.cs new file mode 100644 index 000000000..15f74a00c --- /dev/null +++ b/TextCreator/CardHolderTool/MatterCadEditor.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using MatterHackers.Agg; +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.CustomWidgets; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.VectorMath; + +namespace MatterHackers.MatterControl.MatterCad +{ + public class MatterCadEditor : IObject3DEditor + { + private IObject3D item; + private View3DWidget view3DWidget; + public string Name => "MatterCad"; + + public bool Unlocked { get; } = true; + + public GuiWidget Create(IObject3D item, View3DWidget view3DWidget, ThemeConfig theme) + { + this.view3DWidget = view3DWidget; + this.item = item; + + var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + HAnchor = HAnchor.Stretch + }; + + if (item is MatterCadObject3D) + { + ModifyCadObject(view3DWidget, mainContainer, theme); + } + + mainContainer.MinimumSize = new Vector2(250, 0); + return mainContainer; + } + + public IEnumerable SupportedTypes() => new Type[] + { + typeof(MatterCadObject3D), + }; + + private static FlowLayoutWidget CreateSettingsRow(string labelText) + { + var rowContainer = new FlowLayoutWidget(FlowDirection.LeftToRight) + { + HAnchor = HAnchor.Stretch, + Padding = new BorderDouble(5) + }; + + var label = new TextWidget(labelText + ":", textColor: ActiveTheme.Instance.PrimaryTextColor) + { + Margin = new BorderDouble(0, 0, 3, 0), + VAnchor = VAnchor.Center + }; + rowContainer.AddChild(label); + + rowContainer.AddChild(new HorizontalSpacer()); + + return rowContainer; + } + + private string GetDisplayName(PropertyInfo prop) + { + var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); + return nameAttribute?.DisplayName ?? prop.Name; + } + + private void ModifyCadObject(View3DWidget view3DWidget, FlowLayoutWidget tabContainer, ThemeConfig theme) + { + var allowedTypes = new Type[] { typeof(double), typeof(string), typeof(bool) }; + + var ownedPropertiesOnly = System.Reflection.BindingFlags.Public + | System.Reflection.BindingFlags.Instance + | System.Reflection.BindingFlags.DeclaredOnly; + + var editableProperties = this.item.GetType().GetProperties(ownedPropertiesOnly) + .Where(pi => allowedTypes.Contains(pi.PropertyType) + && pi.GetGetMethod() != null) + .Select(p => new + { + Value = p.GetGetMethod().Invoke(this.item, null), + DisplayName = GetDisplayName(p), + PropertyInfo = p + }); + + foreach (var property in editableProperties) + { + // create a double editor + if (property.Value is double doubleValue) + { + FlowLayoutWidget rowContainer = CreateSettingsRow(property.DisplayName.Localize()); + var doubleEditWidget = new MHNumberEdit(doubleValue, pixelWidth: 50 * GuiWidget.DeviceScale, allowNegatives: true, allowDecimals: true, increment: .05) + { + SelectAllOnFocus = true, + VAnchor = VAnchor.Center + }; + doubleEditWidget.ActuallNumberEdit.EditComplete += (s, e) => + { + double editValue; + if (double.TryParse(doubleEditWidget.Text, out editValue)) + { + property.PropertyInfo.GetSetMethod().Invoke(this.item, new Object[] { editValue }); + } + ((MatterCadObject3D)item).RebuildMeshes(); + }; + rowContainer.AddChild(doubleEditWidget); + tabContainer.AddChild(rowContainer); + } + // create a bool editor + else if (property.Value is bool boolValue) + { + FlowLayoutWidget rowContainer = CreateSettingsRow(property.DisplayName.Localize()); + + var doubleEditWidget = new CheckBox(""); + doubleEditWidget.Checked = boolValue; + doubleEditWidget.CheckedStateChanged += (s, e) => + { + property.PropertyInfo.GetSetMethod().Invoke(this.item, new Object[] { doubleEditWidget.Checked }); + ((MatterCadObject3D)item).RebuildMeshes(); + }; + rowContainer.AddChild(doubleEditWidget); + tabContainer.AddChild(rowContainer); + } + // create a bool editor + else if (property.Value is string stringValue) + { + FlowLayoutWidget rowContainer = CreateSettingsRow(property.DisplayName.Localize()); + var textEditWidget = new MHTextEditWidget(stringValue, pixelWidth: 150 * GuiWidget.DeviceScale) + { + SelectAllOnFocus = true, + VAnchor = VAnchor.Center + }; + textEditWidget.ActualTextEditWidget.EditComplete += (s, e) => + { + property.PropertyInfo.GetSetMethod().Invoke(this.item, new Object[] { textEditWidget.Text }); + ((MatterCadObject3D)item).RebuildMeshes(); + }; + rowContainer.AddChild(textEditWidget); + tabContainer.AddChild(rowContainer); + } + } + + var updateButton = theme.ButtonFactory.Generate("Update".Localize()); + updateButton.Margin = new BorderDouble(5); + updateButton.HAnchor = HAnchor.Right; + updateButton.Click += (s, e) => + { + ((MatterCadObject3D)item).RebuildMeshes(); + }; + tabContainer.AddChild(updateButton); + } + } + + public abstract class MatterCadObject3D : Object3D + { + public override string ActiveEditor { get; set; } = "MatterCadEditor"; + + public abstract void RebuildMeshes(); + } +} \ No newline at end of file diff --git a/TextCreator/CardHolderTool/MeshObjects.cs b/TextCreator/CardHolderTool/MeshObjects.cs new file mode 100644 index 000000000..f9cbd9654 --- /dev/null +++ b/TextCreator/CardHolderTool/MeshObjects.cs @@ -0,0 +1,771 @@ +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 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); + } + } +} \ No newline at end of file diff --git a/TextCreator/TextCreator.csproj b/TextCreator/TextCreator.csproj index e568ded3a..a84689d94 100644 --- a/TextCreator/TextCreator.csproj +++ b/TextCreator/TextCreator.csproj @@ -56,7 +56,8 @@ - + + @@ -70,10 +71,6 @@ {9B062971-A88E-4A3D-B3C9-12B78D15FA66} clipper_library - - {7E61A5BD-E78F-4B80-88C9-3821B4FA062E} - Csg - {94838988-523C-4B11-AD82-8B9B76F23A31} DataConverters2D