Flipping vector rotations solved, thanks guys!

I’m writing some code for Fiber 3 which will let me define n-sided segments, as well as curling. Everything is working except for some segments that are flipping 180 degrees as illustrated in the image below:

http://www.oregonstate.edu/~dennisa/Blender/Fiber3/flip.gif

Every so often the segments suddenly rotate 180 degrees and result in very ugly ‘knots’. I’ve seen this behavior in Blender before when you have a 3D NURBS curve that has an Ext. set.

I could easily correct the problem by simply adding 180 to the current angle, the problem is detecting where these anomalies are happening. The code is provided in a sample file located at http://www.oregonstate.edu/~dennisa/Blender/Fiber3/CrossSection.blend

Hahaha it gives new meaning to my signature! :stuck_out_tongue:

I just realized that it occurs when a slope reverses direction on any axis, so I just need to keep track of the previous slope versus the current slope and add 180 degrees if it does…

Edit:
Woohoo! I compare the previous and current vectors and see if any of the signs have changed. If they do, rotate 180 and the problems are gone!

if (prevVec.x < 0 and newVec.x >=0) or (prevVec.x > 0 and newVec.x <= 0):
	twist -= 180
if (prevVec.y < 0 and newVec.y >=0) or (prevVec.y > 0 and newVec.y <= 0):
	twist -= 180
if (prevVec.z < 0 and newVec.z >=0) or (prevVec.z > 0 and newVec.z <= 0):
	twist -= 180

Does anyone know if there’s just a sign comparison function for floats to make the if statements shorter?

Anyway, here’s an example of a straight and a curly segment:
http://www.oregonstate.edu/~dennisa/Blender/Fiber3/curve1.gif
http://www.oregonstate.edu/~dennisa/Blender/Fiber3/curve2.gif

Why are you doing

vec.x instead of vec[0]

then you could do


for a in vec and b in vec2:
   if (a<0 and b >= 0) or (a > 0 and b <= 0):
      twist -= 180

LetterRip

Turns out I’m still having problems with flippage. Detecting axis crossing must have been a solution to only part of the problem. Downloading the .blend and running the script on all three objects (with reference NURBS objects) and making your own observations on it would be greatly appreciated!

I noticed that if you select two verts that comprise an edge and rotate the edge so that the verts don’t actually move… that affects the twisting in the script. There must be a way to recalculate the polar rotations to point towards the next edge…

I think you might need to multiply the vector by -1 so that is in the correct direction when you change the rotation by 180.

LetterRip

Well, in my non-expert evaluation, the problem lies in your getPerpendicularVec() function. I replaced it with a cross product between the two vectors, and it removed the twisting. However, it didn’t replicate the NURBS examples either. Perhaps you just need to change the slope computations. It didn’t seem to me that they were taking all three dimensions into account (but I don’t understand them, and it’s not your fault: I’m just slow at math).
I haven’t checked out what LetterRip said yet.

Levi

How do you mean you replaced that function with the cross product between the two vectors? What’s the second vector? Maybe it would be easiest if you could post the code that you developed :slight_smile:

The equations solve for X and Y based on a known Z value. I’m just assuming the Z value is 1. Maybe I’m going wrong where Z should be assumed to be -1 sometimes…

I don’t think the cross product is what you want. Nor do I have the code handy. But basically, it would be between two vectors a and b, where for a given vertice v(index), a is v(index-1)-v(index) and b is v(index+1)-v(index).
In other words, it computes the two vectors from a vertice and its two neighbors. So, whereas you’ve only taken one edge into account, you need to take two.
A cross product may not be what you want, though. The only thing I used it for was to prove to myself that the getPerpendicularVec() function was at fault (and not an odd shaped mesh). You merely need to correct the computation of the perpendicular vector.
A cross product may be all you need, or perhaps you need a cross product with a type of constraint (so that it’s consistently pointing in a certain direction), or perhaps something else. I don’t know.
All I know is, you need a different formula, and I don’t have it. :slight_smile:

The equations solve for X and Y based on a known Z value. I’m just assuming the Z value is 1. Maybe I’m going wrong where Z should be assumed to be -1 sometimes…

I think this is where you’re going wrong. You’re forcing yourself into a sort of “gimble lock.” However, there’s more to it than that. Check out the modified circle (the third mesh). There’s a nasty little kink just at the base of where it starts to slope up. This isn’t a 180 twist, and so it won’t be solved by just taking an inverse.

Levi

Creating an ‘orthonormal basis’ from the vector would work, but still wouldn’t be quite ideal:


def getPerpendicularVec(V):
	if (V[0]==0.0) and (V[1]==0.0):
		if (V[2]<0.0):
			u = Mathutils.Vector([-1.0, 0.0, 0.0])
		else:
			u = Mathutils.Vector([1.0, 0.0, 0.0])
		v = Mathutils.Vector([0.0, 1.0, 0.0])
	else:
		d = V[0]*V[0] + V[1]*V[1]
		if (d!=0.0): d=1.0/(d**0.5)
		u = Mathutils.Vector([V[1]*d, -V[0]*d, 0.0])
		v = Mathutils.CrossVecs(V, u)
	return v

I’m not sure if you create those curves yourself using some bezier or spline procedure, but it probably would be possible to calculate a correct tangent vector from the math to create those curves, but somebody else would have to tell you how, I’m no math expert.
Also, not really important, but almost all those extra functions could be replaced by using the Mathutils module functions.

Damnit, eeshlo! You always come through!

…If you’re talking about the multmat function that you gave me for Fiber 2 :slight_smile: I saw the VecMultMat, but that would require I create a vector object from the vertice. I know it’s the simplest thing in the world, but I believe it’s slower.

I’ve been doing some speed tests which I’ve found interesting. In the following, using Vector.length in comparison to a traditional distance funtion, vectors are equal, if not faster in speed:

import Blender
from Blender.sys import time
from Blender.Mathutils import Vector

def fncdistance(pt1,pt2):
	return pow(pow(pt1[0]-pt2[0],2)+pow(pt1[1]-pt2[1],2)+pow(pt1[2]-pt2[2],2),.5)

def fncdistanceVec(pt1,pt2):
	return Vector([pt1[0]-pt2[0],pt1[1]-pt2[1],pt1[2]-pt2[2]]).length

pt1 = [0,2,5]
pt2 = [.25,-6,2]
x = 0
answer = 0

#Traditional
begin = time()
for x in range(100000):
	answer = fncdistance(pt1, pt2)
print 'fncdistance: ', str(time() - begin)

#Vector
begin = time()
for x in range(100000):
	answer = fncdistanceVec(pt1, pt2)
print 'vector.length: ', str(time() - begin)

However if you do

from Blender import Mathutils

instead of

from Blender.Mathutils import Vector

The vector code goes a little slower. I’ll look into going the more built-in way though.

You don’t have to create a vector from the vertex coord, it already is a vector.
You could even replace getPerpendicularVec by OrthoProjectionMatrix although it wouldn’t quite produce the same result.
The vertex creation part of the code could be something like this:


for vnum in range(1, len(me.verts)):
	vect = me.verts[vnum].co - me.verts[vnum-1].co
	vect.normalize()

	newVec = getPerpendicularVec(vect) * radius
	#newVec = OrthoProjectionMatrix('r', 3, vect)[2] * radius
	
	for x in range(points):
		angle = ((360.0/points)*x + rndAngle + twist) % 360.0

		v = VecMultMat(newVec, RotationMatrix(angle, 3, 'r', vect))
		v += VecMultMat(me.verts[vnum].co, obj.getMatrix().rotationPart())
		v += Vector(list(obj.getLocation()))
		mesh.verts.append(NMesh.Vert(v.x, v.y, v.z))

This is the probably the shortest, which doesn’t mean it is also faster of course.
The matrix multiply could also include the translation, but then you would have to resize the vectors (resize4D/resize3D).

:o

Looking at your code is an eye opener! Seeing shortcuts I didn’t know existed before. Like… How the hell am I supposed to know what Vector.normalize() does by looking at the documentation (“Doc: Normalize the vector”)? That’s why I wrote the setVecLength function! grr

I guess one question I have is:
Why does the leftmost example always look smashed? It’s like the vectors’ matrices are ‘squashed’. Try setting the segment vertex count to something like 10 or higher and you’ll see what I mean. It should be a perfectly circular cross section, but it ends up being a pancake.

Yes, sorry, I overlooked that part. The reason is that the vectors you construct from the curve are still in the local space of the curve mesh. Also, to speed up the calculations, you can remove the matrix multiples to transform the mesh to the original object coordinates.
I agree with you that the documentation could be a bit better with some examples of use maybe. However, normalize() is a commonly used name to ‘normalize’ the vector (setting the length to 1). So I guess you could say that the documentation assumes that you are at least somewhat familiar with vector math.

Anyway, here is the code segment that will produce the exact same result as the nurbs reference:


for vnum in range(1, len(me.verts)):
	vect = me.verts[vnum].co - me.verts[vnum-1].co
	vect.normalize()

	newVec = getPerpendicularVec(vect) * radius
	
	for x in range(points):
		angle = ((360.0/points)*x + rndAngle + twist) % 360.0
		v = VecMultMat(newVec, RotationMatrix(angle, 3, 'r', vect))
		v += me.verts[vnum].co
		mesh.verts.append(NMesh.Vert(v.x, v.y, v.z))

	# ...Create faces code here....

name = "whatever"
newobj = NMesh.PutRaw(mesh, name)
if newobj:
	newobj.setName(name)
else:
	newobj = Object.Get(name)
newobj.setMatrix(obj.getMatrix())

eeshlo you are the man! My original code doesn’t even compare to the masterpiece you’ve polished down! Really, you should get the status of co-creator of Fiber 3. You deserve the credit!

Sorry if I’m butting in, but I don’t see the reason for the ‘u’ under the first IF statement.

def getPerpendicularVec(V): 
   if (V[0]==0.0) and (V[1]==0.0): 
      if (V[2]<0.0): 
         u = Mathutils.Vector([-1.0, 0.0, 0.0])
      else: 
         u = Mathutils.Vector([1.0, 0.0, 0.0])
      v = Mathutils.Vector([0.0, 1.0, 0.0]) 
   else: 
      d = V[0]*V[0] + V[1]*V[1] 
      if (d!=0.0): d=1.0/(d**0.5) 
      u = Mathutils.Vector([V[1]*d, -V[0]*d, 0.0]) 
      v = Mathutils.CrossVecs(V, u) 
   return v

Is it needed? Since it is never used, the code could be:

def getPerpendicularVec(V): 
   if (V[0]==0.0) and (V[1]==0.0):
      v = Mathutils.Vector([0.0, 1.0, 0.0]) 
   else: 
      d = V[0]*V[0] + V[1]*V[1] 
      if (d!=0.0): d=1.0/(d**0.5) 
      u = Mathutils.Vector([V[1]*d, -V[0]*d, 0.0]) 
      v = Mathutils.CrossVecs(V, u) 
   return v

Also, since he’s normalizing the vector anyway, ‘d’ wouldn’t be necessary, would it?

Levi

Yes, it was just a copy&paste from another script of mine, but it can be even simplified further in this case:


def getPerpendicularVec(V):
	u = Vector([V.y, -V.x, 0.0])
	if u.length<1e-6: return Vector([0.0, 0.0, 1.0])
	u.normalize()
	return u

And the line ‘vect.normalize()’ can then also be removed.
The rotation angle should in this case be increased by 90 degrees though:


angle = ((360.0/points)*x + rndAngle + twist + 90.0) % 360.0

Ripsting: of course you don’t need to credit me for this at all, I’m only trying to help.

I’ve been working on this for a few hours today and I’ve been able to reach a few milestones. The major one is that Bézier interpolation is now working with the curve code.

http://oregonstate.edu/~dennisa/Blender/Fiber3/curve.blend

Thank you reD_Fox and eeshlo. I have no idea how you cut down your code so much. It’s very effective, and I wouldn’t be where I was at without both of you.

No need to thank me, I really didn’t do anything. Remember that cross product I told you about? Well, I even got that wrong! I looked back at my code, and although it worked (sort of), it wasn’t anything near what you needed. Eeshlo’s your man!
By the way, your work is jaw-dropping as far as I’m concerned. It does stuff that I dare not even dream of doing myself.
So, I’m saying rather, “Thank you!”

Levi

:smiley:
http://www.oregonstate.edu/~dennisa/Blender/Fiber3/curly.jpg

Baked guides (3000 points): 1.30503318159 seconds
Curled the baked guides: 0.040173541609 seconds
Generated 10000-vertex mesh: 0.374289469764 seconds

The speed is looking very good also. The guides need to only be baked once per frame of animation, no matter how many fibers will be generated; and the bézier guides are what takes the longest time. Of course the guide needs to be interpolated into 3000 points if there are 1000 segments with an order of 3 (as in this example). But who in their right mind will be using 10-sided, 1000-segment fibers :P.

For comparison: segments = 50, order= 2, points per segment = 2

Baked guides (100 points): 0.0353720933781 seconds
Curled the baked guides: 0.00201310502598 seconds
Generated 100-vertex mesh: 0.00400190526852 seconds