Skip to content
Pattern.py 8.17 KiB
Newer Older
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################
#
Fabrice Salvaire's avatar
Fabrice Salvaire committed
# Patro - A Python library to make patterns for fashion design
Fabrice Salvaire's avatar
Fabrice Salvaire committed
# 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/>.
#
####################################################################################################

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

import logging

from IntervalArithmetic import Interval2D
from Patro.GeometryEngine.Vector import Vector2D
Fabrice Salvaire's avatar
Fabrice Salvaire committed
from Patro.GraphicEngine.GraphicScene.GraphicItem import GraphicStyle, Font
from Patro.GraphicEngine.GraphicScene.Scene import GraphicScene
from . import Calculation
Fabrice Salvaire's avatar
Fabrice Salvaire committed
from .Calculator import Calculator
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

_module_logger = logging.getLogger(__name__)

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

class Pattern:

    _logger = _module_logger.getChild('Pattern')

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, measurements, unit):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        self._measurements = measurements
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._calculator = Calculator(self._measurements)
        self._calculations = []
        self._calculation_dict = {}
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._unit = unit
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @property
    def measurements(self):
        return self._measurements

    @property
    def calculator(self):
        return self._calculator
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def unit(self):
        return self._unit

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def calculations(self):
        return self._calculations
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def _add_calculation(self, calculation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # Works as a post init
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._calculations.append(calculation)
        self._calculation_dict[calculation.id] = calculation
        if hasattr(calculation, 'name'):
            self._calculation_dict[calculation.name] = calculation

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

    def get_calculation_id(self):
        return len(self._calculations) + 1 # id > 0

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def has_calculation_id(self, id):
        return id in self._calculation_dict

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

    def get_calculation(self, id):
        return self._calculation_dict[id]

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

    def get_point(self, name):
        return self._points[name]

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

    def eval(self):

        self._logger.info('Eval all calculations')
        for calculation in self._calculations:
            if isinstance(calculation, Calculation.Point):
                self._calculator.add_point(calculation)
                calculation.eval()
            elif isinstance(calculation, Calculation.SimpleInteractiveSpline):
                calculation.eval() # for control points
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            calculation.connect_ancestor_for_expressions()
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def dump(self):

        print("\nDump calculations:")
        for calculation in self._calculations:
            if isinstance(calculation, Calculation.Point):
                print(calculation, calculation.vector)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            for dependency in calculation.dependencies:
                print('  ->', dependency)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @property
    def bounding_box(self):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        """Compute the bounding box of the pattern."""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # Fixme: to function
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        bounding_box = None
        for calculation in self._calculations:
            interval = calculation.geometry().bounding_box
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            # print(calculation.geometry(), interval)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            if bounding_box is None:
                bounding_box = interval
            else:
                bounding_box |= interval
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def _calculation_to_path_style(self, calculation, **kwargs):

        return GraphicStyle(
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            stroke_style=calculation.line_style,
            stroke_color=calculation.line_color,
            **kwargs
        )
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def detail_scene(self, scene_cls=GraphicScene):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        """Generate a graphic scene for the detail mode

        We can customise the scene class using the *scene_cls* parameter.
        """
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        scene = scene_cls()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # Fixme: scene bounding box
        scene.bounding_box = self.bounding_box
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # Fixme: implement a transformer class to prevent if ... ?

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        font = Font('', 16)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for calculation in self._calculations:

            if isinstance(calculation, Calculation.Point):
                scene.add_coordinate(calculation.name, calculation.vector)
                scene.circle(calculation.name, '1pt', GraphicStyle(fill_color='black'),
                             user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                label_offset = calculation.label_offset
                offset = Vector2D(label_offset.x, -label_offset.y) # Fixme: ???
                label_position = calculation.vector + offset
                if offset:
                    # arrow must point to the label center and be clipped
                    scene.segment(calculation.vector, label_position, GraphicStyle(line_width='.5pt'),
                                  user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                    scene.text(label_position, calculation.name, font, user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

                if isinstance(calculation, Calculation.LinePropertiesMixin):
                    path_style = self._calculation_to_path_style(calculation, line_width='2pt')
                    if isinstance(calculation, Calculation.AlongLinePoint):
                        scene.segment(calculation.first_point.name, calculation.name, path_style,
                                      user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                    elif isinstance(calculation, Calculation.EndLinePoint):
                        scene.segment(calculation.base_point.name, calculation.name, path_style,
                                      user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                    # elif isinstance(calculation, LineIntersectPoint):
                    #     scene.segment(calculation.point1_line1.name, calculation.name, path_style)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                    #     source += r'\draw[{0}] ({1.point1_line1.name}) -- ({1.name});'.format(style, calculation) + '\n'
                    elif isinstance(calculation, Calculation.NormalPoint):
                        scene.segment(calculation.first_point.name, calculation.name, path_style,
                                      user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

            elif isinstance(calculation, Calculation.Line):
                path_style = self._calculation_to_path_style(calculation, line_width='4pt')
                scene.segment(calculation.first_point.name, calculation.second_point.name, path_style,
                              user_data=calculation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

            elif isinstance(calculation, Calculation.SimpleInteractiveSpline):
                path_style = self._calculation_to_path_style(calculation, line_width='4pt')
                scene.cubic_bezier(calculation.first_point.name,
                                   calculation.control_point1, calculation.control_point2,
                                   calculation.second_point.name,
                                   path_style,
                                   user_data=calculation,
                )
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        return scene