├── .gitignore ├── MeshMachine.sln ├── README.md ├── Reading.md ├── examples └── colorlength3.gh ├── lib └── README └── src ├── ITargetLength.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs └── Resources.resx ├── Resources ├── remesh.png └── remesh2.png ├── remesher.cs ├── remesher.csproj └── remesherInfo.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Referenced Libraries 19 | [Ll]ib/**/*.dll 20 | [Ll]ib/**/*.xml 21 | [Ll]ib/**/*.gha 22 | 23 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 24 | !packages/*/build/ 25 | 26 | # MSTest test Results 27 | [Tt]est[Rr]esult*/ 28 | [Bb]uild[Ll]og.* 29 | 30 | *_i.c 31 | *_p.c 32 | *.ilk 33 | *.meta 34 | *.obj 35 | *.pch 36 | *.pdb 37 | *.pgc 38 | *.pgd 39 | *.rsp 40 | *.sbr 41 | *.tlb 42 | *.tli 43 | *.tlh 44 | *.tmp 45 | *.tmp_proj 46 | *.log 47 | *.vspscc 48 | *.vssscc 49 | .builds 50 | *.pidb 51 | *.log 52 | *.scc 53 | 54 | # Visual C++ cache files 55 | ipch/ 56 | *.aps 57 | *.ncb 58 | *.opensdf 59 | *.sdf 60 | *.cachefile 61 | 62 | # Visual Studio profiler 63 | *.psess 64 | *.vsp 65 | *.vspx 66 | 67 | # Guidance Automation Toolkit 68 | *.gpState 69 | 70 | # ReSharper is a .NET coding add-in 71 | _ReSharper*/ 72 | *.[Rr]e[Ss]harper 73 | 74 | # TeamCity is a build add-in 75 | _TeamCity* 76 | 77 | # DotCover is a Code Coverage Tool 78 | *.dotCover 79 | 80 | # NCrunch 81 | *.ncrunch* 82 | .*crunch*.local.xml 83 | 84 | # Installshield output folder 85 | [Ee]xpress/ 86 | 87 | # DocProject is a documentation generator add-in 88 | DocProject/buildhelp/ 89 | DocProject/Help/*.HxT 90 | DocProject/Help/*.HxC 91 | DocProject/Help/*.hhc 92 | DocProject/Help/*.hhk 93 | DocProject/Help/*.hhp 94 | DocProject/Help/Html2 95 | DocProject/Help/html 96 | 97 | # Click-Once directory 98 | publish/ 99 | 100 | # Publish Web Output 101 | *.Publish.xml 102 | *.pubxml 103 | 104 | # NuGet Packages Directory 105 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 106 | #packages/ 107 | 108 | # Windows Azure Build Output 109 | csx 110 | *.build.csdef 111 | 112 | # Windows Store app package directory 113 | AppPackages/ 114 | 115 | # Others 116 | sql/ 117 | *.Cache 118 | ClientBin/ 119 | [Ss]tyle[Cc]op.* 120 | ~$* 121 | *~ 122 | *.dbmdl 123 | *.[Pp]ublish.xml 124 | *.pfx 125 | *.publishsettings 126 | 127 | # RIA/Silverlight projects 128 | Generated_Code/ 129 | 130 | # Backup & report files from converting an old project file to a newer 131 | # Visual Studio version. Backup files are not needed, because we have git ;-) 132 | _UpgradeReport_Files/ 133 | Backup*/ 134 | UpgradeLog*.XML 135 | UpgradeLog*.htm 136 | 137 | # SQL Server files 138 | App_Data/*.mdf 139 | App_Data/*.ldf 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac crap 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /MeshMachine.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "remesher", "src\remesher.csproj", "{9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug32|Any CPU = Debug32|Any CPU 11 | Debug64|Any CPU = Debug64|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}.Debug32|Any CPU.ActiveCfg = Debug32|Any CPU 16 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}.Debug32|Any CPU.Build.0 = Debug32|Any CPU 17 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}.Debug64|Any CPU.ActiveCfg = Debug64|Any CPU 18 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}.Debug64|Any CPU.Build.0 = Debug64|Any CPU 19 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MeshMachine 2 | =========== 3 | 4 | Remeshing component for Grasshopper using Plankton 5 | -------------------------------------------------------------------------------- /Reading.md: -------------------------------------------------------------------------------- 1 | The approach to triangular remeshing used in MeshMachine draws on the following papers and presentations: 2 | 3 | [Dynamic Remeshing and Applications](https://domino.mpi-inf.mpg.de/intranet/ag4/ag4publ.nsf/0/690dfda53d7bd4d8c1256cd4004eb2f0/$FILE/vorsatz.pdf), by Vorsatz, Rossl, and Seidel 4 | 5 | [A Remeshing Approach to Multiresolution Modeling](http://graphics.uni-bielefeld.de/publications/sgp04.pdf), by Botsch and Kobbelt 6 | 7 | [Mesh Optimization](http://research.microsoft.com/en-us/um/people/hoppe/meshopt.pdf), by Hoppe, DeRose, Duchampy, McDonaldz and Stuetzlez 8 | 9 | [Direct Triangle Meshes Remeshing using Stellar Operators](http://w3.impa.br/~zang/pg2012/pg2012-slides.pdf), by Zang and Prada 10 | 11 | [Explicit Surface Remeshing](http://www.cs.technion.ac.il/~vitus/papers/ExplicitRemeshing.pdf), by Surazhsky and Gotsman 12 | 13 | [Delaunay Mesh Construction](http://www.cs.sfu.ca/~haoz/pubs/dyer_et_al_sgp07.pdf), by Dyer, Zhang and Möller 14 | 15 | [Triangulations and meshes in computational geometry](http://www.cis.upenn.edu/~cis610/sp06edelsbrunner1.pdf), by Edelsbrunner 16 | 17 | [Updating and Constructing Constrained Delaunay and Constrained Regular Triangulations by Flips](http://www.cs.berkeley.edu/~jrs/papers/cdtflip.pdf), by Shewchuk 18 | 19 | [Iterative method for edge length equalization](http://www.gcg.ufjf.br/pub/doc74.pdf), by Pecanha, Souza Filho, Vieira, Lobosco and Dantas 20 | -------------------------------------------------------------------------------- /examples/colorlength3.gh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Piker/MeshMachine/e47321af77b6b8048c1b0fcf29c758fecebfc481/examples/colorlength3.gh -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | Put any third-party assemblies in here. 2 | -------------------------------------------------------------------------------- /src/ITargetLength.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Grasshopper; 7 | using Grasshopper.Kernel; 8 | using Grasshopper.Kernel.Types; 9 | using Grasshopper.Kernel.Data; 10 | using Grasshopper.Kernel.Parameters; 11 | using Rhino.Geometry; 12 | using Plankton; 13 | using PlanktonGh; 14 | 15 | namespace remesher 16 | { 17 | public interface ITargetLength 18 | { 19 | double Calculate(PlanktonMesh P, int HalfEdge); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Rhino.PlugIns; 5 | 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("MeshMachine")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("MeshMachine")] 15 | [assembly: AssemblyCopyright("Copyright © Daniel Piker 2014")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [assembly: Guid("427ba99f-32eb-4df7-a2fd-0c37912565e2")] // This will also be the Guid of the Rhino plug-in 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [assembly: AssemblyVersion("1.0.*")] 37 | [assembly: AssemblyVersion("1.0.0.0")] 38 | [assembly: AssemblyFileVersion("1.0.0.0")] 39 | -------------------------------------------------------------------------------- /src/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34014 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace remesher.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("remesher.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap remesh { 67 | get { 68 | object obj = ResourceManager.GetObject("remesh", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap remesh2 { 77 | get { 78 | object obj = ResourceManager.GetObject("remesh2", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\remesh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\remesh2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | -------------------------------------------------------------------------------- /src/Resources/remesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Piker/MeshMachine/e47321af77b6b8048c1b0fcf29c758fecebfc481/src/Resources/remesh.png -------------------------------------------------------------------------------- /src/Resources/remesh2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Piker/MeshMachine/e47321af77b6b8048c1b0fcf29c758fecebfc481/src/Resources/remesh2.png -------------------------------------------------------------------------------- /src/remesher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Grasshopper.Kernel; 4 | using Grasshopper.Kernel.Types; 5 | using Grasshopper.Kernel.Data; 6 | using Grasshopper.Kernel.Parameters; 7 | using Rhino.Geometry; 8 | using Plankton; 9 | using PlanktonGh; 10 | 11 | 12 | 13 | namespace remesher 14 | { 15 | public class remesher : GH_Component 16 | { 17 | public remesher() 18 | : base("remesher", "remesher", 19 | "Remeshing tool", 20 | "Kangaroo", "Mesh") 21 | { 22 | } 23 | 24 | 25 | protected override void RegisterInputParams(GH_InputParamManager pManager) 26 | { 27 | //0 28 | pManager.AddGeometryParameter("Geometry", "Geom", "Input Surface or Mesh", GH_ParamAccess.item); 29 | 30 | //1 31 | pManager.AddGenericParameter("TargetLengthFunction", "L", "A function determining local edge length", GH_ParamAccess.item); 32 | 33 | //2 34 | pManager.AddCurveParameter("FixCurves", "FixC", "Curves which will be kept sharp during remeshing. Can be boundary or internal curves", GH_ParamAccess.list); 35 | pManager[2].Optional = true; 36 | 37 | //3 38 | pManager.AddPointParameter("FixVertices", "FixV", "Points to keep fixed during remeshing", GH_ParamAccess.list); 39 | pManager[3].Optional = true; 40 | 41 | //4 42 | pManager.AddIntegerParameter("Flip", "Flip", "Criterion used to decide when to flip edges (0 for valence based, 1 for angle based)", GH_ParamAccess.item, 1); 43 | 44 | //5 45 | pManager.AddNumberParameter("PullStrength", "Pull", "Strength of pull to target geometry (between 0 and 1). Set to 0 for minimal surfaces", GH_ParamAccess.item, 0.8); 46 | 47 | //6 48 | pManager.AddIntegerParameter("Iterations", "Iter", "Number of steps between outputs", GH_ParamAccess.item, 1); 49 | 50 | //7 51 | pManager.AddBooleanParameter("Reset", "Reset", "True to initialize, false to run remeshing. Connect a timer for continuous remeshing", GH_ParamAccess.item, true); 52 | } 53 | 54 | protected override void RegisterOutputParams(GH_OutputParamManager pManager) 55 | { 56 | pManager.AddGenericParameter("Mesh", "M", "Remeshed result as Plankton Mesh", GH_ParamAccess.item); 57 | } 58 | 59 | private PlanktonMesh P = new PlanktonMesh(); 60 | private Mesh M = new Mesh(); 61 | private List AnchorV = new List(); 62 | private List FeatureV = new List(); 63 | private List FeatureE = new List(); 64 | private bool initialized; 65 | 66 | protected override void SolveInstance(IGH_DataAccess DA) 67 | { 68 | ITargetLength TargetLength = null; 69 | bool reset = false; 70 | int Flip = 0; 71 | List FC = new List(); 72 | List FV = new List(); 73 | double FixT = 0.01; 74 | double PullStrength = 0.8; 75 | double SmoothStrength = 0.8; 76 | double LengthTol = 0.15; 77 | bool Minim = false; 78 | int Iters = 1; 79 | 80 | GH_ObjectWrapper Surf = new GH_ObjectWrapper(); 81 | DA.GetData(0, ref Surf); 82 | 83 | GH_ObjectWrapper Obj = null; 84 | DA.GetData(1, ref Obj); 85 | TargetLength = Obj.Value as ITargetLength; 86 | 87 | DA.GetDataList(2, FC); 88 | DA.GetDataList(3, FV); 89 | DA.GetData(4, ref Flip); 90 | 91 | DA.GetData(5, ref PullStrength); 92 | 93 | DA.GetData(6, ref Iters); 94 | DA.GetData(7, ref reset); 95 | 96 | 97 | if (PullStrength == 0) { Minim = true; } 98 | 99 | if (Surf.Value is GH_Mesh) 100 | { 101 | DA.GetData(0, ref M); 102 | M.Faces.ConvertQuadsToTriangles(); 103 | } 104 | else 105 | { 106 | double L = 1.0; 107 | MeshingParameters MeshParams = new MeshingParameters(); 108 | MeshParams.MaximumEdgeLength = 3 * L; 109 | MeshParams.MinimumEdgeLength = L; 110 | MeshParams.JaggedSeams = false; 111 | MeshParams.SimplePlanes = false; 112 | Brep SB = null; 113 | DA.GetData(0, ref SB); 114 | Mesh[] BrepMeshes = Mesh.CreateFromBrep(SB, MeshParams); 115 | M = new Mesh(); 116 | foreach (var mesh in BrepMeshes) 117 | M.Append(mesh); 118 | } 119 | 120 | if (reset || initialized == false) 121 | { 122 | #region reset 123 | M.Faces.ConvertQuadsToTriangles(); 124 | P = M.ToPlanktonMesh(); 125 | 126 | initialized = true; 127 | 128 | AnchorV.Clear(); 129 | FeatureV.Clear(); 130 | FeatureE.Clear(); 131 | 132 | //Mark any vertices or edges lying on features 133 | for (int i = 0; i < P.Vertices.Count; i++) 134 | { 135 | Point3d Pt = P.Vertices[i].ToPoint3d(); 136 | AnchorV.Add(-1); 137 | for (int j = 0; j < FV.Count; j++) 138 | { 139 | if (Pt.DistanceTo(FV[j]) < FixT) 140 | { AnchorV[AnchorV.Count - 1] = j; } 141 | } 142 | 143 | FeatureV.Add(-1); 144 | for (int j = 0; j < FC.Count; j++) 145 | { 146 | double param = new double(); 147 | FC[j].ClosestPoint(Pt, out param); 148 | if (Pt.DistanceTo(FC[j].PointAt(param)) < FixT) 149 | { FeatureV[FeatureV.Count - 1] = j; } 150 | } 151 | } 152 | 153 | int EdgeCount = P.Halfedges.Count / 2; 154 | for (int i = 0; i < EdgeCount; i++) 155 | { 156 | FeatureE.Add(-1); 157 | int vStart = P.Halfedges[2 * i].StartVertex; 158 | int vEnd = P.Halfedges[2 * i + 1].StartVertex; 159 | 160 | Point3d PStart = P.Vertices[vStart].ToPoint3d(); 161 | Point3d PEnd = P.Vertices[vEnd].ToPoint3d(); 162 | 163 | for (int j = 0; j < FC.Count; j++) 164 | { 165 | double paramS = new double(); 166 | double paramE = new double(); 167 | Curve thisFC = FC[j]; 168 | thisFC.ClosestPoint(PStart, out paramS); 169 | thisFC.ClosestPoint(PEnd, out paramE); 170 | if ((PStart.DistanceTo(thisFC.PointAt(paramS)) < FixT) && 171 | (PEnd.DistanceTo(thisFC.PointAt(paramE)) < FixT)) 172 | { 173 | FeatureE[FeatureE.Count - 1] = j; 174 | } 175 | } 176 | } 177 | #endregion 178 | } 179 | 180 | else 181 | { 182 | for (int iter = 0; iter < Iters; iter++) 183 | { 184 | int EdgeCount = P.Halfedges.Count / 2; 185 | double[] EdgeLength = P.Halfedges.GetLengths(); 186 | List Visited = new List(); 187 | Vector3d[] Normals = new Vector3d[P.Vertices.Count]; 188 | 189 | for (int i = 0; i < P.Vertices.Count; i++) 190 | { 191 | Visited.Add(false); 192 | Normals[i] = Normal(P, i); 193 | } 194 | 195 | double t = LengthTol; //a tolerance for when to split/collapse edges 196 | double smooth = SmoothStrength; //smoothing strength 197 | double pull = PullStrength; //pull to target mesh strength 198 | 199 | // Split the edges that are too long 200 | for (int i = 0; i < EdgeCount; i++) 201 | { 202 | if (P.Halfedges[2 * i].IsUnused == false) 203 | { 204 | int vStart = P.Halfedges[2 * i].StartVertex; 205 | int vEnd = P.Halfedges[2 * i + 1].StartVertex; 206 | 207 | if ((Visited[vStart] == false) 208 | && (Visited[vEnd] == false)) 209 | { 210 | 211 | double L2 = TargetLength.Calculate(P, 2 * i); 212 | 213 | if (EdgeLength[2 * i] > (1 + t) * (4f / 3f) * L2) 214 | { 215 | 216 | int SplitHEdge = P.Halfedges.TriangleSplitEdge(2 * i); 217 | if (SplitHEdge != -1) 218 | { 219 | int SplitCenter = P.Halfedges[SplitHEdge].StartVertex; 220 | P.Vertices.SetVertex(SplitCenter, MidPt(P, i)); 221 | 222 | //update the feature information 223 | FeatureE.Add(FeatureE[i]); 224 | FeatureV.Add(FeatureE[i]); 225 | AnchorV.Add(-1); 226 | 227 | //2 additional new edges have also been created (or 1 if split was on a boundary) 228 | //mark these as non-features 229 | int CEdgeCount = P.Halfedges.Count / 2; 230 | while (FeatureE.Count < CEdgeCount) 231 | { FeatureE.Add(-1); } 232 | 233 | Visited.Add(true); 234 | int[] Neighbours = P.Vertices.GetVertexNeighbours(SplitCenter); 235 | foreach (int n in Neighbours) 236 | { Visited[n] = true; } 237 | } 238 | } 239 | 240 | } 241 | } 242 | } 243 | 244 | //Collapse the edges that are too short 245 | for (int i = 0; i < EdgeCount; i++) 246 | { 247 | if (P.Halfedges[2 * i].IsUnused == false) 248 | { 249 | int vStart = P.Halfedges[2 * i].StartVertex; 250 | int vEnd = P.Halfedges[2 * i + 1].StartVertex; 251 | if ((Visited[vStart] == false) 252 | && (Visited[vEnd] == false)) 253 | { 254 | if (!(AnchorV[vStart] != -1 && AnchorV[vEnd] != -1)) // if both ends are anchored, don't collapse 255 | { 256 | int Collapse_option = 0; //0 for none, 1 for collapse to midpt, 2 for towards start, 3 for towards end 257 | //if neither are anchorV 258 | if (AnchorV[vStart] == -1 && AnchorV[vEnd] == -1) 259 | { 260 | // if both on same feature (or neither on a feature) 261 | if (FeatureV[vStart] == FeatureV[vEnd]) 262 | { Collapse_option = 1; } 263 | // if start is on a feature and end isn't 264 | if ((FeatureV[vStart] != -1) && (FeatureV[vEnd] == -1)) 265 | { Collapse_option = 2; } 266 | // if end is on a feature and start isn't 267 | if ((FeatureV[vStart] == -1) && (FeatureV[vEnd] != -1)) 268 | { Collapse_option = 3; } 269 | } 270 | else // so one end must be an anchor 271 | { 272 | // if start is an anchor 273 | if (AnchorV[vStart] != -1) 274 | { 275 | // if both are on same feature, or if the end is not a feature 276 | if ((FeatureE[i] != -1) || (FeatureV[vEnd] == -1)) 277 | { Collapse_option = 2; } 278 | } 279 | // if end is an anchor 280 | if (AnchorV[vEnd] != -1) 281 | { 282 | // if both are on same feature, or if the start is not a feature 283 | if ((FeatureE[i] != -1) || (FeatureV[vStart] == -1)) 284 | { Collapse_option = 3; } 285 | } 286 | } 287 | 288 | Point3d Mid = MidPt(P, i); 289 | 290 | 291 | double L2 = TargetLength.Calculate(P, 2 * i); 292 | 293 | 294 | 295 | if ((Collapse_option != 0) && (EdgeLength[2 * i] < (1 - t) * 4f / 5f * L2)) 296 | { 297 | int Collapsed = -1; 298 | int CollapseRtn = -1; 299 | if (Collapse_option == 1) 300 | { 301 | Collapsed = P.Halfedges[2 * i].StartVertex; 302 | P.Vertices.SetVertex(Collapsed, MidPt(P, i)); 303 | CollapseRtn = P.Halfedges.CollapseEdge(2 * i); 304 | } 305 | if (Collapse_option == 2) 306 | { 307 | Collapsed = P.Halfedges[2 * i].StartVertex; 308 | CollapseRtn = P.Halfedges.CollapseEdge(2 * i); 309 | } 310 | if (Collapse_option == 3) 311 | { 312 | Collapsed = P.Halfedges[2 * i + 1].StartVertex; 313 | CollapseRtn = P.Halfedges.CollapseEdge(2 * i + 1); 314 | } 315 | if (CollapseRtn != -1) 316 | { 317 | int[] Neighbours = P.Vertices.GetVertexNeighbours(Collapsed); 318 | foreach (int n in Neighbours) 319 | { Visited[n] = true; } 320 | } 321 | } 322 | } 323 | } 324 | } 325 | } 326 | 327 | EdgeCount = P.Halfedges.Count / 2; 328 | 329 | if ((Flip == 0) && (PullStrength > 0)) 330 | { 331 | //Flip edges to reduce valence error 332 | for (int i = 0; i < EdgeCount; i++) 333 | { 334 | if (!P.Halfedges[2 * i].IsUnused 335 | && (P.Halfedges[2 * i].AdjacentFace != -1) 336 | && (P.Halfedges[2 * i + 1].AdjacentFace != -1) 337 | && (FeatureE[i] == -1) // don't flip feature edges 338 | ) 339 | { 340 | int Vert1 = P.Halfedges[2 * i].StartVertex; 341 | int Vert2 = P.Halfedges[2 * i + 1].StartVertex; 342 | int Vert3 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i].NextHalfedge].NextHalfedge].StartVertex; 343 | int Vert4 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i + 1].NextHalfedge].NextHalfedge].StartVertex; 344 | 345 | int Valence1 = P.Vertices.GetValence(Vert1); 346 | int Valence2 = P.Vertices.GetValence(Vert2); 347 | int Valence3 = P.Vertices.GetValence(Vert3); 348 | int Valence4 = P.Vertices.GetValence(Vert4); 349 | 350 | if (P.Vertices.NakedEdgeCount(Vert1) > 0) { Valence1 += 2; } 351 | if (P.Vertices.NakedEdgeCount(Vert2) > 0) { Valence2 += 2; } 352 | if (P.Vertices.NakedEdgeCount(Vert3) > 0) { Valence3 += 2; } 353 | if (P.Vertices.NakedEdgeCount(Vert4) > 0) { Valence4 += 2; } 354 | 355 | int CurrentError = 356 | Math.Abs(Valence1 - 6) + 357 | Math.Abs(Valence2 - 6) + 358 | Math.Abs(Valence3 - 6) + 359 | Math.Abs(Valence4 - 6); 360 | int FlippedError = 361 | Math.Abs(Valence1 - 7) + 362 | Math.Abs(Valence2 - 7) + 363 | Math.Abs(Valence3 - 5) + 364 | Math.Abs(Valence4 - 5); 365 | if (CurrentError > FlippedError) 366 | { 367 | P.Halfedges.FlipEdge(2 * i); 368 | } 369 | } 370 | } 371 | } 372 | else 373 | { 374 | //Flip edges based on angle 375 | for (int i = 0; i < EdgeCount; i++) 376 | { 377 | if (!P.Halfedges[2 * i].IsUnused 378 | && (P.Halfedges[2 * i].AdjacentFace != -1) 379 | && (P.Halfedges[2 * i + 1].AdjacentFace != -1) 380 | && (FeatureE[i] == -1) // don't flip feature edges 381 | ) 382 | { 383 | int Vert1 = P.Halfedges[2 * i].StartVertex; 384 | int Vert2 = P.Halfedges[2 * i + 1].StartVertex; 385 | int Vert3 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i].NextHalfedge].NextHalfedge].StartVertex; 386 | int Vert4 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i + 1].NextHalfedge].NextHalfedge].StartVertex; 387 | 388 | Point3d P1 = P.Vertices[Vert1].ToPoint3d(); 389 | Point3d P2 = P.Vertices[Vert2].ToPoint3d(); 390 | Point3d P3 = P.Vertices[Vert3].ToPoint3d(); 391 | Point3d P4 = P.Vertices[Vert4].ToPoint3d(); 392 | 393 | double A1 = Vector3d.VectorAngle(new Vector3d(P3 - P1), new Vector3d(P4 - P1)) 394 | + Vector3d.VectorAngle(new Vector3d(P4 - P2), new Vector3d(P3 - P2)); 395 | 396 | double A2 = Vector3d.VectorAngle(new Vector3d(P1 - P4), new Vector3d(P2 - P4)) 397 | + Vector3d.VectorAngle(new Vector3d(P2 - P3), new Vector3d(P1 - P3)); 398 | 399 | if (A2 > A1) 400 | { 401 | P.Halfedges.FlipEdge(2 * i); 402 | } 403 | } 404 | } 405 | } 406 | 407 | if (Minim) 408 | { 409 | Vector3d[] SmoothC = LaplacianSmooth(P, 1, smooth); 410 | 411 | for (int i = 0; i < P.Vertices.Count; i++) 412 | { 413 | if (AnchorV[i] == -1) // don't smooth feature vertices 414 | { 415 | P.Vertices.MoveVertex(i, 0.5 * SmoothC[i]); 416 | } 417 | } 418 | } 419 | 420 | Vector3d[] Smooth = LaplacianSmooth(P, 0, smooth); 421 | 422 | for (int i = 0; i < P.Vertices.Count; i++) 423 | { 424 | if (AnchorV[i] == -1) // don't smooth feature vertices 425 | { 426 | // make it tangential only 427 | Vector3d VNormal = Normal(P, i); 428 | double ProjLength = Smooth[i] * VNormal; 429 | Smooth[i] = Smooth[i] - (VNormal * ProjLength); 430 | 431 | P.Vertices.MoveVertex(i, Smooth[i]); 432 | 433 | if (P.Vertices.NakedEdgeCount(i) != 0)//special smoothing for feature edges 434 | { 435 | int[] Neighbours = P.Vertices.GetVertexNeighbours(i); 436 | int ncount = 0; 437 | Point3d Avg = new Point3d(); 438 | 439 | for (int j = 0; j < Neighbours.Length; j++) 440 | { 441 | if (P.Vertices.NakedEdgeCount(Neighbours[j]) != 0) 442 | { 443 | ncount++; 444 | Avg = Avg + P.Vertices[Neighbours[j]].ToPoint3d(); 445 | } 446 | } 447 | Avg = Avg * (1.0 / ncount); 448 | Vector3d move = Avg - P.Vertices[i].ToPoint3d(); 449 | move = move * smooth; 450 | P.Vertices.MoveVertex(i, move); 451 | } 452 | 453 | if (FeatureV[i] != -1)//special smoothing for feature edges 454 | { 455 | int[] Neighbours = P.Vertices.GetVertexNeighbours(i); 456 | int ncount = 0; 457 | Point3d Avg = new Point3d(); 458 | 459 | for (int j = 0; j < Neighbours.Length; j++) 460 | { 461 | if ((FeatureV[Neighbours[j]] == FeatureV[i]) || (AnchorV[Neighbours[j]] != -1)) 462 | { 463 | ncount++; 464 | Avg = Avg + P.Vertices[Neighbours[j]].ToPoint3d(); 465 | } 466 | } 467 | Avg = Avg * (1.0 / ncount); 468 | Vector3d move = Avg - P.Vertices[i].ToPoint3d(); 469 | move = move * smooth; 470 | P.Vertices.MoveVertex(i, move); 471 | } 472 | 473 | //projecting points onto the target along their normals 474 | 475 | if (pull > 0) 476 | { 477 | Point3d Point = P.Vertices[i].ToPoint3d(); 478 | Vector3d normal = Normal(P, i); 479 | Ray3d Ray1 = new Ray3d(Point, normal); 480 | Ray3d Ray2 = new Ray3d(Point, -normal); 481 | double RayPt1 = Rhino.Geometry.Intersect.Intersection.MeshRay(M, Ray1); 482 | double RayPt2 = Rhino.Geometry.Intersect.Intersection.MeshRay(M, Ray2); 483 | Point3d ProjectedPt; 484 | 485 | if ((RayPt1 < RayPt2) && (RayPt1 > 0) && (RayPt1 < 1.0)) 486 | { 487 | ProjectedPt = Point * (1 - pull) + pull * Ray1.PointAt(RayPt1); 488 | } 489 | else if ((RayPt2 < RayPt1) && (RayPt2 > 0) && (RayPt2 < 1.0)) 490 | { 491 | ProjectedPt = Point * (1 - pull) + pull * Ray2.PointAt(RayPt2); 492 | } 493 | else 494 | { 495 | ProjectedPt = Point * (1 - pull) + pull * M.ClosestPoint(Point); 496 | } 497 | 498 | P.Vertices.SetVertex(i, ProjectedPt); 499 | } 500 | 501 | 502 | if (FeatureV[i] != -1) //pull feature vertices onto feature curves 503 | { 504 | Point3d Point = P.Vertices[i].ToPoint3d(); 505 | Curve CF = FC[FeatureV[i]]; 506 | double param1 = 0.0; 507 | Point3d onFeature = new Point3d(); 508 | CF.ClosestPoint(Point, out param1); 509 | onFeature = CF.PointAt(param1); 510 | P.Vertices.SetVertex(i, onFeature); 511 | } 512 | } 513 | else 514 | { 515 | P.Vertices.SetVertex(i, FV[AnchorV[i]]); //pull anchor vertices onto their points 516 | } 517 | } 518 | 519 | 520 | 521 | 522 | //end new 523 | 524 | 525 | 526 | 527 | AnchorV = CompactByVertex(P, AnchorV); //compact the fixed points along with the vertices 528 | FeatureV = CompactByVertex(P, FeatureV); 529 | FeatureE = CompactByEdge(P, FeatureE); 530 | 531 | P.Compact(); //this cleans the mesh data structure of unused elements 532 | } 533 | 534 | } 535 | 536 | DA.SetData(0, P); 537 | } 538 | 539 | protected override System.Drawing.Bitmap Icon 540 | { 541 | get 542 | { 543 | return Properties.Resources.remesh2; 544 | } 545 | } 546 | 547 | public override Guid ComponentGuid 548 | { 549 | get { return new Guid("{2b653315-c690-4670-b001-a2070dd060d4}"); } 550 | } 551 | 552 | private Point3d MidPt(PlanktonMesh P, int E) 553 | { 554 | Point3d Pos1 = P.Vertices[P.Halfedges[2 * E].StartVertex].ToPoint3d(); 555 | Point3d Pos2 = P.Vertices[P.Halfedges[2 * E + 1].StartVertex].ToPoint3d(); 556 | return (Pos1 + Pos2) * 0.5; 557 | } 558 | 559 | private List CompactByVertex(PlanktonMesh P, List L) 560 | { 561 | List L2 = new List(); 562 | 563 | for (int i = 0; i < P.Vertices.Count; i++) 564 | { 565 | if (P.Vertices[i].IsUnused == false) 566 | { 567 | L2.Add(L[i]); 568 | } 569 | } 570 | return L2; 571 | } 572 | 573 | private List CompactByEdge(PlanktonMesh P, List L1) 574 | { 575 | List L2 = new List(); 576 | 577 | int EdgeCount = P.Halfedges.Count / 2; 578 | 579 | for (int i = 0; i < EdgeCount; i++) 580 | { 581 | if (P.Halfedges[2 * i].IsUnused == false) 582 | { 583 | L2.Add(L1[i]); 584 | } 585 | } 586 | return L2; 587 | } 588 | 589 | private Vector3d Normal(PlanktonMesh P, int V) 590 | { 591 | Point3d Vertex = P.Vertices[V].ToPoint3d(); 592 | Vector3d Norm = new Vector3d(); 593 | 594 | int[] OutEdges = P.Vertices.GetHalfedges(V); 595 | int[] Neighbours = P.Vertices.GetVertexNeighbours(V); 596 | Vector3d[] OutVectors = new Vector3d[Neighbours.Length]; 597 | int Valence = P.Vertices.GetValence(V); 598 | 599 | for (int j = 0; j < Valence; j++) 600 | { 601 | OutVectors[j] = P.Vertices[Neighbours[j]].ToPoint3d() - Vertex; 602 | } 603 | 604 | for (int j = 0; j < Valence; j++) 605 | { 606 | if (P.Halfedges[OutEdges[(j + 1) % Valence]].AdjacentFace != -1) 607 | { 608 | Norm += (Vector3d.CrossProduct(OutVectors[(j + 1) % Valence], OutVectors[j])); 609 | } 610 | } 611 | 612 | Norm.Unitize(); 613 | return Norm; 614 | } 615 | 616 | private double CreaseAngle(PlanktonMesh P, int HE) 617 | { 618 | if (P.Halfedges[HE].IsUnused) 619 | { return -1; } 620 | else 621 | { 622 | int Pair = P.Halfedges.GetPairHalfedge(HE); 623 | if (P.Halfedges[HE].AdjacentFace == -1 || P.Halfedges[Pair].AdjacentFace == -1) 624 | { return 0; } 625 | else 626 | { 627 | int Vert1 = P.Halfedges[HE].StartVertex; 628 | int Vert2 = P.Halfedges[Pair].StartVertex; 629 | int Vert3 = P.Halfedges[P.Halfedges[P.Halfedges[HE].NextHalfedge].NextHalfedge].StartVertex; 630 | int Vert4 = P.Halfedges[P.Halfedges[P.Halfedges[Pair].NextHalfedge].NextHalfedge].StartVertex; 631 | 632 | Point3d P1 = P.Vertices[Vert1].ToPoint3d(); 633 | Point3d P2 = P.Vertices[Vert2].ToPoint3d(); 634 | Point3d P3 = P.Vertices[Vert3].ToPoint3d(); 635 | Point3d P4 = P.Vertices[Vert4].ToPoint3d(); 636 | 637 | Vector3d ThisEdge = P2 - P1; 638 | Vector3d Edge1 = P3 - P1; 639 | Vector3d Edge2 = P4 - P1; 640 | 641 | Vector3d Normal1 = Vector3d.CrossProduct(ThisEdge, Edge1); 642 | Vector3d Normal2 = Vector3d.CrossProduct(Edge2, ThisEdge); 643 | 644 | return (Vector3d.VectorAngle(Normal1, Normal2)); 645 | } 646 | } 647 | } 648 | 649 | public double WeightedCombo(Point3d Pos, List SizePoints, List Sizes, int Falloff, double BVal, double BWeight) 650 | { 651 | double WeightedSize = 0, WeightSum = 0; 652 | double[] Weighting = new double[SizePoints.Count]; 653 | 654 | for (int j = 0; j < SizePoints.Count; j++) 655 | { 656 | Weighting[j] = Math.Pow(Pos.DistanceTo(SizePoints[j]), -1.0 * Falloff); 657 | WeightSum += Weighting[j]; 658 | } 659 | 660 | WeightSum += BWeight; 661 | WeightedSize += BWeight * (1.0 / WeightSum) * BVal; 662 | for (int j = 0; j < SizePoints.Count; j++) 663 | { 664 | WeightedSize += Weighting[j] * (1.0 / WeightSum) * Sizes[j]; 665 | } 666 | return WeightedSize; 667 | } 668 | 669 | private static Vector3d[] LaplacianSmooth(PlanktonMesh P, int W, double Strength) 670 | { 671 | int VertCount = P.Vertices.Count; 672 | Vector3d[] Smooth = new Vector3d[VertCount]; 673 | 674 | for (int i = 0; i < VertCount; i++) 675 | { 676 | if ((P.Vertices[i].IsUnused == false) && (P.Vertices.IsBoundary(i) == false)) 677 | { 678 | int[] Neighbours = P.Vertices.GetVertexNeighbours(i); 679 | Point3d Vertex = P.Vertices[i].ToPoint3d(); 680 | Point3d Centroid = new Point3d(); 681 | if (W == 0) 682 | { 683 | for (int j = 0; j < Neighbours.Length; j++) 684 | { Centroid = Centroid + P.Vertices[Neighbours[j]].ToPoint3d(); } 685 | Smooth[i] = ((Centroid * (1.0 / P.Vertices.GetValence(i))) - Vertex) * Strength; 686 | } 687 | if (W == 1) 688 | { 689 | //get the radial vectors of the 1-ring 690 | //get the vectors around the 1-ring 691 | //get the cotangent weights for each edge 692 | 693 | int valence = Neighbours.Length; 694 | 695 | Point3d[] NeighbourPts = new Point3d[valence]; 696 | Vector3d[] Radial = new Vector3d[valence]; 697 | Vector3d[] Around = new Vector3d[valence]; 698 | double[] CotWeight = new double[valence]; 699 | double WeightSum = 0; 700 | 701 | for (int j = 0; j < valence; j++) 702 | { 703 | NeighbourPts[j] = P.Vertices[Neighbours[j]].ToPoint3d(); 704 | Radial[j] = NeighbourPts[j] - Vertex; 705 | } 706 | 707 | for (int j = 0; j < valence; j++) 708 | { 709 | Around[j] = NeighbourPts[(j + 1) % valence] - NeighbourPts[j]; 710 | } 711 | 712 | for (int j = 0; j < Neighbours.Length; j++) 713 | { 714 | //get the cotangent weights 715 | int previous = (j + valence - 1) % valence; 716 | Vector3d Cross1 = Vector3d.CrossProduct(Radial[previous], Around[previous]); 717 | double Cross1Length = Cross1.Length; 718 | double Dot1 = Radial[previous] * Around[previous]; 719 | 720 | int next = (j + 1) % valence; 721 | Vector3d Cross2 = Vector3d.CrossProduct(Radial[next], Around[j]); 722 | double Cross2Length = Cross2.Length; 723 | double Dot2 = Radial[next] * Around[j]; 724 | 725 | CotWeight[j] = Math.Abs(Dot1 / Cross1Length) + Math.Abs(Dot2 / Cross2Length); 726 | WeightSum += CotWeight[j]; 727 | } 728 | 729 | double InvWeightSum = 1.0 / WeightSum; 730 | 731 | Vector3d ThisSmooth = new Vector3d(); 732 | 733 | for (int j = 0; j < Neighbours.Length; j++) 734 | { 735 | ThisSmooth = ThisSmooth + Radial[j] * CotWeight[j]; 736 | } 737 | 738 | Smooth[i] = ThisSmooth * InvWeightSum * Strength; 739 | } 740 | 741 | } 742 | } 743 | return Smooth; 744 | } 745 | } 746 | } -------------------------------------------------------------------------------- /src/remesher.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug32 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {9EA05C6C-A88C-4FF2-8E7D-98FC412F4944} 9 | Library 10 | Properties 11 | remesher 12 | remesher 13 | v4.0 14 | 512 15 | false 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | ..\bin\Debug32\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | true 29 | full 30 | false 31 | ..\bin\Debug\ 32 | DEBUG;TRACE 33 | prompt 34 | false 35 | 36 | 37 | pdbonly 38 | true 39 | ..\bin\Release\ 40 | TRACE 41 | prompt 42 | 4 43 | 44 | 45 | 46 | ..\lib\GH_IO.dll 47 | False 48 | 49 | 50 | ..\lib\Grasshopper.dll 51 | False 52 | 53 | 54 | ..\lib\Plankton.dll 55 | False 56 | 57 | 58 | ..\lib\Plankton.gha 59 | False 60 | 61 | 62 | ..\lib\RhinoCommon.dll 63 | False 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | True 77 | True 78 | Resources.resx 79 | 80 | 81 | 82 | 83 | ResXFileCodeGenerator 84 | Resources.Designer.cs 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 101 | 102 | Copy "$(TargetPath)" "$(TargetDir)$(ProjectName).gha" 103 | Erase "$(TargetPath)" 104 | 105 | 106 | 107 | 108 | Program 109 | C:\Program Files (x86)\Rhinoceros 5.0\System\Rhino4.exe 110 | 111 | 112 | en-US 113 | 114 | 115 | C:\Program Files\Rhinoceros 5.0 (64-bit)\System\Rhino.exe 116 | 117 | 118 | Program 119 | 120 | -------------------------------------------------------------------------------- /src/remesherInfo.cs: -------------------------------------------------------------------------------- 1 | using Grasshopper.Kernel; 2 | 3 | namespace remesher 4 | { 5 | public class remesherInfo : GH_AssemblyInfo 6 | { 7 | public override string AssemblyName 8 | { 9 | get 10 | { 11 | return "MeshMachine"; 12 | } 13 | } 14 | 15 | //Override here any more methods you see fit. 16 | //Start typing public override..., select a property and push Enter. 17 | } 18 | } --------------------------------------------------------------------------------