1 # SPDX-FileCopyrightText: 2019-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 from bpy
.types
import (
10 from bpy
.props
import (
14 from .object_menus
import *
16 # ********** Object Snap Cursor **********
17 class VIEW3D_MT_Snap_Context(Menu
):
20 def draw(self
, context
):
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
):
32 layout
.operator_context
= 'EXEC_AREA'
33 layout
.operator("object.origin_set",
34 text
="Geometry to Origin").type = 'GEOMETRY_ORIGIN'
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
):
47 def draw(self
, context
):
49 layout
.operator_context
= 'INVOKE_REGION_WIN'
50 layout
.menu("VIEW3D_MT_Snap_Origin")
51 layout
.menu("VIEW3D_MT_Snap_Context")
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")
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
):
75 def draw(self
, context
):
77 layout
.operator_context
= 'INVOKE_REGION_WIN'
78 layout
.menu("VIEW3D_MT_Snap_Origin")
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")
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")
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)
117 # Cursor Edge Intersection Defs #
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")
136 verts
= mesh
.vertices
138 is_editmode
= (obj
.mode
== 'EDIT')
140 bpy
.ops
.object.mode_set(mode
='OBJECT')
150 bpy
.ops
.object.mode_set(mode
='EDIT')
153 operator
.report({'ERROR'},
154 "Operator requires exactly 2 edges to be selected")
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
)
163 operator
.report({'ERROR'}, "Selected edges do not intersect")
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"
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
185 self
.report({'INFO'}, 'Global Perspective modes only unable to continue.')
187 edgeIntersect(context
, self
)
191 # Origin To Selected Edit Mode #
192 def vfeOrigin(context
):
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
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"
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
)
221 self
.report({"ERROR"}, "Set Origin to Selected could not be performed")
226 # ********** Edit Mesh Cursor **********
227 class VIEW3D_MT_EditCursorMenu(Menu
):
230 def draw(self
, context
):
232 layout
.operator_context
= 'INVOKE_REGION_WIN'
233 layout
.operator("object.setorigintoselected",
234 text
="Origin to Selected V/F/E")
236 layout
.menu("VIEW3D_MT_Snap_Origin")
237 layout
.menu("VIEW3D_MT_Snap_Context")
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")
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")
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 #
275 bpy
.utils
.register_class(cls
)
278 # Unregister Classes & Hotkeys #
281 for cls
in reversed(classes
):
282 bpy
.utils
.unregister_class(cls
)
285 if __name__
== "__main__":