meshmash / Plankton

A C# half-edge mesh data structure, and components for using this in Grasshopper/Rhino
http://meshmash.github.io/Plankton
GNU Lesser General Public License v3.0
214 stars 66 forks source link

Mesh from closed polylines #16

Open pearswj opened 10 years ago

pearswj commented 10 years ago

Create a mesh from a number of closed polylines.

A mesh from a closed polyline is straightforward:

  1. Add polyline vertices to mesh
  2. add a face with vertex indices 1...n where n is the number of vertices.

Creating a mesh from several polylines could be done in the same way with a secondary procedure to merge duplicate vertices. Duplicates could be merged either before (faster) or after (more flexible) the faces are added.

pearswj commented 9 years ago

Starling's pmDecompose component works pretty well for the purpose of creating a Plankton mesh from several polylines. Perhaps we should add a simple component that wraps up the code below?

image

# V: list of Point3d
# F: tree of int

import clr
clr.AddReferenceToFile("Plankton.dll")

import Plankton

pmesh = Plankton.PlanktonMesh()

for pt in V:
    pmesh.Vertices.Add(pt.X, pt.Y, pt.Z)

for face in F.Branches:
    face = list(face)[:-1] # see comment below
    pmesh.Faces.AddFace(face)
pearswj commented 9 years ago

Actually there's a bug in the Starling component. It counts all the vertices in a closed polyline (where the first vertex appears again at the end).

harrilewis commented 9 years ago

I'm doing some rough and ready plankton.

image

Is there a function to merge vertices within a tolerance?

pearswj commented 9 years ago

Not currently. The above definition using Starling was the best solution I've achieved so far.

danhambleton commented 9 years ago

Provided the vertices are exactly coincident, I've been using:

        PlanktonMesh PMeshFromPolylines(List<Polyline> faces)
        {
            var pMesh = new PlanktonMesh();
            //add n-gon faces
            var verts = new List<Point3d>();
            for (int i = 0; i < faces.Count; i++)
            {
                var currFace = new List<int>();

                for (int j = 0; j < faces[i].Count - 1; j++)
                {
                    var currPt = faces[i].PointAt(j);
                    var id = verts.IndexOf(currPt);
                    if (id < 0)
                    {
                        //push a vertex to list
                        //push that index to current face
                        pMesh.Vertices.Add(currPt.X, currPt.Y, currPt.Z);
                        verts.Add(currPt);
                        currFace.Add(pMesh.Vertices.Count - 1);
                    }
                    else
                    {
                        //push this index to current face
                        currFace.Add(id);
                    }
                }

                pMesh.Faces.AddFace(currFace);
            }

            return pMesh;
        }

And to convert a PlanktonMesh to polylines:

        List<Polyline> PolylinesFromPMesh(PlanktonMesh pMesh)
        {

            var faces = new List<Polyline>();
            for (int i = 0; i < pMesh.Faces.Count; i++)
            {
                var tmpCurve = new Polyline();
                var faceCirculator = pMesh.Halfedges.GetFaceCirculator(pMesh.Faces[i].FirstHalfedge);
                foreach (var id in faceCirculator)
                {
                    var idx = pMesh.Halfedges[id].StartVertex;
                    var tmpPt = new Point3d(pMesh.Vertices[idx].X, pMesh.Vertices[idx].Y, pMesh.Vertices[idx].Z);
                    tmpCurve.Add(tmpPt);
                }

                //close the curve
                tmpCurve.Add(tmpCurve.PointAt(0));
                faces.Add(tmpCurve);
            }

            return faces;
        }
danhambleton commented 9 years ago

Just found the ToPolyline method....I guess that works too ;)

mikeheiss commented 7 years ago

Hi @pearswj, I attempted to duplicate your example using GhPython, but I got the error "Data conversion failed from Goo to PlanktonMesh" on the output. Any ideas? Within the Python window I can print pmesh.Vertices[0].X for example and it works fine.

plankton script

After I get this quad example working, I would like to attempt with ngon faces >4 sides.

pearswj commented 7 years ago

Hey @viper4049, I just tried this again in Rhino 5, Rhino WIP and Rhino WIP for Mac. Only in Rhino WIP for Windows do I see your error. I'll pass this on to @piac as he's responsible for the new Python script component in Grasshopper 1.0.

pearswj commented 7 years ago

@viper4049, I've moved this particular issue to youtrack.

piac commented 7 years ago

@pearswj Check what happens in your public override bool CastFrom(object source) method in meshmash. I think this method is incomplete, as it looks like it lacks the IGH_Goo implementation. What object do you get there? https://github.com/meshmash/Plankton/blob/master/src/PlanktonGh/GH_PlanktonMesh.cs#L267

piac commented 7 years ago

One addition that I think is required is this one:

var cast_as = source as IGH_Goo;
if (cast_as != null)
{
//do stuff with cast_as.CastTo<PlanktonMesh>(out pm);
}

Also, I think this needs to happen at the beginning of your code.

pearswj commented 7 years ago

@viper4049, just to keep you in the loop... I managed to get this working by making sure that the python script was referencing the same Plankton.dll that Grasshopper loads*.

clr.AddReferenceToFileAndPath("C:\Users\Will\AppData\Roaming\Grasshopper\Libraries\Plankton.dll")

*Actually, Grasshopper loads Plankton.gha, which in turn references Plankton.dll. But you get the idea...

pearswj commented 7 years ago

You can also try the following if, like me, hard coded paths make you shiver:

import Grasshopper
appdata = Grasshopper.Folders.DefaultAssemblyFolder

import clr
clr.AddReferenceToFileAndPath(appdata + "Plankton.dll")
mikeheiss commented 7 years ago

Excellent, thanks - the python script now works! This ability to build ngon pmeshes was exactly what I was looking for.

Using predefined cells from LunchBox everything works fine. However when I attempt to make my own mesh edge loops, only parts of the pmesh are created. My end goal is to be able to feed any polyline graph (simply connected, not in loops) and create an ngon pmesh. I'm using curves splitting a surface to get the connected mesh edge loops, then creating a tree of polylines to feed pmDecompose.

If you have time, would you be able to check if you see the same behavior? If not, I can try updating my libraries. If so, any ideas? When I bake the polylines going into pmDecompose everything looks well behaved (even in the areas where the pmesh isn't created). Perhaps it's a tolerance issue with the vertices not lining up perfectly? I tried Topologizer with a tolerance but that didn't help anything.

pmesh test build pmesh

Here's the link to my GH file (for some reason it wouldn't let me upload). (https://dl.dropboxusercontent.com/u/15647695/Build%20pmesh%20test.gh)

pearswj commented 7 years ago

@viper4049, it's the face winding directions that are causing the problem. Plankton only handles manifold meshes, i.e. meshes which have a front and a back. This orientation is determined by the "right-hand rule" i.e. if the vertices of a face are ordered counter-clockwise then the face normal will be out of the page/screen.

screen shot 2016-11-03 at 10 17 20

You could try the following:

if pmesh.Faces.AddFace(face) < 0: # AddFace returns -1 if the face couldn't be added
        pmesh.Faces.AddFace(reversed(face)) # try adding the opposite face

But, this won't work all the time. To do this properly, you need to make the face-winding uniform before building the mesh:

screen shot 2016-11-03 at 11 12 31

I did this here by comparing the normal of the original surface (sampled from the centroid of each face polyline) with the cross product of the first two edges of each face polyline. The comparison uses the dot product which will be either positive or negative, depending on whether the winding of the face polyline agrees with the right hand rule, or not.

untitled

piac commented 7 years ago

make the face-winding uniform

You can also use Weaverbird's Unify Face Windings for that. It works also on polylines that meet at vertices.

pearswj commented 7 years ago

Yes, do that! @piac you're a genius 😎.

mikeheiss commented 7 years ago

Perfect, simpler the better! Thanks so much for your help, I learned a lot through this exercise.

mikeheiss commented 7 years ago

I wrote up this example here in case anyone else was looking for the same thing.

http://www.grasshopper3d.com/group/plankton/forum/topics/cytoskeleton?commentId=2985220%3AComment%3A1633831&groupId=2985220%3AGroup%3A875392