Generate grid of xyz coords on a face or surface

Generate grid of xyz coords on a face or surface

arautio
Advocate Advocate
3,288 Views
6 Replies
Message 1 of 7

Generate grid of xyz coords on a face or surface

arautio
Advocate
Advocate

Taking on another brain crushing task, auto modeling from a point cloud using a simple modeled in place box. I had something similar to this working for topography, looking down in the Z and finding points to generate a topo. Then I thought, well if this can be done in 1 direction why not 6, so, as a test of concept, I created a grid of circles on a box face and use the center points to project into the cloud to generate a mesh. Got that working, now I am trying to programmatically generate a grid of points on each face of my box . Was able to generate equidistant points along the edges, just stuck on how to fill in the area between. Thought divided surface might work, but that seems tied to a pattern, where I just need a grid that I can pull world coords from every point, and have a normal associated so I know which way to look for cloud points. Incrementing circle size on test box just to see where things are starting and ending. Will look at cleaning up the mesh and how to stitch the multiple sides in the future. Also noticed the mesh is flipped inside/out, not sure how to flip the face direction of a mesh, or assign smoothing if that's possible.

Attaching some images, and the code so far.

 

Manually created field of circles on a plane adjacent to cloud model:

RevitAPI-AutoModelFromPointCloud1.jpg

The resulting meshes from the test points, projected from each side:

 

RevitAPI-AutoModelFromPointCloud2.jpg

Current progress on auto generation of points on the box faces:

RevitAPI-AutoModelFromPointCloud3.jpg

You just need a point cloud and a modeled in place box, also will need triangulate form GitHub, everything else is out of box Revit API.


The box can have any rotations on any axis.
The icnt and iicnt counters are just to limit the box face receiving the grid for troubleshooting.

 

For now, until I get it working, I cancel the script after the generation of points on box portion of the code has ran.

public void ModelFromCloudPointsBOX()
{	
    UIDocument uidoc = this.ActiveUIDocument;
	Document doc = uidoc.Document;
	Autodesk.Revit.DB.View activev = doc.ActiveView;
	Autodesk.Revit.ApplicationServices.Application app = uidoc.Application.Application;

	ElementId elementid = null;
	elementid = uidoc.Selection.PickObject(ObjectType.Element, "Select Point Cloud To Surface").ElementId;
	Element e = doc.GetElement(elementid);
	Instance ei = e as Instance;
	PointCloudInstance pcInstance = ei as PointCloudInstance;
	
	ElementId belementid = uidoc.Selection.PickObject(ObjectType.Element, "Select BOX enclosing EQUIPMENT to model").ElementId;
	Element be = doc.GetElement(belementid);
	Instance bei = be as Instance;

	using (Autodesk.Revit.DB.Transaction t0 = new Autodesk.Revit.DB.Transaction(doc, "Auto Model Equipment from Point Cloud"))
    {
    t0.Start();
    
    var geE = (be as FamilyInstance).get_Geometry(new Options() {ComputeReferences = true});

            foreach (var geO in geE) 
            {
                if (geO is GeometryInstance) 
                {
                    var geI = geO as GeometryInstance;
                    foreach (var g in geI.GetSymbolGeometry(geI.Transform)) 
                    {
                        var solid = g as Solid;
                        if (solid != null && solid.Volume>0) 
                        {   
							int icnt = 0;                  	
                            foreach (Face geomFace in solid.Faces)
				            {
                            	
								if (icnt == 0)
								{
                            	XYZ e00 = new XYZ(0,0,0);
                            	XYZ e01 = new XYZ(0,0,0);

                            	double l0 = 0;
                            	EdgeArrayArray earrayarray = geomFace.EdgeLoops;
                            	
								int iicnt = 0;                            	
                            	foreach (EdgeArray earray in earrayarray)
                            	{
                            		
									if (iicnt == 0)
									{
                            		List<XYZ> allpts = new List<XYZ>();
                            		
                            		int counter = 0;
                            		while (counter < earray.Size)
                            		{
	                            		Autodesk.Revit.DB.Edge eg = earray.get_Item(counter);
	                            		Curve ecurv = eg.AsCurve();
	                            		e00 = ecurv.GetEndPoint(0);
	                            		e01 = ecurv.GetEndPoint(1);
	                            		l0 = ecurv.Length;
	                            		
	                            		UV fuv = new UV(e00.X, e00.Y);
	                            		XYZ fnorm = geomFace.ComputeNormal(fuv);                          		
	                            		//XYZ midpt = new XYZ((e00.X + e01.X) / 2, (e00.Y + e01.Y) / 2, (e00.Z + e01.Z) / 2);
	                            		
	                            		double dist0 = 1 / l0;
										double dist = 0;
										XYZ xyz1 = new XYZ(0,0,0);
										List<XYZ> pts = new List<XYZ>();
										
										while (dist <= (1 *1.001))
	                            		{
	                            			xyz1 = new XYZ(((1 - dist) * e00.X + dist * e01.X), ((1 - dist) * e00.Y + dist * e01.Y), ((1 - dist) * e00.Z + dist * e01.Z));
	                            			bool pfound = false;
	                            			foreach (XYZ point in allpts)
	                            			{
	                            				if (Math.Round(point.X,2) == Math.Round(xyz1.X,2) & Math.Round(point.Y,2) == Math.Round(xyz1.Y,2) & Math.Round(point.Z,2) == Math.Round(xyz1.Z,2))
	                            				{
	                            					pfound = true;
	                            				}
	                            			}
	                            			if (pfound == false)
	                            			{
	                            				allpts.Add(xyz1);
	                            				pts.Add(xyz1);
	                            			}
	                            			dist = dist + dist0;     			
	                            		}
	                            		
	  		                            double rad = .05;			
	                            		foreach (XYZ xyzpt in pts)
	                            		{
											Autodesk.Revit.DB.Plane plane = Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(fnorm,xyzpt);
										    SketchPlane sp = SketchPlane.Create(doc, plane);
										    doc.ActiveView.SketchPlane = sp;
										        
											ModelArc arc = null;
											Arc geomarc = Arc.Create(plane, rad, 0, 360);
											arc = doc.Create.NewModelCurve(geomarc, sp) as ModelArc;
											rad = rad + .05;
	                            		}
	                            		
	                            		counter +=1;
                            		}
									}
                            		iicnt +=1;
                            	}                         	                           	
				            }
							icnt +=1;
							}
                        }
                    }
                }
            }
    
	t0.Commit();
	}
    
    
// to be replaced by point generation on each box face-----------------------------------------------------------------------------------
	IList<Reference> felems = uidoc.Selection.PickObjects(ObjectType.Element, "Select Model Line Circles to Generate Topo Points From");
	List<XYZ> tpoints = new List<XYZ>();
	
	using (Autodesk.Revit.DB.Transaction t1 = new Autodesk.Revit.DB.Transaction(doc, "Record Topo Points"))
	{   
	t1.Start();
        foreach (Reference eref in felems)
        {
            ElementId elid = eref.ElementId;
            Element el = doc.GetElement(elid);
            
            if (el.Category.Name.Contains("Lines"))
            {
            	ModelArc elfi = el as ModelArc;
            	Arc arcy = elfi.GeometryCurve as Arc;
                if (null != arcy)
                {
                    List<Autodesk.Revit.DB.Plane> planes = new List<Autodesk.Revit.DB.Plane>();
                    XYZ searchpoint = new XYZ(0, 0, 0);
                    XYZ adjust = new XYZ(.04, 0, .04);
                    XYZ adjustz = new XYZ(0, 8, 0);

                    XYZ adjustmod = new XYZ(.01, 0, .01);
                    bool pointfound = false;
                    int counter = 0;

                    while (counter < 3 & pointfound == false)
                    {
                        counter += 1;
                        pointfound = false;
                        planes.Clear();
                        searchpoint = arcy.Center;

                        // X boundaries
                        planes.Add(Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(XYZ.BasisX, searchpoint - adjust));
                        planes.Add(Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(-XYZ.BasisX, (searchpoint + adjust)));

                        // Y boundaries
                        planes.Add(Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(XYZ.BasisY, searchpoint - adjustz));
                        planes.Add(Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(-XYZ.BasisY, (searchpoint + adjustz)));

                        // Z boundaries
                        planes.Add(Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(XYZ.BasisZ, searchpoint - adjust));
                        planes.Add(Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(-XYZ.BasisZ, (searchpoint + adjust)));

                        // Create filter
                        PointCloudFilter pcf = PointCloudFilterFactory.CreateMultiPlaneFilter(planes);
                        var points = pcInstance.GetPoints(pcf, .01, 100);

                        if (points.Count > 0)
                        {
                            pointfound = true;
                            double zlowpoint = 10000;
                            foreach (CloudPoint cp in points)
                            {
                                if (cp.Y < zlowpoint)
                                {
                                    zlowpoint = cp.Y;
                                }
                            }

                            double zpoint = zlowpoint * 3.2808;
                            XYZ tpoint = new XYZ(searchpoint.X, zpoint, searchpoint.Z);  
  
                            if (tpoints.Count == 0)
                            {
                        		tpoints.Add(tpoint);
                            }
                            
                            var p = tpoints.Count;
                            p-=1;
                            bool pmatch = false;
                            
                            while (p >= 0 & pmatch == false)
                            {   
                            	if (Math.Round(tpoints[p].Z, 6) == Math.Round(tpoint.Z, 6))
                            	{
                            		if (Math.Round(tpoints[p].X, 6) == Math.Round(tpoint.X, 6))
                            		{
                            			pmatch = true;
                            		}
                            	}
								p-=1;
                            }
							if (pmatch != true)
                            {
                            	tpoints.Add(tpoint);
                            	break;
                            }                            
                        }
                        adjust = adjust + adjustmod;
                    }
                }
            }
		}   
	t1.Commit();
	}
        
   	
// Build the Surface ----------------------------------------------------------------------------------------------------------------
    FilteredElementCollector collector = new FilteredElementCollector( doc ).OfClass( typeof( Material ) );
   	IEnumerable<Material> materialsEnum = collector.ToElements().Cast<Material>();
   	var materialReturn1 = from materialElement in materialsEnum where materialElement.Name == "Default" select materialElement;
   	Element mat1 = materialReturn1.First();
   	ElementId  materialId = mat1.Id;
	
   	List<Triangulator.Geometry.Point> Vertices = new List<Triangulator.Geometry.Point>();
   	foreach (XYZ coord in tpoints)
   	{
   		Triangulator.Geometry.Point pNew = new Triangulator.Geometry.Point(coord.X, coord.Z, coord.Y);
   		Vertices.Add(pNew);
   	}
   	
   	List<Triangulator.Geometry.Triangle> tris = Triangulator.Delauney.Triangulate(Vertices);

    List<XYZ> loopVertices = new List<XYZ>(3);

    TessellatedShapeBuilder builder = new TessellatedShapeBuilder();
    builder.OpenConnectedFaceSet(true);
    
    foreach (Triangle t in tris)
    {
    	loopVertices.Clear();
    	XYZ newp = new XYZ( ((float)Vertices[t.p1].X),((float)Vertices[t.p1].Z),((float)Vertices[t.p1].Y));
    	loopVertices.Add(newp);
    	newp = new XYZ( ((float)Vertices[t.p2].X),((float)Vertices[t.p2].Z),((float)Vertices[t.p2].Y));
    	loopVertices.Add(newp);
    	newp = new XYZ( ((float)Vertices[t.p3].X),((float)Vertices[t.p3].Z),((float)Vertices[t.p3].Y));
    	loopVertices.Add(newp);
    	builder.AddFace(new TessellatedFace(loopVertices, materialId));
    }
            
    builder.CloseConnectedFaceSet();
    builder.Build();
    TessellatedShapeBuilderResult result = builder.GetBuildResult();

    using (Autodesk.Revit.DB.Transaction t3 = new Autodesk.Revit.DB.Transaction(doc, "Create tessellated direct shape"))
    {
    t3.Start();

       DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
       ds.ApplicationId = "Application id";
       ds.ApplicationDataId = "Geometry object id";

       ds.SetShape(result.GetGeometricalObjects());
    t3.Commit();
    }
        	
}



public void DivideSurface(Document document, Autodesk.Revit.DB.Form form)
{
    Autodesk.Revit.ApplicationServices.Application application = document.Application;
    Options opt = application.Create.NewGeometryOptions();
    opt.ComputeReferences = true;

    Autodesk.Revit.DB.GeometryElement geomElem = form.get_Geometry(opt);
    foreach (GeometryObject geomObj in geomElem)
    {
        Solid solid = geomObj as Solid;
        foreach (Face face in solid.Faces)
        {
            if (face.Reference != null)
            {
                DividedSurface ds = DividedSurface.Create(document,face.Reference);
                // create a divided surface with fixed number of U and V grid lines
                SpacingRule srU = ds.USpacingRule;
                srU.SetLayoutFixedNumber(16, SpacingRuleJustification.Center, 0, 0);

                SpacingRule srV = ds.VSpacingRule;
                srV.SetLayoutFixedNumber(24, SpacingRuleJustification.Center, 0, 0);

                break;  // just divide one face of form
            }
        }
    }
}

 

0 Likes
Accepted solutions (2)
3,289 Views
6 Replies
Replies (6)
Message 2 of 7

RPTHOMAS108
Mentor
Mentor
Accepted solution

Not quite following your mesh generation approach but to generate a grid of points on a face I would be using Face.Evaluate(UV) between the limits of Face.GetBoundingBox, face parametrisation depends on face type.

0 Likes
Message 3 of 7

arautio
Advocate
Advocate

With face evaluate can the xyz of the points generated be converted to world coords,  the challenge is getting points but with world coordinates not local uv coords.   Is there an example somewhere that shows how to use the face evaluate to get points into a List<XYZ>?

 

To clarify the mesh generation,  the user models an in place box around a piece of point cloud equipment,  then runs the script and selects the cloud and the box,   it then searches from the grid of points on each face of the box, inwards (reverse of the normal) and looks for cloud points closest to that box face, and then creates a mesh from those queried points. Essentially shrink-wrapping from every side.

0 Likes
Message 4 of 7

arautio
Advocate
Advocate

Trying to wrap my head around the divided surface method.

How do I get the grid nodes from the divided surface.  Not seeing a method for get grid node anywhere.

 

DividedSurface ds = DividedSurface.Create(doc,geomFace.Reference);
                                SpacingRule srU = ds.USpacingRule;
                                srU.SetLayoutFixedNumber(1, SpacingRuleJustification.Center, 00);
                
                                SpacingRule srV = ds.VSpacingRule;
                                srV.SetLayoutFixedNumber(1, SpacingRuleJustification.Center, 00);

                                Element host = ds.Host;
                                Face dividedface = host.GetGeometryObjectFromReference(ds.HostReference) as Face;
                                
                                UV uv = ds.GetGridNodeUV(node);        \\ Don't know how to get the grid node????
                                XYZ Pnt = dividedface.Evaluate(uv);

0 Likes
Message 5 of 7

arautio
Advocate
Advocate

Sooo... After much investment of time, it appears that Divided surface only works on Mass or Generic Model,  I just have a modeled in place cube.   So, back to the original question,  how do I divide up a face into equidistant xyz points?

0 Likes
Message 6 of 7

jeremy_tammik
Alumni
Alumni
Accepted solution

Exactly as Richard very kindly explained in his answer above:

 

 

That returns the desired XYZ coordinates.

 

 

 

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 7 of 7

arautio
Advocate
Advocate

Thank you! Thank you!    Would like to give you both credit,  Richard's answer was the right direction, but was a bit vague, Jeremy, you listed the needed steps in a much clearer manner.   This is what I came up with and it worked great!  Face will always be regular so I did not add check for position on face.  Assuming by regular you mean rectangular?

 

                                BoundingBoxUV fbbuv = geomFace.GetBoundingBox();
                                UV fuvmin = fbbuv.Min;
                                UV fuvmax = fbbuv.Max;
                                
                                List<XYZ> uvpts = new List<XYZ>();
                                int uvdivisionU = 1;
                                while (uvdivisionU <= fbbuv.Max.U)
                                {
                                    int uvdivisionV = 1;
                                    while (uvdivisionV <= fbbuv.Max.V)
                                    {
                                        UV nuv = new UV(uvdivisionU,uvdivisionV);
                                        XYZ uvpt = geomFace.Evaluate(nuv);
                                        uvpts.Add(uvpt);
                                        uvdivisionV +=1;
                                    }
                                    uvdivisionU +=1;
                                }

0 Likes