Join intersecting straight lines

Hello,
When reading the edges of a solid using the Face EdgeLoops property, some times an edge cam be composed of multiple segments.
I would like to obtain the joined elements as a single curve/line.

Have anyone faced this? As depending on the solid the number of joined lines can vary I am finding it difficult to reconstruct the loops simplifying them.

When I retrieve the solid geometry from a part, for example, them a face from the solid and finally a curve from one of the faces edge the edge can contain more than one line.

how do I join the lines into one single line that would represent the total of the edge?

It may be simple but I am stuck! :smiley:

Thank you!

What have you tried so far? Unless I’m misunderstanding your query, is it not the case of using PolyCurve.ByJoinedCurves to get a closed loop from your selected face edge geometry?

1 Like

Hi Thomas, It is similar, but, in some cases when a connecting part face if divided because that face is shared, the edge ends up being composed of multiple segments. So even by doing that I get that extra intermediate node on a line that should be contiguous but instead is composed of segments.

In my case, my analysis consists of the horizontal lines only, so that simplifies things.

I sorta found a workaround by checking the curves that intersect it other and grouping them and finding the endpoints that do not repeat, and using it to form the contiguous line I was needing if that makes any sense.

Mostly I use Dynamo as a prototyping environment for more complex tools that will be reused by other users. So I did all that using the python node.

Thanks for engaging in the discussion!

Try this.


Merge Collinear in Grouped Curves.dyf (77.5 KB)

2 Likes

Thanks Greg indeed the solution I used had a very similar logic.

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
import Autodesk
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
#from Revit import GeometryConversion as gp

import math

curves = IN[0]
#The next 2 methods will assume that the directions is known.
#The start point of a curve
def startPoint(curve):
    return curve.GetEndPoint(0)

#The end point of a curve
def endPoint(curve):
    return curve.GetEndPoint(1)
#Groups lines to be joined in sublists with the curves that have to be joined    
def joinCurves(list):
	comp=[]
	re=[]
	unjoined = []
	for c in curves:
		c = c.ToRevitType()
		match = False
		for co in comp:
			if startPoint(c).IsAlmostEqualTo(startPoint(co)) and endPoint(c).IsAlmostEqualTo(endPoint(co)):
				match = True
		if match:
			continue
		else:
			comp.append(c)			
			joined = []
			for c2 in curves:
				
				match = False
				c2 = c2.ToRevitType()
				for co in comp:
					if startPoint(c2).IsAlmostEqualTo(startPoint(co)) and endPoint(c2).IsAlmostEqualTo(endPoint(co)):
						match = True
				if match:
					continue
				else:
					if c2.Intersect(c) == SetComparisonResult.Disjoint:
						continue
					elif c2.Intersect(c) ==  SetComparisonResult.Equal:
						continue
					elif c2.Intersect(c) == SetComparisonResult.Subset:
						comp.append(c2)
						joined.append(c2.ToProtoType())
		joined.append(c.ToProtoType())
		re.append(joined)
		
	return re
		
result = joinCurves(curves)
def removeIntermediate(list):
	
	pts = []
	corner=[]
	for i in list:
		i= i.ToRevitType()
		pts.append(startPoint(i))
		pts.append(endPoint(i))
	for p in pts:
		counter = 0
		for pi in pts:
			if p.IsAlmostEqualTo(pi):
				counter+=1
		if counter < 2:
			corner.append(p)
	return corner[0], corner[1]
	
	
combined = []
for r in result:
	if isinstance(r, list) and len(r)> 1:
		n = removeIntermediate(r)
		nline = Line.CreateBound(n[0], n[1]).ToProtoType()
		combined.append(nline)
	else:
		try:
			combined.append(r[0])
		except:
			combined.append(r)
			
OUT = combined

The difference is I am filtering the curve-loops differently as I am not using polylines.

3 Likes

and i bet yours runs faster! lol

1 Like

Pretty much instant I would say. But the code as you can see still has room for improvement as I was at first much more concerned about finding the solution. Now I will probably work o making it more readable and clean.

Awesome. Would you mind sharing it when you’re done?

I don’t mind at all, but it has a lot to do with remembering, I tend to be forgetful, this is a tiny portion of a much bigger code, so I will probably work on the refactoring much later.

1 Like

Another thing is, I am only using horizontally aligned edges in relation to the base plane of the wall that forms the parts, though this part of the code should work for any orientation, it will join any lines that have a matching pair of endpoints (Curves.GetEndPoint()), so you may have to adjust it for your case regardless of my final code.

When I did this I was finding a clean perimeter of a room. I found that, very rarely, there would be an extra line on top of the boundary that I had to first remove before I could clean up the linework. It probably came from my method of finding the room edges but it was there nonetheless.

Hi Greg, as promised,

This is the optimized version of the code.

    #Groups lines to be joined in sublists with the curves that have to be joined    
def joinCurves(curves):

	comp=[]
	re=[]
	col=[]
	
	for c in curves:
		c = c.ToRevitType()
		counter = 0
		for c2 in curves:
			c2 = c2.ToRevitType()
			if not c2.Intersect(c) == SetComparisonResult.Disjoint:
				counter += 1
		comp.append([c, counter])
		
		len1 = len(comp)-1
		
	counter = 0
	joined = []
	
	for c in comp:
		if c[1] == 1:
			re.append(c[0].ToProtoType())
			if len(joined) > 0:
				re.append(joined)
				joined = []
				
		elif c[1] > 1 and counter < len1: 
			if len(joined) > 1 and comp[counter-1][1] == 2 and comp[counter-2][1] == 2:
				re.append(joined)
				joined = []
				joined.append(c[0].ToProtoType())
			else:
				joined.append(c[0].ToProtoType())
			
		elif c[1] > 1 and counter == len1:
			joined.append(c[0].ToProtoType())
			re.append(joined)
			
		counter += 1
		
	return re
	
#Remove itermendiate nodes of lines to be joined:
## spL1 ------------ epL1
##                   spL2-------------------epL2

#the startpoit/endpoint (spL2-epL1) is a intermediate node.
def removeIntermediate(list):

	pts = []
	corner=[]
	for i in list:
		try:
			i=i.ToRevitType()
		except:
			"DO NOTHING"
		pts.append(startPoint(i))
		pts.append(endPoint(i))
	for p in pts:
		counter = 0
		for pi in pts:
			if p.IsAlmostEqualTo(pi):
				counter+=1
		if counter < 2:
			corner.append(p)
	return corner[0], corner[1]

This counts the amount of intersections per node and joins and filter them accordingly and it is intended to be used with a geometry that all the lines are co-planar and the list of lines is already organized by z height and direction.

2 Likes

As the API returns a sorted CurveLoop the solution is much easier, you just need to check in order one line and the next one, and the next one, and so on.

curves:Geometry[];

[Imperative]
{
   curveA:Line = curves[0];
   curveB:Line = curves[1];

   nCurves:var = [];
   for (i in GetKeys(curves))
   {
      vecA:Vector = curveA.Direction.Normalized();
      vecB:Vector = curveB.Direction.Normalized();

      if (vecA.IsParallel(vecB))
      {
         pt1:Point = curveA.StartPoint;
         pt2:Point = curveB.EndPoint;
         nCurve:Line = Line.ByStartPointEndPoint(pt1, pt2);
         curveA = nCurve;
         curveB = curves[i+2];
      }
      else
      {
         nCurves = List.AddItemToEnd(curveA, nCurves);
         curveA = curveB;
         curveB = curves[i+2];
      }
   }
   return = nCurves;
};

It’s great that DesignScript dinamically extents the lists.

Regards,

2 Likes

Aces! Thanks!

Hi! Can you change it for work with sublists?