def getmat(bone, active, context, ignoreparent):
    '''Helper function for visual transform copy,
       gets the active transform in bone space
    '''
    data_bone = context.active_object.data.bones[bone.name]
    #all matrices are in armature space unless commented otherwise
    otherloc = active.matrix  # final 4x4 mat of target, location.
    bonemat_local = Matrix(data_bone.matrix_local)  # self rest matrix
    if data_bone.parent:
        parentposemat = Matrix(
           context.active_object.pose.bones[data_bone.parent.name].matrix)
        parentbonemat = Matrix(data_bone.parent.matrix_local)
    else:
        parentposemat = bonemat_local.copy()
        parentbonemat = bonemat_local.copy()

        # FIXME! why copy from the parent if setting identity ?, Campbell
        parentposemat.identity()
        parentbonemat.identity()

    if parentbonemat == parentposemat or ignoreparent:
        newmat = bonemat_local.inverted() * otherloc
    else:
        bonemat = parentbonemat.inverted() * bonemat_local

        newmat = bonemat.inverted() * parentposemat.inverted() * otherloc
    return newmat
Esempio n. 2
0
    def write_matrix4x4(self, mat: Matrix) -> None:
        new_mat = mat.copy()
        convert_ls3d_matrix4x4(new_mat)

        packed = struct.pack("<16f", *new_mat[0], *new_mat[1], *new_mat[2],
                             *new_mat[3])
        self.stream.write(packed)
def getmat(bone, active, context, ignoreparent):
    '''Helper function for visual transform copy,
       gets the active transform in bone space
    '''
    data_bone = context.active_object.data.bones[bone.name]
    #all matrices are in armature space unless commented otherwise
    otherloc = active.matrix  # final 4x4 mat of target, location.
    bonemat_local = Matrix(data_bone.matrix_local)  # self rest matrix
    if data_bone.parent:
        parentposemat = Matrix(
            context.active_object.pose.bones[data_bone.parent.name].matrix)
        parentbonemat = Matrix(data_bone.parent.matrix_local)
    else:
        parentposemat = bonemat_local.copy()
        parentbonemat = bonemat_local.copy()

        # FIXME! why copy from the parent if setting identity ?, Campbell
        parentposemat.identity()
        parentbonemat.identity()

    if parentbonemat == parentposemat or ignoreparent:
        newmat = bonemat_local.inverted() * otherloc
    else:
        bonemat = parentbonemat.inverted() * bonemat_local

        newmat = bonemat.inverted() * parentposemat.inverted() * otherloc
    return newmat
Esempio n. 4
0
    def execute(self, ctx):
        index = get_index(ctx)
        if index != None:
            if self.redo:
                if VP.view[index].index < len(VP.view[index].direction):
                    VP.view[index].index += 1
            else:
                if VP.view[index].index > 0:
                    VP.view[index].index -= 1

            if 0 <= VP.view[index].index < len(VP.view[index].direction):
                dindix = VP.view[index].index
                v = Matrix(VP.view[index].direction[dindix])
                ctx.area.spaces.active.region_3d.view_matrix = v.copy()
                VP.view[index].last = v.copy()
                VP.record = False
        return {"FINISHED"}
Esempio n. 5
0
def orientBone(bone, m, arma, gm_tonext, toscale):

    armaBone = arma.bones[bone.name]

    if bone.name in gm_tonext:

        (raw_tonext, raw_left) = gm_tonext[bone.name]

        if bone.name == 'Neck1' or bone.name == 'Neck':
            resize = 1
        elif armaBone.length > 0.1:
            resize = (raw_tonext.length * 10) / armaBone.length
        elif bone.name == 'Hips':
            lowerback_trans = arma.bones['LowerBack'].head
            resize = raw_tonext.length / (lowerback_trans.length / 10)
        elif bone.name == 'Spine1':
            resize = raw_tonext.length / (arma.bones['Neck'].head.length / 10)
        elif bone.name == "LeftHand":
            resize = raw_tonext.length / (
                arma.bones['LeftFingerBase'].head.length / 10)
        elif bone.name == "RightHand":
            resize = raw_tonext.length / (
                arma.bones['RightFingerBase'].head.length / 10)
        else:
            resize = 1

        # todo: document the silly reason for the above trickery

        localscale = toscale * resize

        tonext = m * raw_tonext
        up = tonext.normalized()

        leftP = raw_left.normalized()

        left = m * leftP
        fwd = left.cross(up)

        M_up3 = Matrix(((left.x, up.x, fwd.x), (left.y, up.y, fwd.y),
                        (left.z, up.z, fwd.z)))

        M_down3 = M_up3.copy()
        M_down3.invert()

        assignBone(bone, M_up3, armaBone, toscale * resize)

        m = M_down3 * m

    else:
        assignBone(bone, Matrix.Identity(3), armaBone, 1)

    for c in bone.children:
        orientBone(c, m, arma, gm_tonext, 1 / resize)
    def getWorld(self):
        t = Vector(self.translation).to_4d()
        mr = Matrix()
        for row in range(3):
            mr[row][0:3] = self.rotation[row]

        mr.transpose()  # = Inverse rotation

        p = -(mr * t)  # Camera position in world coordinates
        p[3] = 1.0

        m = mr.copy()
        m.col[3] = p  # Set translation to camera position
        return m
Esempio n. 7
0
    def getWorld(self):
        t = Vector(self.translation).to_4d()
        mr = Matrix()
        for row in range(3):
            mr[row][0:3] = self.rotation[row]

        mr.transpose() # = Inverse rotation
        
        p = -(mr * t) # Camera position in world coordinates
        p[3] = 1.0

        m = mr.copy()
        m.col[3] = p # Set translation to camera position
        return m
def get_world_matrix_from_translation_vec(translation_vec, rotation):
    t = Vector(translation_vec).to_4d()
    camera_rotation = Matrix()
    for row in range(3):
        camera_rotation[row][0:3] = rotation[row]

    camera_rotation.transpose()  # = Inverse rotation

    camera_center = -(camera_rotation * t)  # Camera position in world coordinates
    camera_center[3] = 1.0

    camera_rotation = camera_rotation.copy()
    camera_rotation.col[3] = camera_center  # Set translation to camera position
    return camera_rotation
    def test_matrix_inverse_safe(self):
        mat = Matrix(
            ((1, 4, 0, -1), (2, -1, 0, -2), (0, 3, 0, 3), (-2, 9, 0, 0)))

        # Warning, if we change epsilon in py api we have to update this!!!
        epsilon = 1e-8
        inv_mat_safe = mat.copy()
        inv_mat_safe[0][0] += epsilon
        inv_mat_safe[1][1] += epsilon
        inv_mat_safe[2][2] += epsilon
        inv_mat_safe[3][3] += epsilon
        inv_mat_safe.invert()
        '''
        inv_mat_safe = Matrix(((1.0, -0.5, 0.0, -0.5),
                               (0.222222, -0.111111, -0.0, 0.0),
                               (-333333344.0, 316666656.0, 100000000.0,  150000000.0),
                               (0.888888, -0.9444444, 0.0, -0.5)))
        '''

        self.assertEqual(mat.inverted_safe(), inv_mat_safe)
Esempio n. 10
0
    def test_matrix_inverse_safe(self):
        mat = Matrix(((1, 4, 0, -1),
                      (2, -1, 0, -2),
                      (0, 3, 0, 3),
                      (-2, 9, 0, 0)))

        # Warning, if we change epsilon in py api we have to update this!!!
        epsilon = 1e-8
        inv_mat_safe = mat.copy()
        inv_mat_safe[0][0] += epsilon
        inv_mat_safe[1][1] += epsilon
        inv_mat_safe[2][2] += epsilon
        inv_mat_safe[3][3] += epsilon
        inv_mat_safe.invert()
        '''
        inv_mat_safe = Matrix(((1.0, -0.5, 0.0, -0.5),
                               (0.222222, -0.111111, -0.0, 0.0),
                               (-333333344.0, 316666656.0, 100000000.0,  150000000.0),
                               (0.888888, -0.9444444, 0.0, -0.5)))
        '''

        self.assertEqual(mat.inverted_safe(), inv_mat_safe)
Esempio n. 11
0
class Line(Projection):
    """
        2d Line
        Internally stored as p: origin and v:size and direction
        moving p will move both ends of line
        moving p0 or p1 move only one end of line
            p1
            ^
            | v
            p0 == p
    """
    def __init__(self, p=None, v=None, p0=None, p1=None):
        """
            Init by either
            p: Vector or tuple origin
            v: Vector or tuple size and direction
            or
            p0: Vector or tuple 1 point location
            p1: Vector or tuple 2 point location
            Will convert any into Vector 2d
            both optionnals
        """
        Projection.__init__(self)
        if p is not None and v is not None:
            self.p = Vector(p).to_2d()
            self.v = Vector(v).to_2d()
        elif p0 is not None and p1 is not None:
            self.p = Vector(p0).to_2d()
            self.v = Vector(p1).to_2d() - self.p
        else:
            self.p = Vector((0, 0))
            self.v = Vector((0, 0))
        self.line = None

    @property
    def copy(self):
        return Line(self.p.copy(), self.v.copy())

    @property
    def p0(self):
        return self.p

    @property
    def p1(self):
        return self.p + self.v

    @p0.setter
    def p0(self, p0):
        """
            Note: setting p0
            move p0 only
        """
        p1 = self.p1
        self.p = Vector(p0).to_2d()
        self.v = p1 - p0

    @p1.setter
    def p1(self, p1):
        """
            Note: setting p1
            move p1 only
        """
        self.v = Vector(p1).to_2d() - self.p

    @property
    def length(self):
        """
            3d length
        """
        return self.v.length

    @property
    def angle(self):
        """
            2d angle on xy plane
        """
        return atan2(self.v.y, self.v.x)

    @property
    def a0(self):
        return self.angle

    @property
    def angle_normal(self):
        """
            2d angle of perpendicular
            lie on the right side
            p1
            |--x
            p0
        """
        return atan2(-self.v.x, self.v.y)

    @property
    def reversed(self):
        return Line(self.p, -self.v)

    @property
    def oposite(self):
        return Line(self.p + self.v, -self.v)

    @property
    def cross_z(self):
        """
            2d Vector perpendicular on plane xy
            lie on the right side
            p1
            |--x
            p0
        """
        return Vector((self.v.y, -self.v.x))

    @property
    def cross(self):
        return Vector((self.v.y, -self.v.x))

    def signed_angle(self, u, v):
        """
            signed angle between two vectors range [-pi, pi]
        """
        return atan2(u.x * v.y - u.y * v.x, u.x * v.x + u.y * v.y)

    def delta_angle(self, last):
        """
            signed delta angle between end of line and start of this one
            this value is object's a0 for segment = self
        """
        if last is None:
            return self.angle
        return self.signed_angle(last.straight(1, 1).v, self.straight(1, 0).v)

    def normal(self, t=0):
        """
            2d Line perpendicular on plane xy
            at position t in current segment
            lie on the right side
            p1
            |--x
            p0
        """
        return Line(self.lerp(t), self.cross_z)

    def sized_normal(self, t, size):
        """
            2d Line perpendicular on plane xy
            at position t in current segment
            and of given length
            lie on the right side when size > 0
            p1
            |--x
            p0
        """
        return Line(self.lerp(t), size * self.cross_z.normalized())

    def lerp(self, t):
        """
            3d interpolation
        """
        return self.p + self.v * t

    def intersect(self, line):
        """
            2d intersection on plane xy
            return
            True if intersect
            p: point of intersection
            t: param t of intersection on current line
        """
        c = line.cross_z
        d = self.v.dot(c)
        if d == 0:
            return False, 0, 0
        t = c.dot(line.p - self.p) / d
        return True, self.lerp(t), t

    def intersect_ext(self, line):
        """
            same as intersect, but return param t on both lines
        """
        c = line.cross_z
        d = self.v.dot(c)
        if d == 0:
            return False, 0, 0, 0
        dp = line.p - self.p
        c2 = self.cross_z
        u = c.dot(dp) / d
        v = c2.dot(dp) / d
        return u > 0 and v > 0 and u < 1 and v < 1, self.lerp(u), u, v

    def point_sur_segment(self, pt):
        """ _point_sur_segment
            point: Vector 2d
            t: param t de l'intersection sur le segment courant
            d: distance laterale perpendiculaire positif a droite
        """
        dp = pt - self.p
        dl = self.length
        if dl == 0:
            return dp.length < 0.00001, 0, 0
        d = (self.v.x * dp.y - self.v.y * dp.x) / dl
        t = self.v.dot(dp) / (dl * dl)
        return t > 0 and t < 1, d, t

    def steps(self, len):
        steps = max(1, round(self.length / len, 0))
        return 1 / steps, int(steps)

    def in_place_offset(self, offset):
        """
            Offset current line
            offset > 0 on the right part
        """
        self.p += offset * self.cross_z.normalized()

    def offset(self, offset):
        """
            Return a new line
            offset > 0 on the right part
        """
        return Line(self.p + offset * self.cross_z.normalized(), self.v)

    def tangeant(self, t, da, radius):
        p = self.lerp(t)
        if da < 0:
            c = p + radius * self.cross_z.normalized()
        else:
            c = p - radius * self.cross_z.normalized()
        return Arc(c, radius, self.angle_normal, da)

    def straight(self, length, t=1):
        return Line(self.lerp(t), self.v.normalized() * length)

    def translate(self, dp):
        self.p += dp

    def rotate(self, a):
        """
            Rotate segment ccw arroud p0
        """
        ca = cos(a)
        sa = sin(a)
        self.v = Matrix([
            [ca, -sa],
            [sa, ca]
            ]) @ self.v
        return self

    def scale(self, length):
        self.v = length * self.v.normalized()
        return self

    def tangeant_unit_vector(self, t):
        return self.v.normalized()

    def as_curve(self, context):
        """
            Draw Line with open gl in screen space
            aka: coords are in pixels
        """
        curve = bpy.data.curves.new('LINE', type='CURVE')
        curve.dimensions = '2D'
        spline = curve.splines.new('POLY')
        spline.use_endpoint_u = False
        spline.use_cyclic_u = False
        pts = self.pts
        spline.points.add(len(pts) - 1)
        for i, p in enumerate(pts):
            x, y, z = p
            spline.points[i].co = (x, y, 0, 1)
        curve_obj = bpy.data.objects.new('LINE', curve)
        context.scene.collection.objects.link(curve_obj)
        curve_obj.select_set(state=True)

    def make_offset(self, offset, last=None):
        """
            Return offset between last and self.
            Adjust last and self start to match
            intersection point
        """
        line = self.offset(offset)
        if last is None:
            return line

        if hasattr(last, "r"):
            res, d, t = line.point_sur_segment(last.c)
            c = (last.r * last.r) - (d * d)
            # print("t:%s" % t)
            if c <= 0:
                # no intersection !
                p0 = line.lerp(t)
            else:
                # center is past start of line
                if t > 0:
                    p0 = line.lerp(t) - line.v.normalized() * sqrt(c)
                else:
                    p0 = line.lerp(t) + line.v.normalized() * sqrt(c)
            # compute da of arc
            u = last.p0 - last.c
            v = p0 - last.c
            da = self.signed_angle(u, v)
            # da is ccw
            if last.ccw:
                # da is cw
                if da < 0:
                    # so take inverse
                    da = 2 * pi + da
            elif da > 0:
                # da is ccw
                da = 2 * pi - da
            last.da = da
            line.p0 = p0
        else:
            # intersect line / line
            # 1 line -> 2 line
            c = line.cross_z
            d = last.v.dot(c)
            if d == 0:
                return line
            v = line.p - last.p
            t = c.dot(v) / d
            c2 = last.cross_z
            u = c2.dot(v) / d
            # intersect past this segment end
            # or before last segment start
            # print("u:%s t:%s" % (u, t))
            if u > 1 or t < 0:
                return line
            p = last.lerp(t)
            line.p0 = p
            last.p1 = p

        return line

    @property
    def pts(self):
        return [self.p0.to_3d(), self.p1.to_3d()]
Esempio n. 12
0
class Line(Projection):
    """
        2d Line
        Internally stored as p: origin and v:size and direction
        moving p will move both ends of line
        moving p0 or p1 move only one end of line
            p1
            ^
            | v
            p0 == p
    """
    def __init__(self, p=None, v=None, p0=None, p1=None):
        """
            Init by either
            p: Vector or tuple origin
            v: Vector or tuple size and direction
            or
            p0: Vector or tuple 1 point location
            p1: Vector or tuple 2 point location
            Will convert any into Vector 2d
            both optionnals
        """
        Projection.__init__(self)
        if p is not None and v is not None:
            self.p = Vector(p).to_2d()
            self.v = Vector(v).to_2d()
        elif p0 is not None and p1 is not None:
            self.p = Vector(p0).to_2d()
            self.v = Vector(p1).to_2d() - self.p
        else:
            self.p = Vector((0, 0))
            self.v = Vector((0, 0))
        self.line = None

    @property
    def copy(self):
        return Line(self.p.copy(), self.v.copy())

    @property
    def p0(self):
        return self.p

    @property
    def p1(self):
        return self.p + self.v

    @p0.setter
    def p0(self, p0):
        """
            Note: setting p0
            move p0 only
        """
        p1 = self.p1
        self.p = Vector(p0).to_2d()
        self.v = p1 - p0

    @p1.setter
    def p1(self, p1):
        """
            Note: setting p1
            move p1 only
        """
        self.v = Vector(p1).to_2d() - self.p

    @property
    def length(self):
        """
            3d length
        """
        return self.v.length

    @property
    def angle(self):
        """
            2d angle on xy plane
        """
        return atan2(self.v.y, self.v.x)

    @property
    def a0(self):
        return self.angle

    @property
    def angle_normal(self):
        """
            2d angle of perpendicular
            lie on the right side
            p1
            |--x
            p0
        """
        return atan2(-self.v.x, self.v.y)

    @property
    def reversed(self):
        return Line(self.p, -self.v)

    @property
    def oposite(self):
        return Line(self.p + self.v, -self.v)

    @property
    def cross_z(self):
        """
            2d Vector perpendicular on plane xy
            lie on the right side
            p1
            |--x
            p0
        """
        return Vector((self.v.y, -self.v.x))

    @property
    def cross(self):
        return Vector((self.v.y, -self.v.x))

    def signed_angle(self, u, v):
        """
            signed angle between two vectors range [-pi, pi]
        """
        return atan2(u.x * v.y - u.y * v.x, u.x * v.x + u.y * v.y)

    def delta_angle(self, last):
        """
            signed delta angle between end of line and start of this one
            this value is object's a0 for segment = self
        """
        if last is None:
            return self.angle
        return self.signed_angle(last.straight(1, 1).v, self.straight(1, 0).v)

    def normal(self, t=0):
        """
            2d Line perpendicular on plane xy
            at position t in current segment
            lie on the right side
            p1
            |--x
            p0
        """
        return Line(self.lerp(t), self.cross_z)

    def sized_normal(self, t, size):
        """
            2d Line perpendicular on plane xy
            at position t in current segment
            and of given length
            lie on the right side when size > 0
            p1
            |--x
            p0
        """
        return Line(self.lerp(t), size * self.cross_z.normalized())

    def lerp(self, t):
        """
            3d interpolation
        """
        return self.p + self.v * t

    def intersect(self, line):
        """
            2d intersection on plane xy
            return
            True if intersect
            p: point of intersection
            t: param t of intersection on current line
        """
        c = line.cross_z
        d = self.v * c
        if d == 0:
            return False, 0, 0
        t = (c * (line.p - self.p)) / d
        return True, self.lerp(t), t

    def intersect_ext(self, line):
        """
            same as intersect, but return param t on both lines
        """
        c = line.cross_z
        d = self.v * c
        if d == 0:
            return False, 0, 0, 0
        dp = line.p - self.p
        c2 = self.cross_z
        u = (c * dp) / d
        v = (c2 * dp) / d
        return u > 0 and v > 0 and u < 1 and v < 1, self.lerp(u), u, v

    def point_sur_segment(self, pt):
        """ _point_sur_segment
            point: Vector 2d
            t: param t de l'intersection sur le segment courant
            d: distance laterale perpendiculaire positif a droite
        """
        dp = pt - self.p
        dl = self.length
        if dl == 0:
            return dp.length < 0.00001, 0, 0
        d = (self.v.x * dp.y - self.v.y * dp.x) / dl
        t = (self.v * dp) / (dl * dl)
        return t > 0 and t < 1, d, t

    def steps(self, len):
        steps = max(1, round(self.length / len, 0))
        return 1 / steps, int(steps)

    def in_place_offset(self, offset):
        """
            Offset current line
            offset > 0 on the right part
        """
        self.p += offset * self.cross_z.normalized()

    def offset(self, offset):
        """
            Return a new line
            offset > 0 on the right part
        """
        return Line(self.p + offset * self.cross_z.normalized(), self.v)

    def tangeant(self, t, da, radius):
        p = self.lerp(t)
        if da < 0:
            c = p + radius * self.cross_z.normalized()
        else:
            c = p - radius * self.cross_z.normalized()
        return Arc(c, radius, self.angle_normal, da)

    def straight(self, length, t=1):
        return Line(self.lerp(t), self.v.normalized() * length)

    def translate(self, dp):
        self.p += dp

    def rotate(self, a):
        """
            Rotate segment ccw arroud p0
        """
        ca = cos(a)
        sa = sin(a)
        self.v = Matrix([[ca, -sa], [sa, ca]]) * self.v
        return self

    def scale(self, length):
        self.v = length * self.v.normalized()
        return self

    def tangeant_unit_vector(self, t):
        return self.v.normalized()

    def as_curve(self, context):
        """
            Draw Line with open gl in screen space
            aka: coords are in pixels
        """
        curve = bpy.data.curves.new('LINE', type='CURVE')
        curve.dimensions = '2D'
        spline = curve.splines.new('POLY')
        spline.use_endpoint_u = False
        spline.use_cyclic_u = False
        pts = self.pts
        spline.points.add(len(pts) - 1)
        for i, p in enumerate(pts):
            x, y, z = p
            spline.points[i].co = (x, y, 0, 1)
        curve_obj = bpy.data.objects.new('LINE', curve)
        context.scene.objects.link(curve_obj)
        curve_obj.select = True

    def make_offset(self, offset, last=None):
        """
            Return offset between last and self.
            Adjust last and self start to match
            intersection point
        """
        line = self.offset(offset)
        if last is None:
            return line

        if hasattr(last, "r"):
            res, d, t = line.point_sur_segment(last.c)
            c = (last.r * last.r) - (d * d)
            # print("t:%s" % t)
            if c <= 0:
                # no intersection !
                p0 = line.lerp(t)
            else:
                # center is past start of line
                if t > 0:
                    p0 = line.lerp(t) - line.v.normalized() * sqrt(c)
                else:
                    p0 = line.lerp(t) + line.v.normalized() * sqrt(c)
            # compute da of arc
            u = last.p0 - last.c
            v = p0 - last.c
            da = self.signed_angle(u, v)
            # da is ccw
            if last.ccw:
                # da is cw
                if da < 0:
                    # so take inverse
                    da = 2 * pi + da
            elif da > 0:
                # da is ccw
                da = 2 * pi - da
            last.da = da
            line.p0 = p0
        else:
            # intersect line / line
            # 1 line -> 2 line
            c = line.cross_z
            d = last.v * c
            if d == 0:
                return line
            v = line.p - last.p
            t = (c * v) / d
            c2 = last.cross_z
            u = (c2 * v) / d
            # intersect past this segment end
            # or before last segment start
            # print("u:%s t:%s" % (u, t))
            if u > 1 or t < 0:
                return line
            p = last.lerp(t)
            line.p0 = p
            last.p1 = p

        return line

    @property
    def pts(self):
        return [self.p0.to_3d(), self.p1.to_3d()]
Esempio n. 13
0
class Turtle:
    """This is the base class of the turtle that does not create any objects, but can be used to perform a dry-run interpretation to query the turtle state at different moments"""
    def __init__(self, _linewidth, _materialindex):
        # turtle state consists of a 4x4 matrix and some drawing attributes
        self.mat = Matrix()
        self.linewidth = _linewidth
        self.materialindex = _materialindex
        # stack to save and restore turtle state
        self.stack = []
        # rotate such that heading is in +Z (we want to grow upwards in blender)
        # we thus have heading = +Z, left = -Y, up = +X
        self.mat @= Matrix.Rotation(radians(270), 4, 'Y')

    def push(self):
        """Push turtle state to stack"""
        # push state to stack
        self.stack.append(
            (self.mat.copy(), self.linewidth, self.materialindex))

    def pop(self):
        """Pop last turtle state from stack and use as current"""
        (self.mat, self.linewidth, self.materialindex) = self.stack.pop()

    def move(self, stepsize):
        """Move turtle in its heading direction."""
        vec = self.mat @ Vector((stepsize, 0, 0, 0))
        self.mat.col[3] += vec

    def turn(self, angle_degrees):
        self.mat @= Matrix.Rotation(radians(angle_degrees), 4, 'Z')

    def pitch(self, angle_degrees):
        self.mat @= Matrix.Rotation(radians(angle_degrees), 4, 'Y')

    def roll(self, angle_degrees):
        self.mat @= Matrix.Rotation(radians(angle_degrees), 4, 'X')

    def look_at(self, target):
        """
        Let turtle look at a given 3D targed vector point.
        The heading vector will point toward x, y, z 
        and the heading, up, and left vectors will have the same
        relative orientation (handedness) as before.
        """
        turtle_pos = self.mat.col[3].xyz
        turtle_to_target = target - turtle_pos
        turtle_to_target.normalize()
        result_mat = Matrix()

        # position stays same
        result_mat.col[3] = self.mat.col[3]

        # heading towards target
        result_mat.col[0] = turtle_to_target.resized(4)

        # use old up vector to compute orthogonal left vector
        # the cross product defaults to right hand order but we store a left vector
        # thus we negate the cross product vector
        old_up = self.mat.col[1].xyz.normalized()
        left = Vector.cross(old_up, turtle_to_target).normalized()
        result_mat.col[2] = left.resized(4)

        # compute new up vector from left and heading vector
        # since the left and new up vectors were constructed using the same left hand order
        # the left hand order is preserved.
        result_mat.col[1] = Vector.cross(
            left, turtle_to_target).normalized().resized(4)

        self.mat = result_mat

    def draw_internode_module(self, length=None, width=None):
        """DELIBERATRELY NOT IMPLEMENTED"""
        pass

    def draw_module_from_custom_object(self, objname=None, objscale=None):
        """DELIBERATRELY NOT IMPLEMENTED"""
        pass
Esempio n. 14
0
class Render():
    def __init__(self):
        self.view_handler = None
        self._matrix = Matrix()
        self.gridmatrix = None
        self.amount = 0

        self.shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
        self.batch = None

    def enable(self):
        if self.view_handler:
            return

        self.disable()

        self.view_handler = bpy.types.SpaceView3D.draw_handler_add(
            self.draw, (), 'WINDOW', 'POST_VIEW')
        self.tag_redraw_all()

    def disable(self):
        if self.view_handler:
            bpy.types.SpaceView3D.draw_handler_remove(self.view_handler,
                                                      'WINDOW')
        self.view_handler = None
        self.tag_redraw_all()

    def tag_redraw_all(self):
        workplane.util.all_view3d(lambda region: region.tag_redraw())

    def screen_coord_to_workplane_intersection(self, region, rv3d, co):
        vec = region_2d_to_vector_3d(region, rv3d, co)
        loc = region_2d_to_location_3d(region, rv3d, co, vec)
        x = intersect_line_plane(loc, loc + vec, self._matrix.translation,
                                 self._matrix.col[2])
        return x

    def update_grid_matrix(self, matrix):
        self._matrix = matrix

        # translation = Matrix()
        # if self.gridmatrix and not workplane.data.is_simple_preview():
        #     translation = Matrix.Translation(self.gridmatrix.translation)

        self.gridmatrix = self._matrix.copy()
        # self.gridmatrix @= translation

        #print(self.gridmatrix)

    def calc_grid_size(self, region, rv3d):
        left = (0, int(region.height * 0.5))
        right = (region.width, int(region.height * 0.5))
        top = (int(region.width * 0.5), region.height)
        bottom = (int(region.width * 0.5), 0)
        center = (int(region.width * 0.5), int(region.height * 0.5))

        coords = (top, right, bottom, left)

        #grab intersection of workplane and screen center, and round to nearest even
        middle = self.screen_coord_to_workplane_intersection(
            region, rv3d, center)
        v = self._matrix.inverted_safe() @ middle
        middle = self._matrix @ Vector((round(v.x), round(v.y), 0))

        dist = 500
        for co in coords:
            intersection = self.screen_coord_to_workplane_intersection(
                region, rv3d, co)

            if intersection and middle:
                d = (middle - intersection).length
                dist = min(d, dist)

        amount = int(dist) * 2
        amount = min(amount, 1000)  #cap this...
        amount += 20 - (amount % 20)  #round to next even 10

        return amount, middle

    def create_grid_buffers(self, amount, middle):
        step = 1
        if amount >= 1024:
            step = 10
        elif amount >= 512:
            step = 5
        elif amount >= 256:
            step = 2

        if workplane.data.is_simple_preview():
            amount = 20

        half = int((amount - (amount % step)) * 0.5)
        offset = Vector((-half, -half, 0))

        lines = []
        colors = []

        for i in range(0, amount):

            scale = 0.5
            x = i / float(amount)

            #bell like graph shape for the alpha to get nice fading:
            #alpha = math.pow(4, scale) * math.pow(x *(1-x), scale)

            #triang graph:
            alpha = self.tri(0.5, x * 2 * 3.141) * 0.5 + 0.5

            #gray = bpy.context.user_preferences.themes[0].view_3d.grid
            #gray = (gray.r, gray.g, gray.b, alpha)
            gray = (1.0, 1.0, 1.0, alpha * 0.1)

            # gray = 1.0, 1.0, 1.0, 1.0

            if i % step != 0:
                continue
            '''                
            if i % 5 == 0:
                gray  = (1.0, 1.0, 1.0, 0.1 )
            '''

            a1 = Vector((0, i, 0)) + offset
            a2 = Vector((amount, i, 0)) + offset
            b1 = Vector((i, 0, 0)) + offset
            b2 = Vector((i, amount, 0)) + offset

            lines.append((a1, a2, b1, b2))
            colors.append((gray, gray, gray, gray))

            if i == amount - 1:
                a1 = Vector((0, i + 1, 0)) + offset
                a2 = Vector((amount, i + 1, 0)) + offset
                b1 = Vector((i + 1, 0, 0)) + offset
                b2 = Vector((i + 1, amount, 0)) + offset

                lines.append((a1, a2, b1, b2))
                colors.append((gray, gray, gray, gray))

        lines = np.array(lines)
        lines = lines.ravel()
        lines = lines.reshape(-1, 3)
        lines = lines.tolist()
        # print(lines)

        colors = np.array(colors)
        colors = colors.ravel()
        colors = colors.reshape(-1, 4)
        colors = colors.tolist()

        self.batch = batch_for_shader(self.shader, 'LINES', {
            "pos": lines,
            "color": colors
        })

    def draw(self):

        if not workplane.data.get_visibility():
            return

        # if not workplane.update.WorkPlaneUpdater.Running:
        #     bpy.ops.workplane.internal_workplane_updater()

        context = bpy.context
        region = context.region
        rv3d = context.space_data.region_3d

        amount, middle = self.calc_grid_size(region, rv3d)

        if not workplane.data.is_simple_preview():
            self.gridmatrix.translation = middle

        if self.amount != amount:
            self.amount = amount
            self.create_grid_buffers(amount, middle)

        # print(self.lines)

        if self.batch:
            bgl.glEnable(bgl.GL_BLEND)
            bgl.glEnable(bgl.GL_DEPTH_TEST)
            bgl.glEnable(bgl.GL_LINE_SMOOTH)
            bgl.glLineWidth(2)

            view_matrix = bpy.context.region_data.perspective_matrix

            gpu.matrix.push()
            gpu.matrix.load_matrix(self.gridmatrix)

            gpu.matrix.push_projection()
            gpu.matrix.load_projection_matrix(view_matrix)

            self.shader.bind()
            self.batch.draw(self.shader)

            # restore opengl defaults
            gpu.matrix.pop()
            gpu.matrix.pop_projection()

            bgl.glDisable(bgl.GL_BLEND)
            bgl.glDisable(bgl.GL_DEPTH_TEST)
            bgl.glDisable(bgl.GL_LINE_SMOOTH)
            bgl.glLineWidth(1)

    #merci: http://www.iquilezles.org/apps/graphtoy/utils.js?v=1
    def tri(self, a, x):
        x = x / (2.0 * 3.141)
        x = x % 1.0
        if x < 0.0:
            x += 1.0

        if x < a:
            x = x / a
        else:
            x = 1.0 - (x - a) / (1.0 - a)
        return -1.0 + 2.0 * x