Script to align objects to a face and line on that face

It is very difficult and in some cases simply impossible to precisely align objects within Blender. This is ok for organic modeling but unacceptable for mechanical modeling. This script solves that problem.

With this script you can select a face and a line on that face and it will create an empty aligned with the normal of that face and the line. This empty can be parented to the object and this way any face on the object can be precisely aligned with any face on another object.

Additionally, you can change the local transform orientation of an object in rotation as well, instead of just the build-in set origin feature. Combined with the feature above, you can precisely align the local transform of an object with one of it’s faces and a line of that face.

For example, align objects from this:

To this:

Or change the Local Transform Orientation from this:

To this:

Blender Project page:
http://projects.blender.org/tracker/index.php?func=detail&aid=31620&group_id=153&atid=467

Wiki:
http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/Precise_Align

Please note that this is my first Blender script, or first python script for that matter, so if you notice anything which can be improved, please let me know.

User Interface for Edit mode and Object mode:

Change Log:

v0.9

-Can now set the position of the Local Transform or the Object orientation separately.
-Changed layout and changed “Source Empty” to “Source Transform”.
-Fixed a bug with Align Object: Position. It did now work correctly when the active object was a child of another object (used local position instead of global).
-Fixed a bug with the Align Local Transform logic which caused incorrect rotations.
-Fixed a bug which caused matrix trashing caused by the user creating a left handed instead of a right handed coordinate system.
-Feedback given if the Empty Coordinate System is invalid.
-Changed selection system from one Face and an Edge to 3 vertices instead. This provides more flexibility for Coordinate System creation.

v1.0
-The parent is automatically removed when using Align Object Rotation as well, otherwise the rotation doesn’t work.
-Removed error saying that context.active_object.name does not exist.

I added my script to the Blender Py Scripts Upload. You can find it here:
http://projects.blender.org/tracker/index.php?func=detail&aid=31620&group_id=153&atid=467

One problem though. I want to edit my page (add wiki page, modify description, etc) but I can’t figure out how to do so. Why does such a simple thing has to be so hard to find? Any help?

hi,
you need to join wiki, then add your empty page to list here: http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts
then, look at another page yo like press edit, then copy the wiki text to your empty page. (use other page as template for yours)
from there you can easy add your own addon details.
if you drop into irc freenode #blenderpython, I’m happy to help.

Actually, I meant editing this page:
http://projects.blender.org/tracker/index.php?func=detail&aid=31620&group_id=153&atid=467

I want to add a link to the wiki I created. Wiki is here, but still work in progress:
http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/Precise_Align

Interesting project.
What I’d hope would be a workflow of this kind:

  • I want to align face/object A to a face/object B
  • activate this add-on / script
  • select the face/object to be aligned (the one which will move)
  • select the target face/object
  • done, the movement is performed and no empties are left behind.

Whould this be possible with - hopefully - minor changes to this script?
Thanks!

updated projects page with wiki link & comment.

Meta-Androcto,
Thanks for updating.

Carrozza,
That would be ideal, and I thought of this before. However, the problems is that this kind of work flow is very non-Blender. Unfortunately you can’t select lines and faces of two separate objects at the same time. It would be possible to select an edge and face of one object, go to another object, enter Edit mode again, and select an edge and face of another object. But you will have to show the user which edges and faces were selected previously. The only way to do this is to manually draw OpenGl lines and faces on top of the objects, like the Enhanced 3D Cursor script does.

Perhaps I could make such a system in the future but to be honest, I thing this is the job of the Blender team. The feature I made is very standard for a 3d modeling program and I have the reserved opinion that Blender should get their act together and incorporate a proper system by default. Best would be probably a more advanced snap-to system which enables you to snap face-to-face, in move mode ® and snap line to line in rotate mode ®, with one axis select (z for example) to set a temporary constraint. Or even better, a constraint system a-la Autodesk Inventor. But again, I think it should be up to Blender.

OK, Elecman, I get your point and agree.
Cheers!

Version 0.3 now, but I can’t upload it to the Blender Project page. Errors, and more errors, so please check the link in the comments of the Project page. Thanks.

Changelog for the new version, please?
Thanks!

I don’t use much BPY; in fact i mainly use the BGE, but here’s my input:
Could you use the greasepencil to draw two lines / store the first intersection as the object to align, then use a script to get the intersected edges relative to the camera’s orientation?

Perhaps that would be possible, but it is not the aim of this script. With this script it is possible to precisely align geometry, instead of eyeballing it. Using a grease pencil is basically eyeballing, so you might as well use the build-in alignment features such as manually rotating and moving the geometry.

Changelog added.

Weird Bug:
Everything works fine in Scripting view, but in Default view, in Edit mode, some of the panel items are missing.
Anyone has any idea why that is?

First test with a cube and a sphere, I got this when clicking “Create empty”.

Traceback (most recent call last):
File “D:\Software\Graphics\Blender\2.63\scripts\addons_contrib\space_view3d_precise_align3.py”, line 577, in execute
valid, matrix = CreateFaceEdgeRotation(context, context.active_object, False, bpy.context.scene.normal_prop, bpy.context.scene.line_prop, bpy.context.scene.tangent_prop, bpy.context.scene.swap_line_vertex_prop, bpy.context.scene.axis_normal_dropdown_prop, bpy.context.scene.axis_line_dropdown_prop, bpy.context.scene.axis_tangent_dropdown_prop)
AttributeError: ‘Scene’ object has no attribute ‘normal_prop’
location:<unknown location>:-1

Yes, that is caused by the bug I just mentioned. If you go to Scripting view, it works fine. Then you can go back to Default view and it works fine as well. Now that is weird…

Edit:
Fixed it. You can find version 0.4 in the comments of the Tracker. I still can’t upload new versions, so you have to follow the external link for the latest version.

The problem was caused by all the property decelerations being located in main instead of the class where the panel is drawn. Still weird that it doesn’t cause any error in Scripting view though.

I am experiencing a nasty crash. It is caused by the undo button. The class which holds the reference to the empty is trashed when the undo button is used. This “feature” is mentioned in the manual as well:

Many hard to fix crashes end up being because of referencing freed data, when removing data be sure not to
hold any references to it.

Modules or classes that remain active while Blender is used, should not hold references to data the user may
remove, instead, fetch data from the context each time the script is activated.

…not holding references to data when Blender is used interactively by the user is the only
way to ensure the script doesn’t become unstable.

Well, so how am I supposed to hold a reference to an empty in Edit mode then? Fetch it from the context? How is that possible, the script cannot just guess which empty to use…

Edit:
Ok, I fixed it by modifying the name of the empty based on the name of it’s object. Then I use
obj = bpy.data.objects[name]
to find the object at a later stage. It works fine that way.

You can do this already w/blender using cubes and parenting!

In some cases, yes. In other cases, it cannot be done. it depends if the face you are using is nicely lined up with the world axis or not. Either way, using this script involves much less steps then the current 20-step method of aligning objects.

Note that it is easy to align two object’s faces, but this is only half the story. What if you want to align the edges of the faces as well? That becomes problematic and you will have to revert to “eyeballing”, which is unacceptable for mechanical modeling.

Of course you can set the rotation manually (30 degrees x, 10 degrees y, etc) but that only works with simple models with faces in a standard direction.

Furthermore, if you want to align the Local Transform Orientation, with one of the faces on it’s own object, it is simply impossible. You can set the position using “Origin to 3D Cursor”, but that’s about it.

Here is another thread discussing the issue:
/uploads/default/original/3X/d/f/df1bab86d511815c563079346d4af95e8c055322.jpgd=1336619279

I would love to hear your method involving cubes and parenting though. Sounds interesting.

You can already do that.

For the rotational part, look at the function:
SetLocalTransformRotation(context)
In there you will find this line:
rotation_before = i.matrix_world.to_quaternion()
This is where the matrix of each selected object is fetched.
Somewhere below that you will find this line:
i.rotation_quaternion = context.active_object.matrix_world.to_quaternion()
That is were each selected object’s local transform matrix is set to the local transform matrix of the active object.

For the positional part, look at the function:
SetLocalOrigin(context, obj_to_be_changed, destination)
which is called from
SetLocalTransformRotation(context)
This line sets the postion:
bpy.context.scene.cursor_location = destination.location
Instead of destination.location, you can use a vector. If you have a matrix already, the position vector
is located in the third column of a matrix: mat.col[3]

Setting the local transform of all the selected objects to the transform of the active object is done via the GUI with the buttons “Align local transform:” Position and Rotation.

If you insist in supplying a bare matrix as the source, instead of an object, I suggest you make an object first (such as an empty) and give your matrix to that object. Then supply that object to the functions I mentioned and it should work just fine.

But I don’t see why you would do that. In what real life situation would you not want to use an “active object” as your matrix but some arbitrary matrix in code instead?

In case you take not an object but an edge with a point (or normal with origin) as an input, or in case you do some matrix calculations before assigning the matrix to the objects. It just gives you more flexibility… I recently worked with triangles and their circumcentre, which had to be calculated… I think it makes scripting easier if you don’t have to script the selection and deselection…