Exemple #1
0
class LinearTransform(object):
    def __init__(self, x_axis=None, y_axis=None):
        self.x_axis = x_axis if x_axis is not None else Vector(1.0, 0.0)
        self.y_axis = y_axis if y_axis is not None else Vector(0.0, 1.0)

    def Copy(self):
        return LinearTransform(self.x_axis.Copy(), self.y_axis.Copy())

    def Serialize(self):
        json_data = {
            'x_axis': self.x_axis.Serialize(),
            'y_axis': self.y_axis.Serialize()
        }
        return json_data

    def Deserialize(self, json_data):
        self.x_axis = Vector().Deserialize(json_data['x_axis'])
        self.y_axis = Vector().Deserialize(json_data['y_axis'])
        return self

    def IsTransform(self, transform, epsilon=1e-7):
        return self.x_axis.IsPoint(transform.x_axis,
                                   epsilon) and self.y_axis.IsPoint(
                                       transform.y_axis, epsilon)

    def Identity(self):
        self.x_axis = Vector(1.0, 0.0)
        self.y_axis = Vector(0.0, 1.0)

    def IsIdentity(self, epsilon=1e-7):
        if not self.x_axis.IsPoint(Vector(1.0, 0.0), epsilon):
            return False
        if not self.y_axis.IsPoint(Vector(0.0, 1.0), epsilon):
            return False
        return True

    def Transform(self, object):
        if isinstance(object, Vector):
            return self.x_axis * object.x + self.y_axis * object.y
        elif isinstance(object, LinearTransform):
            transform = LinearTransform()
            transform.x_axis = self.Transform(object.x_axis)
            transform.y_axis = self.Transform(object.y_axis)
            return transform

    def __call__(self, object):
        return self.Transform(object)

    def __mul__(self, other):
        return self.Transform(other)

    def Determinant(self):
        return self.x_axis.Cross(self.y_axis)

    def Invert(self):
        inverse = self.Inverted()
        if inverse is None:
            return False
        else:
            self.x_axis = inverse.x_axis
            self.y_axis = inverse.y_axis
            return True

    def Inverted(self):
        try:
            det = self.Determinant()
            inverse = LinearTransform()
            scale = 1.0 / det
            inverse.x_axis = Vector(self.y_axis.y, -self.x_axis.y) * scale
            inverse.y_axis = Vector(-self.y_axis.x, self.x_axis.x) * scale
            return inverse
        except ZeroDivisionError:
            return None

    def Rotation(self, angle):
        self.Identity()
        self.x_axis = self.x_axis.Rotated(angle)
        self.y_axis = self.y_axis.Rotated(angle)

    def Reflection(self, vector):
        self.Identity()
        self.x_axis = self.x_axis.Reflected(vector)
        self.y_axis = self.y_axis.Reflected(vector)

    def Scale(self, x_scale, y_scale):
        self.x_axis = Vector(x_scale, 0.0)
        self.y_axis = Vector(0.0, y_scale)

    def Decompose(self):
        pass
Exemple #2
0
class AffineTransform(object):
    def __init__(self, x_axis=None, y_axis=None, translation=None):
        self.linear_transform = LinearTransform(x_axis, y_axis)
        self.translation = translation if translation is not None else Vector(
            0.0, 0.0)

    def Copy(self):
        return AffineTransform(self.linear_transform.x_axis.Copy(),
                               self.linear_transform.y_axis.Copy(),
                               self.translation.Copy())

    def Serialize(self):
        json_data = {
            'linear_transform': self.linear_transform.Serialize(),
            'translation': self.translation.Serialize()
        }
        return json_data

    def Deserialize(self, json_data):
        self.linear_transform = LinearTransform().Deserialize(
            json_data['linear_transform'])
        self.translation = Vector().Deserialize(json_data['translation'])
        return self

    def IsTransform(self, transform, epsilon=1e-7):
        if not self.linear_transform.IsTransform(transform.linear_transform,
                                                 epsilon):
            return False
        if not self.translation.IsPoint(transform.translation, epsilon):
            return False
        return True

    def Identity(self):
        self.linear_transform.Identity()
        self.translation = Vector(0.0, 0.0)

    def IsIdentity(self, epsilon=1e-7):
        if not self.linear_transform.IsIdentity(epsilon):
            return False
        return True if self.translation.IsZero(epsilon) else False

    def Transform(self, object):
        from math2d_polygon import Polygon
        from math2d_region import Region, SubRegion
        from math2d_aa_rect import AxisAlignedRectangle
        if isinstance(object, Vector):
            return self.linear_transform.Transform(object) + self.translation
        elif isinstance(object, AffineTransform):
            transform = AffineTransform()
            transform.linear_transform.x_axis = self.linear_transform.Transform(
                object.linear_transform.x_axis)
            transform.linear_transform.y_axis = self.linear_transform.Transform(
                object.linear_transform.y_axis)
            transform.translation = self.Transform(object.translation)
            return transform
        elif isinstance(object, LineSegment):
            return LineSegment(self.Transform(object.point_a),
                               self.Transform(object.point_b))
        elif isinstance(object, Polygon):
            polygon = Polygon()
            for vertex in object.vertex_list:
                polygon.vertex_list.append(self.Transform(vertex))
            return polygon
        elif isinstance(object, Region):
            region = Region()
            for sub_region in object.sub_region_list:
                region.sub_region_list.append(self.Transform(sub_region))
            return region
        elif isinstance(object, SubRegion):
            sub_region = SubRegion()
            sub_region.polygon = self.Transform(object.polygon)
            for hole in object.hole_list:
                sub_region.hole_list.append(self.Transform(hole))
            return sub_region
        elif isinstance(object, AxisAlignedRectangle):
            return AxisAlignedRectangle(
                min_point=self.Transform(object.min_point),
                max_point=self.Transform(object.max_point))

    def __call__(self, object):
        return self.Transform(object)

    def __mul__(self, other):
        return self.Transform(other)

    def Determinant(self):
        return self.linear_transform.Determinant()

    def Invert(self):
        self.linear_transform.Invert()
        self.translation = -self.linear_transform.Transform(self.translation)

    def Inverted(self):
        inverse = self.Copy()
        inverse.Invert()
        return inverse

    def Rotation(self, center, angle):
        translation_a = AffineTransform(None, None, -center)
        translation_b = AffineTransform(None, None, center)
        rotation = AffineTransform()
        rotation.linear_transform.Rotation(angle)
        transform = translation_b * rotation * translation_a
        self.linear_transform = transform.linear_transform
        self.translation = transform.translation

    def Reflection(self, center, vector):
        translation_a = AffineTransform(None, None, -center)
        translation_b = AffineTransform(None, None, center)
        reflection = AffineTransform()
        reflection.linear_transform.Reflection(vector)
        transform = translation_b * reflection * translation_a
        self.linear_transform = transform.linear_transform
        self.translation = transform.translation

    def Translation(self, translation):
        self.linear_transform.Identity()
        self.translation = translation

    def RigidBodyMotion(self, rotation_angle, translation):
        self.linear_transform.Rotation(rotation_angle)
        self.translation = translation

    def Decompose(self):
        pass
class LineSegment(object):
    def __init__(self, point_a=None, point_b=None):
        self.point_a = point_a if point_a is not None else Vector(0.0, 0.0)
        self.point_b = point_b if point_b is not None else Vector(0.0, 0.0)

    def Serialize(self):
        json_data = {
            'point_a': self.point_a.Serialize(),
            'point_b': self.point_b.Serialize()
        }
        return json_data

    def Deserialize(self, json_data):
        self.point_a = Vector().Deserialize(json_data['point_a'])
        self.point_b = Vector().Deserialize(json_data['point_b'])
        return self

    def Copy(self):
        return LineSegment(self.point_a.Copy(), self.point_b.Copy())

    def Direction(self):
        return self.point_b - self.point_a

    def Length(self):
        return self.Direction().Length()

    def Lerp(self, lerp_value):
        return self.point_a + (self.point_b - self.point_a) * lerp_value

    def LerpValue(self, point, epsilon=1e-7):
        try:
            vector_a = self.point_b - self.point_a
            vector_b = point - self.point_a
            cross = vector_a.Cross(vector_b)
            if math.fabs(cross) >= epsilon:
                return None
            lerp_value = vector_a.Dot(vector_b) / vector_a.Dot(vector_a)
            return lerp_value
        except ZeroDivisionError:
            return None

    def ContainsPoint(self, point, epsilon=1e-7):
        lerp_value = self.LerpValue(point, epsilon)
        if lerp_value is None:
            return False
        return True if -epsilon < lerp_value < 1.0 + epsilon else False

    def IsEndPoint(self, point, epsilon=1e-7):
        return self.point_a.IsPoint(point, epsilon) or self.point_b.IsPoint(
            point, epsilon)

    def IntersectWith(self, other, epsilon=1e-7):
        if isinstance(other, LineSegment):
            numer_a = (other.point_b - other.point_a).Cross(self.point_a -
                                                            other.point_a)
            numer_b = (self.point_b - self.point_a).Cross(other.point_a -
                                                          self.point_a)
            denom = (self.point_b - self.point_a).Cross(other.point_b -
                                                        other.point_a)
            try:
                lerp_value_a = numer_a / denom
                lerp_value_b = numer_b / -denom
            except ZeroDivisionError:
                return None
            if -epsilon <= lerp_value_a <= 1.0 + epsilon and -epsilon <= lerp_value_b <= 1.0 + epsilon:
                point = self.Lerp(lerp_value_a)
                # Let's scrutinize further.  Two disjoint and parallel line segments sometimes warrant this.
                if self.ContainsPoint(point, epsilon) and other.ContainsPoint(
                        point, epsilon):
                    return point
                else:
                    return None

    def Distance(self, point):
        return (point - self.ClosestPoint(point)).Length()

    def ClosestPoint(self, point):
        vector = (self.point_b - self.point_a).Normalized()
        length = (point - self.point_a).Dot(vector)
        if length < 0.0:
            return self.point_a
        elif length > self.Length():
            return self.point_b
        else:
            return self.point_a + vector * length

    @staticmethod
    def ReduceLineList(given_line_list, epsilon):
        if len(given_line_list) < 2:
            return given_line_list
        else:
            i = int(
                len(given_line_list) / 2 if len(given_line_list) %
                2 == 0 else (len(given_line_list) + 1) / 2)
            line_list_a = given_line_list[0:i]
            line_list_b = given_line_list[i:]
            line_list_a = LineSegment.ReduceLineList(line_list_a, epsilon)
            line_list_b = LineSegment.ReduceLineList(line_list_b, epsilon)

            pair_queue = []
            for i in range(len(line_list_a)):
                for j in range(len(line_list_b)):
                    pair_queue.append((i, j))

            while (len(pair_queue) > 0):
                pair = pair_queue.pop()
                line_seg_a = line_list_a[pair[0]]
                line_seg_b = line_list_b[pair[1]]
                line_seg = LineSegment.CompressLineSegments(
                    line_seg_a, line_seg_b, epsilon)
                if line_seg is not None:
                    line_list_a[pair[0]] = None
                    line_list_b[pair[1]] = None
                    pair_queue = [
                        queued_pair for queued_pair in pair_queue if
                        queued_pair[0] != pair[0] and queued_pair[1] != pair[1]
                    ]
                    line_list_b.append(line_seg)  # Arbitrarily choose list B.
                    for i in range(len(line_list_a)):
                        if line_list_a[i] is not None:
                            pair_queue.append((i, len(line_list_b) - 1))

            line_list_a = [line for line in line_list_a if line is not None]
            line_list_b = [line for line in line_list_b if line is not None]
            return line_list_a + line_list_b

    @staticmethod
    def CompressLineSegments(line_seg_a, line_seg_b, epsilon):
        if line_seg_a.point_a.IsPoint(line_seg_b.point_a,
                                      epsilon) and line_seg_a.point_b.IsPoint(
                                          line_seg_b.point_b, epsilon):
            return line_seg_a
        elif line_seg_a.point_a.IsPoint(
                line_seg_b.point_b, epsilon) and line_seg_a.point_b.IsPoint(
                    line_seg_b.point_a, epsilon):
            return line_seg_a

        mid_point = None
        if line_seg_a.IsEndPoint(line_seg_b.point_a):
            mid_point = line_seg_b.point_a
        elif line_seg_a.IsEndPoint(line_seg_b.point_b):
            mid_point = line_seg_b.point_b

        if mid_point is not None:
            point_a = line_seg_a.point_a if not line_seg_a.point_a.IsPoint(
                mid_point, epsilon) else line_seg_a.point_b
            point_b = line_seg_b.point_a if not line_seg_b.point_a.IsPoint(
                mid_point, epsilon) else line_seg_b.point_b

            new_line_seg = LineSegment(point_a=point_a, point_b=point_b)
            distance = new_line_seg.Distance(mid_point)
            if distance < epsilon:
                return new_line_seg