Transforming normal vectors

rhinocommon

(David Rutten) #1

I’ve got a custom type consisting of various Point3d and Vector3d arrays. The points signify corners of a shape and the vectors represent normal directions. I can transform my shape using a Transform matrix consisting of translation, rotation and scaling factors and all is well. However as soon as I introduce a tapering factor (transform.M32 = -0.1) my normals start going wonky. For example a 2x2x1 box being transformed results in badnormalsbad.3dm (53.7 KB), and the normals are all over the place.

Am I supposed to be able to run vectors through a taper transform and have them retain perpendicularity?

EDIT: Nope, one is not supposed to be able to taper vectors and have them remain perpendicular. This GH file shows the problem transform.gh (12.2 KB)

What is the recommended method for transforming normal vectors through shear and taper transforms?


#2

Recalculate them from the new point locations?


(David Rutten) #3

That’s potentially a very expensive solution though. I was thinking something along the lines of creating some points around the original vector using perpendiculars, transforming those and then then cross-producting them back together again. There’s probably a very clever vector-mathematical solution to this I can’t see yet.


(David Rutten) #4

This seems to work, not particularly elegant though:

  private Vector3d TransformVectorSmart(Vector3d vector, Point3d anchor, Transform transform)
  {
    Vector3d x = Vector3d.XAxis;
    x.PerpendicularTo(vector);
    Vector3d y = Vector3d.CrossProduct(vector, x);

    Point3d p0 = anchor + x;
    Point3d p1 = anchor + y;
    anchor.Transform(transform);
    p0.Transform(transform);
    p1.Transform(transform);

    x = p0 - anchor;
    y = p1 - anchor;

    return Vector3d.CrossProduct(x, y);
  }

(Mikko Oksanen) #5

@DavidRutten, you need to use OnXform::GetSurfaceNormalXform to get the normal transform that is then applied to the vectors. Technically it gives you the inverse transpose of the xform used for the points.


(David Rutten) #6

I tried using the inverse of the transpose (and the transpose of the inverse), didn’t quite work. I also tried calling the GetSurfaceNormalXform method in C++, but that one was even worse :confused:

Using the GH testfile above I can’t get the horizontal normal vectors to tilt away from the horizontal using either approach. I’m not sure where to start with debugging this.


(Mikko Oksanen) #7

@DavidRutten, seems to work over here, but probably doesn’t give the result you are after. I did this…

Transform transform = moveXY * scaleXYZ *taperZ * rotateYZ * rotateXY;
Transform nt = transform.Transpose();
nt.TryGetInverse(out nt);

and then used “nt” for transforming the normals. What you are after is face normals, and those need to be computed as a cross product of any two adjacent edges of each face after transforming them first, and then unitize the result.


(David Rutten) #8

Understood. It’s sort of what I’m doing now, invent two edges that are both perpendicular to the vector and each other, transform those and then get the cross product. I’ve yet to test whether I always get the same result if I use a Plane instead.


(Mikko Oksanen) #9

Umm, perpendicular to what vector? If you use the normals you have you’ll just end up with the same vector. Using a plane should work, so does using any two non-parallel edges of the shape. They don’t have to be perpendicular for cross product, just not parallel.

You may also want to move rotations first:
Transform transform = rotateYZ * rotateXY * moveXY * scaleXYZ * taperZ;
That way the box is tapered first, and then rotated.


(Mikko Oksanen) #10

Never mind, I missed the “transform those” step.