Geom.intersect_line_line question
-
model = Sketchup.active_model entities = model.entities dpoint_1=[-3.989142.m, 3.877123.m, -0.m] dpoint_2=[-3.989142.m, -3.647636.m, -0.m] curve_array=[[-3.989142.m, 3.877123.m, -0.m],[-4.457867.m, 3.358428.m, -0.m],[-4.853319.m, 2.781918.m, -0.m], [-5.16845.m, 2.157867.m, -0.m],[-5.397644.m, 1.4974.m, -0.m],[-5.536815.m, 0.812289.m, -0.m], [-5.583482.m, 0.114744.m, -0.m],[-5.536815.m, -0.582801.m, -0.m],[-5.397644.m, -1.267913.m, -0.m], [-5.16845.m, -1.92838.m, -0.m],[-4.853319.m, -2.55243.m, -0.m],[-4.457867.m, -3.128941.m, -0.m],[-3.989142.m, -3.647636.m, -0.m]] curve_center=[-0.347015.m, 0.114744.m, -0.m] center= [-5.583482.m, 0.114744.m, -0.m] offset1_vec =center.vector_to dpoint_1 offset2_vec =center.vector_to dpoint_2 offset1 = center.offset offset1_vec,(0.2.m) offset2 = center.offset offset2_vec,(0.2.m) line1_vector = center.vector_to curve_center line2_vector = center.vector_to curve_center line1 = [offset1,line1_vector] line2 = [offset2,line2_vector] entities.add_curve(curve_array) entities.add_cline(offset1,line1_vector) entities.add_cline(offset2,line2_vector) curve_array_length =(curve_array.length)-1 curve_array_length.times do |point| line = [curve_array.at(point),curve_array.at(point +1) ] pt1 = Geom.intersect_line_line(line, line1) pt2 = Geom.intersect_line_line(line, line2) puts "pt1;#{pt1}" puts "pt2;#{pt1}" end
Hi Guys
Just wondering why the above code sample returns an intersection with each code block iteration.
If it was working correctly it should return 2 point3d's instead every line comparison is showing up as an intersection?
I am sure there must be something basic that I have missed.
Some times another set of eyes works wonders.Cheers Brett
-
It is working correctly.
A 'line' *isn'*t an 'edge'.
An 'edge' has a 'line' - a 'line' is simply an array of a point and a vector = [point, vector] which stretches infinitely.
So if you have an edge intersecting a coplanar curve its line will intersect with the line of every edge in the curve, unless a curve-edge is exactly parallel with the intersecting edge - as then their lines will never intersect...
I think what you actually want is to test for an edge intersecting other edges...
First you narrow it down to the list of intersection-points you are getting - make an array of them as you collect them.
Now iterate again through these points and check if they actually fall 'on' the 'intersecting-edge' itself rather than just its 'line'.
To do this write a simple sub-method inside your main code...def is_point_on_edge?(point,edge) dis=edge.length spt=edge.start.position ept=edge.end.position if point.distance(spt)<=dis and point.distance(ept)<=dis return true else return false end end
to use it simply use it inside a block iterating the points
intersecting_points << point if is_point_on_edge?(point,edge)
The test is true if the edges intersect at a vertex - Note how this could return two identical points for a start or end when that is the intersection point as a vertex is still 'on' the edge - after making the array of points [0/1/2/3/4 long (with possible duplicates at vertices) for an 'arc', but for a 'curve' that could zigzag this could be any number as it's edges pass across the intersecting edge...]you can then simply test for equality of any of them in theintersecting_points
array and keep just one if so...
is_point_on_edge?
assumes you already established that 'point' is on 'edge.line' - you could readily combine the two tests into one if you wanted to... -
Thanks for the reply and suggestion Tig.
For some reason I thought if you use two point3d's to form a line, you created a line segment, instead of an infinite line.
The dangers of coding when your tired.Regards
Brett
-
@brett mcallister said:
Thanks for the reply and suggestion Tig.
For some reason I thought if you use two point3d's to form a line, you created a line segment, instead of an infinite line.
The dangers of coding when your tired.Regards
Brett
When you specify an edge by two points [confusingly it IS referred to as a 'line' in
entities.add_line(pt0,pt1)
] then what you get IS an edge [or a 'line-segment' as you call it]... BUT when you get that edge's 'line' withedge.line
then it becomes as infinitely long 'thing' - a vector through a fixed point... that could intersect with other 'lines' - to get 'physical' intersections of edges use a method like I outlined.......... -
Here's my confusion with the Geom.intersect_line_line --
when I'm processing a large file, that seems to be a huge amount of additional processing, with it always assuming the two lines are infinite.
In particular,Geom.intersect_line_line(edge1.line,edge2.line)
I can appreciate why it treats them as infinite, but what about this:
Geom.intersect_line_line(edge1.vertices,edge2.vertices)
If it's going to accept the vertices as parameters, why can't it treat them as fixed length?
Put that on my wishlist.
--J
-
A 3 item array, and the special kinds of arrays - i.e. a point and a vector both have xyz values.
Many of Sketchup tools also accept a vertex as if it were a point, doing the vertex.position conversion for you.
You can confuse Sketchup by passing something other than a the correct 'types' as a 'line' and thereby having it think it is one.
A 'line' is a two item array containing a point and a vector [or the equivalent arrays for these].
Every edge has a 'line' consisting of its start vertex position [point] and the vector from that point to its end vertex position.
A 'line' in geometry is considered infinite.
So two 'lines' might intersect and return a point, but that intersection-point may well not be on either of the edges that own those 'lines'...
An edge has two vertices - so edge.vertices is effectively returning an array of two xyz arrays [vertex, vertex].
This array of two vertices could be confused as the [point, vector] needed to define that 'line' BUT the second element is a NOT a vector, or anything equivalent to the edge's vector, so the result will be wrong !
If you want to intersect the 'lines' of two edges and find out if its intersection-point [if successful] is physically on one [or both] of those two edges then there are several examples of code to do that. Rick Wilson did one to check if such a point was between the two points defined by the edge's start.position and end.position - i.e. it was on the edge, will additional flags for ignoring/accepting if the point were exactly at a start/end position...
You don't have the right kind of arrays passed from two 'edge.vertices' to get a meaningful intersection with another pair of 'edge.vertices' - there is no vector information in there - that's why edge.line was invented ! It is relatively straightforward to get the two edges' lines, see if they intersect, and then see if the intersection-point falls on neither, one or two of the edges, even checking for start/end-point being coincident with it as valid or not... -
Thanks for clarifying a bit.
It's just difficult for the likes of me, coming from a strong background in the CAD realm (AutoCAD and Revit).
In AutoCAD, you have intersect(p1, p2, p3, p4, INF), where INF is the flag to treat the line segments as infinite.
Further, you can very quickly build a selection of all objects within certain bounds.
So coming from that experience, it just feels like I'm having to needlessly iterate through all entities in a model to perform some simple intersection checking.
I've got a routine that does exactly what you describe, but with 1,500 edges in a model (which seems like a ridiculously small set), checking them all against each other just seems to take too long.--J
-
@honkinberry said:
I've got a routine that does exactly what you describe, but with 1,500 edges in a model (which seems like a ridiculously small set), checking them all against each other just seems to take too long.
What is that routine? Ruby is slow and it behaves in strange ways that I've often find surprising ways to optimize code. If you have a snippet we can have a look and have a stab at it.
-
So given an array of edges 'elist' (imported from AutoCAD), I'm checking for intersections.
elist.each do |e1| # so given this line, we look for intersections, to amend [p1,p2] to [p1,px,pn,p2] p1 = e1.start.position p2 = e1.end.position plist = [p1,p2] line1 = e1.line # [point . vector] # for this line segment, compare to all other edges elist.each do |e2| if ( e1 != e2 ) # only if not the same edge of course line2 = e2.line # [point . vector] v1 = line1[1] # vector v2 = line2[1] # vector if ( v1 != v2 ) # if vectors are differnet, they aren't parallel int = Geom.intersect_line_line(line1,line2) # but assumes infinite! if ( int ) # make sure it's on both lines, and not an endpoint of line1 z1 = e2.start.position z2 = e2.end.position if ( !plist.include?(int) ) # not end point or already found if ( pointonline(int,p1,p2) ) # and on line1 if ( pointonline(int,z1,z2) ) # and on line2 # so adding our intersection to line 1 plist << int end # if co-linear2 end # if co-linear1 end # if not end point end # if intersection end # if not parallel end # if not same segment end # subloop # ... do stuff with amended plist ... end # entity loop
Missing is a standard helper function 'pointonline' which checks if given point falls on the given line segment.
Given about 1,500 edges, it seems it can easily take 30 seconds, which just feels far too long for my taste.
Many thanks if you have any tips to optimize it!--J
-
As I thought I explained...
Rick Wilson already made a function called something like
point.between_points?(pt1, pt2, cond)
So to see if a point on a an edge use that passing theedge.start.position
andedge.end.position
as the arguments - returns true or false: the 'cond
' determines if the point that is 'exactly on' the edge's start/end is be be regarded as 'between' the given points...I posted an example of this three years ago!
http://sketchucation.com/forums/viewtopic.php?p=243509#p243509Note that changing/adding-to the built-in API class methods is probably not the best way to do it... BUT you can easily use the principal to make your own equivalent of this method, wrapped in your own module/class/methods...
-
-
Sorry, guess I wasn't clear.
I have the pointonline function, and yes, that's easy, hence why I didn't bother including it.
My issue, is that in a model with 1,500 edges, the afore posted routine takes over 30 seconds to complete.
What I was hoping to do, was to optimize that routine if possible. As near as I can tell, I have to iterate through all edges, and compare each to all other edges, perform an intersect_line_line, and then check if that point falls on both edges. Correct?
I tried adding some basic coordinate validation, checking the X and Y coordinates of each edge end point first, to see if an intersection might be possible, but just took even more processing time.
I'm about to check the exact processing hit for each of the key tests, to see what is the big hangup, but thought I'd post what I have so far to see if there's any thing that jumps out as slow.--J
-
Yea, Ruby is so slow that extra checks might just make it slower. Perhaps you can use the BoundingBox class?
In general, try to make the API methods to the most work as then it's performed in C++.
-
I've tried a few variations now.
added a BoundingBox intersection testing, and if that hits, then do an intersect_line_line.
That ends up taking about 10% more time.
Then on a whim, I even removed the Vector checking, that saves time.
So no matter what I try, indeed, it seems the fastest is to get to the intersect_line_line as quickly as possible, as that's even faster than comparing the vectors first.The really unfortunate part, now, is that I've found in trying to add a progress bar at the status line, that just seems to add a huge amount of time to the operation. I'd rather have a spinning pinwheel for 30 seconds than a progress bar for 2 minutes. I guess I could try optimizing the progress bar function, it looks like it's updating the status bar text every iteration, regardless of whether the percentage has changed.
Thanks as always for the tips!
--J
-
Then it sounds like moving to an Ruby C Extension is what might give real performance improvements.
Advertisement