Area calculation based on a set of crossing lines / Python

I’d like to calculate the area of a variety of polygonal surfaces starting from a series of lines crossing each other within a rectangular boondary.
Thought about “SplitByPoints” to split the lines into segments using the mutual intersection points between the lines. But how could I obtain a list of polygonal “surfaces” from all those segments to finally extract the areas from?

= = = = =

geometry = [Imperative]
{
// create rectangular line points
start_e1 = Point.ByCoordinates(0,0);
end_e1 = Point.ByCoordinates(10,0);
start_e2 = Point.ByCoordinates(10,0);
end_e2 = Point.ByCoordinates(10,10);
start_e3 = Point.ByCoordinates(10,10);
end_e3 = Point.ByCoordinates(0,10);
start_e4 = Point.ByCoordinates(0,10);
end_e4 = Point.ByCoordinates(0,0);

// create free line points
start_l1 = Point.ByCoordinates(1,0);
end_l1 = Point.ByCoordinates(8,10);
start_l2 = Point.ByCoordinates(4,0);
end_l2 = Point.ByCoordinates(2,10);
start_l3 = Point.ByCoordinates(9,0);
end_l3 = Point.ByCoordinates(6,10);
start_l4 = Point.ByCoordinates(0,2);
end_l4 = Point.ByCoordinates(10,1);
start_l5 = Point.ByCoordinates(0,4);
end_l5 = Point.ByCoordinates(10,8);
start_l6 = Point.ByCoordinates(0,8);
end_l6 = Point.ByCoordinates(10,2);

// create lists of start-/end-points
startpoints = {start_e1,start_e2,start_e3,start_e4,start_l1,start_l2,start_l3,start_l4,start_l5,start_l6};
endpoints = {end_e1,end_e2,end_e3,end_e4,end_l1,end_l2,end_l3,end_l4,end_l5,end_l6};

// create lines
lines = Line.ByStartPointEndPoint(startpoints,endpoints);
return = lines;
};

= = = = =

here is one paper on the topic:

seems to me you need to find the intersections of all lines, then multiple subsets of all sets of these points, then call the polygon.bypoints function on all subsets, if it succeeds you found a polygon, if not, disregard that result.

1 Like

In conjunction with @Michael_Kirschner2 workflow, an additional criteria of “polygon contains no curves inside perimeter” would be needed to identfy only the polygons shown in your original example, and not polygons containing polygons :wink:

@Alexander_Kraus Here is a heavy approach (ver 2.0) …

rect = Rectangle.ByCornerPoints(Point.ByCoordinates([0,0,10,10],[0,10,10,0]));
startpoints = Autodesk.Point.ByCoordinates([1,4,9,0,0,0],[0,0,0,2,4,8]);
endpoints = Autodesk.Point.ByCoordinates([8,2,6,10,10,10],[10,10,10,1,8,2]);
lines = Line.ByStartPointEndPoint(startpoints,endpoints);

//Cutting Tool
tool = Solid.ByUnion(List.Flatten([rect,lines],-1).Extrude(Vector.ZAxis()).Thicken(0.2));

//Divisions
divisions = List.Clean(PolyCurve.ByJoinedCurves(rect.Patch().Split(tool).PerimeterCurves()),false).Offset(0.1,false).Patch();
1 Like

Had to change the list bracket type from square to braces, but then it worked.
Thanks a lot !
The underlying rectangular surface is being cut at the outer edges of the solid (tool), so the polycurves 0.1 offset is to compensate the solid 0.2 thickness, right?
Would it be possible to use the same solution within a Python Code Block?
Which would make it more accessible for looping…

Thanks for your answer and the paper.
Maybe the “List Permutations Block” could be a solution.
But one would have to carry out the operation for all “lenghts” from 3 to N, 3 being the minimum number of points to ceate a surface, N being the number of intersection points.

If, by looping you mean applying it to multiple rectangles, you could do so by defining a function in Design Script itself.

splitDef.dyn (12.3 KB) (ver 2.0)

def spl(rect,lines:var[]..[])
{
//Cutting Tool
tool = Solid.ByUnion(List.Flatten([rect,lines],-1).Extrude(Vector.ZAxis()).Thicken(0.4));

//Divisions
divisions = List.Clean(PolyCurve.ByJoinedCurves(rect.Patch().Split(tool).PerimeterCurves()),false).Offset(0.2,false).Patch();
return divisions;
};

Yes. Ideally this could have been avoided if a polysurface as a cutting tool had worked.

As I’m using ver 2.0, you might have to make that change to the file attached here too :slight_smile:

not the right answer for a Dynamo forum- but I’ve been playing around with the geometry algorithms in GIS recently.

‘polygonize’ does exactly what you want

I don’t know enough Python to know if something like shapely.ops.polygonize_fullcould be used in Dynamo
http://toblerity.org/shapely/manual.html

Andrew

The lines will eventually be generated by random and the resulting polygon areas should be within a defined range, e.g. between 25 and 75. The final match should be achieved by Looping a list of “surface areas” in Python.

I copied the DS code into a Python block to show what is basically intended…
But I dn’t know if methods like “extruding” or “splitting” of lists are supported by Python?
It seems to me as if anything you do in the Python block, you only do it for one single object?

The following image shows a sample with just 2 lines and 4 surfaces.

image

= = = = =
import clr
clr.AddReference(‘ProtoGeometry’)
from Autodesk.DesignScript.Geometry import *

import sys
sys.path.append(r’C:\Program Files (x86)\IronPython 2.7\Lib’)
import random

clr.AddReference(‘DSCoreNodes’)
import DSCore
from DSCore import *

#Rectangle
c1 = Point.ByCoordinates(0,0)
c2 = Point.ByCoordinates(10,0)
c3 = Point.ByCoordinates(10,10)
c4 = Point.ByCoordinates(0,10)
rect = Rectangle.ByCornerPoints(c1,c2,c3,c4)

#Lines
p11 = Point.ByCoordinates(random.randint(0,10),0)
p12 = Point.ByCoordinates(random.randint(0,10),10)
l1 = Line.ByStartPointEndPoint(p11,p12)
p21 = Point.ByCoordinates(0,random.randint(0,10))
p22 = Point.ByCoordinates(10,random.randint(0,10))
l2 = Line.ByStartPointEndPoint(p21,p22)
lines = [l1,l2]

#Extrude
tool = Solid.ByUnion(List.Flatten([rect,lines],-1).Extrude(Vector.ZAxis()).Thicken(0.2));

#Divisions
divisions = List.Clean(PolyCurve.ByJoinedCurves(rect.Patch().Split(tool).PerimeterCurves()),false).Offset(0.1,false).Patch();

#List of Areas of Divisions

#LOOPWHILE Minimum Area < 2 (e.g.) or Maximum Area > 10 (e.g.)
while MinArea < 25 or MaxArea > 75:
LOOP CONTENT…

#Assign your output to the OUT variable
OUT = divisions
= = = = =

Just had a brief look at the manual, but Shapely is just for 2D, isn’t it?
In 2D it looks as if it could work with Python looping.
But we need a solution working for both 2D and 3D construction…

Surfaces.GeometrySplit (from LunchBox) will split surfaces recursively. You just need to separate your boundary curves (and create a surface) and your cutting curves.

4 Likes

Thanks. The node seems to be called “Intersection.GeometrySplit” in Dynamo 2.0.0 now.
I also tried the “Surface.Split (surface, line)” method with looping in Python, which works, as none of the returned surfaces is smaller than 49. But only for a single cutting line and not for a list of lines which would be necessary.

Does anyone know how to make Python’s “Surface.Split (surface, line)” work for lists?

image

= = = = =
import clr
clr.AddReference(‘ProtoGeometry’)
from Autodesk.DesignScript.Geometry import *

import sys
sys.path.append(r’C:\Program Files (x86)\IronPython 2.7\Lib’)
import random

clr.AddReference(‘DSCoreNodes’)
import DSCore
from DSCore import *

#Surf
c1 = Point.ByCoordinates(0,0,0)
c2 = Point.ByCoordinates(10,0,0)
c3 = Point.ByCoordinates(10,10,0)
c4 = Point.ByCoordinates(0,10,0)
corners =[c1,c2,c3,c4]
surf = Surface.ByPerimeterPoints(corners)

#First split line
p11 = Point.ByCoordinates(random.random()*10,0,0)
p12 = Point.ByCoordinates(random.random()*10,10,0)
l1 = Line.ByStartPointEndPoint(p11,p12)
#Optional further split lines
p21 = Point.ByCoordinates(0,random.random()*10,0)
p22 = Point.ByCoordinates(10,random.random()*10,0)
l2 = Line.ByStartPointEndPoint(p21,p22)
#Optional list of lines
lines = [l1,l2]

#Surface split
s = Surface.Split (surf, l1)
a = [s[0].Area, s[1].Area]
a_min = min(a)

#Looping for surface resriction
while a_min < 49:
c1 = Point.ByCoordinates(0,0,0)
c2 = Point.ByCoordinates(10,0,0)
c3 = Point.ByCoordinates(10,10,0)
c4 = Point.ByCoordinates(0,10,0)
corners =[c1,c2,c3,c4]
surf = Surface.ByPerimeterPoints(corners)

p11 = Point.ByCoordinates(random.random()*10,0,0)
p12 = Point.ByCoordinates(random.random()*10,10,0)
l1 = Line.ByStartPointEndPoint(p11,p12)
s = Surface.Split (surf, l1)
a = [s[0].Area, s[1].Area]
a_min = min(a)

#Assign your output to the OUT variable
OUT = s
= = = = =

Surface.Split (surface, line)

Hi @Alexander_Kraus

Yes, I believe Shapely is 2D- but the polygons produced could be extruded or otherwise used as the basis for 3D elements.
(I didn’t realise that there was a 3D aspect- the question seemed to be about lines and areas)

Andrew

Andrew

As discussed in your other thread - when you call a function in python or imperative design script you must call it with the parameters it expects - so to deal with lists you need to loop.

Here is how you could do the same without Python …
splitRect.dyn (15.2 KB) (ver 2.0)
Note: I’ve introduced a max attempts variable to control the duration of the run. As a result the max and min area criteria may not be satisfied. For better results increase attempts, but it could take forever.

rctg1 = Rectangle.ByWidthLength(10,10);
//Number of Lines
cnt;
//Minimum Area of Polygon
min;
//Maximum Area of Polygon
max;
//Max attempts
atm = 500;

[Imperative]
{
	surf1 = rctg1.Patch();
	lins1 = [];
	c = 0;
	t = [];
	sed = 0;
	test1 = true;
	sed = sed + 1;
	while(test1 && sed<atm)
	{
		lins1 = lins(rctg1,cnt,sed);
		surf1 = spl(rctg1,lins1);
		for (s in surf1)
		{
			if (s.Area<min || s.Area>max)
			{
				t[c] = true;
				c = c + 1;
			}
			else
			{
				t[c] = false;
				c = c + 1;
			}
		}
		sed = sed + 1;
		test1 = List.Contains(t,true);
	}
	return surf1;
};

//Functions
def lins(rec,cnt,sed)
{
	edgs1 = rec.Explode();
	parm1 = List.TakeItems(List.Shuffle(0.1..1..#(Math.Random(sed)*50)),cnt/2);
	parm2 = List.TakeItems(List.Shuffle(0.1..1..#(Math.Random(sed+1)*50)),cnt/2);
	parm3 = List.TakeItems(List.Shuffle(0.1..1..#(Math.Random(sed+2)*50)),cnt/2);
	parm4 = List.TakeItems(List.Shuffle(0.1..1..#(Math.Random(sed+3)*50)),cnt/2);
	pnts1 = edgs1[0].PointAtParameter(parm1);
	pnts2 = edgs1[1].PointAtParameter(parm2);
	pnts3 = edgs1[2].PointAtParameter(parm3);
	pnts4 = edgs1[3].PointAtParameter(parm4);
	lins1 = List.TakeItems(List.Flatten(Line.ByStartPointEndPoint([pnts1,pnts2],[pnts3,pnts4]),-1),cnt);
	return lins1;
};


def spl(rect,lines:var[]..[])
{
//Cutting Tool
tool = Solid.ByUnion(List.Flatten([rect,lines],-1).Extrude(Vector.ZAxis()).Thicken(0.4));

//Divisions
divisions = List.Clean(PolyCurve.ByJoinedCurves(rect.Patch().Split(tool).PerimeterCurves()),false).Offset(0.2,false).Patch();
return divisions;
};
6 Likes

Nice. The same function approach should also work for the “Intersection.GeometrySplit” solution proposed by @Nick_Boyts.

Would looping be faster in Python compared to DS in this case? What do you think?