def interpolate_arc(self, pL, pC, pR, r, n, reverse=False): dCL = (pL - pC).normalized() dCR = (pR - pC).normalized() a2 = dCL.angle(dCR, 0.0) * 0.5 dCM = (dCL + dCR).normalized() t = (0.0 if a2 < 1e-6 else (r / math.sin(a2))) dCM_ = dCM * t c = pC + dCM_ # center of the circle cp = dCM_.project(dCR) # projection on CR dR = cp - dCM_ dM = -dCM * r aM = dM.angle_signed(Vector((1, 0)), 0.0) aR = dR.angle_signed(Vector((1, 0)), 0.0) da = aR - aM if (da > math.pi): da -= math.pi * 2 if (da < -math.pi): da += math.pi * 2 if n > 0: for i in range(n + 1): t = float(i) / float(n) if reverse: t = 1.0 - t a = aM + da * t dr = Vector((r * math.cos(a), r * math.sin(a))) res = c + dr yield res else: cR = Vector((r * math.cos(aR), r * math.sin(aR))) cM = cR.project(dCM) if reverse: yield c + cR yield c + cM else: yield c + cM yield c + cR
def _calc_landmark_angle_from_viewer_rotation(rot): from mathutils import Vector # We want an angle around Z based on the current viewer rotation. Idea # is to create a vector from the viewer rotation, project that onto a # Z-Up plane and use the resulting vector to get an angle around Z. view_rot_vec = Vector((0, 0, 1)) view_rot_vec.rotate(rot) angle_vec = view_rot_vec - view_rot_vec.project(Vector((0, 0, 1))) # We could probably use a 3D version of Vector.angle_signed() here, but # that's not available. So manually calculate it via a quaternion delta. forward_vec = Vector((0, -1, 0)) diff = angle_vec.rotation_difference(forward_vec) return diff.angle * -diff.axis[2]
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() # Move no_z = no no_y = no_z.orthogonal() no_x = no_z.cross(no_y) matrix = self.gizmo_move.matrix_basis matrix.identity() matrix.col[0].xyz = no_x matrix.col[1].xyz = no_y matrix.col[2].xyz = no_z # The location callback handles the location. # `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.gizmo_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
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() # Move no_z = no no_y = no_z.orthogonal() no_x = no_z.cross(no_y) matrix = self.widget_move.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
def modal(self, context, event): myobj = context.selected_objects[self.objIndex] annotation = myobj.AnnotationGenerator[0].annotations[self.idx] # Set Tweak Flags if event.ctrl: tweak_snap = True else: tweak_snap = False if event.shift: tweak_precise = True else: tweak_precise = False if event.type == 'MOUSEMOVE': sensitivity = 0.01 vecDelta = Vector( ((event.mouse_x - self.init_mouse_x) * sensitivity, (event.mouse_y - self.init_mouse_y) * sensitivity, 0)) viewRot = context.area.spaces[0].region_3d.view_rotation vecDelta.rotate(viewRot) mat = myobj.matrix_world rot = mat.to_quaternion() i = Vector((-1, 0, 0)) j = Vector((0, -1, 0)) k = Vector((0, 0, -1)) axis = 0 axisInd = 0 if self.constrainAxis[0]: init = self.init_x axis = i axisInd = 0 axisText = 'X: ' if self.constrainAxis[1]: init = self.init_y axis = j axisInd = 1 axisText = 'Y: ' if self.constrainAxis[2]: init = self.init_z axis = k axisInd = 2 axisText = 'Z: ' axis.rotate(rot) delta = vecDelta.project(axis) delta = delta.magnitude if axis.dot(vecDelta) > 0: delta = -delta if tweak_snap: delta = round(delta) if tweak_precise: delta /= 10.0 annotation.annotationOffset[axisInd] = init + delta context.area.header_text_set("Move " + axisText + "%.4f" % delta) elif event.type == 'LEFTMOUSE': #Setting hide_viewport is a stupid hack to force Gizmos to update after operator completes context.object.hide_viewport = False context.area.header_text_set(None) return {'FINISHED'} elif event.type in {'RIGHTMOUSE', 'ESC'}: #Setting hide_viewport is a stupid hack to force Gizmos to update after operator completes context.object.hide_viewport = False context.area.header_text_set(None) annotation.annotationOffset[0] = self.init_x annotation.annotationOffset[1] = self.init_y annotation.annotationOffset[2] = self.init_z return {'CANCELLED'} return {'RUNNING_MODAL'}
class Brushes(): def __init__(self): # The brushes are evaluated in world space, so as to avoid distortion # caused by a nonuniform object space. self.derived_brushes = list() self.primary_brush = Brush() self.symmetry_axes = list() self.symmetry_center = Vector() def derive_radial(self, count): derived_brushes = self.derived_brushes primary_brush = self.primary_brush primary_brush_center = primary_brush.center primary_brush_normal = primary_brush.normal primary_brush_radius = primary_brush.radius # Derive radially symmetrical brushes around each axis of symmetry. for axis in self.symmetry_axes: # Create a translation matrix to account for an axis of symmetry # that is offset from the world origin. symmetry_axis_offset = Matrix.Translation(self.symmetry_center) for i in range(1, count): # Create a rotation matrix to rotate a point around the axis of # symmetry that is centered at the world origin. rotation_matrix =\ Matrix.Rotation(2 * pi * i / count, 4, axis) # Create a transformation matrix to rotate a point around the # axis of symmetry that may be offset from the world origin. transformation_matrix = symmetry_axis_offset * ( rotation_matrix * symmetry_axis_offset.inverted() ) # Derive the radial brush, and append it to the list of # derived brushes. derived_brush = Brush() derived_brush.center =\ transformation_matrix * primary_brush_center derived_brush.normal = (( transformation_matrix * primary_brush_normal - ( transformation_matrix * Vector((0, 0, 0)) ) ).normalized() ) derived_brush.radius = primary_brush_radius derived_brush.transformation_matrix = transformation_matrix derived_brushes.append(derived_brush) def derive_mirrored(self): derived_brushes = self.derived_brushes primary_brush = self.primary_brush # Derive symmetrical brushes across each plane of symmetry. This # process generates b * 2 ** p - b derived brushes, where "b" is the # number of brushes (primary and derived) prior to calling this method, # and "p" is the number of symmetry planes. for axis in self.symmetry_axes: # Create a scaling matrix to mirror a point across the plane of # symmetry centered at the world origin. symmetry_mirror = Matrix.Scale(-1, 4, axis) # Create a translation matrix to account for a plane of symmetry # that is offset from the world origin. symmetry_plane_offset = Matrix.Translation( 2 * self.symmetry_center.project(axis) ) # Create a transformation matrix to mirror a point across the # plane of symmetry that may be offset from the world origin. transformation_matrix = symmetry_plane_offset * symmetry_mirror # Create the mirror image of each brush, and append it to the list # of derived brushes. for brush in [primary_brush] + derived_brushes: derived_brush = Brush() derived_brush.center = transformation_matrix * brush.center derived_brush.normal = (( transformation_matrix * brush.normal - ( transformation_matrix * Vector((0, 0, 0)) ) ).normalized() ) derived_brush.radius = brush.radius derived_brush.transformation_matrix = transformation_matrix * ( brush.transformation_matrix ) derived_brushes.append(derived_brush) def determine_influence(self, octree, falloff_curve, ignore_backfacing=False, mesh_object=None): coordinate_map = octree.coordinate_map map_manager = MapManager() primary_brush = self.primary_brush vertex_filter = VertexFilter() vertex_filter.mesh_object = mesh_object # Determine the primary brush's influence. center = primary_brush.center radius = primary_brush.radius vertex_filter.indices = octree.get_indices_in_box(center, radius) vertex_filter.coordinate_map = coordinate_map distance_map = vertex_filter.discard_outside_of_sphere(center, radius) primary_brush.indices = vertex_filter.indices # Only proceed if at least one vertex is within the primary brush's # influence. if primary_brush.indices: # Create the falloff map for the vertex indices that are within the # primary brush's influence. map_manager.map_ = distance_map map_manager.clip_domain(primary_brush.indices, 'RETAIN') primary_brush.falloff_map =\ falloff_curve.get_falloff_map_from_distance_map( distance_map, radius ) # Calculate the primary brush's normal. primary_brush_falloff_map = primary_brush.falloff_map primary_brush_normal = primary_brush.normal normal = Vector((0, 0, 0)) normal_sampling_radius = 0.333 * radius model_matrix = bpy.context.active_object.matrix_world vertices = bpy.context.active_object.data.vertices for vertex_index, distance in distance_map.items(): # Only vertices within the normal sampling radius contribute to # the primary brush's normal. if distance <= normal_sampling_radius: # Disregard vertices that face away from the primary # brush's initial normal. vertex_normal = model_matrix * ( vertices[vertex_index].normal ).normalized() if vertex_normal.dot(primary_brush_normal) > 0: # Each vertex normal contributes in proportion to its # falloff value. normal += vertex_normal * ( primary_brush_falloff_map[vertex_index] ) normal.normalize() if normal.length_squared > 0: primary_brush.normal = normal.normalized() # Discard vertices facing away from the primary brush, if # necessary. if ignore_backfacing: vertex_filter.indices = primary_brush.indices vertex_filter.discard_backfacing(primary_brush.normal, 'WORLD') primary_brush.indices = vertex_filter.indices # Determine each derived brush's influence. self.update_derived() for brush in self.derived_brushes: # Determine which vertex indices are within the brush's influence. center = brush.center radius = brush.radius vertex_filter.indices = octree.get_indices_in_box(center, radius) vertex_filter.coordinate_map = octree.coordinate_map distance_map =\ vertex_filter.discard_outside_of_sphere(center, radius) if ignore_backfacing: vertex_filter.discard_backfacing(brush.normal, 'WORLD') brush.indices = vertex_filter.indices # Only proceed if at least one vertex is within the brush's # influence. if primary_brush.indices: # Create the falloff map for the vertex indices that are within # the brush's influence. map_manager.map_ = distance_map map_manager.clip_domain(brush.indices, 'RETAIN') brush.falloff_map =\ falloff_curve.get_falloff_map_from_distance_map( distance_map, radius ) def generate_color_maps(self, color_ramp): primary_brush = self.primary_brush derived_brushes = self.derived_brushes # Clear any existing data from the color maps. for brush in [primary_brush] + derived_brushes: brush.color_map = dict() # With multiple brushes, brush volumes can overlap, and one or more # indices may have multiple falloff values associated with it. if derived_brushes: # Create a combined falloff map from the brushes, ensuring that # each index maps to its most intense falloff value. combined_falloff_map = dict() for brush in [primary_brush] + derived_brushes: falloff_map = brush.falloff_map for index in falloff_map: if index not in combined_falloff_map or\ combined_falloff_map[index] < falloff_map[index]: combined_falloff_map[index] = falloff_map[index] # Create a combined color map from the combined falloff map. combined_color_map = color_ramp.get_color_map_from_falloff_map( combined_falloff_map ) # Create each brush's color map from the combined color map. for brush in [primary_brush] + derived_brushes: brush.color_map = { index: combined_color_map[index] for index in brush.indices } # Brush volume overlap is not a concern if only the primary brush # exists. else: # Create the primary brush's color map from its falloff map. primary_brush.color_map =\ color_ramp.get_color_map_from_falloff_map( primary_brush.falloff_map ) def ray_cast_primary_brush_onto_mesh(self, region_x, region_y, mesh_object, ignore_backfacing=False): context = bpy.context primary_brush = self.primary_brush # Set the primary brush's parameters by casting a ray from the region # space coordinates onto the mesh object. ray_caster = RayCaster() ray_caster.coordinate_system = 'WORLD' ray_caster.mesh_object = mesh_object ray_caster.set_ray_from_region(region_x, region_y) location, normal, face_index = ray_caster.ray_cast() # Indicate that the primary brush is not on the mesh if no intersection # occurred. if face_index == -1: primary_brush.is_on_mesh = False # Otherwise, if an intersection occurred, determine if it is valid. else: # Ignore ray cast hits on backfacing polygons, if specified. ray_direction =\ (ray_caster.ray_target - ray_caster.ray_origin).normalized() if ignore_backfacing and ray_direction.dot(normal) > 0: primary_brush.is_on_mesh = False # For a valid ray cast hit, indicate that the primary brush is on # the mesh at the point of intersection and oriented to the mesh's # surface normal at this location. else: primary_brush.is_on_mesh = True primary_brush.center = location primary_brush.normal = normal def resize_primary_brush(self, radius): context = bpy.context primary_brush = self.primary_brush region_height = context.region.height region_width = context.region.width # Determine the world space radius of the primary brush necessary to # project a circle onto the view plane with the specified region space # radius. # Determine the z-depth of the primary brush's center in normalized # device coordinates. projection_matrix = context.region_data.perspective_matrix co = primary_brush.center.copy() co.resize(4) co.w = 1 co.xyzw = projection_matrix * co w = co.w co.xyz /= w NDC_z_depth = co.z # Determine the region space coordinates of the primary brush's center. region_x = (co.x + 1) * region_width / 2 region_y = (co.y + 1) * region_height / 2 # Determine the NDC coordinates of a point on the edge of the # circle that should result from projecting the brush onto the view # plane. co = Vector((region_x, region_y)) + Vector((radius, 0)) co.x = co.x * 2 / region_width - 1 co.y = co.y * 2 / region_height - 1 co.resize(3) co.z = NDC_z_depth # Calculate the world space radius of the primary brush. co.resize(4) co.w = 1 co.xyzw = projection_matrix.inverted() * co w = co.w co.resize(3) co.xyz /= w primary_brush.radius = (co - primary_brush.center).length def reset(self): self.__init__() def set_symmetry_from_object(self, mesh_object, object_axes=set()): # Set the center of symmetry to the mesh object's world space center. self.symmetry_center = mesh_object.location # Set the brushes' world space axes of symmetry to the specified axes # of the mesh object. model_matrix = mesh_object.matrix_world symmetry_axes = self.symmetry_axes if 'X' in object_axes: symmetry_axes.append(( model_matrix * Vector((1, 0, 0)) - ( model_matrix * Vector((-1, 0, 0)) ) ).normalized() ) if 'Y' in object_axes: symmetry_axes.append(( model_matrix * Vector((0, 1, 0)) - ( model_matrix * Vector((0, -1, 0)) ) ).normalized() ) if 'Z' in object_axes: symmetry_axes.append(( model_matrix * Vector((0, 0, 1)) - ( model_matrix * Vector((0, 0, -1)) ) ).normalized() ) def update_derived(self): # Update the parameters of each derived brush according to the current # state of the primary brush. primary_brush = self.primary_brush primary_brush_center = primary_brush.center primary_brush_normal = primary_brush.normal primary_brush_radius = primary_brush.radius for brush in self.derived_brushes: transformation_matrix = brush.transformation_matrix brush.center = transformation_matrix * primary_brush_center brush.normal = (( transformation_matrix * primary_brush_normal - ( transformation_matrix * Vector((0, 0, 0)) ) ).normalized() ) brush.radius = primary_brush_radius
def draw_prepare(self, context): selected = context.selected_objects if len(selected) == 1: self.multiselection = False else: self.multiselection = True ob = context.active_object orig_loc, orig_rot, orig_scale = ob.matrix_basis.decompose() # view_loc, view_rot, view_scale = self.my_view_orientation(context).decompose() z_rot_mat = orig_rot.to_matrix().to_4x4() z2_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(-180), 4, 'X') x_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(90), 4, 'Y') x2_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(-90), 4, 'Y') y_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(90), 4, 'X') y2_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(-90), 4, 'X') orig_loc_mat = Matrix.Translation(orig_loc) orig_scale_mat = Matrix.Scale(orig_scale[0], 4, (1, 0, 0)) @ Matrix.Scale( orig_scale[1], 4, (0, 1, 0)) @ Matrix.Scale( orig_scale[2], 4, (0, 0, 1)) z_matrix_world = orig_loc_mat @ z_rot_mat @ orig_scale_mat z2_matrix_world = orig_loc_mat @ z2_rot_mat @ orig_scale_mat x_matrix_world = orig_loc_mat @ x_rot_mat @ orig_scale_mat x2_matrix_world = orig_loc_mat @ x2_rot_mat @ orig_scale_mat y_matrix_world = orig_loc_mat @ y_rot_mat @ orig_scale_mat y2_matrix_world = orig_loc_mat @ y2_rot_mat @ orig_scale_mat location = ob.location region = context.region rv3d = context.region_data location_2d = bpy_extras.view3d_utils.location_3d_to_region_2d( region, rv3d, location) circle1_offset = Vector((location_2d.x - 140, location_2d.y - 160)) circle2_offset = Vector((location_2d.x - 180, location_2d.y - 160)) circle3_offset = Vector((location_2d.x - 220, location_2d.y - 160)) circle1_loc = bpy_extras.view3d_utils.region_2d_to_location_3d( region, rv3d, circle1_offset, location) circle2_loc = bpy_extras.view3d_utils.region_2d_to_location_3d( region, rv3d, circle2_offset, location) circle3_loc = bpy_extras.view3d_utils.region_2d_to_location_3d( region, rv3d, circle3_offset, location) circle1_matrix = Matrix.Translation( circle1_loc) @ z_rot_mat @ orig_scale_mat circle2_matrix = Matrix.Translation( circle2_loc) @ z_rot_mat @ orig_scale_mat circle3_matrix = Matrix.Translation( circle3_loc) @ z_rot_mat @ orig_scale_mat mpr_z = self.mpr_z mpr_z2 = self.mpr_z2 mpr_x = self.mpr_x mpr_x2 = self.mpr_x2 mpr_y = self.mpr_y mpr_y2 = self.mpr_y2 circle1 = self.circle1 circle2 = self.circle2 circle3 = self.circle3 view_inv = self.my_view_orientation(context) rv3d = context.space_data.region_3d view_inv = rv3d.view_matrix.to_3x3() # view y axis plane_no = view_inv[1].normalized() op = self.my_target_operator(context) co = circle1_loc no = Vector(plane_no).normalized() circle1.matrix_basis = circle1_matrix.normalized() self.view_inv = view_inv self.rotate_axis = view_inv[2].xyz self.rotate_up = view_inv[1].xyz no_z = self.rotate_axis no_y = (no - (no.project(no_z))).normalized() no_x = self.rotate_axis.cross(no_y) matrix = circle1.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 co = circle2_loc circle2.matrix_basis = circle2_matrix.normalized() self.view_inv = view_inv self.rotate_axis = view_inv[2].xyz self.rotate_up = view_inv[1].xyz no_z = self.rotate_axis no_y = (no - (no.project(no_z))).normalized() no_x = self.rotate_axis.cross(no_y) matrix = circle2.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 circle3.matrix_basis = circle3_matrix.normalized() self.view_inv = view_inv self.rotate_axis = view_inv[2].xyz self.rotate_up = view_inv[1].xyz co = circle3_loc no_z = self.rotate_axis no_y = (no - (no.project(no_z))).normalized() no_x = self.rotate_axis.cross(no_y) matrix = circle3.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 if self.multiselection is True: if get_preferences().Hops_mirror_modes_multi == "SYMMETRY": circle1.color = 0.545, 0.863, 0 else: circle1.color = 0.4, 0.4, 0.4 else: if get_preferences().Hops_mirror_modes == "SYMMETRY": circle1.color = 0.157, 0.565, 1 else: circle1.color = 0.4, 0.4, 0.4 if self.multiselection is True: if get_preferences().Hops_mirror_modes_multi == "VIA_ACTIVE": circle2.target_set_operator("hops.mirror_execute_option_4") circle2.color = 0.545, 0.863, 0 else: circle2.color = 0.4, 0.4, 0.4 else: if get_preferences().Hops_mirror_modes == "BISECT": circle2.target_set_operator("hops.mirror_execute_option_2") circle2.color = 0.157, 0.565, 1 else: circle2.color = 0.4, 0.4, 0.4 if self.multiselection is True: circle3.hide = True else: circle3.hide = False if get_preferences().Hops_mirror_modes == "MODIFIER": circle3.color = 0.157, 0.565, 1 else: circle3.color = 0.4, 0.4, 0.4 mpr_z.matrix_basis = z_matrix_world.normalized() mpr_z2.matrix_basis = z2_matrix_world.normalized() mpr_x.matrix_basis = x_matrix_world.normalized() mpr_x2.matrix_basis = x2_matrix_world.normalized() mpr_y.matrix_basis = y_matrix_world.normalized() mpr_y2.matrix_basis = y2_matrix_world.normalized()
def draw_prepare(self, context): ob = context.object array = get_modifier_with_type(ob, "ARRAY") orig_loc, orig_rot, orig_scale = ob.matrix_local.decompose() z_rot_mat = orig_rot.to_matrix().to_4x4() x_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(90), 4, 'Y') y_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation( radians(-90), 4, 'X') orig_scale_mat = Matrix.Scale(orig_scale[0], 4, (1, 0, 0)) @ Matrix.Scale( orig_scale[1], 4, (0, 1, 0)) @ Matrix.Scale( orig_scale[2], 4, (0, 0, 1)) inv = ob.matrix_world.copy() inv.invert() location = ob.location region = context.region rv3d = context.region_data self.mpr_x.hide = True self.mpr_y.hide = True self.mpr_z.hide = True if array is not None: self.mpr_x.hide = False self.mpr_y.hide = False self.mpr_z.hide = False orig_loc_mat = Matrix.Translation(orig_loc) if ob.modifiers[ "Array"].use_relative_offset is True and ob.modifiers[ "Array"].use_constant_offset is False: dimensions_x = ob.dimensions[0] / ( abs(ob.modifiers["Array"].relative_offset_displace[0]) * (array.count - 1) + 1) dimensions_y = ob.dimensions[1] / ( abs(ob.modifiers["Array"].relative_offset_displace[1]) * (array.count - 1) + 1) dimensions_z = ob.dimensions[2] / ( abs(ob.modifiers["Array"].relative_offset_displace[2]) * (array.count - 1) + 1) offset_x = dimensions_x * ob.modifiers[ "Array"].relative_offset_displace[0] * ob.scale[0] offset_y = dimensions_y * ob.modifiers[ "Array"].relative_offset_displace[1] * ob.scale[1] offset_z = dimensions_z * ob.modifiers[ "Array"].relative_offset_displace[2] * ob.scale[2] elif ob.modifiers[ "Array"].use_relative_offset is False and ob.modifiers[ "Array"].use_constant_offset is True: offset_x = ob.modifiers["Array"].constant_offset_displace[ 0] * ob.scale[0] offset_y = ob.modifiers["Array"].constant_offset_displace[ 1] * ob.scale[1] offset_z = ob.modifiers["Array"].constant_offset_displace[ 2] * ob.scale[2] else: offset_x = 0 offset_y = 0 offset_z = 0 location = ob.location + (Vector( (offset_x, offset_y, offset_z)) @ inv) orig_loc_mat_offset_x = Matrix.Translation(orig_loc + (Vector( (0, offset_y, offset_z)) @ inv)) orig_loc_mat_offset_y = Matrix.Translation(orig_loc + (Vector( (offset_x, 0, offset_z)) @ inv)) orig_loc_mat_offset_z = Matrix.Translation(orig_loc + (Vector( (offset_x, offset_y, 0)) @ inv)) x_matrix_world = orig_loc_mat_offset_x @ x_rot_mat @ orig_scale_mat y_matrix_world = orig_loc_mat_offset_y @ y_rot_mat @ orig_scale_mat z_matrix_world = orig_loc_mat_offset_z @ z_rot_mat @ orig_scale_mat mpr_z = self.mpr_z mpr_x = self.mpr_x mpr_y = self.mpr_y def move_get_cb_x(): return ob.hops.array_x def move_get_cb_y(): return ob.hops.array_y def move_get_cb_z(): return ob.hops.array_z def move_set_cb_x(value): if ob.modifiers[ "Array"].use_relative_offset is True and ob.modifiers[ "Array"].use_constant_offset is False: ob.hops.array_x = value ob.modifiers["Array"].relative_offset_displace[ 0] = ob.hops.array_x / dimensions_x elif ob.modifiers[ "Array"].use_relative_offset is False and ob.modifiers[ "Array"].use_constant_offset is True: ob.hops.array_x = value ob.modifiers["Array"].constant_offset_displace[ 0] = ob.hops.array_x def move_set_cb_y(value): if ob.modifiers[ "Array"].use_relative_offset is True and ob.modifiers[ "Array"].use_constant_offset is False: ob.hops.array_y = value ob.modifiers["Array"].relative_offset_displace[ 1] = ob.hops.array_y / dimensions_y elif ob.modifiers[ "Array"].use_relative_offset is False and ob.modifiers[ "Array"].use_constant_offset is True: ob.hops.array_y = value ob.modifiers["Array"].constant_offset_displace[ 1] = ob.hops.array_y def move_set_cb_z(value): if ob.modifiers[ "Array"].use_relative_offset is True and ob.modifiers[ "Array"].use_constant_offset is False: ob.hops.array_z = value ob.modifiers["Array"].relative_offset_displace[ 2] = ob.hops.array_z / dimensions_z elif ob.modifiers[ "Array"].use_relative_offset is False and ob.modifiers[ "Array"].use_constant_offset is True: ob.hops.array_z = value ob.modifiers["Array"].constant_offset_displace[ 2] = ob.hops.array_z mpr_x.target_set_handler("offset", get=move_get_cb_x, set=move_set_cb_x) mpr_y.target_set_handler("offset", get=move_get_cb_y, set=move_set_cb_y) mpr_z.target_set_handler("offset", get=move_get_cb_z, set=move_set_cb_z) mpr_z.matrix_basis = z_matrix_world.normalized() mpr_x.matrix_basis = x_matrix_world.normalized() mpr_y.matrix_basis = y_matrix_world.normalized() location_2d = bpy_extras.view3d_utils.location_3d_to_region_2d( region, rv3d, location) circle1_offset = Vector((location_2d.x - 120, location_2d.y - 140)) circle2_offset = Vector((location_2d.x - 160, location_2d.y - 140)) circle3_offset = Vector((location_2d.x - 200, location_2d.y - 140)) circle1_loc = bpy_extras.view3d_utils.region_2d_to_location_3d( region, rv3d, circle1_offset, location) circle2_loc = bpy_extras.view3d_utils.region_2d_to_location_3d( region, rv3d, circle2_offset, location) circle3_loc = bpy_extras.view3d_utils.region_2d_to_location_3d( region, rv3d, circle3_offset, location) circle1_matrix = Matrix.Translation( circle1_loc) @ z_rot_mat @ orig_scale_mat circle2_matrix = Matrix.Translation( circle2_loc) @ z_rot_mat @ orig_scale_mat circle3_matrix = Matrix.Translation( circle3_loc) @ z_rot_mat @ orig_scale_mat circle1 = self.circle1 circle2 = self.circle2 circle3 = self.circle3 view_inv = self.my_view_orientation(context) rv3d = context.space_data.region_3d view_inv = rv3d.view_matrix.to_3x3() # view y axis plane_no = view_inv[1].normalized() op = self.my_target_operator(context) co = circle1_loc no = Vector(plane_no).normalized() circle1.matrix_basis = circle1_matrix.normalized() self.view_inv = view_inv self.rotate_axis = view_inv[2].xyz self.rotate_up = view_inv[1].xyz no_z = self.rotate_axis no_y = (no - (no.project(no_z))).normalized() no_x = self.rotate_axis.cross(no_y) matrix = circle1.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 co = circle2_loc circle2.matrix_basis = circle2_matrix.normalized() self.view_inv = view_inv self.rotate_axis = view_inv[2].xyz self.rotate_up = view_inv[1].xyz no_z = self.rotate_axis no_y = (no - (no.project(no_z))).normalized() no_x = self.rotate_axis.cross(no_y) matrix = circle2.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 circle3.matrix_basis = circle3_matrix.normalized() self.view_inv = view_inv self.rotate_axis = view_inv[2].xyz self.rotate_up = view_inv[1].xyz co = circle3_loc no_z = self.rotate_axis no_y = (no - (no.project(no_z))).normalized() no_x = self.rotate_axis.cross(no_y) matrix = circle3.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