Determining if a point is "in front" or "behind" a surface
-
Hi there,
I've been trying to figure out how to determine if a given point is "in front of " a surface or "behind" it, relative to the surface's normal vector.
I can't seem to figure out any good solution so if anyone has a clue it'd be great.
Thanks!
-
hmmm
Project the point to the faces in the surface, find the closest one. Then compare the vector from the point to the face with the face normal ... ?
-
EDIT: Ahh, Thom beat me while I was typing up the code.
I think one way you can do it is to use the "project to plane"method. This returns a point that is on a plane, and nearest to the provided point. The vector between those 2 points is the face normal (or the opposite of the face normal). Then just test if those vectors (the one you found via projecting and the face normal) are in the same direction or not. See the code below:
face = my_face point = my_point point2 = point.project_to__plane( face.plane ) vector = point2.vector_to( point ) if vector.samedirection?( face.normal ) puts "point is 'in front' of the face" else puts "point is 'behind' the face" end
That should do it I think. I did not get a chance to test that for syntax accuracy or results.
Chris
-
Thanks to both of you, it seems rather obvious now! I'll try it as soon as I can
-
Gents, Ruby is slow enough without doing extra work!
face.plane already gives the normal + distance along then normal for the face plane.
So just do:
d = face.normal.dot( [testX, testY, testZ] ) - face.plane[3]
puts(d > 0 ? "in front" : "behind")
-
If the point in ON the face's plane the above would give false result of "behind":
module YourLibModule module_function def point_behind?( pt, face ) return nil if face.plane.nil? return false if pt.on_plane?(face.plane) d = face.normal.dot( pt.to_a ) - face.plane[3] d < 0 # ? true ; false end #def def point_in_front?( pt, face ) return nil if face.plane.nil? return false if pt.on_plane?(face.plane) d = face.normal.dot( pt.to_a ) - face.plane[3] d > 0 # ? true ; false end #def def point_on_face?( pt, face ) return nil if face.plane.nil? pt.on_plane?(face.plane) end #def end # YourLibModule
You could mix the above module into a custom class using
include()
.I wonder if it's worth requesting that the
Sketchup::Face#classify_point()
method be amended to return numerics for "in_front" and "behind" ?? -
In Adam's succinct example it can just return the value of the distance.
Usage:
distance(point, face)
> 0 in front of face
< 0 behind face
== 0 on face
def distance(point, face) return nil unless point.class==Sketchup;;Point3d and face.class==Sketchup;;Face face.normal.dot([point.x, point.y, point.z]) - face.plane[3] end
This is then a more general purpose tool that gives the distance a point is from a face - that the user can use as needed in various ways e.g.
dist=distance(point, face).abs
to get the actual distance or error if bad arguments.
Or
` d=distance(point, face)
if not dpassed bad arguments >> nil
elsif d > 0
in front
elsif d < 0
behind
else
on face
end`
-
@dan rathbun said:
def point_behind?( pt, face )
return nil if face.plane.nil? return false if pt.on_plane?(face.plane)
d = face.normal.dot( pt.to_a ) - face.plane[3]
d < 0 # ? true : false
end #defMust say I really don't like this kind of half-hearted defensive programming style. Why would a face not have a plane? And if you're going to test for that, test for the face being a Sketchup::Face, and if you going to test for that, then also test for pt being a Geom::Point3d... and on and on and on...
Code like this, is going to be embedded within a "hierarchy of confidence" set of functions. You don't need to be chasing your tail all the time - especially in a very slow language like Ruby.
You should have strong validation on data as it enters your system, then after that you don't litter code with meaningless tests that simply slow stuff down and reduce readability.
There, I've got that off my chest. And nothing to do with France - England match result either..
Adam
-
@tig said:
In Adam's succinct example it can just return the value of the distance.
That would ignore the internal Sketchup 0.001" tolerance. I was attempting to make sure that the tolerance applied.
EDIT: Does the
distance()
method apply the internal tolerance ?? -
Yes Type validation would (as TIG showed,) is usually the norm for public libraries.
If it's a private library and you are the only one calling it's methods, you will know the rules (arg types,) and such validation can be left out to speed things up.That said.. I put the nil test in as the API doc refers to "a plane if successful" but does not really say what the return value is, if the call is not successful. And as there is no "real" Plane class we cannot do a type check based on that, but if it's an array with 4 elements.
Comment it out if you feel it's unnecessary. (I won't feel bad.)
Advertisement