From cf6b02c8ebe3948215a9d3ab28f98b5a849e54d2 Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Mon, 30 Mar 2020 22:14:19 -0700 Subject: [PATCH] adding new repair operation --- .../RepairObject3D.cs | 312 ++++++++++++++++++ .../ApplicationView/ApplicationController.cs | 24 +- StaticData/Icons/repair.png | Bin 0 -> 1349 bytes 3 files changed, 330 insertions(+), 6 deletions(-) create mode 100644 MatterControl.MeshOperations/RepairObject3D.cs create mode 100644 StaticData/Icons/repair.png diff --git a/MatterControl.MeshOperations/RepairObject3D.cs b/MatterControl.MeshOperations/RepairObject3D.cs new file mode 100644 index 000000000..8e1e67570 --- /dev/null +++ b/MatterControl.MeshOperations/RepairObject3D.cs @@ -0,0 +1,312 @@ +/* +Copyright (c) 2018, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using g3; +using gs; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DesignTools.Operations; +using MatterHackers.PolygonMesh; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; + +namespace MatterHackers.MatterControl.DesignTools +{ + public class RepairObject3D : OperationSourceContainerObject3D, IPropertyGridModifier + { + public RepairObject3D() + { + Name = "Repair".Localize(); + } + + [Description("Ensure that each reduced point is on the surface of the original mesh. This is not normally required and slows the computation significantly.")] + public bool FaceOrientation { get; set; } = true; + + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); + + var rebuildLocks = this.RebuilLockAll(); + + var valuesChanged = false; + + // check if we have be initialized + + return TaskBuilder( + "Repair".Localize(), + (reporter, cancellationToken) => + { + SourceContainer.Visible = true; + RemoveAllButSource(); + + foreach (var sourceItem in SourceContainer.VisibleMeshes()) + { + var originalMesh = sourceItem.Mesh; + var reducedMesh = Repair(originalMesh, cancellationToken); + + var newMesh = new Object3D() + { + Mesh = reducedMesh + }; + newMesh.CopyProperties(sourceItem, Object3DPropertyFlags.All); + this.Children.Add(newMesh); + } + + SourceContainer.Visible = false; + rebuildLocks.Dispose(); + + if (valuesChanged) + { + Invalidate(InvalidateType.DisplayValues); + } + + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + + return Task.CompletedTask; + }); + } + + public Mesh Repair(Mesh inMesh, CancellationToken cancellationToken) + { + var mesh = inMesh.ToDMesh3(); + if (FaceOrientation) + { + var repaired = new MeshRepairOrientation(mesh); + repaired.OrientComponents(); + mesh = repaired.Mesh; + } + + { + int repeat_count = 0; + + repeat_all: + // Remove parts of the mesh we don't want before we bother with anything else + // TODO: maybe we need to repair orientation first? if we want to use MWN... + do_remove_inside(mesh); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + // make sure orientation of connected components is consistent + // TODO: what about mobius strip problems? + repair_orientation(mesh, cancellationToken, false); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + // Do safe close-cracks to handle easy cases + repair_cracks(true, RepairTolerance); + + if (Mesh.IsClosed()) goto all_done; + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + // Collapse tiny edges and then try easy cases again, and + // then allow for handling of ambiguous cases + collapse_all_degenerate_edges(RepairTolerance * 0.5, true); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + repair_cracks(true, 2 * RepairTolerance); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + repair_cracks(false, 2 * RepairTolerance); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + if (Mesh.IsClosed()) goto all_done; + + // Possibly we have joined regions with different orientation (is it?), fix that + // TODO: mobius strips again + repair_orientation(false); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + + // get rid of any remaining single-triangles before we start filling holes + remove_loners(); + + // Ok, fill simple holes. + int nRemainingBowties = 0; + int nHoles; bool bSawSpans; + fill_trivial_holes(out nHoles, out bSawSpans); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + if (Mesh.IsClosed()) goto all_done; + + // Now fill harder holes. If we saw spans, that means boundary loops could + // not be resolved in some cases, do we disconnect bowties and try again. + fill_any_holes(out nHoles, out bSawSpans); + if (Cancelled()) return false; + if (bSawSpans) + { + disconnect_bowties(out nRemainingBowties); + fill_any_holes(out nHoles, out bSawSpans); + } + + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + if (mesh.IsClosed()) + { + goto all_done; + } + + // We may have a closed mesh now but it might still have bowties (eg + // tetrahedra sharing vtx case). So disconnect those. + disconnect_bowties(out nRemainingBowties); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + // If the mesh is not closed, we will do one more round to try again. + if (repeat_count == 0 && mesh.IsClosed() == false) + { + repeat_count++; + goto repeat_all; + } + + // Ok, we didn't get anywhere on our first repeat. If we are still not + // closed, we will try deleting boundary triangles and repeating. + //* Repeat this N times. + if (repeat_count <= ErosionIterations && Mesh.IsClosed() == false) + { + repeat_count++; + MeshFaceSelection bdry_faces = new MeshFaceSelection(Mesh); + foreach (int eid in MeshIterators.BoundaryEdges(Mesh)) + { + bdry_faces.SelectEdgeTris(eid); + } + + MeshEditor.RemoveTriangles(Mesh, bdry_faces, true); + goto repeat_all; + } + + all_done: + // Remove tiny edges + if (MinEdgeLengthTol > 0) + { + collapse_all_degenerate_edges(MinEdgeLengthTol, false); + } + + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + + // finally do global orientation + repair_orientation(mesh, cancellationToken, true); + if (cancellationToken.IsCancellationRequested) + { + return inMesh; + } + } + + return mesh.ToMesh(); + } + + void repair_orientation(DMesh3 Mesh, CancellationToken cancellationToken, bool bGlobal) + { + MeshRepairOrientation orient = new MeshRepairOrientation(Mesh); + orient.OrientComponents(); + if (cancellationToken.IsCancellationRequested) + { + return; + } + + if (bGlobal) + { + orient.SolveGlobalOrientation(); + } + } + + bool remove_interior(DMesh3 Mesh, out int nRemoved) + { + RemoveOccludedTriangles remove = new RemoveOccludedTriangles(Mesh); + remove.PerVertex = true; + remove.InsideMode = RemoveOccludedTriangles.CalculationMode.FastWindingNumber; + remove.Apply(); + nRemoved = remove.RemovedT.Count(); + return true; + } + + bool remove_occluded(DMesh3 Mesh, out int nRemoved) + { + RemoveOccludedTriangles remove = new RemoveOccludedTriangles(Mesh); + remove.PerVertex = true; + remove.InsideMode = RemoveOccludedTriangles.CalculationMode.SimpleOcclusionTest; + remove.Apply(); + nRemoved = remove.RemovedT.Count(); + return true; + } + + bool do_remove_inside(DMesh3 Mesh) + { + int nRemoved = 0; + if (RemoveMode == RemoveModes.Interior) + { + return remove_interior(Mesh, out nRemoved); + } + else if (RemoveMode == RemoveModes.Occluded) + { + return remove_occluded(Mesh, out nRemoved); + } + + return true; + } + + public void UpdateControls(PublicPropertyChange change) + { + //if (change.Context.GetEditRow(nameof(TargetPercent)) is GuiWidget percentWidget) + //{ + // percentWidget.Visible = Mode == ReductionMode.Polygon_Percent; + //} + } + } +} \ No newline at end of file diff --git a/MatterControlLib/ApplicationView/ApplicationController.cs b/MatterControlLib/ApplicationView/ApplicationController.cs index 0764337f7..258507a7c 100644 --- a/MatterControlLib/ApplicationView/ApplicationController.cs +++ b/MatterControlLib/ApplicationView/ApplicationController.cs @@ -951,6 +951,18 @@ namespace MatterHackers.MatterControl IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null, }, new SceneSelectionOperation() + { + OperationType = typeof(TwistObject3D), + TitleResolver = () => "Twist".Localize(), + Action = (sceneContext) => + { + var curve = new TwistObject3D(); + curve.WrapSelectedItemAndSelect(sceneContext.Scene); + }, + Icon = (invertIcon) => AggContext.StaticData.LoadIcon("twist.png", 16, 16, invertIcon), + IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null, + }, + new SceneSelectionOperation() { OperationType = typeof(HollowOutObject3D), TitleResolver = () => "Hollow Out".Localize(), @@ -976,16 +988,16 @@ namespace MatterHackers.MatterControl }, new SceneSelectionOperation() { - OperationType = typeof(TwistObject3D), - TitleResolver = () => "Twist".Localize(), + OperationType = typeof(RepairObject3D), + TitleResolver = () => "Repair".Localize(), Action = (sceneContext) => { - var curve = new TwistObject3D(); - curve.WrapSelectedItemAndSelect(sceneContext.Scene); + var hollowOut = new RepairObject3D(); + hollowOut.WrapSelectedItemAndSelect(sceneContext.Scene); }, - Icon = (invertIcon) => AggContext.StaticData.LoadIcon("twist.png", 16, 16, invertIcon), + Icon = (invertIcon) => AggContext.StaticData.LoadIcon("repair.png", 16, 16, invertIcon), IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null, - } + }, } }, new OperationGroup("Other") diff --git a/StaticData/Icons/repair.png b/StaticData/Icons/repair.png new file mode 100644 index 0000000000000000000000000000000000000000..6158758d436a3bd66101ff7c47925e47a92cbbe6 GIT binary patch literal 1349 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANM!_o_l7N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsH`t97wSF;2BbZknfv=EpGwWm%=_(oo=^ReY5iMd zciE?hf2!nplT2CzEegaZG1tzDd}77z=csQq*W;0t`WKyFd&?fm?dvG-kK1B>YTN0} zA`d-oEAGwM@^;Dn?Cm1I_9#`Q)Kw)EE&s9TuEg2nFRs7y@(Ivt-Mg}X?HWNDe-F*< zC@14NT^rXF{_w*MZqP5p_r!yu= zPQLTzA*+JPwq?7%#Ol_pn*CTfg3&S7$l5HT?=x4SmTi`|SbF1pIYXK7os+l9G6*Sd zPcl?dC{Phv>e6l~Em%_WK{LYA`-w1%$5iQ7Mza-l2MXuyG(4X1bJM5PW6N0;MAmmO z`c7ktFw}Z=e9_{zuC8MXR(blyZl0#1sphL0IqUw4q@;^iCe7TLs;ws%YrAu2^|wiu z+;)kxg11eueICO#NvuV0M(l&YQzw_t`FO>6ZrQJy=Ys<6OvBc$jQTn&JM+$xhhOto zMrPbA(w=MOwet8n+tSzfiuIYdnx!0BsLU&E_esNitMAvDQhO)-HZ{FC`DnAodZ&1{r#XDXb{5^%w0rmUagCl&LVvNJyhX*5f{Y`L5v#C-5ZXX%Br?;PJv zzteQA{%CK#o1RZeeN)Vg7iK%x-`ThD+Nocz6InC%wFX~yX0yvFcoBEw^dH|}Z?k`_ zXS~;l4FB0Tvi++#uz%J=`Hb=jXHJ+O`pCe*z?S6g?!o{DKb?2iGcYi47I;J!Gca%q zgD@k*tT_@43=Hfgp1!W^PnhLIRU|Dr8@DnrFuw3~aSVw#eD?az>~Kec10Vg97P>9= zOiX^n>A6&+=|+VDmzi0Tazu2elGsmU7VGtHZ(MCQ2(FJ<5~!%a$)c*I#kgKxE9*?+ zX8vhU{|d#|{Jz}Sy50Ejx$nBx#=HL{N1C+f+LkSrx4)R7dLyjnXpRZn#tP)Prb<=jTaV(!J zAS-deL*$9G&=5L@m+68${p*x@YWStwiWZ<99deR*Y&ejC-+jc*dFHAJqPbCk@tBNe(8Xk6aUPE z$ujewGi_NgMRxs+P0bOJ>PM{hWC`Ucr5r0+v_JU!%o`2YJty?v5PB19A;J}wXdh$l z62GN_`L=gWpPSwFS8OkO#q5pUzlqo>*H30m-K4bhjM?dV8$aw_@=i8`KfnDE|8B1u zvxl`C?w<`@u+EJ^ee#_5hi{*#)4HCVrnPfl{PCA6-f>Bl>x^o1<~dzC(08YQX02~t tnE6J-(!A3gm-^2>?6s7!dj4O1KVz%h3zr$a3wc0^#M9N!Wt~$(6993cVygfE literal 0 HcmV?d00001