Skip to content
Primitive.py 9.46 KiB
Newer Older
####################################################################################################
#
Fabrice Salvaire's avatar
Fabrice Salvaire committed
# Patro - A Python library to make patterns for fashion design
# Copyright (C) 2017 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
from .Bounding_Box import bounding_box_from_points
from .Vector import Vector2D

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Base class for geometric primitive"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    # __dimension__ = None # in [2, 3] for 2D / 3D primitive

    __vector_cls__ = None

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def dimension(self):
        """Dimension in [2, 3] for 2D / 3D primitive"""
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        raise NotImplementedError

    ##############################################
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def is_infinite(self):
        """True if the primitive has infinite extend like a line"""
        return False

    ##############################################

    @property
    def number_of_points(self):
        """Number of points which define the primitive."""
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __len__(self):
        return self.number_of_points

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    @property
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def is_reversible(self):
        """True if the order of the points is reversible"""
        # Fixme: True if number_of_points > 1 ???
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return False

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def points(self):
        raise NotImplementedError

    ##############################################

    # @points.setter
    # def points(self):
    #     raise NotImplementedError

    def _set_points(self, points):
        raise NotImplementedError

    ##############################################

    def __repr__(self):
        return self.__class__.__name__ + str([str(p) for p in self.points])

    ##############################################

    def clone(self):
        return self.__class__(*self.points)

    ##############################################

    def bounding_box(self):
        """Bounding box of the primitive.

        Return None if primitive is infinite.
        """

        if self.is_infinite:
            return None
        else:
            return bounding_box_from_points(self.points)

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def reverse(self):
        return self

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def transform(self, transformation):

        # for point in self.points:
        #     point *= transformation # don't work

        self._set_points([transformation*p for p in self.points])

####################################################################################################

class Primitive2D:
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    # __dimension__ = 2

    __vector_cls__ = Vector2D

    @property
    def dimension(self):
        return 2
Fabrice Salvaire's avatar
Fabrice Salvaire committed

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class ReversiblePrimitiveMixin:
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    @property
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def is_reversible(self):
        True
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def reversed_points(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        raise NotImplementedError
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # return reversed(list(self.points))

    ##############################################

    def reverse(self):
        return self.__class__(*self.reversed_points)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    @property
    def start_point(self):
        raise NotImplementedError

    @property
    def end_point(self):
        raise NotImplementedError

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class Primitive1P(Primitive):

    ##############################################

    def __init__(self, p0):

        self.p0 = p0

    ##############################################

    @property
    def number_of_points(self):
        return 1

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

    @p0.setter
    def p0(self, value):
        self._p0 = self.__vector_cls__(value)

    ##############################################

    @property
    def points(self):
        return iter(self._p0) # Fixme: efficiency ???

    @property
    def reversed_points(self):
        return self.points

    ##############################################

    def _set_points(self, points):
        self._p0 = points

####################################################################################################

class Primitive2P(Primitive1P, ReversiblePrimitiveMixin):

    ##############################################

    def __init__(self, p0, p1):

        # We don't call super().__init__(p0) for speed
        self.p0 = p0
        self.p1 = p1

    ##############################################

    # Redundant code ... until we don't use self._points = []

    @property
    def number_of_points(self):
        return 2

    @property
    def p1(self):
        return self._p1

    @p1.setter
    def p1(self, value):
        self._p1 = self.__vector_cls__(value)

    ##############################################

    @property
    def start_point(self):
        return self._p0

    @property
    def end_point(self):
        return self._p1

    ##############################################

    @property
    def points(self):
        return iter(self._p0, self._p1)

    @property
    def reversed_points(self):
        return iter(self._p1, self._p0)

    ##############################################

    def _set_points(self, points):
        self._p0, self._p1 = points

####################################################################################################

class Primitive3P(Primitive2P):

    ##############################################

    def __init__(self, p0, p1, p2):

        self.p0 = p0
        self.p1 = p1
        self.p2 = p2

    ##############################################

    @property
    def number_of_points(self):
        return 3

    @property
    def p2(self):
        return self._p2

    @p2.setter
    def p2(self, value):
        self._p2 = self.__vector_cls__(value)

    ##############################################

    @property
    def end_point(self):
        return self._p2

    ##############################################

    @property
    def points(self):
        return iter(self._p0, self._p1, self._p2)

    @property
    def reversed_points(self):
        return iter(self._p2, self._p1, self._p0)

    ##############################################

    def _set_points(self, points):
        self._p0, self._p1, self._p2 = points

####################################################################################################

class Primitive3P(Primitive2P):

    ##############################################

    def __init__(self, p0, p2, p2, p3):

        self.p0 = p0
        self.p2 = p2
        self.p2 = p2
        self.p3 = p3

    ##############################################

    @property
    def number_of_points(self):
        return 4

    @property
    def p3(self):
        return self._p3

    @p3.setter
    def p3(self, value):
        self._p3 = self.__vector_cls__(value)

    ##############################################

    @property
    def end_point(self):
        return self._p3

    ##############################################

    @property
    def points(self):
        return iter(self._p0, self._p1, self._p2, self._p3)

    @property
    def reversed_points(self):
        return iter(self._p3, self._p2, self._p1, self._p0)

    ##############################################

    def _set_points(self, points):
        self._p0, self._p1, self._p2, self._p3 = points

####################################################################################################

class PrimitiveNP(PrimitiveP, ReversiblePrimitiveMixin):

    ##############################################

    def __init__(self, *points):

        self._points = [self.__vector_cls__(p) for p in points]

    ##############################################

    @property
    def number_of_points(self):
        return len(self._points)

    ##############################################

    @property
    def start_point(self):
        return self._points[0]

    @property
    def end_point(self):
        return self._points[-1]

    ##############################################

    @property
    def points(self):
        return iter(self._points)

    @property
    def reversed_points(self):
        return reversed(self._points)

    ##############################################

    def _set_points(self, points):
        self._points = points

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __getitem__(self, _slice):
        return self._points[_slice]