diff --git a/GUI/PathCAM.cs b/GUI/PathCAM.cs index 720d922..3365b2e 100644 --- a/GUI/PathCAM.cs +++ b/GUI/PathCAM.cs @@ -149,7 +149,9 @@ void worker_LoadMesh(object sender, DoWorkEventArgs e) e.Result = triangleMesh; } - + // For debugging, generate paths for the first loaded + // trianglemesh every frame (allows drawing code inside the generation) + //TriangleMeshGUI m = null; void IOpenGLDrawable.Draw() { try @@ -157,7 +159,15 @@ void IOpenGLDrawable.Draw() foreach (var mesh in inProgressMeshes) { mesh.Draw(); + //if (m == null) + //{ + // m = mesh; + //} } + //if (m != null) + //{ + // PathPlanner.PlanPaths(m, m.Tabs.ConvertAll(tab => tab as Tabs), router); + //} } catch (Exception) { @@ -375,7 +385,7 @@ private void InitializeComponent() // this.showRobotFormCheckbox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.showRobotFormCheckbox.AutoSize = true; - this.showRobotFormCheckbox.Location = new System.Drawing.Point(-1, 449); + this.showRobotFormCheckbox.Location = new System.Drawing.Point(-1, 549); this.showRobotFormCheckbox.Name = "showRobotFormCheckbox"; this.showRobotFormCheckbox.Size = new System.Drawing.Size(15, 14); this.showRobotFormCheckbox.TabIndex = 69; @@ -395,7 +405,7 @@ private void InitializeComponent() // this.robotControl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.robotControl.BackColor = System.Drawing.Color.Transparent; - this.robotControl.Location = new System.Drawing.Point(-1, 327); + this.robotControl.Location = new System.Drawing.Point(-1, 427); this.robotControl.Name = "robotControl"; this.robotControl.Size = new System.Drawing.Size(169, 136); this.robotControl.TabIndex = 8; @@ -414,7 +424,7 @@ private void InitializeComponent() this.drawing3D.MinimumSize = new System.Drawing.Size(10, 10); this.drawing3D.Name = "drawing3D"; this.drawing3D.RightToLeft = System.Windows.Forms.RightToLeft.Yes; - this.drawing3D.Size = new System.Drawing.Size(100, 98); + this.drawing3D.Size = new System.Drawing.Size(300, 198); this.drawing3D.TabIndex = 68; this.drawing3D.VSync = false; // @@ -422,7 +432,7 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(584, 462); + this.ClientSize = new System.Drawing.Size(784, 562); this.Controls.Add(this.showRobotFormCheckbox); this.Controls.Add(this.saveGcodeButton); this.Controls.Add(this.button2); diff --git a/Geometry/Slice.cs b/Geometry/Slice.cs index 32d80dd..6f3ca51 100644 --- a/Geometry/Slice.cs +++ b/Geometry/Slice.cs @@ -85,7 +85,7 @@ public Slice(TriangleMesh mesh, Plane plane) else { Slice s = new Slice(lineHandler.GetOuterLoops(), plane); - this.Add(s); + this.Union(s); } } //GL.End(); @@ -760,7 +760,7 @@ private Slice GetPairs(bool outside) return s; } - public void Add(Slice other) + public void Union(Slice other) { Clipper c = new Clipper(); c.Clear(); diff --git a/Robot/Robot.cs b/Robot/Robot.cs index 6aeea09..0541959 100644 --- a/Robot/Robot.cs +++ b/Robot/Robot.cs @@ -121,6 +121,7 @@ private void NewDataAvailable(SerialPortWrapper.SimpleSerialPacket packet) { lock (thisLock) { + elapsedCounter = 0; if (currentCommand == null) { Console.WriteLine("Error: Received data, but no command was sent!"); @@ -173,7 +174,7 @@ private void NewDataAvailable(SerialPortWrapper.SimpleSerialPacket packet) currentCommand = GetNextCommand(locations); - elapsedCounter = 0; + serial.Transmit(currentCommand.GenerateCommand(), 0x21); } else diff --git a/Router/Paths/PathPlanner.cs b/Router/Paths/PathPlanner.cs index 3640bba..3855dbe 100644 --- a/Router/Paths/PathPlanner.cs +++ b/Router/Paths/PathPlanner.cs @@ -30,20 +30,44 @@ namespace Router.Paths public class PathPlanner { /// - /// Find a toolpath which will remove everything described in the slice. + /// Get slices from the top of the triangle mesh to the bottom, spaced no more than + /// the specified cut depth in router. The first slice is the highest. /// - /// - public static void PlanPaths(Slice slice) + /// + /// + /// + private static List GetSlices(TriangleMesh triangles, Router router) { - // 1. Get all small holes in the slice - rout them first - // 2. Rout everything remaining in the slice from + float maxCutDepth = router.MaxCutDepth; + float minZ = triangles.MinPoint.Z; + float maxZ = triangles.MaxPoint.Z; + + float skin = 0.002f; // Depth above the minpoint and below the maxpoint to slice. + + // Figure out an even maximum cut depth + int layers = (int)((maxZ - minZ) / maxCutDepth + 0.95f); + float actualCutDepth = (maxZ - minZ - 2*skin) / layers; + actualCutDepth = Math.Min(maxZ - minZ - 2*skin, actualCutDepth); + + List slices = new List(); + + for (float height = maxZ - skin; height > -actualCutDepth / 2.0f; height -= actualCutDepth) + { + var slice = new Slice(triangles, new Plane(Vector3.UnitZ, new Vector3(0, 0, height))); + foreach (var higherSlice in slices) + { + slice.Union(higherSlice); + } + slices.Add(slice); + } + return slices; } public static List PlanPaths(TriangleMesh triangles, List tabs, Router router) { List routs = new List(); - float toolRadius = router.ToolDiameter / 2.0f; // Router units are inches + float toolRadius = router.ToolDiameter / 2.0f; float maxCutDepth = router.MaxCutDepth; float lastPassHeight = router.LastPassHeight; float cleanPassFactor = 0.90f; // 90% of the tool radius will be removed on the clean pass @@ -51,17 +75,30 @@ public static List PlanPaths(TriangleMesh triangles, List tabs, float minZ = triangles.MinPoint.Z; float maxZ = triangles.MaxPoint.Z; - Slice boundary = new Slice(triangles, new Plane(Vector3.UnitZ, new Vector3(0, 0, minZ))); + var slices = GetSlices(triangles, router); + //GL.PushMatrix(); + //foreach (var s in slices) + //{ + // GL.Translate(triangles.MaxPoint.X - triangles.MinPoint.X, 0, 0); + // DrawSlice(Color.Orange, Color.Black, s); + //} + //GL.PopMatrix(); + + Slice top = slices[0]; + slices.RemoveAt(0); + Slice boundary = new Slice (slices[slices.Count - 1]); + boundary.Offset(toolRadius * (cleanPassFactor + 1.05f)); // Note: this is slightly larger to allow some polygon width to exist + + // Enable complete removal of holes with no tabs foreach (var tab in tabs) { if (tab.TabLocations.Count() == 0) { - boundary.Add(tab.Boundary); + boundary.Union(tab.Boundary); } } - Slice top = new Slice(triangles, new Plane(Vector3.UnitZ, new Vector3(0, 0, maxZ))); top.SubtractFrom(boundary); //GL.PushMatrix(); @@ -70,7 +107,6 @@ public static List PlanPaths(TriangleMesh triangles, List tabs, //GL.PopMatrix(); Slice holes = top.PolygonsWithoutHoles(); - List holeRouts = new List(); foreach (var polygon in holes.IndividualPolygons()) @@ -78,21 +114,15 @@ public static List PlanPaths(TriangleMesh triangles, List tabs, holeRouts.Add(new Hole(polygon, toolRadius, cleanPassFactor)); } - // Figure out a nice even maximum cut depth - int layers = (int)((maxZ - minZ) / maxCutDepth + 0.95f); - float actualCutDepth = (maxZ - minZ) / layers; - - for (float height = minZ; height < maxZ; height += actualCutDepth) + foreach (Slice current in slices) { - Slice current = new Slice(triangles, new Plane(Vector3.UnitZ, new Vector3(0, 0, height))); current.Offset(toolRadius); - GL.PushMatrix(); - GL.Translate(0, 0, -0.001f); - //DrawSlice(Color.Tan, Color.Gray, boundary); - GL.PopMatrix(); - //DrawSlice(Color.Red, Color.Blue, current); + //GL.PushMatrix(); + //GL.Translate(0, 0, -0.001f); + ////DrawSlice(Color.Tan, Color.Gray, boundary); + //GL.PopMatrix(); + ////DrawSlice(Color.Red, Color.Blue, current); - Slice original = new Slice(current); current.SubtractFrom(boundary); @@ -123,20 +153,20 @@ public static List PlanPaths(TriangleMesh triangles, List tabs, // Rout all outside paths. These will be done from top down, one layer at a time for structural reasons. // For the top several layers, two paths could be combined... - //var withHoles = outsidePairs.PolygonsWithHoles(); - var outsideRouts = RoutAreasWithHoles(outsidePairs, toolRadius, cleanPassFactor, tabs, false); + var outsideRouts = RoutAreasWithHoles(insidePairs.PolygonsWithHoles(), toolRadius, cleanPassFactor, tabs, true); var newLines = new List(); foreach (var line in outsideRouts) { var r = new LineStrip(); r.AddRange(line.Vertices); r.Append(line.Vertices[0]); + r.Vertices.Reverse(); newLines.Add(r); } - routs.InsertRange(0, newLines); + routs.AddRange(newLines); - outsideRouts = RoutAreasWithHoles(insidePairs.PolygonsWithHoles(), toolRadius, cleanPassFactor, tabs, true); + outsideRouts = RoutAreasWithHoles(outsidePairs, toolRadius, cleanPassFactor, tabs, false); newLines = new List(); foreach (var line in outsideRouts) { @@ -145,7 +175,7 @@ public static List PlanPaths(TriangleMesh triangles, List tabs, r.Append(line.Vertices[0]); newLines.Add(r); } - routs.InsertRange(0, newLines); + routs.AddRange(newLines); } foreach (var hole in holeRouts) @@ -327,16 +357,16 @@ public List GetRouts() GL.PushMatrix(); Slice lastPolygon = null; - for (int i = polygons.Count - 1; i >= 0; i--) + foreach (var p in polygons) { - var p = polygons[i]; var routLast = p.GetLines(Slice.LineType.Outside).First(s => true); routLast.Vertices.Add(routLast.Vertices[0]); + routLast.Vertices.Reverse(); Slice obliterate = new Slice(p); obliterate.Offset(-toolRadius * cleanRoutFactor); - var routFirst = PathTree.ObliterateSlice(obliterate, toolRadius * 2.0f); + var routFirst = PathTree.ObliterateSlice(obliterate, toolRadius * 2.0f, true); if (lastPolygon == null) { @@ -429,6 +459,7 @@ private class PathTree private Slice slice; private List children = new List(); private List badTrees; + private bool reverse; #region Public Methods @@ -439,13 +470,13 @@ private class PathTree /// /// maximum distance between disjoint paths /// - public static List ObliterateSlice(Slice polygons, float maxShrink) + public static List ObliterateSlice(Slice polygons, float maxShrink, bool reverse = false) { List lines = new List(); foreach (Slice slice in polygons.IndividualPolygons()) { - PathTree tree = new PathTree(); + PathTree tree = new PathTree(reverse); Slice inside = new Slice(slice.GetLines(Slice.LineType.Hole), slice.Plane); Slice shrink = new Slice(slice); while (shrink.Area() > 0) @@ -474,21 +505,28 @@ public static List ObliterateSlice(Slice polygons, float maxShrink) #region Private Methods - private PathTree() + private PathTree(bool reverse) { slice = null; badTrees = new List(); + this.reverse = reverse; } private PathTree(Slice slice, PathTree parent) { this.badTrees = parent.badTrees; this.slice = slice; + this.reverse = parent.reverse; } private LineStrip CreatePath() { - return slice.GetLines(Geometry.Slice.LineType.Outside).First(s => true); + var path = slice.GetLines(Geometry.Slice.LineType.Outside).First(s => true); + if (reverse) + { + path.Vertices.Reverse(); + } + return path; } private void Draw(Color lineColor, Color planeColor)