Example #1
0
 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]
Example #3
0
    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
Example #4
0
    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'}
Example #6
0
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
Example #7
0
    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()
Example #8
0
    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