2018-07-14 21:55:20 +00:00
|
|
|
# Example of an operator which uses gizmos to control its properties.
|
2017-06-26 05:57:14 +00:00
|
|
|
#
|
2018-09-07 20:54:03 +00:00
|
|
|
# Usage: Run this script, then in mesh edit-mode press F3
|
2017-06-26 05:57:14 +00:00
|
|
|
# to activate the operator "Select Side of Plane"
|
2018-07-14 21:55:20 +00:00
|
|
|
# The gizmos can then be used to adjust the plane in the 3D view.
|
2017-06-26 05:57:14 +00:00
|
|
|
#
|
|
|
|
import bpy
|
|
|
|
import bmesh
|
|
|
|
|
|
|
|
from bpy.types import (
|
|
|
|
Operator,
|
2018-07-14 21:55:20 +00:00
|
|
|
GizmoGroup,
|
2017-06-26 05:57:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
from bpy.props import (
|
|
|
|
FloatVectorProperty,
|
|
|
|
)
|
|
|
|
|
2018-06-26 20:56:39 +00:00
|
|
|
|
2017-06-26 05:57:14 +00: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:
|
2018-09-07 20:51:02 +00:00
|
|
|
co = matrix @ v.co
|
2017-06-26 05:57:14 +00:00
|
|
|
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 20:18:09 +00:00
|
|
|
plane_co: FloatVectorProperty(
|
2017-06-26 05:57:14 +00:00
|
|
|
size=3,
|
|
|
|
default=(0, 0, 0),
|
|
|
|
)
|
2018-07-11 20:18:09 +00:00
|
|
|
plane_no: FloatVectorProperty(
|
2017-06-26 05:57:14 +00: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"):
|
2019-03-01 01:35:48 +00:00
|
|
|
self.plane_co = context.scene.cursor.location
|
2017-06-26 05:57:14 +00:00
|
|
|
|
|
|
|
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-12-03 23:15:02 +00:00
|
|
|
wm.gizmo_group_type_ensure(SelectSideOfPlaneGizmoGroup.bl_idname)
|
2017-06-26 05:57:14 +00: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 21:55:20 +00:00
|
|
|
# Gizmos for plane_co, plane_no
|
|
|
|
class SelectSideOfPlaneGizmoGroup(GizmoGroup):
|
2018-07-15 12:24:10 +00:00
|
|
|
bl_idname = "MESH_GGT_select_side_of_plane"
|
2018-07-14 21:55:20 +00:00
|
|
|
bl_label = "Side of Plane Gizmo"
|
2017-06-26 05:57:14 +00: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-12-03 23:15:02 +00:00
|
|
|
wm.gizmo_group_type_unlink_delayed(SelectSideOfPlaneGizmoGroup.bl_idname)
|
2017-06-26 05:57:14 +00:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def setup(self, context):
|
|
|
|
from mathutils import Matrix, Vector
|
|
|
|
|
|
|
|
# ----
|
2018-09-06 10:13:01 +00:00
|
|
|
# Move
|
2017-06-26 05:57:14 +00:00
|
|
|
|
2018-09-06 10:13:01 +00:00
|
|
|
def move_get_cb():
|
2018-07-14 21:55:20 +00:00
|
|
|
op = SelectSideOfPlaneGizmoGroup.my_target_operator(context)
|
2017-06-26 05:57:14 +00:00
|
|
|
return op.plane_co
|
|
|
|
|
2018-09-06 10:13:01 +00:00
|
|
|
def move_set_cb(value):
|
2018-07-14 21:55:20 +00:00
|
|
|
op = SelectSideOfPlaneGizmoGroup.my_target_operator(context)
|
2017-06-26 05:57:14 +00:00
|
|
|
op.plane_co = value
|
|
|
|
# XXX, this may change!
|
|
|
|
op.execute(context)
|
|
|
|
|
2018-09-06 10:13:01 +00:00
|
|
|
mpr = self.gizmos.new("GIZMO_GT_move_3d")
|
|
|
|
mpr.target_set_handler("offset", get=move_get_cb, set=move_set_cb)
|
2017-06-26 05:57:14 +00:00
|
|
|
|
|
|
|
mpr.use_draw_value = True
|
|
|
|
|
2017-07-17 05:06:18 +00: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 05:57:14 +00:00
|
|
|
mpr.scale_basis = 0.2
|
|
|
|
|
2018-09-06 10:13:01 +00:00
|
|
|
self.widget_move = mpr
|
2017-06-26 05:57:14 +00:00
|
|
|
|
|
|
|
# ----
|
|
|
|
# Dial
|
|
|
|
|
|
|
|
def direction_get_cb():
|
2018-07-14 21:55:20 +00:00
|
|
|
op = SelectSideOfPlaneGizmoGroup.my_target_operator(context)
|
2017-06-26 05:57:14 +00:00
|
|
|
|
|
|
|
no_a = self.widget_dial.matrix_basis.col[1].xyz
|
|
|
|
no_b = Vector(op.plane_no)
|
|
|
|
|
2018-09-07 20:51:02 +00:00
|
|
|
no_a = (no_a @ self.view_inv).xy.normalized()
|
|
|
|
no_b = (no_b @ self.view_inv).xy.normalized()
|
2017-06-26 05:57:14 +00:00
|
|
|
return no_a.angle_signed(no_b)
|
|
|
|
|
|
|
|
def direction_set_cb(value):
|
2018-07-14 21:55:20 +00:00
|
|
|
op = SelectSideOfPlaneGizmoGroup.my_target_operator(context)
|
2017-06-26 05:57:14 +00:00
|
|
|
matrix_rotate = Matrix.Rotation(-value, 3, self.rotate_axis)
|
2018-09-07 20:51:02 +00:00
|
|
|
no = matrix_rotate @ self.widget_dial.matrix_basis.col[1].xyz
|
2017-06-26 05:57:14 +00:00
|
|
|
op.plane_no = no
|
|
|
|
op.execute(context)
|
|
|
|
|
2018-07-15 12:24:10 +00:00
|
|
|
mpr = self.gizmos.new("GIZMO_GT_dial_3d")
|
2017-06-26 05:57:14 +00: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 05:06:18 +00:00
|
|
|
mpr.color = 0.8, 0.8, 0.8
|
|
|
|
mpr.alpha = 0.5
|
|
|
|
|
2017-12-11 05:01:07 +00:00
|
|
|
mpr.color_highlight = 1.0, 1.0, 1.0
|
2017-07-17 05:06:18 +00:00
|
|
|
mpr.alpha_highlight = 1.0
|
2017-06-26 05:57:14 +00: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()
|
|
|
|
|
2018-09-06 10:13:01 +00:00
|
|
|
# Move
|
2017-06-26 05:57:14 +00:00
|
|
|
no_z = no
|
|
|
|
no_y = no_z.orthogonal()
|
|
|
|
no_x = no_z.cross(no_y)
|
|
|
|
|
2018-09-06 10:13:01 +00:00
|
|
|
matrix = self.widget_move.matrix_basis
|
2017-06-26 05:57:14 +00:00
|
|
|
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 21:55:20 +00:00
|
|
|
SelectSideOfPlaneGizmoGroup,
|
2017-06-26 05:57:14 +00:00
|
|
|
)
|
|
|
|
|
2018-06-26 20:56:39 +00:00
|
|
|
|
2017-06-26 05:57:14 +00: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 20:56:39 +00:00
|
|
|
|
2017-06-26 05:57:14 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
register()
|