Closest XYZ on DB.Line

Hi there,
is there a way to get the closest XYZ point on a certain DB.Curve/DB.Line relatively to another XYZ?

Something like the dynamo node “Geometry.ClosestPointTo”

Thank you

List.MinimumItemByKey where the list are the points and the key is a function of Geometry.DistanceTo with only the lines connected to it.

Thanks for answering @jacob.small. Actually I am performing the task in python (is a big amount of data and using API geometry instead of DS geometry it goes faster).

I try to better explain:
I have a list of XYZ (the API class from Autodesk.Revit.DB) and 4 Autodesk.Revit.DB.Lines.
For each XYZ I want to find the closest point on the closest Line.

thank you

Unfortunately, I didn’t found for the Autodesk.Revit.DB.Lines a method that can help me performing that task.
https://apidocs.co/apps/revit/2020/154b23e2-9f1f-813b-df56-e7c1bb347711.htm

Just because you are in Python doesn’t mean you can’t call Dynamo nodes. :wink:

1 Like

Can you not make use of this method?

this method is returning a double, I would like to have XYZ

Hi Guiseppe

I hope you’re well - you can use Curve.Project() then evaluate the IntersectionResult.Distance. This method projects tangent to the curve hence it naturally gives you the shortest distance to the curve. You’ll still need to write a simple function to collect a sample of points for evaluation as you don’t want to brute force this if you have large datasets, then from that reduced sample, keep the closest point to the curve.

I’m guessing this other XYZ is what you can use for reducing the search space/gathering the sample points? For this part of the problem, I would decide on a grid size and subdivide your search space into this module then store your points into these groups so you can rapidly refine your search set - a bit like how a AABB filter works.

1 Like

If it is a straight line, why not simply find a perpendicular to the given line, through a point in question and then find an intersection?

Can you upload a sample data set and section of code so far so the community can resolve this with you?

Hi Thomas,

Yes, thanks, I’m fine. Hope you too.
To be honest I didn’t get very well your solution (especially the second part of the message) but I’m understanding that is the right way to reach the result.
Thes lines give me an XYZ which seems to be the closest point on curve to the given XYZ. Can you confirm that?

def XYZtoDS(p):
	return	Autodesk.DesignScript.Geometry.Point.ByCoordinates(p.X*304.8, p.Y*304.8, p.Z*304.8)

# INPUT
pt = IN[0].ToRevitType()
crv = IN[1].ToRevitType()
# Script

intRes = crv.Project(pt)

# OUTPUT
OUT = XYZtoDS( intRes.XYZPoint )

The two inputs are basically a point and a curve (both dynamo ProtoGeometry) so I hope that with this I answered also to the last @jacob.small post.

Thanks for your suggestion, anyway if the solution pass through the creation of another geometry, I would think on using the ComputeClosestPoints Method.

I would like to discover a quick and flexible way to found the closest point from a given point to a certain curve (rather linear or not).

I’m afraid to make a mistake, but the code below is not what you are looking for?

####### Daniel_Woodcock1+++++
def ToVals(pt):
	return [pt.X,pt.Y,pt.Z]

def ToPts(ptVals):
	return Point.ByCoordinates(ptVals[0],ptVals[1],ptVals[2])

'''
Taken from Thomas Switzers awesome GitHub repo and modified to suit Dynamo Context
https://gist.github.com/arthur-e/5cf52962341310f438e96c1f3c3398b8
I take zero credit for this work other than porting it across.
'''
def convex_hull_graham(points):
	'''
	Returns points on convex hull in CCW order according to Graham's scan algorithm. 
	By Tom Switzer <thomas.switzer@gmail.com>.
	'''
	TURN_LEFT, TURN_RIGHT, TURN_NONE = (1, -1, 0)

	def cmp(a, b):
		return (a > b) - (a < b)
	
	def turn(p, q, r):
		return cmp((q[0] - p[0])*(r[1] - p[1]) - (r[0] - p[0])*(q[1] - p[1]), 0)
	
	def _keep_left(hull, r):
		while len(hull) > 1 and turn(hull[-2], hull[-1], r) != TURN_LEFT:
			hull.pop()
		if not len(hull) or hull[-1] != r:
			hull.append(r)
		return hull

	points = [ToVals(p) for p in points]	
	points = sorted(points)
	l = reduce(_keep_left, points, [])
	u = reduce(_keep_left, reversed(points), [])
	points = l.extend(u[i] for i in range(1, len(u) - 1)) or l
	points = [ToPts(p) for p in points]
	return  points

xtx1=convex_hull_graham(lk25)
xtx2=Surface.ByPerimeterPoints(xtx1)
####### Daniel_Woodcock1+++++

Hi @Giuseppe_Dotto

Yep, thats what i thought you wanted to do which sheds some light on the second part of my suggestion.

So the first step is there: using Project() to get the closest point to the curve.

My understanding is, you have Point A and you want to know which other points are closest to it - basically you want to gather all the nearest points to Point A and out of this set, find out which one is closest to the curve?

If that is the case then hopefully the final part of my suggestion gets clearer: you want to avoid a brute-force checking Point A against all other points to establish the ‘nearest point’ as this is really inefficient and will hammer performance. Instead, you can optimise it by grouping/partitioning points in the search space into lists based on their XYZ coordinates, say using 10m intervals - i.e. its similar to a grid subdivision which you use to group your points. Now, using Point A’s XYZ coordinates it is possible to identify which group it is nearest to so you only need to brute-force check a much smaller, discreet set. It is similar to how AABB filtering works, hence the reference.

However, you’ll need to consider what happens if Point A is at the edge of one of these point sets, as the closest set isn’t guaranteed to contain the closest point to the curve when its an edge-case. In this scenario you could define a tolerance to Point A and if any of the point sets are within this tolerance you include all of them in the brute-force query.

The green dot is ‘Point A’. The red grid represents the domains used to group the point set into smaller groups which you could store in a dictionary - lets assume the red grid represents 10m subdivisions - it means any point with a X and Y coordinate < 10m will be put into the first grid square ‘group’, and so on. The key could be a hash of the X and Y grid coordinate, then all you need to do to instantaneously grab the closest set, is access this dictionary using Point A’s X and Y coordinate using the same hash, then you can perform the brute-force check to find the nearest point, after which you can then perform the projection to the curve. Hopefully it makes more sense now:
image

2 Likes

Nice explanation @Thomas_Mahon.

@Giuseppe_Dotto you might want to look more into quadtree (or if you’re in 3D space octree) capabilities. Specifically the content which is used for things like mesh building. Some good info here: Quadtree - Wikipedia.

1 Like

Definitely much more clear!
I was looking just for a method “equivalent” to the dynamo node “Geometry.ClosestPointTo” which, in a certain way, I discovered and shared in my previous post.

For this second step, in my mind, there was the possibility to brute force the computer and let it collect the interesting points using the IsAlmostEqualTo(XYZ, tol) or the DistanceTo() method.
But it is totally evident that your suggestion it’s way more efficient!
Thank you very much Thomas, also the explanation is amazing.