Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mesh_tools/mesh_edges_length.py')
-rw-r--r--mesh_tools/mesh_edges_length.py341
1 files changed, 341 insertions, 0 deletions
diff --git a/mesh_tools/mesh_edges_length.py b/mesh_tools/mesh_edges_length.py
new file mode 100644
index 00000000..f4f1d067
--- /dev/null
+++ b/mesh_tools/mesh_edges_length.py
@@ -0,0 +1,341 @@
+# gpl author: Giuseppe De Marco [BlenderLab] inspired by NirenYang
+
+bl_info = {
+ "name": "Set edges length",
+ "description": "Edges length",
+ "author": "Giuseppe De Marco [BlenderLab] inspired by NirenYang",
+ "version": (0, 1, 0),
+ "blender": (2, 71, 0),
+ "location": "Toolbar > Tools > Mesh Tools: set Length(Shit+Alt+E)",
+ "warning": "",
+ "wiki_url": "",
+ "category": "Mesh",
+ }
+
+import bpy
+import bmesh
+from mathutils import Vector
+from bpy.types import Operator
+from bpy.props import (
+ FloatProperty,
+ EnumProperty,
+ )
+
+# GLOBALS
+edge_length_debug = False
+_error_message = "Please select at least one edge to fill select history"
+_error_message_2 = "Edges with shared vertices are not allowed. Please, use scale instead"
+
+# Note : Refactor - removed all the operators apart from LengthSet
+# and merged the other ones as options of length (lijenstina)
+
+
+def get_edge_vector(edge):
+ verts = (edge.verts[0].co, edge.verts[1].co)
+ vector = verts[1] - verts[0]
+
+ return vector
+
+
+def get_selected(bmesh_obj, geometry_type):
+ # geometry type should be edges, verts or faces
+ selected = []
+
+ for i in getattr(bmesh_obj, geometry_type):
+ if i.select:
+ selected.append(i)
+ return tuple(selected)
+
+
+def get_center_vector(verts):
+ # verts = [Vector((x,y,z)), Vector((x,y,z))]
+
+ center_vector = Vector((((verts[1][0] + verts[0][0]) / 2.),
+ ((verts[1][1] + verts[0][1]) / 2.),
+ ((verts[1][2] + verts[0][2]) / 2.)))
+ return center_vector
+
+
+class LengthSet(Operator):
+ bl_idname = "object.mesh_edge_length_set"
+ bl_label = "Set edge length"
+ bl_description = ("Change one selected edge length by a specified target,\n"
+ "existing length and different modes\n"
+ "Note: works only with Edges that not share a vertex")
+ bl_options = {'REGISTER', 'UNDO'}
+
+ old_length: FloatProperty(
+ name="Original length",
+ options={'HIDDEN'},
+ )
+ set_length_type: EnumProperty(
+ items=[
+ ('manual', "Manual",
+ "Input manually the desired Target Length"),
+ ('existing', "Existing Length",
+ "Use existing geometry Edges' characteristics"),
+ ],
+ name="Set Type of Input",
+ )
+ target_length: FloatProperty(
+ name="Target Length",
+ description="Input a value for an Edges Length target",
+ default=1.00,
+ unit='LENGTH',
+ precision=5
+ )
+ existing_length: EnumProperty(
+ items=[
+ ('min', "Shortest",
+ "Set all to shortest Edge of selection"),
+ ('max', "Longest",
+ "Set all to the longest Edge of selection"),
+ ('average', "Average",
+ "Set all to the average Edge length of selection"),
+ ('active', "Active",
+ "Set all to the active Edge's one\n"
+ "Needs a selection to be done in Edge Select mode"),
+ ],
+ name="Existing length"
+ )
+ mode: EnumProperty(
+ items=[
+ ('fixed', "Fixed", "Fixed"),
+ ('increment', "Increment", "Increment"),
+ ('decrement', "Decrement", "Decrement"),
+ ],
+ name="Mode"
+ )
+ behaviour: EnumProperty(
+ items=[
+ ('proportional', "Proportional",
+ "Move vertex locations proportionally to the center of the Edge"),
+ ('clockwise', "Clockwise",
+ "Compute the Edges' vertex locations in a clockwise fashion"),
+ ('unclockwise', "Counterclockwise",
+ "Compute the Edges' vertex locations in a counterclockwise fashion"),
+ ],
+ name="Resize behavior"
+ )
+
+ originary_edge_length_dict = {}
+ edge_lengths = []
+ selected_edges = ()
+
+ @classmethod
+ def poll(cls, context):
+ return (context.edit_object and context.object.type == 'MESH')
+
+ def check(self, context):
+ return True
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.label(text="Original Active length is: {:.3f}".format(self.old_length))
+
+ layout.label(text="Input Mode:")
+ layout.prop(self, "set_length_type", expand=True)
+ if self.set_length_type == 'manual':
+ layout.prop(self, "target_length")
+ else:
+ layout.prop(self, "existing_length", text="")
+
+ layout.label(text="Mode:")
+ layout.prop(self, "mode", text="")
+
+ layout.label(text="Resize Behavior:")
+ layout.prop(self, "behaviour", text="")
+
+ def get_existing_edge_length(self, bm):
+ if self.existing_length != "active":
+ if self.existing_length == "min":
+ return min(self.edge_lengths)
+ if self.existing_length == "max":
+ return max(self.edge_lengths)
+ elif self.existing_length == "average":
+ return sum(self.edge_lengths) / float(len(self.selected_edges))
+ else:
+ bm.edges.ensure_lookup_table()
+ active_edge_length = None
+
+ for elem in reversed(bm.select_history):
+ if isinstance(elem, bmesh.types.BMEdge):
+ active_edge_length = elem.calc_length()
+ break
+ return active_edge_length
+
+ return 0.0
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+
+ obj = context.edit_object
+ bm = bmesh.from_edit_mesh(obj.data)
+
+ bpy.ops.mesh.select_mode(type="EDGE")
+ self.selected_edges = get_selected(bm, 'edges')
+
+ if self.selected_edges:
+ vertex_set = []
+
+ for edge in self.selected_edges:
+ vector = get_edge_vector(edge)
+
+ if edge.verts[0].index not in vertex_set:
+ vertex_set.append(edge.verts[0].index)
+ else:
+ self.report({'ERROR_INVALID_INPUT'}, _error_message_2)
+ return {'CANCELLED'}
+
+ if edge.verts[1].index not in vertex_set:
+ vertex_set.append(edge.verts[1].index)
+ else:
+ self.report({'ERROR_INVALID_INPUT'}, _error_message_2)
+ return {'CANCELLED'}
+
+ # warning, it's a constant !
+ verts_index = ''.join((str(edge.verts[0].index), str(edge.verts[1].index)))
+ self.originary_edge_length_dict[verts_index] = vector
+ self.edge_lengths.append(vector.length)
+ self.old_length = vector.length
+ else:
+ self.report({'ERROR'}, _error_message)
+ return {'CANCELLED'}
+
+ if edge_length_debug:
+ self.report({'INFO'}, str(self.originary_edge_length_dict))
+
+ if bpy.context.scene.unit_settings.system == 'IMPERIAL':
+ # imperial to metric conversion
+ vector.length = (0.9144 * vector.length) / 3
+
+ self.target_length = vector.length
+
+ return wm.invoke_props_dialog(self)
+
+ def execute(self, context):
+
+ bpy.ops.mesh.select_mode(type="EDGE")
+ self.context = context
+
+ obj = context.edit_object
+ bm = bmesh.from_edit_mesh(obj.data)
+
+ self.selected_edges = get_selected(bm, 'edges')
+
+ if not self.selected_edges:
+ self.report({'ERROR'}, _error_message)
+ return {'CANCELLED'}
+
+ for edge in self.selected_edges:
+ vector = get_edge_vector(edge)
+ # what we should see in original length dialog field
+ self.old_length = vector.length
+
+ if self.set_length_type == 'manual':
+ vector.length = abs(self.target_length)
+ else:
+ get_lengths = self.get_existing_edge_length(bm)
+ # check for edit mode
+ if not get_lengths:
+ self.report({'WARNING'},
+ "Operation Cancelled. "
+ "Active Edge could not be determined (needs selection in Edit Mode)")
+ return {'CANCELLED'}
+
+ vector.length = get_lengths
+
+ if vector.length == 0.0:
+ self.report({'ERROR'}, "Operation cancelled. Target length is set to zero")
+ return {'CANCELLED'}
+
+ center_vector = get_center_vector((edge.verts[0].co, edge.verts[1].co))
+
+ verts_index = ''.join((str(edge.verts[0].index), str(edge.verts[1].index)))
+
+ if edge_length_debug:
+ self.report({'INFO'},
+ ' - '.join(('vector ' + str(vector),
+ 'originary_vector ' +
+ str(self.originary_edge_length_dict[verts_index])
+ )))
+ verts = (edge.verts[0].co, edge.verts[1].co)
+
+ if edge_length_debug:
+ self.report({'INFO'},
+ '\n edge.verts[0].co ' + str(verts[0]) +
+ '\n edge.verts[1].co ' + str(verts[1]) +
+ '\n vector.length' + str(vector.length))
+
+ # the clockwise direction have v1 -> v0, unclockwise v0 -> v1
+ if self.target_length >= 0:
+ if self.behaviour == 'proportional':
+ edge.verts[1].co = center_vector + vector / 2
+ edge.verts[0].co = center_vector - vector / 2
+
+ if self.mode == 'decrement':
+ edge.verts[0].co = (center_vector + vector / 2) - \
+ (self.originary_edge_length_dict[verts_index] / 2)
+ edge.verts[1].co = (center_vector - vector / 2) + \
+ (self.originary_edge_length_dict[verts_index] / 2)
+
+ elif self.mode == 'increment':
+ edge.verts[1].co = (center_vector + vector / 2) + \
+ self.originary_edge_length_dict[verts_index] / 2
+ edge.verts[0].co = (center_vector - vector / 2) - \
+ self.originary_edge_length_dict[verts_index] / 2
+
+ elif self.behaviour == 'unclockwise':
+ if self.mode == 'increment':
+ edge.verts[1].co = \
+ verts[0] + (self.originary_edge_length_dict[verts_index] + vector)
+ elif self.mode == 'decrement':
+ edge.verts[0].co = \
+ verts[1] - (self.originary_edge_length_dict[verts_index] - vector)
+ else:
+ edge.verts[1].co = verts[0] + vector
+
+ else:
+ # clockwise
+ if self.mode == 'increment':
+ edge.verts[0].co = \
+ verts[1] - (self.originary_edge_length_dict[verts_index] + vector)
+ elif self.mode == 'decrement':
+ edge.verts[1].co = \
+ verts[0] + (self.originary_edge_length_dict[verts_index] - vector)
+ else:
+ edge.verts[0].co = verts[1] - vector
+
+ if bpy.context.scene.unit_settings.system == 'IMPERIAL':
+ """
+ # yards to metric conversion
+ vector.length = ( 3. * vector.length ) / 0.9144
+ # metric to yards conversion
+ vector.length = ( 0.9144 * vector.length ) / 3.
+ """
+ for mvert in edge.verts:
+ # school time: 0.9144 : 3 = X : mvert
+ mvert.co = (0.9144 * mvert.co) / 3
+
+ if edge_length_debug:
+ self.report({'INFO'},
+ '\n edge.verts[0].co' + str(verts[0]) +
+ '\n edge.verts[1].co' + str(verts[1]) +
+ '\n vector' + str(vector) + '\n v1 > v0:' + str((verts[1] >= verts[0]))
+ )
+ bmesh.update_edit_mesh(obj.data, True)
+
+ return {'FINISHED'}
+
+
+def register():
+ bpy.utils.register_class(LengthSet)
+
+
+def unregister():
+ bpy.utils.unregister_class(LengthSet)
+
+
+if __name__ == "__main__":
+ register()