####################################################################################################
#
# 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 .
#
####################################################################################################
"""This module implements the val XML file format and is designed so as to decouple the XML format
and the calculation API.
The purpose of each XmlObjectAdaptator sub-classes is to serve as a bidirectional adaptor between
the XML format and the API.
Valentina File Format Concept
* all entities which are referenced later in the file are identified by a unique positive integer
over the file, usually incremented from 1.
* a file contains one or several "pieces"
* pieces correspond to independent scopes, one cannot access calculations of another piece
* pieces share the same referential, usually the root point of a piece is placed next
to the previous piece
* a piece has "calculations" and "details"
* a calculations corresponds to a point, a segment, or a Bézier curve ...
* a detail corresponds to a garment piece defined by segments and curves
* one can define several details within a piece
"""
####################################################################################################
__all__ = [
'Point',
'Line',
'Spline',
'ModelingPoint',
'ModelingSpline',
'Detail',
'DetailData',
'DetailPatternInfo',
'DetailGrainline',
'DetailNode',
]
####################################################################################################
import Patro.Pattern.SketchOperation as SketchOperation
from Patro.Common.Xml.Objectivity import (
Attribute,
BoolAttribute,
IntAttribute, FloatAttribute,
StringAttribute,
XmlObjectAdaptator
)
from Patro.GeometryEngine.Vector import Vector2D
from Patro.GraphicStyle import Colors, StrokeStyle
####################################################################################################
class ColorAttribute(Attribute):
__COLORS__ = (
'black',
'blue',
'cornflowerblue',
'darkBlue',
'darkGreen',
'darkRed',
'darkviolet',
'deeppink',
'deepskyblue',
'goldenrod',
'green',
'lightsalmon',
'lime',
'mediumseagreen',
'orange',
'violet',
'yellow',
)
##############################################
@classmethod
def from_xml(cls, value):
return Colors.ensure_color(value)
####################################################################################################
class StrokeStyleAttribute(Attribute):
__STROKE_STYLE__ = {
'dashDotDotLine': StrokeStyle.DashDotDotLine,
'dashDotLine': StrokeStyle.DashDotLine,
'dashLine': StrokeStyle.DashLine,
'dotLine': StrokeStyle.DotLine,
'hair': StrokeStyle.SolidLine, # should be solid
'none': StrokeStyle.NoPen,
}
##############################################
@classmethod
def from_xml(cls, value):
return cls.__STROKE_STYLE__[value]
####################################################################################################
class ValentinaBuiltInVariables:
# defined in libs/ifc/ifcdef.cpp
current_length = 'CurrentLength'
current_seam_allowance = 'CurrentSeamAllowance'
angle_line = 'AngleLine_'
increment = 'Increment_'
line = 'Line_'
measurement = 'M_'
seg = 'Seg_'
arc = 'ARC_'
elarc = 'ELARC_'
spl = 'SPL_'
angle1 = 'Angle1'
angle2 = 'Angle2'
c1_length = 'C1Length'
c2_length = 'C2Length'
radius = 'Radius'
rotation = 'Rotation'
spl_path = 'SplPath'
angle1_arc = angle1 + arc
angle1_elarc = angle1 + elarc
angle1_spl_path = angle1 + spl_path
angle1_spl = angle1 + spl
angle2_arc = angle2 + arc
angle2_elarc = angle2 + elarc
angle2_spl_path = angle2 + spl_path
angle2_spl = angle2 + spl
c1_length_spl_path = c1_length + spl_path
c1_length_spl = c1_length + spl
c2_length_spl_path = c2_length + spl_path
c2_length_spl = c2_length + spl
radius1_elarc = radius + '1' + elarc
radius2_elarc = radius + '2' + elarc
radius_arc = radius + arc
rotation_elarc = rotation + elarc
####################################################################################################
VALENTINA_ATTRIBUTES = (
'aScale',
'angle',
'angle1',
'angle2',
'arc',
'axisP1',
'axisP2',
'axisType',
'baseLineP1',
'baseLineP2',
'basePoint',
'c1Center',
'c1Radius',
'c2Center',
'c2Radius',
'cCenter',
'cRadius',
'center',
'closed',
'color',
'crossPoint',
'curve',
'curve1',
'curve2',
'cut',
'dartP1',
'dartP2',
'dartP3',
'duplicate',
'firstArc',
'firstPoint',
'firstToCountour',
'forbidFlipping',
'forceFlipping',
'hCrossPoint',
'height',
'idObject',
'inLayout',
'kAsm1',
'kAsm2',
'kCurve',
'lastToCountour',
'length',
'length1',
'length2',
'lineColor',
'mx',
'mx1',
'mx2',
'my',
'my1',
'my2',
'name',
'name1',
'name2',
'p1Line',
'p1Line1',
'p1Line2',
'p2Line',
'p2Line1',
'p2Line2',
'pShoulder',
'pSpline',
'pathPoint',
'penStyle',
'placeLabelType',
'point1',
'point2',
'point3',
'point4',
'radius',
'radius1',
'radius2',
'rotationAngle',
'secondArc',
'secondPoint',
'showLabel',
'showLabel1',
'showLabel2',
'suffix',
'tangent',
'thirdPoint',
'type',
'typeLine',
'vCrossPoint',
'version',
'width',
'x',
'y',
)
####################################################################################################
class MxMyMixin:
__attributes__ = (
FloatAttribute('mx'),
FloatAttribute('my'),
)
####################################################################################################
class CalculationMixin:
__attributes__ = (
IntAttribute('id'),
)
__operation__ = None # operation's class
##############################################
def call_operation_function(self, sketch, kwargs):
# Fixme: map valentina name -> ...
method = getattr(sketch, self.__operation__.__name__)
return method(**kwargs)
##############################################
def to_operation(self, sketch):
raise NotImplementedError
##############################################
@classmethod
def from_operation(operation):
raise NotImplementedError
####################################################################################################
class CalculationTypeMixin(CalculationMixin):
##############################################
def to_xml(self):
return XmlObjectAdaptator.to_xml(self, type=self.__type__)
####################################################################################################
class LinePropertiesMixin:
__attributes__ = (
ColorAttribute('line_color', 'lineColor'),
StrokeStyleAttribute('line_style', 'typeLine'),
)
class XyMixin:
__attributes__ = (
StringAttribute('x'),
StringAttribute('y'),
)
class FirstSecondPointMixin:
__attributes__ = (
IntAttribute('first_point', 'firstPoint'),
IntAttribute('second_point', 'secondPoint'),
)
class FirstSecondThirdPointMixin(FirstSecondPointMixin):
__attributes__ = (
IntAttribute('third_point', 'thirdPoint'),
)
class BasePointMixin:
__attributes__ = (
IntAttribute('base_point', 'basePoint'),
)
class Line1Mixin:
__attributes__ = (
IntAttribute('point1_line1', 'p1Line1'),
IntAttribute('point2_line1', 'p2Line1'),
)
class Line2Mixin:
__attributes__ = (
IntAttribute('point1_line2', 'p1Line2'),
IntAttribute('point2_line2', 'p2Line2'),
)
class Line12Mixin(Line1Mixin, Line2Mixin):
pass
class LengthMixin:
__attributes__ = (
StringAttribute('length'),
)
class AngleMixin:
__attributes__ = (
StringAttribute('angle'),
)
class LengthAngleMixin(LengthMixin, AngleMixin):
pass
####################################################################################################
class PointMixin(CalculationTypeMixin, MxMyMixin):
__tag__ = 'point'
__attributes__ = (
StringAttribute('name'),
)
##############################################
def to_operation(self, sketch):
kwargs = self.to_dict(exclude=('mx', 'my')) # id'
kwargs['label_offset'] = Vector2D(self.mx, self.my)
return self.call_operation_function(sketch, kwargs)
##############################################
@classmethod
def from_operation(cls, operation):
kwargs = cls.get_dict(operation, exclude=('mx', 'my'))
label_offset = operation.label_offset
kwargs['mx'] = label_offset.x
kwargs['my'] = label_offset.y
return cls(**kwargs)
####################################################################################################
class PointLinePropertiesMixin(PointMixin, LinePropertiesMixin):
pass
####################################################################################################
class AlongLinePoint(PointLinePropertiesMixin, FirstSecondPointMixin, LengthMixin, XmlObjectAdaptator):
#
__type__ = 'alongLine'
__operation__ = SketchOperation.AlongLinePoint
####################################################################################################
class BissectorPoint(PointLinePropertiesMixin, FirstSecondThirdPointMixin, LengthMixin, XmlObjectAdaptator):
#
__type__ = 'bisector'
# __operation__ = SketchOperation.BissectorPoint
####################################################################################################
# __type__ = 'curveIntersectAxis'
#
# __type__ = 'cutArc'
#
# __type__ = 'cutSpline'
#
# __type__ = 'cutSplinePath'
#
####################################################################################################
class EndLinePoint(PointLinePropertiesMixin, BasePointMixin, LengthAngleMixin, XmlObjectAdaptator):
#
__type__ = 'endLine'
__operation__ = SketchOperation.EndLinePoint
####################################################################################################
class HeightPoint(PointLinePropertiesMixin, BasePointMixin, Line1Mixin, XmlObjectAdaptator):
#
__type__ = 'height'
# __operation__ = SketchOperation.HeightPoint
####################################################################################################
class LineIntersectPoint(PointMixin, Line12Mixin, XmlObjectAdaptator):
#
__type__ = 'lineIntersect'
__operation__ = SketchOperation.LineIntersectPoint
####################################################################################################
class LineIntersectAxisPoint(PointLinePropertiesMixin, BasePointMixin, Line1Mixin, AngleMixin, XmlObjectAdaptator):
#
__type__ = 'lineIntersectAxis'
# __operation__ = SketchOperation.LineIntersectAxisPoint
####################################################################################################
class NormalPoint(PointLinePropertiesMixin, FirstSecondPointMixin, LengthAngleMixin, XmlObjectAdaptator):
#
__type__ = 'normal'
__operation__ = SketchOperation.NormalPoint
####################################################################################################
# __type__ = 'pointFromArcAndTangent'
#
# __type__ = 'pointFromCircleAndTangent'
#
# __type__ = 'pointOfContact'
#
####################################################################################################
class PointOfIntersection(PointMixin, FirstSecondPointMixin, XmlObjectAdaptator):
#
__type__ = 'pointOfIntersection'
__operation__ = SketchOperation.PointOfIntersection
####################################################################################################
# __type__ = 'pointOfIntersectionArcs'
#
# __type__ = 'pointOfIntersectionCircles'
#
# __type__ = 'pointOfIntersectionCurves'
#
####################################################################################################
class ShoulderPoint(PointLinePropertiesMixin, Line1Mixin, LengthMixin, XmlObjectAdaptator):
#
__type__ = 'shoulder'
# __operation__ = SketchOperation.ShoulderPoint
__attributes__ = (
IntAttribute('shoulder_point', 'pShoulder'),
)
####################################################################################################
class SinglePoint(PointMixin, XyMixin, XmlObjectAdaptator):
#
__type__ = 'single'
__operation__ = SketchOperation.SinglePoint
####################################################################################################
# __type__ = 'triangle'
#
# __type__ = 'trueDarts'
#
####################################################################################################
class Point:
# We cannot use a metaclass to auto-register due to XmlObjectAdaptator (right ?)
__TYPES__ = {
'alongLine': AlongLinePoint,
'bisector': None,
'curveIntersectAxis': None,
'cutArc': None,
'cutSpline': None,
'cutSplinePath': None,
'endLine': EndLinePoint,
'height': None,
'lineIntersect': LineIntersectPoint,
'lineIntersectAxis': None,
'normal': NormalPoint,
'pointFromArcAndTangent': None,
'pointFromCircleAndTangent': None,
'pointOfContact': None,
'pointOfIntersection': PointOfIntersection,
'pointOfIntersectionArcs': None,
'pointOfIntersectionCircles': None,
'pointOfIntersectionCurves': None,
'shoulder': None,
'single': SinglePoint,
'triangle': None,
'trueDarts': None,
}
####################################################################################################
class Line(CalculationMixin, LinePropertiesMixin, FirstSecondPointMixin, XmlObjectAdaptator):
#
__tag__ = 'line'
__operation__ = SketchOperation.Line
##############################################
def to_operation(self, sketch):
return self.call_operation_function(sketch, self.to_dict()) # exclude=('id')
##############################################
@classmethod
def from_operation(cls, operation):
kwargs = cls.get_dict(operation)
return cls(**kwargs)
####################################################################################################
class SplineMixin(CalculationTypeMixin):
__tag__ = 'spline'
####################################################################################################
class SimpleInteractiveSpline(SplineMixin, XmlObjectAdaptator):
#
__type__ = 'simpleInteractive'
__attributes__ = (
IntAttribute('first_point', 'point1'),
IntAttribute('second_point', 'point4'),
StringAttribute('length1'),
StringAttribute('length2'),
StringAttribute('angle1'),
StringAttribute('angle2'),
StringAttribute('line_color', 'color'),
)
__operation__ = SketchOperation.SimpleInteractiveSpline
##############################################
def to_operation(self, sketch):
return self.call_operation_function(sketch, self.to_dict()) # exclude=('id')
##############################################
@classmethod
def from_operation(cls, operation):
kwargs = cls.get_dict(operation)
return cls(**kwargs)
####################################################################################################
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
####################################################################################################
class Spline:
# We cannot use a metaclass to auto-register due to XmlObjectAdaptator (right ?)
__TYPES__ = {
'cubicBezier': None,
'cubicBezierPath': None,
'pathInteractive': None,
'simpleInteractive': SimpleInteractiveSpline,
}
####################################################################################################
#
#
# __ARC_TYPE__ = (
# 'arcWithLength',
# 'simple',
# )
#
# __ELLIPSE_TYPE__ = (
# 'simple',
# )
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
# __OPERATION_TYPE__ = (
# 'flippingByAxis',
# 'flippingByLine',
# 'moving',
# 'rotation',
# )
####################################################################################################
class ModelingItemMixin:
__attributes__ = (
IntAttribute('id'),
IntAttribute('object_id', 'idObject'),
StringAttribute('type'),
BoolAttribute('in_use', 'inUse'),
)
###################################################################################################
class ModelingPoint(ModelingItemMixin, MxMyMixin, XmlObjectAdaptator):
#
pass
####################################################################################################
class ModelingSpline(ModelingItemMixin, XmlObjectAdaptator):
#
pass
####################################################################################################
class Detail(MxMyMixin, XmlObjectAdaptator):
#
__attributes__ = (
IntAttribute('id'),
IntAttribute('version'),
BoolAttribute('forbidFlipping'),
IntAttribute('width'),
BoolAttribute('united'),
StringAttribute('name'),
BoolAttribute('inLayout'),
BoolAttribute('seamAllowance'),
)
##############################################
def __init__(self, modeling, *args, **kwargs):
XmlObjectAdaptator.__init__(self, *args, **kwargs)
self._modeling = modeling
self._nodes = []
##############################################
def append_node(self, node):
self._nodes.append(node)
##############################################
def iter_on_nodes(self):
for node in self._nodes:
yield node, self._modeling[node.object_id]
####################################################################################################
class VisibleRotationMixin:
__attributes__ = (
BoolAttribute('visible'),
IntAttribute('rotation'),
)
####################################################################################################
class HeightWidthMixin:
__attributes__ = (
IntAttribute('height'),
IntAttribute('width'),
)
####################################################################################################
class FontSizeMixin:
__attributes__ = (
IntAttribute('fontSize'),
)
####################################################################################################
class DetailData(HeightWidthMixin, MxMyMixin, FontSizeMixin, VisibleRotationMixin, XmlObjectAdaptator):
#
__attributes__ = (
StringAttribute('letter'),
)
####################################################################################################
class DetailPatternInfo(HeightWidthMixin, MxMyMixin, FontSizeMixin, VisibleRotationMixin, XmlObjectAdaptator):
#
pass
####################################################################################################
class DetailGrainline(MxMyMixin, VisibleRotationMixin, XmlObjectAdaptator):
#
__attributes__ = (
IntAttribute('arrows'),
IntAttribute('length'),
)
####################################################################################################
class DetailNode(XmlObjectAdaptator):
#
#
__attributes__ = (
IntAttribute('object_id', 'idObject'),
StringAttribute('type'),
BoolAttribute('reverse'),
)