[Closed] Calculate face normal
Really what I have are three positions and I want to get a normal from them, I don’t have a face or edges.
Is it as easy as using the three verts to get an average position and then using vert 1 and 2 to calculate x and y and then use the cross to get z (normal)?
Or is there a better way then that.
essentially yes. you shouldn’t need to find their average position unless you need it for something else.
for verts A B C simply:
normalize (cross (B - A) (C - A))
EDIT: Beat me to it Dave, but I noticed our formulas were different.
isn’t a vector between 2 points defined as AB = B – A
where you seem to have it the other way around. typo? or am i wrong?
Ah yes, that makes sense. I do need the position anyways as I need to build a matrix from it.
Hi Paul,
here is a small function that might be of help. It returns a transform matrix centerd in the three points average position, oriented with Z axis pointing normal to the plane defined, with Y axis trying to match the UpVector provided, while X follows to define an orthogonal Matrix. It offers a little more control over MatrixFromNormal(). Mind that the order of points provided determines Z axis positive or negative direction.
function getMartixFrom3Points p3Point_1 p3Point_2 p3Point_3 p3UpVector =
(
local p3Vect_A = p3Point_2 - p3Point_1
local p3Vect_B = p3Point_3 - p3Point_1
local p3AveragePos = (p3Point_1 + p3Point_2 + p3Point_3) / 3
local p3Axis_Z = normalize(cross p3Vect_A p3Vect_B)
local p3Axis_X = normalize(cross p3UpVector p3Axis_Z)
local p3Axis_Y = normalize(cross p3Axis_Z p3Axis_X)
-- uncomment for visual feedback
/*
Point pos:(p3AveragePos) wireColor:white size:1
Point pos:(p3AveragePos + p3Axis_X) wireColor:red size:1
Point pos:(p3AveragePos + p3Axis_Y) wireColor:green size:1
Point pos:(p3AveragePos + p3Axis_Z) wireColor:blue size:1
*/
return Matrix3 p3Axis_X p3Axis_Y p3Axis_Z p3AveragePos
)
- Enrico
Thanks, I already had it going but it is interesting to see how others go about it.
Just so you know, you shouldn’t use return in Max script functions as it slows them down.
In new versions of MAX (2008?) the “return” isn’t an issue any more. It does’t slow down. More, using “return” saves memory a bit. But “exit” and “continue” still slow loops down.
Well, the “return” issue seems to be a false myth, at least in recent Max versions. I don’t have a clue about what was happening in older ones. It seems Max is smart enough to avoid being fooled by the only last return in the code, but I haven’t explored other cases with more returns in the same function. As a matter of fact previous code with and without “return” has shown same results on an average of ten iterations each: about 5.4 seconds on Athlon 64 X2 5000+.
(
gc()
local startTime = timeStamp()
with undo off
for i = 1 to 1000000 do
getMartixFrom3Points [1,1,0] [1,0,1] [0,1,1] [0,0,1]
local stopTime = timeStamp()
format "Processing took % seconds
" ((stopTime - startTime) / 1000.0)
)
- Enrico
Taken almost straight from the MAXScript Reference, How To Make It Faster?
(original example and functions by Larry Minton, I just added the timing and loops)
(
fn test1a v = (if v == true do return 1; 0)
fn test1b v = (if v == true then 1 else 0)
st = timestamp()
for i = 1 to 100000 do test1a true
format "Test1a true: %
" (timestamp()-st)
st = timestamp()
for i = 1 to 100000 do test1a false
format "Test1a false: %
" (timestamp()-st)
st = timestamp()
for i = 1 to 100000 do test1b true
format "Test1b true: %
" (timestamp()-st)
st = timestamp()
for i = 1 to 100000 do test1b false
format "Test1b false: %
" (timestamp()-st)
)
Test1a true: 15547
Test1a false: 93
Test1b true: 79
Test1b false: 93
OK
In other words, the ‘if true then return’ case is 167.172 times slower than the false case in the first test. Test performed in Max 2010.
OK. If you are using “return” at the end of function to return a result it doesn’t slow down. If you are using “return” to break a loop (for example) and return a result – it slows.
fn boolToInt_1 a = (if a == on then return 1 else return 0) – it’s OK
fn boolToInt_2 a = (if a == on do return 1; return 0) – it’s not OK
The return rule does not apply to end of function, MAXScript strips it down internally so it is still fast. If you had return inside the function to bail out due to a condition, it would be possibly couple of orders of magnitude slower.
Regarding the order of points – assuming that A, B and C are running counter-clockwise from your Point of View, to get the normal pointing at you, you have to use
normalize (cross (B-A) (C-B))
since B-A is the first edge running counterclockwise and C-B is the second edge running CCW. (The third edge is (A-C) so it is also valid).
Using vectors that are not running counterclockwise from your POV can cause the result of the cross product to flip its direction and produce a normal pointing away from you. That’s why EMesh vertices in Max have to be created in CCW order to get the face visible.
When you apply Flip Normals using a Normals modifier, all it does is flip the order of two vertices inside the face which causes the cross product to flip sign and produce the opposite normal. Max calculates face normals on the fly using this method, so the vertex order is what matters.
Lesson learnt Bobo, anyway the case you’re presenting is what we can call the “return inside the function”, and for sure it could require a C++ exception to exit from the MaxScript function, before actually reaching the natural function end, causing the massive slowdown. I was referring, with Paul, to the “return at the end of the function”, that as you stated is simply stripped down when code is evaluated. I prefer to write it explicitly because of readability.
- Enrico
i’ve started putting return into maxscript functions because of having to do it when programming VB! I realise you dont need it though, but i do think it tells you exactly what is coming back, so to speak.