[Bf-committers] Proposal to Change Mathutils Vectors

José Romero jose.cyborg at gmail.com
Wed Dec 7 16:23:29 CET 2011

On Wed, 07 Dec 2011 13:45:36 +0100
Paul Melis <paul.melis at sara.nl> wrote:

> Hi Andrew,
> On 12/07/2011 11:33 AM, Andrew Hale wrote:
> > Correct me if I'm wrong, but do you propose to have 3D vectors with
> > matrix multiplication just work (i.e. making assumptions about the w
> > coordinate and renormalising if necessary) but retain 4D vectors
> > which compute the product without modification of the w component?
> > This sounds quite logical, however, these transformations deal with
> > points so I'm not sure I understand what you mean by
> > differentiating between vectors and normals.
> Regarding the last point: suppose you have a 3D model consisting of a
> set of points with associated normals. If you want to transform the
> model using some transformation matrix M then you can simply update
> each point by matrix mulplication with M and normalizing the result.
> I.e. you would represent each 3D point coordinate as (x, y, z, 1),
> multiply with M to get (x', y', z', w') and divide by w' to get the
> final transformed coordinates. The normal vectors of the model
> however need to be transformed by multiplying with (M^-1)^T, i.e. the
> transpose of the inverse of M. So you would first compute Mn =
> transpose(inverse(M)) and then compute each transformed normal vector
> using (nx', ny', nz', 0) = Mn * (nx, ny, nz, 0).
> This is the type of situation where a good API can hide lots of
> details the user might not be interested in. The key here I think is
> not to have users use mathematical operations like multiplication,
> but to make them use methods that handle and hide the above
> complexities. For example (obviously pseudocode not based on current
> Blender Python API :)):
> # Load wavefront model
> model = load("blah.obj")
> # Create transformation
> S = Matrix.scale(2, 2, 2)
> R = Matrix.rotate_x(90)
> T = Matrix.translate(1, -4, 3)
> # Combined transformation: 1) scale, 2) rotate, 3) translate
> M = S * R * T
> # Transform model
> for v in model.vertices:
>     new_position = M.transform_point(v.position)
>     new_normal = M.transform_normal(v.normal)
>     ....
> Here, the transform_normal() method could take care of computing
> (M^-1)^T and caching it in the M instance, so it only needs to be
> computed once. The user only works with 3-tuples for points, vectors
> and normals without having to worry about normalization after
> multiplication and such. In fact, the user could be completely
> unaware that M*v multiplication is being doing. Furthermore,
> transform_point() would take care of normalization after
> multiplication, while transform_vector() wouldn't perform that step
> as it isn't necessary for vectors.
> Interestingly I now seem to be arguing that for doing general
> transformation stuff like above you never have to work with 4-tuples
> or homogenous coordinates, more or less supporting your original
> proposal :) I just looked at the RenderMan specification, as existing
> references might provide some insights. The spec defines different
> data types for points, vectors and normals, all being simple float[3]
> arrays. So the type of a value defines the interpretation of the
> value. Interestingly, there's also a "float RtHPoint[4]", which
> probably represents a value in homogenous coordinates, but it's used
> nowhere in the API :)
> I guess the bottom line is whether exposing the 4-tuple Blender vector
> internals has any use when writing Python scripts. On the one hand
> having access to the real underlying data provides great power. But in
> the current Python API there are no high-level methods available and
> users need to use low-level matrix-vector multiplications that forces
> them to having to understand what they're doing. Perhaps adding a
> higher-level API like something above is a good way forward, while
> leaving the low-level stuff in place....
> Regards,
> Paul
+1 for adding high level methods, perhaps instead of overloading the
matrix object you could have a "transformation" object that abstracts
away the linear algebra (actually, implements the concept of linear
transformation, that is easier to grasp than the nitty gritty details
of matrix representation and change of coordinates) and just does what
the user tells it to, an API idea:

Method chaining (each function returns a transformation object
modified by the method)

  T = Transformation.scale(2,2,2).rotate(x=90).translate(1,-4,3)

Callable, having different types for points, vectors and normals a
transform operation could be just:

  for v in model.vertices:
      new_position = T(v.position)
      new_normal = T(v.normal)

Having a different type for normals would also allow for every
operation on them to renormalize and prevent mistakes like multiplying
a normal by zero, but i digress. 

The callable protocol could also be used for transformations
themselves, to combine them:

  T1 = Transformation.rotate(x=54, z=45).scale(y=2)
  T2 = Transformation.translate(1,2,3)
  T3 = T1(T2) # The notation even still looks math-y
  T4 = T2(T1) # Note that the order of operations is important.

If that's not practical, you could use one method per type of operation
like Paul proposed.

It could also be useful to interact with matrices, for example, to get
the matrix representation of that linear transformation it could be
something like T.to_matrix(), maybe a convenience like
T.to_rotation_matrix() to get a 3x3 and a T.from_matrix(M) to convert
from a 3x3 or a 4x4 matrix.

Just my two cents, I'd rather see it abstracted away than promoting
cargo cult linear algebra.

> _______________________________________________
> Bf-committers mailing list
> Bf-committers at blender.org
> http://lists.blender.org/mailman/listinfo/bf-committers

More information about the Bf-committers mailing list