Fix io_anim_camera error exporting cameras with quotes in their name
[blender-addons.git] / space_view3d_spacebar_menu / snap_origin_cursor.py
blobffc163a30d2c41a347885455f7fe56a79c53dfbc
1 # SPDX-License-Identifier: GPL-2.0-or-later
2 # Contributed to by: meta-androcto, JayDez, sim88, sam, lijenstina, mkb, wisaac, CoDEmanX.
5 import bpy
6 from bpy.types import (
7 Operator,
8 Menu,
10 from bpy.props import (
11 BoolProperty,
12 StringProperty,
14 from .object_menus import *
16 # ********** Object Snap Cursor **********
17 class VIEW3D_MT_Snap_Context(Menu):
18 bl_label = "Snapping"
20 def draw(self, context):
21 layout = self.layout
22 toolsettings = context.tool_settings
23 layout.prop(toolsettings, "use_snap")
24 layout.prop(toolsettings, "snap_elements", expand=True)
27 class VIEW3D_MT_Snap_Origin(Menu):
28 bl_label = "Snap Origin"
30 def draw(self, context):
31 layout = self.layout
32 layout.operator_context = 'EXEC_AREA'
33 layout.operator("object.origin_set",
34 text="Geometry to Origin").type = 'GEOMETRY_ORIGIN'
35 layout.separator()
36 layout.operator("object.origin_set",
37 text="Origin to Geometry").type = 'ORIGIN_GEOMETRY'
38 layout.operator("object.origin_set",
39 text="Origin to 3D Cursor").type = 'ORIGIN_CURSOR'
40 layout.operator("object.origin_set",
41 text="Origin to Center of Mass").type = 'ORIGIN_CENTER_OF_MASS'
44 class VIEW3D_MT_CursorMenu(Menu):
45 bl_label = "Snap To"
47 def draw(self, context):
48 layout = self.layout
49 layout.operator_context = 'INVOKE_REGION_WIN'
50 layout.menu("VIEW3D_MT_Snap_Origin")
51 layout.menu("VIEW3D_MT_Snap_Context")
52 layout.separator()
53 layout.operator("view3d.snap_cursor_to_selected",
54 text="Cursor to Selected")
55 layout.operator("view3d.snap_cursor_to_center",
56 text="Cursor to World Origin")
57 layout.operator("view3d.snap_cursor_to_grid",
58 text="Cursor to Grid")
59 layout.operator("view3d.snap_cursor_to_active",
60 text="Cursor to Active")
61 layout.separator()
62 layout.operator("view3d.snap_selected_to_cursor",
63 text="Selection to Cursor").use_offset = False
64 layout.operator("view3d.snap_selected_to_cursor",
65 text="Selection to Cursor (Keep Offset)").use_offset = True
66 layout.operator("view3d.snap_selected_to_grid",
67 text="Selection to Grid")
68 layout.operator("view3d.snap_cursor_selected_to_center",
69 text="Selection and Cursor to World Origin")
72 class VIEW3D_MT_CursorMenuLite(Menu):
73 bl_label = "Snap to"
75 def draw(self, context):
76 layout = self.layout
77 layout.operator_context = 'INVOKE_REGION_WIN'
78 layout.menu("VIEW3D_MT_Snap_Origin")
79 layout.separator()
80 layout.operator("view3d.snap_cursor_to_selected",
81 text="Cursor to Selected")
82 layout.operator("view3d.snap_cursor_to_center",
83 text="Cursor to World Origin")
84 layout.operator("view3d.snap_cursor_to_grid",
85 text="Cursor to Grid")
86 layout.operator("view3d.snap_cursor_to_active",
87 text="Cursor to Active")
88 layout.separator()
89 layout.operator("view3d.snap_selected_to_cursor",
90 text="Selection to Cursor").use_offset = False
91 layout.operator("view3d.snap_selected_to_cursor",
92 text="Selection to Cursor (Keep Offset)").use_offset = True
93 layout.operator("view3d.snap_selected_to_grid",
94 text="Selection to Grid")
95 layout.operator("view3d.snap_cursor_selected_to_center",
96 text="Selection and Cursor to World Origin")
99 # Code thanks to Isaac Weaver (wisaac) D1963
100 class VIEW3D_OT_SnapCursSelToCenter(Operator):
101 bl_idname = "view3d.snap_cursor_selected_to_center"
102 bl_label = "Snap Cursor & Selection to World Origin"
103 bl_description = ("Snap 3D cursor and selected objects to the center \n"
104 "Works only in Object Mode")
106 @classmethod
107 def poll(cls, context):
108 return (context.area.type == "VIEW_3D" and context.mode == "OBJECT")
110 def execute(self, context):
111 context.scene.cursor.location = (0, 0, 0)
112 for obj in context.selected_objects:
113 obj.location = (0, 0, 0)
114 return {'FINISHED'}
117 # Cursor Edge Intersection Defs #
119 def abs(val):
120 if val > 0:
121 return val
122 return -val
125 def edgeIntersect(context, operator):
126 from mathutils.geometry import intersect_line_line
128 obj = context.active_object
130 if (obj.type != "MESH"):
131 operator.report({'ERROR'}, "Object must be a mesh")
132 return None
134 edges = []
135 mesh = obj.data
136 verts = mesh.vertices
138 is_editmode = (obj.mode == 'EDIT')
139 if is_editmode:
140 bpy.ops.object.mode_set(mode='OBJECT')
142 for e in mesh.edges:
143 if e.select:
144 edges.append(e)
146 if len(edges) > 2:
147 break
149 if is_editmode:
150 bpy.ops.object.mode_set(mode='EDIT')
152 if len(edges) != 2:
153 operator.report({'ERROR'},
154 "Operator requires exactly 2 edges to be selected")
155 return
157 line = intersect_line_line(verts[edges[0].vertices[0]].co,
158 verts[edges[0].vertices[1]].co,
159 verts[edges[1].vertices[0]].co,
160 verts[edges[1].vertices[1]].co)
162 if line is None:
163 operator.report({'ERROR'}, "Selected edges do not intersect")
164 return
166 point = line[0].lerp(line[1], 0.5)
167 context.scene.cursor.location = obj.matrix_world @ point
170 # Cursor Edge Intersection Operator #
171 class VIEW3D_OT_CursorToEdgeIntersection(Operator):
172 bl_idname = "view3d.snap_cursor_to_edge_intersection"
173 bl_label = "Cursor to Edge Intersection"
174 bl_description = "Finds the mid-point of the shortest distance between two edges"
176 @classmethod
177 def poll(cls, context):
178 obj = context.active_object
179 return (obj is not None and obj.type == 'MESH')
181 def execute(self, context):
182 # Prevent unsupported Execution in Local View modes
183 space = bpy.context.space_data
184 if space.local_view:
185 self.report({'INFO'}, 'Global Perspective modes only unable to continue.')
186 return {'FINISHED'}
187 edgeIntersect(context, self)
188 return {'FINISHED'}
191 # Origin To Selected Edit Mode #
192 def vfeOrigin(context):
193 try:
194 cursorPositionX = context.scene.cursor.location[0]
195 cursorPositionY = context.scene.cursor.location[1]
196 cursorPositionZ = context.scene.cursor.location[2]
197 bpy.ops.view3d.snap_cursor_to_selected()
198 bpy.ops.object.mode_set()
199 bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN')
200 bpy.ops.object.mode_set(mode='EDIT')
201 context.scene.cursor.location[0] = cursorPositionX
202 context.scene.cursor.location[1] = cursorPositionY
203 context.scene.cursor.location[2] = cursorPositionZ
204 return True
205 except:
206 return False
209 class VIEW3D_OT_SetOriginToSelected(Operator):
210 bl_idname = "object.setorigintoselected"
211 bl_label = "Set Origin to Selected"
212 bl_description = "Set Origin to Selected"
214 @classmethod
215 def poll(cls, context):
216 return (context.area.type == "VIEW_3D" and context.active_object is not None)
218 def execute(self, context):
219 check = vfeOrigin(context)
220 if not check:
221 self.report({"ERROR"}, "Set Origin to Selected could not be performed")
222 return {'CANCELLED'}
224 return {'FINISHED'}
226 # ********** Edit Mesh Cursor **********
227 class VIEW3D_MT_EditCursorMenu(Menu):
228 bl_label = "Snap To"
230 def draw(self, context):
231 layout = self.layout
232 layout.operator_context = 'INVOKE_REGION_WIN'
233 layout.operator("object.setorigintoselected",
234 text="Origin to Selected V/F/E")
235 layout.separator()
236 layout.menu("VIEW3D_MT_Snap_Origin")
237 layout.menu("VIEW3D_MT_Snap_Context")
238 layout.separator()
239 layout.operator("view3d.snap_cursor_to_selected",
240 text="Cursor to Selected")
241 layout.operator("view3d.snap_cursor_to_center",
242 text="Cursor to World Origin")
243 layout.operator("view3d.snap_cursor_to_grid",
244 text="Cursor to Grid")
245 layout.operator("view3d.snap_cursor_to_active",
246 text="Cursor to Active")
247 layout.operator("view3d.snap_cursor_to_edge_intersection",
248 text="Cursor to Edge Intersection")
249 layout.separator()
250 layout.operator("view3d.snap_selected_to_cursor",
251 text="Selection to Cursor").use_offset = False
252 layout.operator("view3d.snap_selected_to_cursor",
253 text="Selection to Cursor (Keep Offset)").use_offset = True
254 layout.operator("view3d.snap_selected_to_grid",
255 text="Selection to Grid")
258 # List The Classes #
260 classes = (
261 VIEW3D_MT_CursorMenu,
262 VIEW3D_MT_CursorMenuLite,
263 VIEW3D_MT_Snap_Context,
264 VIEW3D_MT_Snap_Origin,
265 VIEW3D_OT_SnapCursSelToCenter,
266 VIEW3D_OT_CursorToEdgeIntersection,
267 VIEW3D_OT_SetOriginToSelected,
268 VIEW3D_MT_EditCursorMenu,
272 # Register Classes & Hotkeys #
273 def register():
274 for cls in classes:
275 bpy.utils.register_class(cls)
278 # Unregister Classes & Hotkeys #
279 def unregister():
281 for cls in reversed(classes):
282 bpy.utils.unregister_class(cls)
285 if __name__ == "__main__":
286 register()