#################################################################################################### # # 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'), )