Very cool and well thought…
There had been efforts to build features like this into Blender (for example, the Blueprint tool that can create and remove geometry), but I’m not sure what the status is on them. I think development on these features may have been put on hold to focus more on bug fixing. Those updates were the original reason I held off on porting more of NP Station to 2.8 as they were going to include changes to the parts of Blender’s add-on interface that NP Station used.
Thanks, been a while since that post. Hope the numbers are more in our favor these days
Anybody happen to know a user-friendly means of rotating an object to align to another rotated object’s face? Alike the NP station’s “select 1st, 2nd vertex, rotate and snap to 3rd vertex”.
Been beating my head against a wall with this
copy object to selected has no Interface.
Select Vertex, Edge or Face on your Source Object, then selected Vertex, Edge or Face on your target Object.
Search function (F3) – copy object to selected.
The Script makes a Copy from your Source Object and place it on the Target Object.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
bl_info = {
"name": "Copy Object To Selected",
"author": "Rolando Pera",
"description": "",
"blender": (2, 80, 0),
"location": "",
"warning": "",
"category": "Generic",
}
import bpy
from mathutils import Vector, Matrix, Quaternion
from itertools import chain
def copy_object_at_face(target_obj, target_obj_mesh, source_obj, source_obj_mesh, to_mat_world, this_collection):
# copy the object at the center of each face with the same orientation as its normal
for poly in target_obj_mesh.polygons:
if poly.select is True:
copy = source_obj.copy()
this_collection.objects.link(copy)
copy_mat_world = copy.matrix_world
bpy.ops.mesh.primitive_circle_add(vertices=3, radius=0.2, fill_type="NGON")
parent_obj = bpy.context.active_object
for face in source_obj_mesh.polygons:
if face.select is True:
copy_loc = Matrix.Translation(face.center)
copy_quat = face.normal.to_track_quat("-Z", "Y")
copy_mat = (copy_mat_world @ copy_loc @ copy_quat.to_matrix().to_4x4())
parent_obj.matrix_world = copy_mat
copy.select_set(True)
bpy.ops.object.parent_set(type="OBJECT")
loc = Matrix.Translation(poly.center)
quat = poly.normal.to_track_quat("Z", "Y")
mat = to_mat_world @ loc @ quat.to_matrix().to_4x4()
parent_obj.matrix_world = mat
bpy.ops.object.parent_clear(type="CLEAR_KEEP_TRANSFORM")
copy.select_set(False)
bpy.ops.object.delete()
return
def copy_object_at_vertex(target_obj, target_obj_mesh, source_obj, source_obj_mesh, to_mat_world, this_collection):
# copy the object at selected vertices with the same orientation as their normals
for vertex in target_obj_mesh.vertices:
if vertex.select is True:
copy = source_obj.copy()
this_collection.objects.link(copy)
copy_mat_world = copy.matrix_world
bpy.ops.mesh.primitive_circle_add(vertices=3, radius=0.2,
fill_type="NGON")
parent_obj = bpy.context.active_object
for face in source_obj_mesh.polygons:
if face.select is True:
copy_loc = Matrix.Translation(face.center)
copy_quat = face.normal.to_track_quat("-Z", "Y")
copy_mat = (copy_mat_world @ copy_loc @ copy_quat.to_matrix().to_4x4())
parent_obj.matrix_world = copy_mat
copy.select_set(True)
bpy.ops.object.parent_set(type="OBJECT")
loc = Matrix.Translation(vertex.co)
quat = vertex.normal.to_track_quat("Z", "Y")
mat = to_mat_world @ loc @ quat.to_matrix().to_4x4()
parent_obj.matrix_world = mat
bpy.ops.object.parent_clear(type="CLEAR_KEEP_TRANSFORM")
copy.select_set(False)
bpy.ops.object.delete()
return
def copy_item_at_face(target_obj, target_obj_mesh, source_obj, to_mat_world, this_collection):
# copy the object at the center of each face with the same orientation as its normal
for poly in target_obj_mesh.polygons:
if poly.select is True:
copy = source_obj.copy()
this_collection.objects.link(copy)
loc = Matrix.Translation(poly.center)
quat = poly.normal.to_track_quat("-Z", "Y")
mat = to_mat_world @ loc @ quat.to_matrix().to_4x4()
copy.matrix_world = mat
return
def copy_item_at_vertex(target_obj, target_obj_mesh, source_obj, to_mat_world, this_collection):
# copy the object at selected vertices with the same orientation as their normals
for vertex in target_obj_mesh.vertices:
if vertex.select is True:
copy = source_obj.copy()
this_collection.objects.link(copy)
loc = Matrix.Translation(vertex.co)
quat = vertex.normal.to_track_quat("-Z", "Y")
mat = to_mat_world @ loc @ quat.to_matrix().to_4x4()
copy.matrix_world = mat
return
def create_collection(collection_name):
if collection_name in bpy.data.collections:
return bpy.data.collections[collection_name]
else:
new_collection = bpy.data.collections.new(collection_name)
scene.collection.children.link(new_collection)
return new_collection
class OBJECT_OT_copy_to_selected(bpy.types.Operator):
"""Copy Object to Selected"""
bl_idname = "object.copy_to_selected"
bl_label = "Copy Object to Selected"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
# some initial values
global scene
scene = bpy.context.scene
source_obj_sel = "mesh_obj"
# get the number of objects selected
sel_objs = bpy.context.selected_objects
num_sel_objs = len(sel_objs)
if num_sel_objs != 2:
self.report({"ERROR"}, "Two objects must be selected (source and target).")
return {"FINISHED"}
# get the currently selected mesh selection mode
select_mode = context.tool_settings.mesh_select_mode
sel_mode_face = select_mode[2]
# store the location of 3d cursor
cursor_location = scene.cursor.location.copy()
# get current active object, apply scale, set mode to object and deselect it
target_obj = bpy.context.active_object
if target_obj.type != "MESH":
self.report({"ERROR"}, "The target object is not a Mesh.")
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.make_single_user(type="SELECTED_OBJECTS", obdata=True)
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
target_obj_mesh = target_obj.data
to_mat_world = target_obj.matrix_world
bpy.context.active_object.select_set(False)
sel_obj = bpy.context.selected_objects
# if it is a mesh or a curve, center its origin on selected edit mode element
for obj in sel_obj:
context.view_layer.objects.active = obj
source_obj_mesh = obj.data
if obj.type == "MESH":
sel_poly_source = [f for f in source_obj_mesh.polygons if f.select]
# verify that only one face is selected on source object
if len(sel_poly_source) != 1:
bpy.ops.object.mode_set(mode="EDIT")
self.report({"ERROR"}, "Select one face (none or more than one selected).")
return {"FINISHED"}
obj_mat_world = obj.matrix_world
for face in source_obj_mesh.polygons:
if face.select:
center_loc = obj_mat_world @ Vector(face.center)
scene.cursor.location = center_loc
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
elif obj.type == "CURVE":
sel_points = [
p
for p in chain(*[s.bezier_points for s in source_obj_mesh.splines])
if p.select_control_point
]
bpy.ops.object.mode_set(mode="EDIT")
if len(sel_points) != 1:
self.report({"ERROR"}, "One control point must be selected (none or more selected).")
return {"FINISHED"}
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
source_obj_sel = "none_mesh_obj"
else:
source_obj_sel = "none_mesh_obj"
# set 3d cursor at origin of world
scene.cursor.location = (0.0, 0.0, 0.0)
# get curently active object to copy
source_obj = bpy.context.object
# get name of object to create collection name if it doesn't exists
collection_name = source_obj.name + "_Copies"
""" self.report({"ERROR"}, collection_name)
return {"FINISHED"} """
this_collection = create_collection(collection_name)
# get what elements are selected on target object and the right function to call
sel_poly_target = [p for p in target_obj_mesh.polygons if p.select]
sel_vert_target = [v for v in target_obj_mesh.vertices if v.select]
if sel_poly_target != [] and source_obj_sel == "mesh_obj":
if sel_mode_face is True:
copy_object_at_face(
target_obj,
target_obj_mesh,
source_obj,
source_obj_mesh,
to_mat_world,
this_collection,
)
else:
copy_object_at_vertex(
target_obj,
target_obj_mesh,
source_obj,
source_obj_mesh,
to_mat_world,
this_collection,
)
elif sel_poly_target != [] and source_obj_sel == "none_mesh_obj":
if sel_mode_face is True:
copy_item_at_face(target_obj, target_obj_mesh, source_obj, to_mat_world, this_collection)
else:
copy_item_at_vertex(target_obj, target_obj_mesh, source_obj, to_mat_world, this_collection)
elif sel_vert_target != [] and source_obj_sel == "mesh_obj":
copy_object_at_vertex(target_obj, target_obj_mesh, source_obj, source_obj_mesh, to_mat_world, this_collection)
elif sel_vert_target != [] and source_obj_sel == "none_mesh_obj":
copy_item_at_vertex(target_obj, target_obj_mesh, source_obj, to_mat_world, this_collection)
else:
bpy.context.view_layer.objects.active = target_obj
# target_obj.select = True
target_obj.select_set(True)
bpy.ops.object.mode_set(mode="EDIT")
self.report({"ERROR"}, "At least one face or vertex should be selected on the target.")
return {"FINISHED"}
# set 3d cursor back to original location
scene.cursor.location = cursor_location
# set source object to active
context.view_layer.objects.active = source_obj
# set source object into edit mode and selection to faces. Select all the faces
if source_obj_sel == "mesh_obj":
bpy.ops.object.mode_set(mode="EDIT")
bpy.context.tool_settings.mesh_select_mode = (False, False, True)
bpy.ops.mesh.select_all(action="SELECT")
# change transform to normal and pivot to active element
context.scene.transform_orientation_slots[0].type = "NORMAL"
bpy.data.scenes[0].tool_settings.transform_pivot_point = "ACTIVE_ELEMENT"
elif source_obj_sel == "none_mesh_obj":
# self.report({"ERROR"}, "None mesh object")
# change transform to global and pivot to individual origins
context.scene.transform_orientation_slots[0].type = "GLOBAL"
bpy.data.scenes[0].tool_settings.transform_pivot_point = "INDIVIDUAL_ORIGINS"
# end of def execute()
return {"FINISHED"}
classes = (OBJECT_OT_copy_to_selected,)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
Thank you so much nBurn sir.
the WIP is working in 2.81 what is just amazing.
What a great addon.
This np_station_29.09.19.zip doesn’t seem to be working with Blender 2.82a. Is there a version of np_station that does work with Blender 2.82a?
Maybe, I don’t know.
I am not a maintainer)
We are actually waiting for default base snap abilities in 2.8x
Awsome. And thats just what we need: noun-verb action.
And pivot setting within, not as second action to any transformation.
Already invented by Sketchup-Google and many others…
Noun-verb was invented way before Google existed)
In software like AutoCAD
True but thats not the point.
The point is pivot operation /and UCS also/.
In SKP by Google all is about EASE for quick operation and creation.
This software was built around that paradigm.
Judging by the number of various addons on Origin&Pivot manipulation
sth really unhealthy with it. And this ilness is stubborn and protruding one…;
Recently ,introduced with a bang, dissolving, destructive Extrude, well known as Push/Pull )) - also by SKP…So hard to make it earlier?! I can rememeber discussion on this forum in sth about 2009
Sorry guys, the addon works only with Blender 2.7x.
I’d buy that tool for 2.8+ and I think many architecture related artists and (ex-)sketchup users would. Any chance You’d consider updating it?
I want to be the first one to buy this addon
Thanks guys. I wish it was that easy.
NP_Station is huge and really badly written by a frustrated architect, basically as a proof of concept. If i were to update it i would have to write it from zero and have resources to maintain and upgrade it afterwards. That would require a really good python coder with full time devotion. I am just an architect and it takes one and a half day per day already.
Furthermore i made a decision early on that, as Blender is, NP_Station would remain free forever. The hope was that the Blender community and developers would pick up the importance of having such tools and take it from there. The devs are payed for this and are programmers by vocation. The problem is - the tools never gained the needed momentum and public support and i never gained superhuman powers…
But you might coordinate us for lobbying for that ??
Well, we can’t say i didn’t try before
Perhaps some sort of organizing the CAD (precision modelling) user base could help enhancing the visibility…
Let’s face it, there was only about 320 downloads of NP_Station in the 2.7x era (out of a quarter of a million of registered users of this site). And the addon is free. It seems there is simply not enough users interested in this stuff.
Well, hard to argue the facts indeed…
Do you-however-consider upgrade to 2.9x?
OR what wed just could do to make it implemented?