| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  | # Example of an operator which uses gizmos to control its properties. | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | # | 
					
						
							|  |  |  | # Usage: Run this script, then in mesh edit-mode press Spacebar | 
					
						
							|  |  |  | # to activate the operator "Select Side of Plane" | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  | # The gizmos can then be used to adjust the plane in the 3D view. | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | # | 
					
						
							|  |  |  | import bpy | 
					
						
							|  |  |  | import bmesh | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from bpy.types import ( | 
					
						
							|  |  |  |     Operator, | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |     GizmoGroup, | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from bpy.props import ( | 
					
						
							|  |  |  |     FloatVectorProperty, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 22:56:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | def main(context, plane_co, plane_no): | 
					
						
							|  |  |  |     obj = context.active_object | 
					
						
							|  |  |  |     matrix = obj.matrix_world.copy() | 
					
						
							|  |  |  |     me = obj.data | 
					
						
							|  |  |  |     bm = bmesh.from_edit_mesh(me) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     plane_dot = plane_no.dot(plane_co) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for v in bm.verts: | 
					
						
							|  |  |  |         co = matrix * v.co | 
					
						
							|  |  |  |         v.select = (plane_no.dot(co) > plane_dot) | 
					
						
							|  |  |  |     bm.select_flush_mode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bmesh.update_edit_mesh(me) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SelectSideOfPlane(Operator): | 
					
						
							|  |  |  |     """UV Operator description""" | 
					
						
							|  |  |  |     bl_idname = "mesh.select_side_of_plane" | 
					
						
							|  |  |  |     bl_label = "Select Side of Plane" | 
					
						
							|  |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 22:18:09 +02:00
										 |  |  |     plane_co: FloatVectorProperty( | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |         size=3, | 
					
						
							|  |  |  |         default=(0, 0, 0), | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2018-07-11 22:18:09 +02:00
										 |  |  |     plane_no: FloatVectorProperty( | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |         size=3, | 
					
						
							|  |  |  |         default=(0, 0, 1), | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         return (context.mode == 'EDIT_MESH') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def invoke(self, context, event): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not self.properties.is_property_set("plane_co"): | 
					
						
							|  |  |  |             self.plane_co = context.scene.cursor_location | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not self.properties.is_property_set("plane_no"): | 
					
						
							|  |  |  |             if context.space_data.type == 'VIEW_3D': | 
					
						
							|  |  |  |                 rv3d = context.space_data.region_3d | 
					
						
							|  |  |  |                 view_inv = rv3d.view_matrix.to_3x3() | 
					
						
							|  |  |  |                 # view y axis | 
					
						
							|  |  |  |                 self.plane_no = view_inv[1].normalized() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.execute(context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if context.space_data.type == 'VIEW_3D': | 
					
						
							|  |  |  |             wm = context.window_manager | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |             wm.gizmo_group_type_add(SelectSideOfPlaneGizmoGroup.bl_idname) | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         from mathutils import Vector | 
					
						
							|  |  |  |         main(context, Vector(self.plane_co), Vector(self.plane_no)) | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  | # Gizmos for plane_co, plane_no | 
					
						
							|  |  |  | class SelectSideOfPlaneGizmoGroup(GizmoGroup): | 
					
						
							| 
									
										
										
										
											2018-07-15 14:24:10 +02:00
										 |  |  |     bl_idname = "MESH_GGT_select_side_of_plane" | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |     bl_label = "Side of Plane Gizmo" | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |     bl_space_type = 'VIEW_3D' | 
					
						
							|  |  |  |     bl_region_type = 'WINDOW' | 
					
						
							|  |  |  |     bl_options = {'3D'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Helper functions | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def my_target_operator(context): | 
					
						
							|  |  |  |         wm = context.window_manager | 
					
						
							|  |  |  |         op = wm.operators[-1] if wm.operators else None | 
					
						
							|  |  |  |         if isinstance(op, SelectSideOfPlane): | 
					
						
							|  |  |  |             return op | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def my_view_orientation(context): | 
					
						
							|  |  |  |         rv3d = context.space_data.region_3d | 
					
						
							|  |  |  |         view_inv = rv3d.view_matrix.to_3x3() | 
					
						
							|  |  |  |         return view_inv.normalized() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         op = cls.my_target_operator(context) | 
					
						
							|  |  |  |         if op is None: | 
					
						
							|  |  |  |             wm = context.window_manager | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |             wm.gizmo_group_type_remove(SelectSideOfPlaneGizmoGroup.bl_idname) | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |             return False | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setup(self, context): | 
					
						
							|  |  |  |         from mathutils import Matrix, Vector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # ---- | 
					
						
							|  |  |  |         # Grab | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def grab_get_cb(): | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |             op = SelectSideOfPlaneGizmoGroup.my_target_operator(context) | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |             return op.plane_co | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def grab_set_cb(value): | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |             op = SelectSideOfPlaneGizmoGroup.my_target_operator(context) | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |             op.plane_co = value | 
					
						
							|  |  |  |             # XXX, this may change! | 
					
						
							|  |  |  |             op.execute(context) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-15 14:24:10 +02:00
										 |  |  |         mpr = self.gizmos.new("GIZMO_GT_grab_3d") | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |         mpr.target_set_handler("offset", get=grab_get_cb, set=grab_set_cb) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mpr.use_draw_value = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-17 15:06:18 +10:00
										 |  |  |         mpr.color = 0.8, 0.8, 0.8 | 
					
						
							|  |  |  |         mpr.alpha = 0.5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mpr.color_highlight = 1.0, 1.0, 1.0 | 
					
						
							|  |  |  |         mpr.alpha_highlight = 1.0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |         mpr.scale_basis = 0.2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.widget_grab = mpr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # ---- | 
					
						
							|  |  |  |         # Dial | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def direction_get_cb(): | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |             op = SelectSideOfPlaneGizmoGroup.my_target_operator(context) | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |             no_a = self.widget_dial.matrix_basis.col[1].xyz | 
					
						
							|  |  |  |             no_b = Vector(op.plane_no) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             no_a = (no_a * self.view_inv).xy.normalized() | 
					
						
							|  |  |  |             no_b = (no_b * self.view_inv).xy.normalized() | 
					
						
							|  |  |  |             return no_a.angle_signed(no_b) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def direction_set_cb(value): | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |             op = SelectSideOfPlaneGizmoGroup.my_target_operator(context) | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |             matrix_rotate = Matrix.Rotation(-value, 3, self.rotate_axis) | 
					
						
							|  |  |  |             no = matrix_rotate * self.widget_dial.matrix_basis.col[1].xyz | 
					
						
							|  |  |  |             op.plane_no = no | 
					
						
							|  |  |  |             op.execute(context) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-15 14:24:10 +02:00
										 |  |  |         mpr = self.gizmos.new("GIZMO_GT_dial_3d") | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  |         mpr.target_set_handler("offset", get=direction_get_cb, set=direction_set_cb) | 
					
						
							|  |  |  |         mpr.draw_options = {'ANGLE_START_Y'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mpr.use_draw_value = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-17 15:06:18 +10:00
										 |  |  |         mpr.color = 0.8, 0.8, 0.8 | 
					
						
							|  |  |  |         mpr.alpha = 0.5 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-11 16:01:07 +11:00
										 |  |  |         mpr.color_highlight = 1.0, 1.0, 1.0 | 
					
						
							| 
									
										
										
										
											2017-07-17 15:06:18 +10:00
										 |  |  |         mpr.alpha_highlight = 1.0 | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.widget_dial = mpr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def draw_prepare(self, context): | 
					
						
							|  |  |  |         from mathutils import Vector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         view_inv = self.my_view_orientation(context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.view_inv = view_inv | 
					
						
							|  |  |  |         self.rotate_axis = view_inv[2].xyz | 
					
						
							|  |  |  |         self.rotate_up = view_inv[1].xyz | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         op = self.my_target_operator(context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         co = Vector(op.plane_co) | 
					
						
							|  |  |  |         no = Vector(op.plane_no).normalized() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Grab | 
					
						
							|  |  |  |         no_z = no | 
					
						
							|  |  |  |         no_y = no_z.orthogonal() | 
					
						
							|  |  |  |         no_x = no_z.cross(no_y) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         matrix = self.widget_grab.matrix_basis | 
					
						
							|  |  |  |         matrix.identity() | 
					
						
							|  |  |  |         matrix.col[0].xyz = no_x | 
					
						
							|  |  |  |         matrix.col[1].xyz = no_y | 
					
						
							|  |  |  |         matrix.col[2].xyz = no_z | 
					
						
							|  |  |  |         matrix.col[3].xyz = co | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Dial | 
					
						
							|  |  |  |         no_z = self.rotate_axis | 
					
						
							|  |  |  |         no_y = (no - (no.project(no_z))).normalized() | 
					
						
							|  |  |  |         no_x = self.rotate_axis.cross(no_y) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         matrix = self.widget_dial.matrix_basis | 
					
						
							|  |  |  |         matrix.identity() | 
					
						
							|  |  |  |         matrix.col[0].xyz = no_x | 
					
						
							|  |  |  |         matrix.col[1].xyz = no_y | 
					
						
							|  |  |  |         matrix.col[2].xyz = no_z | 
					
						
							|  |  |  |         matrix.col[3].xyz = co | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | classes = ( | 
					
						
							|  |  |  |     SelectSideOfPlane, | 
					
						
							| 
									
										
										
										
											2018-07-14 23:55:20 +02:00
										 |  |  |     SelectSideOfPlaneGizmoGroup, | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 22:56:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | def register(): | 
					
						
							|  |  |  |     for cls in classes: | 
					
						
							|  |  |  |         bpy.utils.register_class(cls) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def unregister(): | 
					
						
							|  |  |  |     for cls in reversed(classes): | 
					
						
							|  |  |  |         bpy.utils.unregister_class(cls) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 22:56:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 15:57:14 +10:00
										 |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     register() |