Skip to content
Pattern.py 29.1 KiB
Newer Older
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################
#
# PyValentina - A Python implementation of Valentina Pattern Drafting Software
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 lxml import etree

from ArithmeticInterval import Interval2D

Fabrice Salvaire's avatar
Fabrice Salvaire committed
from .Evaluator import Expression
from .Measurement import VitParser
from Valentina.Geometry.Line2D import Line2D
from Valentina.Geometry.Vector2D import Vector2D
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

_module_logger = logging.getLogger(__name__)

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

# {'type': 'trueDarts', 'name1': 'A34', 'dartP2': '139', 'dartP1': '141',
# 'my2': '-3.87275', 'point2': '146', 'dartP3': '140', 'id': '144', 'mx2': '0.794387',
# 'my1': '-2.44561', 'name2': 'A35', 'point1': '145', 'baseLineP2': '63', 'mx1': '-3.64071',
# 'baseLineP1': '68'}

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

def vector_to_interval2d(vector):
    x, y = vector.x, vector.y
    return Interval2D((x, x), (y, y))
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

class Operation:

    _logger = _module_logger.getChild('Operation')

    # Improvements:
    #  XML <-> Python
    #  for each calculation
    #  provide mapping between xml and Python attributes
    #  and type: string, int, float, string expression
    # Mixin classes

    __ARC_TYPE__ = (
        'arcWithLength', # to be implemented
        'simple', # to be implemented
        )

    __ELLIPSE_TYPE__ = (
        'simple', # to be implemented
        )

    __OPERATION_TYPE__ = (
        'flippingByAxis', # to be implemented
        'flippingByLine', # to be implemented
        'moving', # to be implemented
        'rotation', # to be implemented
    )

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

    @staticmethod
    def from_xml(element, pattern):

        if element.tag == 'point':
            return Point.from_xml(element, pattern)
        elif element.tag == 'line':
            return Line.from_xml(element, pattern)
        elif element.tag == 'spline':
            return Curve.from_xml(element, pattern)
        elif element.tag == 'spline':
            return Curve.from_xml(element, pattern)
        # elif element.tag == 'operation': # Fixme: name clash
        #     return Operation.from_xml(element, pattern)
        # elif element.tag == 'arc':
        #     return Arc.from_xml(element, pattern)
        # elif element.tag == 'ellipse':
        #     return Ellipse.from_xml(element, pattern)
        else:
            return Operation(pattern, element.attrib['id'])

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

    @staticmethod
    def xml_attributes(element, pattern, attributes, keys):

        attrib = element.attrib
        kwargs = {key:attrib.get(attribute, None) for key, attribute in zip(keys, attributes)}
        kwargs['pattern'] = pattern
        return kwargs

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

    def __init__(self, pattern, id_):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        self._pattern = pattern
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._id = id_

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

    @property
    def id(self):
        return self._id

    @property
    def pattern(self):
        return self._pattern
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def __repr__(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        return self.__class__.__name__ + ' {0._id}'.format(self)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def eval(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        self._logger.info('Eval {}'.format(self))
        self.eval_internal()

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

    def eval_internal(self):

        pass
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

class Pattern:

    _logger = _module_logger.getChild('Pattern')

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

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

        self._measurements = measurements
        self._evaluator = measurements.evaluator
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._operations = []
        self._operation_dict = {}
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

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

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

    @property
    def evaluator(self):
        return self._evaluator

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def operations(self):
        return self._operations

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def add(self, operation):

        if operation is not None:
            self._operations.append(operation)
            self._operation_dict[operation.id] = operation

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

    def get_operation(self, id_):

        return self._operation_dict[id_]

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

    def get_point(self, name):

        return self._points[name]

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

    def eval(self):

        self._logger.info('Eval all operations')
        for operation in self._operations:
            if isinstance(operation, Point):
                self._evaluator.add_point(operation)
                operation.eval()
            elif isinstance(operation, Curve):
                operation.eval() # for control points
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def dump(self):

        print("\nDump operations:")
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for operation in self._operations:
            if isinstance(operation, Point):
                print(operation, operation.vector)
            else:
                print(operation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    def bounding_box(self):

        interval = None
        for operation in self._operations:
            if isinstance(operation, Point):
                interval_point = vector_to_interval2d(operation.vector)
                if interval is None:
                    interval = interval_point
                else:
                    interval |= interval_point
            elif isinstance(operation, Curve):
                interval |= vector_to_interval2d(operation.control_point1)
                interval |= vector_to_interval2d(operation.control_point2)
        return interval

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

class LineProperties:

    __COLORS__ = (
        'black',
        'blue',
        'cornflowerblue',
        'darkBlue',
        'darkGreen',
        'darkRed',
        'darkviolet',
        'deeppink',
        'deepskyblue',
        'goldenrod',
        'green',
        'lightsalmon',
        'lime',
        'mediumseagreen',
        'orange',
        'violet',
        'yellow',
    )

    # Fixme: line style ?
    __LINE_TYPE__ = (
        'dashDotDotLine',
        'dashDotLine',
        'dashLine',
        'dotLine',
        'hair', # should be solid
        'none',
    )

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

    def __init__(self, line_type=None, line_color=None):

        self._line_type = line_type
        self._line_color = line_color

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

    @property
    def line_color(self):
        return self._line_color

    @property
    def line_type(self):
        return self._line_type

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

class Point(Operation):

    __POINT_TYPES__ = (
        'alongLine',
        'bisector', # to be implemented
        'curveIntersectAxis', # to be implemented
        'cutArc', # to be implemented
        'cutSpline', # to be implemented
        'cutSplinePath', # to be implemented
        'endLine',
        'height', # to be implemented
        'lineIntersect',
        'lineIntersectAxis', # to be implemented
        'normal',
        'pointFromArcAndTangent', # to be implemented
        'pointFromCircleAndTangent', # to be implemented
        'pointOfContact', # to be implemented
        'pointOfIntersection',
        'pointOfIntersectionArcs', # to be implemented
        'pointOfIntersectionCircles', # to be implemented
        'pointOfIntersectionCurves', # to be implemented
        'shoulder', # to be implemented
        'single',
        'triangle', # to be implemented
        'trueDarts', # to be implemented
    )

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

    def __init__(self, pattern, id_, name,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(Point, self).__init__(id_)
        Operation.__init__(self, pattern, id_)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._name = name
        self._mx = float(mx) # Fixme: pass type in xml_attributes?
        self._my = float(my)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        self._vector = None

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

    @property
    def name(self):
        return self._name

    @property
    def mx(self):
        return self._mx

    @property
    def my(self):
        return self._my

    @property
    def vector(self):
        return self._vector

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        if element.tag != 'point':
            raise ValueError
        type_ = element.attrib['type']
        if type_ == 'single':
            return SinglePoint.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        elif type_ == 'alongLine':
            return AlongLinePoint.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        elif type_ == 'endLine':
            return EndLinePoint.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        elif type_ == 'lineIntersect':
            return LineIntersectPoint.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        elif type_ == 'normal':
            return NormalPoint.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        elif type_ == 'pointOfIntersection':
            return PointOfIntersectionPoint.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        else:
            return None

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

    def _post_eval_internal(self):

        self._logger.info('{0._name} {0._vector}'.format(self))

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

class SinglePoint(Point):

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

    def __init__(self, pattern, id_, name,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(SinglePoint, self).__init__(id_, name, mx, my, line_type, line_color)
        Point.__init__(self, pattern, id_, name, mx, my)
        self._x = Expression(x, pattern.evaluator)
        self._y = Expression(y, pattern.evaluator)

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

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # {'y': '1.0', 'mx': '0.1', 'type': 'single', 'my': '0.2', 'id': '1', 'name': 'A0', 'x': '0.7'}
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        kwargs = Operation.xml_attributes(element, pattern,
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                                          ('id', 'name', 'x', 'y', 'mx', 'my'),
                                          ('id_', 'name', 'x', 'y', 'mx', 'my'))
        return SinglePoint(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' {0._name} = ({0._x}, {0._y})'.format(self)

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

    def eval_internal(self):

        self._vector = Vector2D(self._x.value, self._y.value)
        self._post_eval_internal()
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

class AlongLinePoint(Point, LineProperties):

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

    def __init__(self, pattern, id_, name,
                 first_point, second_point, length,
                 mx=0, my=0,
                 line_type=None, line_color=None,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(AlongLinePoint, self).__init__(id_, name, mx, my, line_type, line_color)
        Point.__init__(self, pattern, id_, name, mx, my)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        LineProperties.__init__(self, line_type, line_color)
        self._first_point = pattern.get_operation(first_point)
        self._second_point = pattern.get_operation(second_point)
        self._length = Expression(length, pattern.evaluator)

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

    @property
    def first_point(self):
        return self._first_point

    @property
    def second_point(self):
        return self._second_point

    @property
    def length(self):
        return self._length
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # {'lineColor': 'black', 'firstPoint': '138', 'id': '141', 'mx': '-4.2484', 'typeLine': 'none',
        #  'my': '1.01162', 'name': 'A33', 'length': 'Line_A30_A32', 'type': 'alongLine', 'secondPoint': '68'}

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'name', 'firstPoint', 'secondPoint', 'length', 'mx', 'my', 'lineColor', 'typeLine'),
                                          ('id_', 'name', 'first_point', 'second_point', 'length', 'mx', 'my', 'line_color', 'line_type'))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return AlongLinePoint(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' {0._name} = ({0._first_point.name}, {0._second_point.name}, {0._length})'.format(self)

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

    def eval_internal(self):

        vector = self._second_point.vector - self._first_point.vector
        self._pattern.evaluator.set_current_segment(vector)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._vector = self._first_point.vector + vector.to_normalised()*self._length.value
        self._pattern.evaluator.unset_current_segment()
        self._post_eval_internal()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class EndLinePoint(Point, LineProperties):

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

    def __init__(self, pattern, id_, name,
                 base_point, angle, length,
                 mx=0, my=0,
                 line_type=None, line_color=None,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(EndLinePoint, self).__init__(id_, name, mx, my, line_type, line_color)
        Point.__init__(self, pattern, id_, name, mx, my)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        LineProperties.__init__(self, line_type, line_color)
        self._base_point = pattern.get_operation(base_point)
        self._angle = Expression(angle, pattern.evaluator)
        self._length = Expression(length, pattern.evaluator)

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

    @property
    def base_point(self):
        return self._base_point

    @property
    def length(self):
        return self._length

    @property
    def angle(self):
        return self._angle
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # {'basePoint': '1', 'name': 'A1', 'id': '2', 'angle': '0', 'length': 'waist_circ/2+10',
        #  'typeLine': 'dashDotLine', 'my': '0.264583', 'type': 'endLine', 'mx': '0.132292', 'lineColor': 'black'}

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'name', 'basePoint', 'angle', 'length', 'mx', 'my', 'lineColor', 'typeLine'),
                                          ('id_', 'name', 'base_point', 'angle', 'length', 'mx', 'my', 'line_color', 'line_type'))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return EndLinePoint(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' {0._name} = ({0._base_point.name}, {0._angle}, {0._length})'.format(self)

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

    def eval_internal(self):

        self._vector = self._base_point._vector + Vector2D.from_angle(self._angle.value)*self._length.value
        self._post_eval_internal()
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

class LineIntersectPoint(Point, LineProperties):

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

    def __init__(self, pattern, id_, name,
                 point1_line1, point2_line1, point1_line2, point2_line2,
                 mx=0, my=0,
                 line_type=None, line_color=None,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(LineIntersectPoint, self).__init__(id_, name, mx, my, line_type, line_color)
        Point.__init__(self, pattern, id_, name, mx, my)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        LineProperties.__init__(self, line_type, line_color)
        self._point1_line1 = pattern.get_operation(point1_line1)
        self._point2_line1 = pattern.get_operation(point2_line1)
        self._point1_line2 = pattern.get_operation(point1_line2)
        self._point2_line2 = pattern.get_operation(point2_line2)

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

    @property
    def point1_line1(self):
        return self._point1_line1

    @property
    def point2_line1(self):
        return self._point2_line1

    @property
    def point1_line2(self):
        return self._point1_line2

    @property
    def point2_line2(self):
        return self._point2_line2
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # {'type': 'lineIntersect', 'p2Line1': '32', 'mx': '0.132292', 'p1Line2': '10',
        #  'p2Line2': '11', 'p1Line1': '27', 'id': '39', 'my': '0.264583', 'name': 'Cp'}

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'name', 'p1Line1', 'p2Line1', 'p1Line2', 'p2Line2', 'mx', 'my', 'lineColor', 'typeLine'),
                                          ('id_', 'name', 'point1_line1', 'point2_line1', 'point1_line2', 'point2_line2', 'mx', 'my', 'line_color', 'line_type'))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return LineIntersectPoint(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' {0._name} = ({0._point1_line1.name}, {0._point2_line1.name}, {0._point1_line2.name}, {0._point2_line2.name})'.format(self)

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

    def eval_internal(self):

        line1 = Line2D(self._point1_line1.vector, self._point2_line1.vector)
        line2 = Line2D(self._point1_line2.vector, self._point2_line2.vector)
        self._vector = line1.intersection(line2)
        self._post_eval_internal()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class NormalPoint(Point, LineProperties):

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

    def __init__(self, pattern, id_, name,
                 first_point, second_point, angle, length,
                 mx=0, my=0,
                 line_type=None, line_color=None,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(NormalPoint, self).__init__(id_, name, mx, my, line_type, line_color)
        Point.__init__(self, pattern, id_, name, mx, my)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        LineProperties.__init__(self, line_type, line_color)
        self._first_point = pattern.get_operation(first_point)
        self._second_point = pattern.get_operation(second_point)
        self._angle = Expression(angle, pattern.evaluator)
        self._length = Expression(length, pattern.evaluator)

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

    @property
    def first_point(self):
        return self._first_point

    @property
    def second_point(self):
        return self._second_point

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

    @property
    def length(self):
        return self._length
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # {'my': '-4.18524', 'secondPoint': '63', 'name': 'A36', 'angle': '0', 'length': '0.5',
        #  'firstPoint': '138', 'typeLine': 'hair', 'type': 'normal', 'mx': '-1.57131', 'id': '147', 'lineColor': 'black'}

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'name', 'firstPoint', 'secondPoint', 'angle', 'length', 'mx', 'my', 'lineColor', 'typeLine'),
                                          ('id_', 'name', 'first_point', 'second_point', 'angle', 'length', 'mx', 'my', 'line_color', 'line_type'))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return NormalPoint(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' {0._name} = ({0._first_point.name}, {0._second_point.name}, {0._angle}, {0._length})'.format(self)

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

    def eval_internal(self):

        vector = self._second_point.vector - self._first_point.vector
        self._pattern.evaluator.set_current_segment(vector)
        direction = vector.to_normalised()
        direction = direction.rotate_counter_clockwise_90()
        angle = self._angle.value
        if angle:
            direction = direction.rotate_counter_clockwise(angle)
        self._vector = self._first_point.vector + direction*self._length.value
        self._pattern.evaluator.unset_current_segment()
        self._post_eval_internal()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class PointOfIntersectionPoint(Point):

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

    def __init__(self, pattern, id_, name,
                 first_point, second_point,
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # super(PointOfIntersectionPoint, self).__init__(id_, name, mx, my)
        Point.__init__(self, pattern, id_, name, mx, my)
        self._first_point = pattern.get_operation(first_point)
        self._second_point = pattern.get_operation(second_point)

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

    @property
    def first_point(self):
        return self._first_point

    @property
    def second_point(self):
        return self._second_point
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

    @staticmethod
    def from_xml(element, pattern):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # {'id': '71', 'secondPoint': '56', 'type': 'pointOfIntersection', 'firstPoint': '59', 'name': 'Nc', 'mx': '0.132292', 'my': '0.264583'}

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'name', 'firstPoint', 'secondPoint', 'mx', 'my'),
                                          ('id_', 'name', 'first_point', 'second_point', 'mx', 'my'))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return PointOfIntersectionPoint(**kwargs)

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

    def __repr__(self):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__class__.__name__ + ' {0._name} = ({0._first_point.name}, {0._second_point.name})'.format(self)
    ##############################################

    def eval_internal(self):

        self._vector = Vector2D(self._first_point.vector.x, self._second_point.vector.y)
        self._post_eval_internal()

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

class Line(Operation, LineProperties):

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

    def __init__(self, pattern, id_,
                 first_point, second_point,
                 line_type=None, line_color=None,
                 ):

        Operation.__init__(self, pattern, id_)
        LineProperties.__init__(self, line_type, line_color)
        self._first_point = pattern.get_operation(first_point)
        self._second_point = pattern.get_operation(second_point)

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

    @property
    def first_point(self):
        return self._first_point

    @property
    def second_point(self):
        return self._second_point

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

    @staticmethod
    def from_xml(element, pattern):

        # {'typeLine': 'hair', 'lineColor': 'black', 'firstPoint': '74', 'secondPoint': '72', 'id': '76'}

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'firstPoint', 'secondPoint', 'lineColor', 'typeLine'),
                                          ('id_', 'first_point', 'second_point', 'line_color', 'line_type'))
        return Line(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' ({0._first_point.name}, {0._second_point.name})'.format(self)

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

    def eval_internal(self):

        pass

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

class Curve(Operation, LineProperties):

    __SPLINE_TYPE__ = (
        'cubicBezier', # to be implemented
        'cubicBezierPath', # to be implemented
        'pathInteractive', # to be implemented
        'simpleInteractive',
        )

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

    def __init__(self, pattern, id_,
                 first_point, second_point,
                 angle1, length1,
                 angle2, length2,
                 line_type=None, line_color=None,
                 ):

        Operation.__init__(self, pattern, id_)
        LineProperties.__init__(self, line_type, line_color)
        self._first_point = pattern.get_operation(first_point)
        self._second_point = pattern.get_operation(second_point)
        self._angle1 = Expression(angle1, pattern.evaluator)
        self._length1 = Expression(length1, pattern.evaluator)
        self._angle2 = Expression(angle2, pattern.evaluator)
        self._length2 = Expression(length2, pattern.evaluator)

        self._control_point1 = None # Fixme: not yet computed
        self._control_point2 = None

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

    @property
    def first_point(self):
        return self._first_point

    @property
    def second_point(self):
        return self._second_point

    @property
    def angle1(self):
        return self._angle1

    @property
    def length1(self):
        return self._length1

    @property
    def angle2(self):
        return self._angle2

    @property
    def length2(self):
        return self._length2

    @property
    def control_point1(self):
        return self._control_point1

    @property
    def control_point2(self):
        return self._control_point2

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

    @staticmethod
    def from_xml(element, pattern):

        # 'type': 'simpleInteractive',
        #   'length2': '8.65783', 'angle2': '85.3921',
        #    'point4': '31',
        #    'color': 'black',
        #    'length1': '8.85757', 'angle1': '251.913',
        #    'point1': '20',
        #    'id': '97'
        # }

        kwargs = Operation.xml_attributes(element, pattern,
                                          ('id', 'point1', 'point4', 'angle1', 'length1', 'angle2', 'length2', 'color'),
                                          ('id_', 'first_point', 'second_point', 'angle1', 'length1', 'angle2', 'length2', 'line_color'))
        return Curve(**kwargs)

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

    def __repr__(self):

        return self.__class__.__name__ + ' ({0._first_point.name}, {0._second_point.name}, {0._angle1}, {0._length1}, {0._angle2}, {0._length2})'.format(self)

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

    def eval_internal(self):

        self._control_point1 = self.first_point.vector + Vector2D.from_angle(self._angle1.value)*self._length1.value
        self._control_point2 = self.second_point.vector + Vector2D.from_angle(self._angle2.value)*self._length2.value
        self._logger.info("Control points : {} {}".format(self._control_point1, self._control_point2))

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

class ValParser:

    _logger = _module_logger.getChild('ValParser')

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

    def parse(self, val_path):

        with open(val_path, 'rb') as f:
            source = f.read()

        tree = etree.fromstring(source)

        measurements_path = self._get_xpath_element(tree, 'measurements').text
        measurements = VitParser().parse(measurements_path)

        pattern = Pattern(measurements)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        elements = self._get_xpath_element(tree, 'draw/calculation')
        for element in elements:
            operation = Operation.from_xml(element, pattern)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            pattern.add(operation)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        return pattern

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

    def _get_xpath_element(self, root, path):

        return root.xpath(path)[0]