Skip to content
...@@ -27,11 +27,11 @@ import numpy as np ...@@ -27,11 +27,11 @@ import numpy as np
from IntervalArithmetic import Interval2D, IntervalInt2D from IntervalArithmetic import Interval2D, IntervalInt2D
from Patro.Common.Math.Functions import sign, trignometric_clamp #, is_in_trignometric_range from Patro.Common.Math.Functions import sign, trignometric_clamp #, is_in_trignometric_range
from .Primitive import Primitive2D from .Primitive import Primitive, Primitive2DMixin
#################################################################################################### ####################################################################################################
class Vector2DBase(Primitive2D): class Vector2DBase(Primitive, Primitive2DMixin):
__data_type__ = None __data_type__ = None
...@@ -202,6 +202,7 @@ class Vector2DInt(Vector2DBase): ...@@ -202,6 +202,7 @@ class Vector2DInt(Vector2DBase):
############################################## ##############################################
@property
def bounding_box(self): def bounding_box(self):
x, y = self.x, self.y x, y = self.x, self.y
return IntervalInt2D((x, x) , (y, y)) return IntervalInt2D((x, x) , (y, y))
...@@ -214,6 +215,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -214,6 +215,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def bounding_box(self): def bounding_box(self):
x, y = self.x, self.y x, y = self.x, self.y
return Interval2D((x, x) , (y, y)) return Interval2D((x, x) , (y, y))
...@@ -226,18 +228,21 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -226,18 +228,21 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def magnitude_square(self): def magnitude_square(self):
"""Return the square of the magnitude of the vector""" """Return the square of the magnitude of the vector"""
return np.dot(self._v, self._v) return np.dot(self._v, self._v)
############################################## ##############################################
@property
def magnitude(self): def magnitude(self):
"""Return the magnitude of the vector""" """Return the magnitude of the vector"""
return math.sqrt(self.magnitude_square()) return math.sqrt(self.magnitude_square)
############################################## ##############################################
@property
def orientation(self): def orientation(self):
"""Return the orientation in degree""" """Return the orientation in degree"""
...@@ -262,7 +267,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -262,7 +267,7 @@ class Vector2DFloatBase(Vector2DBase):
elif self.y == 0: elif self.y == 0:
return 0 if self.x >= 0 else 180 return 0 if self.x >= 0 else 180
else: else:
orientation = math.degrees(math.atan(self.tan())) orientation = math.degrees(math.atan(self.tan))
if self.x < 0: if self.x < 0:
if self.y > 0: if self.y > 0:
orientation += 180 orientation += 180
...@@ -292,6 +297,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -292,6 +297,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def normal(self): def normal(self):
"""Return a new vector equal to self rotated of 90 degree in the counter clockwise direction """Return a new vector equal to self rotated of 90 degree in the counter clockwise direction
...@@ -305,6 +311,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -305,6 +311,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def anti_normal(self): def anti_normal(self):
"""Return a new vector equal to self rotated of 90 degree in the clockwise direction """Return a new vector equal to self rotated of 90 degree in the clockwise direction
...@@ -318,6 +325,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -318,6 +325,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def parity(self): def parity(self):
"""Return a new vector equal to self rotated of 180 degree """Return a new vector equal to self rotated of 180 degree
...@@ -332,6 +340,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -332,6 +340,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def tan(self): def tan(self):
"""Return the tangent""" """Return the tangent"""
# RuntimeWarning: divide by zero encountered in double_scalars # RuntimeWarning: divide by zero encountered in double_scalars
...@@ -339,6 +348,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -339,6 +348,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def inverse_tan(self): def inverse_tan(self):
"""Return the inverse tangent""" """Return the inverse tangent"""
return self.x / self.y return self.x / self.y
...@@ -357,9 +367,23 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -357,9 +367,23 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
def is_parallel(self, other): # perp dot product
# perp = (-y1, x1)
# perp dot = -y1*x2 + x1*y2 = x1*y2 - x2*y1
perp_dot = cross
##############################################
def is_parallel(self, other, cross=False):
"""Self is parallel with other""" """Self is parallel with other"""
return round(self.cross(other), 7) == 0
cross = self.cross(other)
test = round(cross, 7) == 0
if cross:
return test, cross
else:
return test
############################################## ##############################################
...@@ -371,14 +395,14 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -371,14 +395,14 @@ class Vector2DFloatBase(Vector2DBase):
def cos_with(self, direction): def cos_with(self, direction):
"""Return the cosinus of self with direction""" """Return the cosinus of self with direction"""
cos = direction.dot(self) / (direction.magnitude() * self.magnitude()) cos = direction.dot(self) / (direction.magnitude * self.magnitude)
return trignometric_clamp(cos) return trignometric_clamp(cos)
############################################## ##############################################
def projection_on(self, direction): def projection_on(self, direction):
"""Return the projection of self on direction""" """Return the projection of self on direction"""
return direction.dot(self) / direction.magnitude() return direction.dot(self) / direction.magnitude
############################################## ##############################################
...@@ -387,7 +411,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -387,7 +411,7 @@ class Vector2DFloatBase(Vector2DBase):
"""Return the sinus of self with other""" """Return the sinus of self with other"""
# turn from direction to self # turn from direction to self
sin = direction.cross(self) / (direction.magnitude() * self.magnitude()) sin = direction.cross(self) / (direction.magnitude * self.magnitude)
return trignometric_clamp(sin) return trignometric_clamp(sin)
...@@ -395,7 +419,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -395,7 +419,7 @@ class Vector2DFloatBase(Vector2DBase):
def deviation_with(self, direction): def deviation_with(self, direction):
"""Return the deviation of self with other""" """Return the deviation of self with other"""
return direction.cross(self) / direction.magnitude() return direction.cross(self) / direction.magnitude
############################################## ##############################################
...@@ -422,7 +446,7 @@ class Vector2D(Vector2DFloatBase): ...@@ -422,7 +446,7 @@ class Vector2D(Vector2DFloatBase):
@staticmethod @staticmethod
def from_angle(angle): def from_angle(angle):
"""Create the unitary vector (cos(angle), sin(angle)). The *angle* is in degree.""" """Create the unitary vector (cos(angle), sin(angle)). *angle* is in degree."""
rad = math.radians(angle) rad = math.radians(angle)
...@@ -430,6 +454,15 @@ class Vector2D(Vector2DFloatBase): ...@@ -430,6 +454,15 @@ class Vector2D(Vector2DFloatBase):
############################################## ##############################################
@staticmethod
def from_polar(radius, angle):
"""Create the polar vector (radius*cos(angle), radius*sin(angle)). *angle* is in degree."""
return Vector2D.from_angle(angle) * radius # Fixme: classmethod
##############################################
@staticmethod @staticmethod
def middle(p0, p1): def middle(p0, p1):
"""Return the middle point.""" """Return the middle point."""
...@@ -465,13 +498,13 @@ class Vector2D(Vector2DFloatBase): ...@@ -465,13 +498,13 @@ class Vector2D(Vector2DFloatBase):
def normalise(self): def normalise(self):
"""Normalise the vector""" """Normalise the vector"""
self._v /= self.magnitude() self._v /= self.magnitude
############################################## ##############################################
def to_normalised(self): def to_normalised(self):
"""Return a normalised vector""" """Return a normalised vector"""
return NormalisedVector2D(self._v / self.magnitude()) return NormalisedVector2D(self._v / self.magnitude)
############################################## ##############################################
...@@ -490,7 +523,7 @@ class NormalisedVector2D(Vector2DFloatBase): ...@@ -490,7 +523,7 @@ class NormalisedVector2D(Vector2DFloatBase):
super(NormalisedVector2D, self).__init__(*args) super(NormalisedVector2D, self).__init__(*args)
#! if self.magnitude() != 1.: #! if self.magnitude != 1.:
#! raise ValueError("Magnitude != 1") #! raise ValueError("Magnitude != 1")
# if not (is_in_trignometric_range(self.x) and # if not (is_in_trignometric_range(self.x) and
......
...@@ -22,3 +22,13 @@ ...@@ -22,3 +22,13 @@
implements standard primitives like line, segment and Bezier curve. implements standard primitives like line, segment and Bezier curve.
""" """
####################################################################################################
from .Primitive import Primitive2DMixin
from .Vector import Vector2D
####################################################################################################
# Fixme: to fix cyclic import issue
Primitive2DMixin.__vector_cls__ = Vector2D
####################################################################################################
#
# 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/>.
#
####################################################################################################
__all__ = [
'DxfPainter',
]
####################################################################################################
import logging
from Patro.GeometryEngine.Vector import Vector2D
from .Painter import Painter
try:
import ezdxf
except ImportError:
ezdxf = None
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class DxfPainterBase(Painter):
##############################################
def __init__(self, path, scene, paper):
super().__init__(scene)
self._path = path
self._paper = paper
self._coordinates = {}
##############################################
def _cast_position(self, position):
# Fixme: to base class
if isinstance(position, str):
return self._coordinates[position]
elif isinstance(position, Vector2D):
return position
##############################################
def paint_CoordinateItem(self, item):
# Fixme: to base class
self._coordinates[item.name] = item.position
####################################################################################################
class EzdxfPainter(DxfPainterBase):
# AC1009 AutoCAD R12
# AC1012 AutoCAD R13 -> R2000
# AC1014 AutoCAD R14 -> R2000
# AC1015 AutoCAD R2000
# AC1018 AutoCAD R2004
# AC1021 AutoCAD R2007
# AC1024 AutoCAD R2010
# AC1027 AutoCAD R2013
# AC1032 AutoCAD R2018
__STROKE_STYLE__ = {
None: None,
'dashDotLine': 'DASHDOT',
'dotLine': 'DOTTED',
'hair': 'CONTINUOUS',
'none': 'PHANTOM', # Fixme: ???
'solid': 'CONTINUOUS',
}
__COLOR__ = {
None : None,
'black': 0,
}
##############################################
def __init__(self, path, scene, paper, dxf_version='R2010'):
super().__init__(path, scene, paper)
self._dxf_version = dxf_version
self._drawing = ezdxf.new(dxf_version)
self._model_space= self._drawing.modelspace() # add new entities to the model space
self._model_space.page_setup(
size=(self._paper.widh, self._paper.height),
# margins=(top, right, bottom, left),
units='mm',
)
# print('Available line types:')
# for line_type in self._drawing.linetypes:
# print('{}: {}'.format(line_type.dxf.name, line_type.dxf.description))
self.paint()
self._drawing.saveas(path)
##############################################
def _cast_position(self, position):
position = super()._cast_position(position)
position = position.clone() * 10 # Fixme: cm -> mm
return position
##############################################
def _cast_positions(self, positions):
return [list(self._cast_position(position)) for position in positions]
##############################################
def _graphic_style(self, item):
# cf. https://ezdxf.readthedocs.io/en/latest/graphic_base_class.html#common-dxf-attributes-for-dxf-r13-or-later
path_syle = item.path_style
color = self.__COLOR__[path_syle.stroke_color] # see also true_color color_name (AutoCAD R2004)
line_type = self.__STROKE_STYLE__[path_syle.stroke_style]
# https://ezdxf.readthedocs.io/en/latest/graphic_base_class.html#GraphicEntity.dxf.lineweight
# line_weight = float(path_syle.line_width.replace('pt', '')) / 3 # Fixme: pt ???
return {'linetype': line_type, 'color': color} # 'lineweight':line_weight
##############################################
def paint_TextItem(self, item):
position = self._cast_position(item.position)
# Fixme: anchor position
# https://ezdxf.readthedocs.io/en/latest/tutorials/text.html
self._model_space.add_text(item.text).set_pos(list(position), align='CENTER')
##############################################
def paint_CircleItem(self, item):
# in fact a graphic dot
pass # skipped for DXF
##############################################
def paint_SegmentItem(self, item):
positions = self._cast_position(item.positions)
self._model_space.add_line(
*positions,
dxfattribs=self._graphic_style(item),
)
##############################################
def paint_CubicBezierItem(self, item):
positions = self._cast_position(item.positions)
# https://ezdxf.readthedocs.io/en/latest/layouts.html#Layout.add_open_spline
self._model_space.add_open_spline(
*positions,
degree=3,
dxfattribs=self._graphic_style(item),
)
####################################################################################################
_driver_to_cls = {
'ezdxf': EzdxfPainter,
}
def DxfPainter(*args, **kwargs):
"""Wrapper to driver classes"""
driver = kwargs.get('driver', 'ezdxf')
del kwargs['driver']
return _driver_to_cls[driver](*args, **kwargs)
####################################################################################################
#
# 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/>.
#
####################################################################################################
####################################################################################################
import logging
from matplotlib import pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
from Patro.GeometryEngine.Vector import Vector2D
from .Painter import Painter, Tiler
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class MplPainter(Painter):
__STROKE_STYLE__ = {
None: None,
'dashDotLine': 'dashdot', # '--'
'dotLine': 'dotted', # ':'
'hair': 'solid', # '-'
'none': None,
'solid': 'solid',
}
__COLOR__ = {
None : None,
'black': 'black',
}
##############################################
def __init__(self, scene, paper):
super().__init__(scene)
self._paper = paper
self._figure = plt.figure(
# figsize=(self._paper.width_in, self._paper.height_in),
# dpi=200,
)
self._axes = self._figure.add_subplot(111)
bounding_box = scene.bounding_box
factor = 10 / 100
x_margin = bounding_box.x.length * factor
y_margin = bounding_box.y.length * factor
margin = max(x_margin, y_margin)
bounding_box = bounding_box.clone().enlarge(margin)
self._axes.set_xlim(bounding_box.x.inf, bounding_box.x.sup)
self._axes.set_ylim(bounding_box.y.inf, bounding_box.y.sup)
self._axes.set_aspect('equal')
self._coordinates = {}
self.paint()
##############################################
def show(self):
plt.show()
##############################################
def _add_path(self, item, vertices, codes):
path = Path(vertices, codes)
path_syle = item.path_style
color = self.__COLOR__[path_syle.stroke_color]
line_style = self.__STROKE_STYLE__[path_syle.stroke_style]
line_width = float(path_syle.line_width.replace('pt', '')) / 3
patch = patches.PathPatch(path, edgecolor=color, facecolor='none', linewidth=line_width, linestyle=line_style)
self._axes.add_patch(patch)
##############################################
def paint_CoordinateItem(self, item):
self._coordinates[item.name] = item.position
##############################################
def _cast_position(self, position):
if isinstance(position, str):
return self._coordinates[position]
elif isinstance(position, Vector2D):
return position
##############################################
def paint_TextItem(self, item):
position = self._cast_position(item.position)
# Fixme: anchor position
self._axes.text(position.x, position.y, item.text)
##############################################
def paint_CircleItem(self, item):
center = list(self._cast_position(item.position))
circle = plt.Circle(center, .5, color='black')
self._axes.add_artist(circle)
##############################################
def paint_SegmentItem(self, item):
vertices = [list(self._cast_position(position)) for position in item.positions]
codes = [Path.MOVETO, Path.LINETO]
self._add_path(item, vertices, codes)
##############################################
def paint_CubicBezierItem(self, item):
vertices = [list(self._cast_position(position)) for position in item.positions]
codes = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
self._add_path(item, vertices, codes)
...@@ -83,6 +83,14 @@ class PaperSize: ...@@ -83,6 +83,14 @@ class PaperSize:
def width(self): def width(self):
return self._width return self._width
@property
def height_in(self):
return self._height / 25.4
@property
def width_in(self):
return self._width / 25.4
@property @property
def margin(self): def margin(self):
return self._margin return self._margin
####################################################################################################
#
# 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/>.
#
####################################################################################################
__all__ = [
'PdfPainter',
]
####################################################################################################
import logging
from Patro.GeometryEngine.Vector import Vector2D
from .Painter import Painter, Tiler
try:
import reportlab
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm, cm
except ImportError:
reportlab = None
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class PdfPainterBase(Painter):
##############################################
def __init__(self, path, scene, paper):
super().__init__(scene)
self._path = path
self._paper = paper
self._coordinates = {}
##############################################
def _cast_position(self, position):
# Fixme: to base class
if isinstance(position, str):
return self._coordinates[position]
elif isinstance(position, Vector2D):
return position
##############################################
def paint_CoordinateItem(self, item):
# Fixme: to base class
self._coordinates[item.name] = item.position
##############################################
@staticmethod
def mm_to_pt(x):
return x * 72 / 25.4
##############################################
@property
def page_size(self):
return [self.mm_to_pt(x) for x in (self._paper.width, self._paper.height)]
####################################################################################################
class ReportlabPainter(PdfPainterBase):
__STROKE_STYLE__ = {
None: None,
'dashDotLine': (6, 3),
'dotLine': (1, 2),
'hair': (),
'none': (), # Fixme: ???
'solid': (),
}
__COLOR__ = {
None : None,
'black': 'black',
}
##############################################
def __init__(self, path, scene, paper):
super().__init__(path, scene, paper)
self._canvas = canvas.Canvas(
str(self._path),
pagesize=self.page_size,
# bottomup=1,
# pageCompression=0,
# encoding=rl_config.defaultEncoding,
# verbosity=0,
# encrypt=None,
)
# self._canvas.setAuthor()
# self._canvas.addOutlineEntry(title, key, level=0, closed=None)
# self._canvas.setTitle(title)
# self._canvas.setSubject(subject)
bounding_box = scene.bounding_box
print(bounding_box, bounding_box.x.length, bounding_box.y.length)
self._canvas.translate(-bounding_box.x.inf*cm, -bounding_box.y.inf*cm)
self._canvas.translate(1*cm, 1*cm) # Fixme: margin
self._canvas.setFont('Times-Roman', 20)
self.paint()
self._canvas.showPage()
self._canvas.save()
##############################################
def _cast_position(self, position):
position = super()._cast_position(position)
return position.clone() * cm * .7 # Fixme:
##############################################
def _cast_positions(self, positions):
vertices = []
for position in positions:
vertices += list(self._cast_position(position))
return vertices
##############################################
def _set_graphic_style(self, item):
path_syle = item.path_style
color = self.__COLOR__[path_syle.stroke_color]
self._canvas.setStrokeColor(color)
line_style = self.__STROKE_STYLE__[path_syle.stroke_style]
self._canvas.setDash(*line_style)
line_width = float(path_syle.line_width.replace('pt', '')) / 3 # Fixme: pt ???
self._canvas.setLineWidth(line_width)
##############################################
def paint_TextItem(self, item):
position = self._cast_position(item.position)
# Fixme: anchor position
self._canvas.drawString(position.x, position.y, item.text)
##############################################
def paint_CircleItem(self, item):
position = self._cast_position(item.position)
self._canvas.saveState()
self._canvas.setFillColor('black')
self._canvas.circle(position.x, position.y, 2*mm, fill=1)
self._canvas.restoreState()
##############################################
def paint_SegmentItem(self, item):
self._canvas.line(*self._cast_positions(item.positions))
##############################################
def paint_CubicBezierItem(self, item):
self._canvas.bezier(*self._cast_positions(item.positions))
####################################################################################################
_driver_to_cls = {
'reportlab': ReportlabPainter,
}
def PdfPainter(*args, **kwargs):
"""Wrapper to driver classes"""
driver = kwargs.get('driver', 'reportlab')
del kwargs['driver']
return _driver_to_cls[driver](*args, **kwargs)
####################################################################################################
#
# 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/>.
#
####################################################################################################
__all__ = [
'SvgPainter',
]
# See also
# https://github.com/mozman/svgwrite
# https://svgwrite.readthedocs.io/en/master/
####################################################################################################
import logging
from Patro.FileFormat.Svg.SvgFile import SvgFile
from Patro.FileFormat.Svg import SvgFormat
from Patro.GeometryEngine.Vector import Vector2D
from Patro.GeometryEngine.Transformation import AffineTransformation2D
from .Painter import Painter
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class SvgPainter(Painter):
__STROKE_STYLE__ = {
None: None,
'dashDotLine': [6, 3],
'dotLine': [1, 2],
'hair': None,
'none': None, # Fixme: ???
'solid': None,
}
__COLOR__ = {
None : None,
'black': 'black',
}
##############################################
def __init__(self, path, scene, paper):
super().__init__(scene)
self._path = path
self._paper = paper
self._coordinates = {}
bounding_box = scene.bounding_box
self._transformation = AffineTransformation2D.Scale(10, -10)
self._transformation *= AffineTransformation2D.Translation(-Vector2D(bounding_box.x.inf, bounding_box.y.sup))
self._tree = []
self._append(SvgFormat.Style(text='''
.normal { font: 12px sans-serif; }
'''))
self.paint()
self._svg_file = SvgFile()
self._svg_file.write(paper, self._tree, transformation=None, path=path)
##############################################
def _append(self, element):
self._tree.append(element)
##############################################
def _cast_position(self, position):
# Fixme: to base class
if isinstance(position, str):
position = self._coordinates[position]
return self._transformation * position
##############################################
def paint_CoordinateItem(self, item):
# Fixme: to base class
self._coordinates[item.name] = item.position
##############################################
def _cast_positions(self, positions):
vertices = []
for position in positions:
vertices += list(self._cast_position(position))
return vertices
##############################################
def _graphic_style(self, item):
path_syle = item.path_style
color = self.__COLOR__[path_syle.stroke_color]
line_style = self.__STROKE_STYLE__[path_syle.stroke_style]
line_width = str(float(path_syle.line_width.replace('pt', '')) / 3) # Fixme: pt ???
kwargs = dict(stroke=color, stroke_width=line_width)
if line_style:
kwargs['stroke_dasharray'] = line_style
return kwargs
##############################################
def paint_TextItem(self, item):
x, y = list(self._cast_position(item.position))
# Fixme: anchor position
text = SvgFormat.Text(x=x, y=y, text=item.text, fill='black')
self._append(text)
##############################################
def paint_CircleItem(self, item):
x, y = list(self._cast_position(item.position))
circle = SvgFormat.Text(cx=x, cy=y, r=2, fill='black', _class='normal')
self._append(circle)
##############################################
def paint_SegmentItem(self, item):
x1, y1, x2, y2 = self._cast_positions(item.positions)
line = SvgFormat.Line(
x1=x1,
y1=y1,
x2=x2,
y2=y2,
**self._graphic_style(item),
)
self._append(line)
##############################################
def paint_CubicBezierItem(self, item):
path = SvgFormat.Path(
path_data='M {} {} C {} {}, {} {}, {} {}'.format(*self._cast_positions(item.positions)),
fill='none',
**self._graphic_style(item),
)
self._append(path)
...@@ -72,7 +72,7 @@ class TexPainter(Painter): ...@@ -72,7 +72,7 @@ class TexPainter(Painter):
def __init__(self, path, scene, paper): def __init__(self, path, scene, paper):
super(TexPainter, self).__init__(scene) super().__init__(scene)
self._document = Document(path, 'article', '12pt') self._document = Document(path, 'article', '12pt')
self._paper = paper self._paper = paper
......
...@@ -36,12 +36,12 @@ class TikzFigure(Environment): ...@@ -36,12 +36,12 @@ class TikzFigure(Environment):
'none': None, 'none': None,
'solid': 'solid', 'solid': 'solid',
} }
__COLOR__ = { __COLOR__ = {
None : None, None : None,
'black': 'black', 'black': 'black',
} }
@staticmethod @staticmethod
def format_path_style(path_syle): def format_path_style(path_syle):
......
...@@ -22,7 +22,9 @@ ...@@ -22,7 +22,9 @@
import logging import logging
from .Calculator import Calculator, NamedExpression import sympy
import yaml
from .PersonalData import PersonalData from .PersonalData import PersonalData
#################################################################################################### ####################################################################################################
...@@ -31,7 +33,7 @@ _module_logger = logging.getLogger(__name__) ...@@ -31,7 +33,7 @@ _module_logger = logging.getLogger(__name__)
#################################################################################################### ####################################################################################################
class Measurement(NamedExpression): class Measurement:
"""Class to define a measurement""" """Class to define a measurement"""
...@@ -39,25 +41,24 @@ class Measurement(NamedExpression): ...@@ -39,25 +41,24 @@ class Measurement(NamedExpression):
def __init__(self, measurements, name, value, full_name='', description=''): def __init__(self, measurements, name, value, full_name='', description=''):
# Valentina defines custom measurement with a @ prefix
name = str(name) name = str(name)
self._valentina_name = name
if self.is_custom():
name = '__custom__' + name[1:]
for c in name: for c in name:
if not c.isalnum() and c != '_': if not c.isalnum() and c != '_':
raise ValueError('Invalid measurement name "{}"'.format(self._valentina_name)) raise ValueError('Invalid measurement name "{}"'.format(name))
NamedExpression.__init__(self, name, value, calculator=measurements.calculator)
self._measurements = measurements
self._name = name
self._full_name = str(full_name) # for human self._full_name = str(full_name) # for human
self._description = str(description) # describe the purpose of the measurement self._description = str(description) # describe the purpose of the measurement
self._expression = sympy.sympify(value)
self._evaluated_expression = None
self._value = None
############################################## ##############################################
@property @property
def valentina_name(self): def name(self):
return self._valentina_name return self._name
@property @property
def full_name(self): def full_name(self):
...@@ -67,16 +68,27 @@ class Measurement(NamedExpression): ...@@ -67,16 +68,27 @@ class Measurement(NamedExpression):
def description(self): def description(self):
return self._description return self._description
@property
def expression(self):
return self._expression
############################################## ##############################################
def is_custom(self): @property
return self._valentina_name.startswith('@') def evaluated_expression(self):
if self._evaluated_expression is None:
# variable order doesn't matter, sympy do the job
self._evaluated_expression = self._expression.subs(self._measurements._expressions)
return self._evaluated_expression
############################################## @property
def value(self):
if self._value is None:
self._value = float(self.evaluated_expression.evalf(3)) # ensure a float or raise
return self._value
def eval(self): def __float__(self):
super(Measurement, self).eval() return self.value
self._calculator._update_cache(self)
#################################################################################################### ####################################################################################################
...@@ -84,6 +96,8 @@ class Measurements: ...@@ -84,6 +96,8 @@ class Measurements:
"""Class to store a set of measurements""" """Class to store a set of measurements"""
__measurement_cls__ = Measurement
_logger = _module_logger.getChild('Measurements') _logger = _module_logger.getChild('Measurements')
############################################## ##############################################
...@@ -91,19 +105,14 @@ class Measurements: ...@@ -91,19 +105,14 @@ class Measurements:
def __init__(self): def __init__(self):
self._unit = None self._unit = None
self._pattern_making_system = None self._pattern_making_system = None # Fixme: purpose ???
self._personal = PersonalData() self._personal = PersonalData()
self._measures = [] self._measurements = {} # name -> Measurement
self._measure_dict = {} self._expressions = {} # name -> expression for sympy substitution
self._calculator = Calculator(self)
############################################## ##############################################
@property
def calculator(self):
return self._calculator
@property @property
def personal(self): def personal(self):
return self._personal return self._personal
...@@ -131,12 +140,19 @@ class Measurements: ...@@ -131,12 +140,19 @@ class Measurements:
############################################## ##############################################
def __iter__(self): def __iter__(self):
return iter(self._measures) return iter(self._measurements.values())
##############################################
def sorted_iter(self):
for name in sorted(self._measurements.keys()):
yield self._measurements[name]
############################################## ##############################################
def __getitem__(self, name): def __getitem__(self, name):
return self._measure_dict[name] return self._measurements[name]
############################################## ##############################################
...@@ -144,26 +160,56 @@ class Measurements: ...@@ -144,26 +160,56 @@ class Measurements:
# Fixme: name ? # Fixme: name ?
measurement = Measurement(self, *args, **kgwars) measurement = self.__measurement_cls__(self, *args, **kgwars)
self._measures.append(measurement) self._measurements[measurement.name] = measurement
self._measure_dict[measurement.name] = measurement self._expressions[measurement.name] = measurement.expression
if measurement.is_custom():
self._measure_dict[measurement.valentina_name] = measurement return measurement
############################################## ##############################################
def eval(self): def dump(self):
template = '''{0.name} = {0.expression}
= {0.evaluated_expression}
= {0.value}
'''
# Fixme: eval / compute a graph from the ast to evaluate for measurement in self.sorted_iter():
self._logger.info('Eval all measurements') print(template.format(measurement))
for measure in self:
measure.eval()
############################################## ##############################################
def dump(self): def save_as_yaml(self, yaml_path):
measurements = {}
for measurement in self.sorted_iter():
# for measurement in self:
expression = measurement.expression
if isinstance(expression, sympy.Integer):
expression = int(expression)
elif isinstance(expression, sympy.Float):
expression = float(expression)
else:
expression = str(expression)
data = [expression]
if measurement.full_name or measurement.description:
data += [measurement.full_name, measurement.description]
measurements[measurement.name] = data
with open(yaml_path, 'w') as fh:
yaml_string = yaml.dump(measurements, default_flow_style=False, width=160)
fh.write(yaml_string)
##############################################
print("\nDump measurements:") def load_yaml(self, yaml_path):
for measure in self:
print(measure.name, '=', measure.expression) with open(yaml_path, 'r') as fh:
print(' =', measure.value) measurements = yaml.load(fh.read())
for name, data in measurements.items():
if len(data) > 1:
value, full_name, description = data
else:
value = data[0]
full_name = description = ''
self.add(name, value, full_name, description)
####################################################################################################
#
# Patro - A Python library to make patterns for fashion design
# Copyright (C) 2018 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/>.
#
####################################################################################################
####################################################################################################
from pathlib import Path
import yaml
####################################################################################################
class Measurement:
##############################################
def __init__(self, name, full_name, description, default_value=0):
self._name = name
self._full_name = full_name
self._description = description
self._default_value = default_value
##############################################
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
##############################################
@property
def full_name(self):
return self._full_name
@full_name.setter
def full_name(self, value):
self._full_name = value
##############################################
@property
def description(self):
return self._description
@description.setter
def description(self, value):
self._description = value
##############################################
@property
def default_value(self):
return self._default_value
@default_value.setter
def default_value(self, value):
self._default_value = value
####################################################################################################
class ValentinaMeasurement(Measurement):
##############################################
def __init__(self, code, name, full_name, description, default_value):
super().__init__(name, full_name, description, default_value)
self._code = code
##############################################
@property
def code(self):
return self._code
@code.setter
def code(self, value):
self._code = value
####################################################################################################
class StandardMeasurement:
##############################################
def __init__(self):
self._names = {}
##############################################
def __len__(self):
return len(self._names)
##############################################
def __iter__(self):
return iter(self._names.values())
##############################################
def __getitem__(self, name):
return self._names[name]
##############################################
def __contains__(self, name):
return name in self._names
##############################################
def add(self, measurement):
if measurement.name not in self._names:
self._names[measurement.name] = measurement
else:
raise NameError('{} is already registered'.format(measurement.name))
##############################################
def dump(self):
template = '''{0.name}
- {0.full_name}
- {0.description}
- {0.default_value}
'''
for measurement in self:
print(template.format(measurement))
####################################################################################################
class ValentinaStandardMeasurement(StandardMeasurement):
##############################################
def __init__(self):
super().__init__()
yaml_path = Path(__file__).parent.joinpath('data', 'valentina-standard-measurements.yaml')
with open(yaml_path, 'r') as fh:
data = yaml.load(fh.read())
for topic in data.values():
for code, measurement_data in topic['measurements'].items():
measurement = ValentinaMeasurement(code, *measurement_data)
self.add(measurement)
####################################################################################################
#
# 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/>.
#
####################################################################################################
####################################################################################################
import logging
from .Measurement import Measurement, Measurements
from .StandardMeasurement import ValentinaStandardMeasurement
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
_valentina_standard_measurement = ValentinaStandardMeasurement()
####################################################################################################
class ValentinaMeasurement(Measurement):
"""Class to define a Valentina measurement"""
# CUSTOM_PREFIX = '__custom__'
CUSTOM_PREFIX = 'C_'
##############################################
@classmethod
def replace_custom_prefix(cls, string):
# Fixme: how to only replace when there is no clash ?
return string.replace('@', cls.CUSTOM_PREFIX)
##############################################
def __init__(self, measurements, name, value, full_name='', description=''):
# Valentina defines custom measurement with a @ prefix
self._valentina_name = str(name)
name = self.replace_custom_prefix(self._valentina_name)
# if self.is_custom():
# name = name[1:]
# if name in _valentina_standard_measurement:
# name = self.CUSTOM_PREFIX + name
value = self.replace_custom_prefix(value)
super().__init__(measurements, name, value, full_name, description)
##############################################
@property
def valentina_name(self):
return self._valentina_name
##############################################
def is_custom(self):
return self._valentina_name.startswith('@')
####################################################################################################
class ValentinaMeasurements(Measurements):
"""Class to store a set of Valentina measurements"""
__measurement_cls__ = ValentinaMeasurement
_logger = _module_logger.getChild('ValentinaMeasurements')
##############################################
def add(self, *args, **kgwars):
# Fixme: name ?
measurement = super().add(*args, **kgwars)
if measurement.is_custom():
self._measurements[measurement.valentina_name] = measurement
A:
description: Direct Height
measurements:
A01:
- height
- 'Height: Total'
- Vertical distance from crown of head to floor.
- 0
A02:
- height_neck_back
- 'Height: Neck Back'
- Vertical distance from the Neck Back (cervicale vertebra) to the floor.
- 0
A03:
- height_scapula
- 'Height: Scapula'
- Vertical distance from the Scapula (Blade point) to the floor.
- 0
A04:
- height_armpit
- 'Height: Armpit'
- Vertical distance from the Armpit to the floor.
- 0
A05:
- height_waist_side
- 'Height: Waist Side'
- Vertical distance from the Waist Side to the floor.
- 0
A06:
- height_hip
- 'Height: Hip'
- Vertical distance from the Hip level to the floor.
- 0
A07:
- height_gluteal_fold
- 'Height: Gluteal Fold'
- Vertical distance from the Gluteal fold, where the Gluteal muscle meets the top of the back thigh, to the floor.
- 0
A08:
- height_knee
- 'Height: Knee'
- Vertical distance from the fold at the back of the Knee to the floor.
- 0
A09:
- height_calf
- 'Height: Calf'
- Vertical distance from the widest point of the calf to the floor.
- 0
A10:
- height_ankle_high
- 'Height: Ankle High'
- Vertical distance from the deepest indentation of the back of the ankle to the floor.
- 0
A11:
- height_ankle
- 'Height: Ankle'
- Vertical distance from point where the front leg meets the foot to the floor.
- 0
A12:
- height_highhip
- 'Height: Highhip'
- Vertical distance from the Highhip level, where front abdomen is most prominent, to the floor.
- 0
A13:
- height_waist_front
- 'Height: Waist Front'
- Vertical distance from the Waist Front to the floor.
- 0
A14:
- height_bustpoint
- 'Height: Bustpoint'
- Vertical distance from Bustpoint to the floor.
- 0
A15:
- height_shoulder_tip
- 'Height: Shoulder Tip'
- Vertical distance from the Shoulder Tip to the floor.
- 0
A16:
- height_neck_front
- 'Height: Neck Front'
- Vertical distance from the Neck Front to the floor.
- 0
A17:
- height_neck_side
- 'Height: Neck Side'
- Vertical distance from the Neck Side to the floor.
- 0
A18:
- height_neck_back_to_knee
- 'Height: Neck Back to Knee'
- Vertical distance from the Neck Back (cervicale vertebra) to the fold at the back of the knee.
- height_neck_back - height_knee
A19:
- height_waist_side_to_knee
- 'Height: Waist Side to Knee'
- Vertical distance from the Waist Side to the fold at the back of the knee.
- height_waist_side - height_knee
A20:
- height_waist_side_to_hip
- 'Height: Waist Side to Hip'
- Vertical distance from the Waist Side to the Hip level.
- height_waist_side - height_hip
A21:
- height_knee_to_ankle
- 'Height: Knee to Ankle'
- Vertical distance from the fold at the back of the knee to the point where the front leg meets the top of the foot.
- height_knee - height_ankle
A22:
- height_neck_back_to_waist_side
- 'Height: Neck Back to Waist Side'
- 'Vertical distance from Neck Back to Waist Side. (''Height: Neck Back'' - ''Height: Waist Side'').'
- height_neck_back - height_waist_side
A23:
- height_waist_back
- 'Height: Waist Back'
- 'Vertical height from Waist Back to floor. (''Height: Waist Front'''' - ''Leg: Crotch to floor'''').'
- height_waist_front - leg_crotch_to_floor
B:
description: Direct Width
measurements:
B01:
- width_shoulder
- 'Width: Shoulder'
- Horizontal distance from Shoulder Tip to Shoulder Tip.
- 0
B02:
- width_bust
- 'Width: Bust'
- Horizontal distance from Bust Side to Bust Side.
- 0
B03:
- width_waist
- 'Width: Waist'
- Horizontal distance from Waist Side to Waist Side.
- 0
B04:
- width_hip
- 'Width: Hip'
- Horizontal distance from Hip Side to Hip Side.
- 0
B05:
- width_abdomen_to_hip
- 'Width: Abdomen to Hip'
- Horizontal distance from the greatest abdomen prominence to the greatest hip prominence.
- 0
C:
description: Indentation
measurements:
C01:
- indent_neck_back
- 'Indent: Neck Back'
- Horizontal distance from Scapula (Blade point) to the Neck Back.
- 0
C02:
- indent_waist_back
- 'Indent: Waist Back'
- Horizontal distance between a flat stick, placed to touch Hip and Scapula, and Waist Back.
- 0
C03:
- indent_ankle_high
- 'Indent: Ankle High'
- Horizontal Distance between a flat stick, placed perpendicular to Heel, and the greatest indentation of Ankle.
- 0
D:
description: Circumference and Arc
measurements:
D01:
- hand_palm_length
- 'Hand: Palm length'
- Length from Wrist line to base of middle finger.
- 0
D02:
- hand_length
- 'Hand: Length'
- Length from Wrist line to end of middle finger.
- 0
D03:
- hand_palm_width
- 'Hand: Palm width'
- Measure where Palm is widest.
- 0
D04:
- hand_palm_circ
- 'Hand: Palm circumference'
- Circumference where Palm is widest.
- 0
D05:
- hand_circ
- 'Hand: Circumference'
- Tuck thumb toward smallest finger, bring fingers close together. Measure circumference around widest part of hand.
- 0
E:
description: Vertical
measurements:
E01:
- foot_width
- 'Foot: Width'
- Measure at widest part of foot.
- 0
E02:
- foot_length
- 'Foot: Length'
- Measure from back of heel to end of longest toe.
- 0
E03:
- foot_circ
- 'Foot: Circumference'
- Measure circumference around widest part of foot.
- 0
E04:
- foot_instep_circ
- 'Foot: Instep circumference'
- Measure circumference at tallest part of instep.
- 0
F:
description: Horizontal
measurements:
F01:
- head_circ
- 'Head: Circumference'
- Measure circumference at largest level of head.
- 0
F02:
- head_length
- 'Head: Length'
- Vertical distance from Head Crown to bottom of jaw.
- 0
F03:
- head_depth
- 'Head: Depth'
- Horizontal distance from front of forehead to back of head.
- 0
F04:
- head_width
- 'Head: Width'
- Horizontal distance from Head Side to Head Side, where Head is widest.
- 0
F05:
- head_crown_to_neck_back
- 'Head: Crown to Neck Back'
- 'Vertical distance from Crown to Neck Back. (''Height: Total'' - ''Height: Neck Back'').'
- height - height_neck_back
F06:
- head_chin_to_neck_back
- 'Head: Chin to Neck Back'
- 'Vertical distance from Chin to Neck Back. (''Height'' - ''Height: Neck Back'' - ''Head: Length'')'
- height - height_neck_back - head_length
G:
description: Bust
measurements:
G01:
- neck_mid_circ
- Neck circumference, midsection
- Circumference of Neck midsection, about halfway between jaw and torso.
- 0
G02:
- neck_circ
- Neck circumference
- Neck circumference at base of Neck, touching Neck Back, Neck Sides, and Neck Front.
- 0
G03:
- highbust_circ
- Highbust circumference
- Circumference at Highbust, following shortest distance between Armfolds across chest, high under armpits.
- 0
G04:
- bust_circ
- Bust circumference
- Circumference around Bust, parallel to floor.
- 0
G05:
- lowbust_circ
- Lowbust circumference
- Circumference around LowBust under the breasts, parallel to floor.
- 0
G06:
- rib_circ
- Rib circumference
- Circumference around Ribs at level of the lowest rib at the side, parallel to floor.
- 0
G07:
- waist_circ
- Waist circumference
- Circumference around Waist, following natural contours. Waists are typically higher in back.
- 0
G08:
- highhip_circ
- Highhip circumference
- Circumference around Highhip, where Abdomen protrusion is greatest, parallel to floor.
- 0
G09:
- hip_circ
- Hip circumference
- Circumference around Hip where Hip protrusion is greatest, parallel to floor.
- 0
G10:
- neck_arc_f
- Neck arc, front
- From Neck Side to Neck Side through Neck Front.
- 0
G11:
- highbust_arc_f
- Highbust arc, front
- From Highbust Side (Armpit) to HIghbust Side (Armpit) across chest.
- 0
G12:
- size
- Size
- Same as bust_arc_f.
- 0
G13:
- lowbust_arc_f
- Lowbust arc, front
- From Lowbust Side to Lowbust Side across front.
- 0
G14:
- rib_arc_f
- Rib arc, front
- From Rib Side to Rib Side, across front.
- 0
G15:
- waist_arc_f
- Waist arc, front
- From Waist Side to Waist Side across front.
- 0
G16:
- highhip_arc_f
- Highhip arc, front
- From Highhip Side to Highhip Side across front.
- 0
G17:
- hip_arc_f
- Hip arc, front
- From Hip Side to Hip Side across Front.
- 0
G18:
- neck_arc_half_f
- Neck arc, front, half
- Half of 'Neck arc, front'. ('Neck arc, front' / 2).
- neck_arc_f/2
G19:
- highbust_arc_half_f
- Highbust arc, front, half
- Half of 'Highbust arc, front'. From Highbust Front to Highbust Side. ('Highbust arc, front' / 2).
- highbust_arc_f/2
G20:
- bust_arc_half_f
- Bust arc, front, half
- Half of 'Bust arc, front'. ('Bust arc, front'/2).
- bust_arc_f/2
G21:
- lowbust_arc_half_f
- Lowbust arc, front, half
- Half of 'Lowbust arc, front'. ('Lowbust Arc, front' / 2).
- lowbust_arc_f/2
G22:
- rib_arc_half_f
- Rib arc, front, half
- Half of 'Rib arc, front'. ('Rib Arc, front' / 2).
- rib_arc_f/2
G23:
- waist_arc_half_f
- Waist arc, front, half
- Half of 'Waist arc, front'. ('Waist arc, front' / 2).
- waist_arc_f/2
G24:
- highhip_arc_half_f
- Highhip arc, front, half
- Half of 'Highhip arc, front'. ('Highhip arc, front' / 2).
- highhip_arc_f/2
G25:
- hip_arc_half_f
- Hip arc, front, half
- Half of 'Hip arc, front'. ('Hip arc, front' / 2).
- hip_arc_f/2
G26:
- neck_arc_b
- Neck arc, back
- From Neck Side to Neck Side across back. ('Neck circumference' - 'Neck arc, front').
- neck_circ - neck_arc_f
G27:
- highbust_arc_b
- Highbust arc, back
- From Highbust Side to Highbust Side across back. ('Highbust circumference' - 'Highbust arc, front').
- highbust_circ - highbust_arc_f
G28:
- bust_arc_b
- Bust arc, back
- From Bust Side to Bust Side across back. ('Bust circumference' - 'Bust arc, front').
- bust_circ - bust_arc_f
G29:
- lowbust_arc_b
- Lowbust arc, back
- From Lowbust Side to Lowbust Side across back. ('Lowbust circumference' - 'Lowbust arc, front').
- lowbust_circ - lowbust_arc_f
G30:
- rib_arc_b
- Rib arc, back
- From Rib Side to Rib side across back. ('Rib circumference' - 'Rib arc, front').
- rib_circ - rib_arc_f
G31:
- waist_arc_b
- Waist arc, back
- From Waist Side to Waist Side across back. ('Waist circumference' - 'Waist arc, front').
- waist_circ - waist_arc_f
G32:
- highhip_arc_b
- Highhip arc, back
- From Highhip Side to Highhip Side across back. ('Highhip circumference' - 'Highhip arc, front').
- highhip_circ - highhip_arc_f
G33:
- hip_arc_b
- Hip arc, back
- From Hip Side to Hip Side across back. ('Hip circumference' - 'Hip arc, front').
- hip_circ - hip_arc_f
G34:
- neck_arc_half_b
- Neck arc, back, half
- Half of 'Neck arc, back'. ('Neck arc, back' / 2).
- neck_arc_b/2
G35:
- highbust_arc_half_b
- Highbust arc, back, half
- Half of 'Highbust arc, back'. From Highbust Back to Highbust Side. ('Highbust arc, back' / 2).
- highbust_arc_b/2
G36:
- bust_arc_half_b
- Bust arc, back, half
- Half of 'Bust arc, back'. ('Bust arc, back' / 2).
- bust_arc_b/2
G37:
- lowbust_arc_half_b
- Lowbust arc, back, half
- Half of 'Lowbust Arc, back'. ('Lowbust arc, back' / 2).
- lowbust_arc_b/2
G38:
- rib_arc_half_b
- Rib arc, back, half
- Half of 'Rib arc, back'. ('Rib arc, back' / 2).
- rib_arc_b/2
G39:
- waist_arc_half_b
- Waist arc, back, half
- Half of 'Waist arc, back'. ('Waist arc, back' / 2).
- waist_arc_b/2
G40:
- highhip_arc_half_b
- Highhip arc, back, half
- Half of 'Highhip arc, back'. From Highhip Back to Highbust Side. ('Highhip arc, back'/ 2).
- highhip_arc_b/2
G41:
- hip_arc_half_b
- Hip arc, back, half
- Half of 'Hip arc, back'. ('Hip arc, back' / 2).
- hip_arc_b/2
G42:
- hip_with_abdomen_arc_f
- Hip arc with Abdomen, front
- Curve stiff paper around front of abdomen, tape at sides. Measure from Hip Side to Hip Side overpaper across front.
- 0
G43:
- body_armfold_circ
- Body circumference at Armfold level
- Measure around arms and torso at Armfold level.
- 0
G44:
- body_bust_circ
- Body circumference at Bust level
- Measure around arms and torso at Bust level.
- 0
G45:
- body_torso_circ
- Body circumference of full torso
- Circumference around torso from mid-shoulder around crotch back up to mid-shoulder.
- 0
G46:
- hip_circ_with_abdomen
- Hip circumference, including Abdomen
- Measurement at Hip level, including the depth of the Abdomen. (Hip arc, back + Hip arc withabdomen, front).
- hip_arc_b + hip_with_abdomen_arc_f
H:
description: Balance
measurements:
H01:
- neck_front_to_waist_f
- Neck Front to Waist Front
- From Neck Front, over tape between Breastpoints, down to Waist Front.
- 0
H02:
- neck_front_to_waist_flat_f
- Neck Front to Waist Front flat
- From Neck Front down between breasts to Waist Front.
- 0
H03:
- armpit_to_waist_side
- Armpit to Waist Side
- From Armpit down to Waist Side.
- 0
H04:
- shoulder_tip_to_waist_side_f
- Shoulder Tip to Waist Side, front
- From Shoulder Tip, curving around Armscye Front, then down to Waist Side.
- 0
H05:
- neck_side_to_waist_f
- Neck Side to Waist level, front
- From Neck Side straight down front to Waist level.
- 0
H06:
- neck_side_to_waist_bustpoint_f
- Neck Side to Waist level, through Bustpoint
- From Neck Side over Bustpoint to Waist level, forming a straight line.
- 0
H07:
- neck_front_to_highbust_f
- Neck Front to Highbust Front
- Neck Front down to Highbust Front.
- 0
H08:
- highbust_to_waist_f
- Highbust Front to Waist Front
- From Highbust Front to Waist Front. Use tape to bridge gap between Bustpoints. ('Neck Front toWaist Front' - 'Neck Front to Highbust Front').
- neck_front_to_waist_f - neck_front_to_highbust_f
H09:
- neck_front_to_bust_f
- Neck Front to Bust Front
- From Neck Front down to Bust Front. Requires tape to cover gap between Bustpoints.
- 0
H10:
- bust_to_waist_f
- Bust Front to Waist Front
- From Bust Front down to Waist level. ('Neck Front to Waist Front' - 'Neck Front to Bust Front').
- neck_front_to_waist_f - neck_front_to_bust_f
H11:
- lowbust_to_waist_f
- Lowbust Front to Waist Front
- From Lowbust Front down to Waist Front.
- 0
H12:
- rib_to_waist_side
- Rib Side to Waist Side
- From lowest rib at side down to Waist Side.
- 0
H13:
- shoulder_tip_to_armfold_f
- Shoulder Tip to Armfold Front
- From Shoulder Tip around Armscye down to Armfold Front.
- 0
H14:
- neck_side_to_bust_f
- Neck Side to Bust level, front
- From Neck Side straight down front to Bust level.
- 0
H15:
- neck_side_to_highbust_f
- Neck Side to Highbust level, front
- From Neck Side straight down front to Highbust level.
- 0
H16:
- shoulder_center_to_highbust_f
- Shoulder center to Highbust level, front
- From mid-Shoulder down front to Highbust level, aimed at Bustpoint.
- 0
H17:
- shoulder_tip_to_waist_side_b
- Shoulder Tip to Waist Side, back
- From Shoulder Tip, curving around Armscye Back, then down to Waist Side.
- 0
H18:
- neck_side_to_waist_b
- Neck Side to Waist level, back
- From Neck Side straight down back to Waist level.
- 0
H19:
- neck_back_to_waist_b
- Neck Back to Waist Back
- From Neck Back down to Waist Back.
- 0
H20:
- neck_side_to_waist_scapula_b
- Neck Side to Waist level, through Scapula
- From Neck Side across Scapula down to Waist level, forming a straight line.
- 0
H21:
- neck_back_to_highbust_b
- Neck Back to Highbust Back
- From Neck Back down to Highbust Back.
- 0
H22:
- highbust_to_waist_b
- Highbust Back to Waist Back
- From Highbust Back down to Waist Back. ('Neck Back to Waist Back' - 'Neck Back to Highbust Back').
- neck_back_to_waist_b - neck_back_to_highbust_b
H23:
- neck_back_to_bust_b
- Neck Back to Bust Back
- From Neck Back down to Bust Back.
- 0
H24:
- bust_to_waist_b
- Bust Back to Waist Back
- From Bust Back down to Waist level. ('Neck Back to Waist Back' - 'Neck Back to Bust Back').
- neck_back_to_waist_b - neck_back_to_bust_b
H25:
- lowbust_to_waist_b
- Lowbust Back to Waist Back
- From Lowbust Back down to Waist Back.
- 0
H26:
- shoulder_tip_to_armfold_b
- Shoulder Tip to Armfold Back
- From Shoulder Tip around Armscye down to Armfold Back.
- 0
H27:
- neck_side_to_bust_b
- Neck Side to Bust level, back
- From Neck Side straight down back to Bust level.
- 0
H28:
- neck_side_to_highbust_b
- Neck Side to Highbust level, back
- From Neck Side straight down back to Highbust level.
- 0
H29:
- shoulder_center_to_highbust_b
- Shoulder center to Highbust level, back
- From mid-Shoulder down back to Highbust level, aimed through Scapula.
- 0
H30:
- waist_to_highhip_f
- Waist Front to Highhip Front
- From Waist Front to Highhip Front.
- 0
H31:
- waist_to_hip_f
- Waist Front to Hip Front
- From Waist Front to Hip Front.
- 0
H32:
- waist_to_highhip_side
- Waist Side to Highhip Side
- From Waist Side to Highhip Side.
- 0
H33:
- waist_to_highhip_b
- Waist Back to Highhip Back
- From Waist Back down to Highhip Back.
- 0
H34:
- waist_to_hip_b
- Waist Back to Hip Back
- From Waist Back down to Hip Back. Requires tape to cover the gap between buttocks.
- 0
H35:
- waist_to_hip_side
- Waist Side to Hip Side
- From Waist Side to Hip Side.
- 0
H36:
- shoulder_slope_neck_side_angle
- Shoulder Slope Angle from Neck Side
- Angle formed by line from Neck Side to Shoulder Tip and line from Neck Side parallel to floor.
- 0
H37:
- shoulder_slope_neck_side_length
- Shoulder Slope length from Neck Side
- Vertical distance between Neck Side and Shoulder Tip.
- 0
H38:
- shoulder_slope_neck_back_angle
- Shoulder Slope Angle from Neck Back
- Angle formed by line from Neck Back to Shoulder Tip and line from Neck Back parallel to floor.
- 0
H39:
- shoulder_slope_neck_back_height
- Shoulder Slope length from Neck Back
- Vertical distance between Neck Back and Shoulder Tip.
- 0
H40:
- shoulder_slope_shoulder_tip_angle
- Shoulder Slope Angle from Shoulder Tip
- Angle formed by line from Neck Side to Shoulder Tip and vertical line at Shoulder Tip.
- 0
H41:
- neck_back_to_across_back
- Neck Back to Across Back
- From neck back, down to level of Across Back measurement.
- 0
H42:
- across_back_to_waist_b
- Across Back to Waist back
- From middle of Across Back down to Waist back.
- neck_back_to_waist_b - neck_back_to_across_back
I:
description: Arm
measurements:
I01:
- shoulder_length
- Shoulder length
- From Neck Side to Shoulder Tip.
- 0
I02:
- shoulder_tip_to_shoulder_tip_f
- Shoulder Tip to Shoulder Tip, front
- From Shoulder Tip to Shoulder Tip, across front.
- 0
I03:
- across_chest_f
- Across Chest
- From Armscye to Armscye at narrowest width across chest.
- 0
I04:
- armfold_to_armfold_f
- Armfold to Armfold, front
- From Armfold to Armfold, shortest distance between Armfolds, not parallel to floor.
- 0
I05:
- shoulder_tip_to_shoulder_tip_half_f
- Shoulder Tip to Shoulder Tip, front, half
- Half of' Shoulder Tip to Shoulder tip, front'. ('Shoulder Tip to Shoulder Tip, front' / 2).
- shoulder_tip_to_shoulder_tip_f/2
I06:
- across_chest_half_f
- Across Chest, half
- Half of 'Across Chest'. ('Across Chest' / 2).
- across_chest_f/2
I07:
- shoulder_tip_to_shoulder_tip_b
- Shoulder Tip to Shoulder Tip, back
- From Shoulder Tip to Shoulder Tip, across the back.
- 0
I08:
- across_back_b
- Across Back
- From Armscye to Armscye at the narrowest width of the back.
- 0
I09:
- armfold_to_armfold_b
- Armfold to Armfold, back
- From Armfold to Armfold across the back.
- 0
I10:
- shoulder_tip_to_shoulder_tip_half_b
- Shoulder Tip to Shoulder Tip, back, half
- Half of 'Shoulder Tip to Shoulder Tip, back'. ('Shoulder Tip to Shoulder Tip, back' / 2).
- shoulder_tip_to_shoulder_tip_b/2
I11:
- across_back_half_b
- Across Back, half
- Half of 'Across Back'. ('Across Back' / 2).
- across_back_b/2
I12:
- neck_front_to_shoulder_tip_f
- Neck Front to Shoulder Tip
- From Neck Front to Shoulder Tip.
- 0
I13:
- neck_back_to_shoulder_tip_b
- Neck Back to Shoulder Tip
- From Neck Back to Shoulder Tip.
- 0
I14:
- neck_width
- Neck Width
- Measure between the 'legs' of an unclosed necklace or chain draped around the neck.
- 0
J:
description: Leg
measurements:
J01:
- bustpoint_to_bustpoint
- Bustpoint to Bustpoint
- From Bustpoint to Bustpoint.
- 0
J02:
- bustpoint_to_neck_side
- Bustpoint to Neck Side
- From Neck Side to Bustpoint.
- 0
J03:
- bustpoint_to_lowbust
- Bustpoint to Lowbust
- From Bustpoint down to Lowbust level, following curve of bust or chest.
- 0
J04:
- bustpoint_to_waist
- Bustpoint to Waist level
- From Bustpoint to straight down to Waist level, forming a straight line (not curving along thebody).
- 0
J05:
- bustpoint_to_bustpoint_half
- Bustpoint to Bustpoint, half
- Half of 'Bustpoint to Bustpoint'. ('Bustpoint to Bustpoint' / 2).
- bustpoint_to_bustpoint/2
J06:
- bustpoint_neck_side_to_waist
- Bustpoint, Neck Side to Waist level
- From Neck Side to Bustpoint, then straight down to Waist level. ('Neck Side to Bustpoint' +'Bustpoint to Waist level').
- bustpoint_to_neck_side + bustpoint_to_waist
J07:
- bustpoint_to_shoulder_tip
- Bustpoint to Shoulder Tip
- From Bustpoint to Shoulder tip.
- 0
J08:
- bustpoint_to_waist_front
- Bustpoint to Waist Front
- From Bustpoint to Waist Front, in a straight line, not following the curves of the body.
- 0
J09:
- bustpoint_to_bustpoint_halter
- Bustpoint to Bustpoint Halter
- From Bustpoint around Neck Back down to other Bustpoint.
- 0
J10:
- bustpoint_to_shoulder_center
- Bustpoint to Shoulder Center
- From center of Shoulder to Bustpoint.
- 0
K:
description: Crotch and Rise
measurements:
K01:
- shoulder_tip_to_waist_front
- Shoulder Tip to Waist Front
- From Shoulder Tip diagonal to Waist Front.
- 0
K02:
- neck_front_to_waist_side
- Neck Front to Waist Side
- From Neck Front diagonal to Waist Side.
- 0
K03:
- neck_side_to_waist_side_f
- Neck Side to Waist Side, front
- From Neck Side diagonal across front to Waist Side.
- 0
K04:
- shoulder_tip_to_waist_back
- Shoulder Tip to Waist Back
- From Shoulder Tip diagonal to Waist Back.
- 0
K05:
- shoulder_tip_to_waist_b_1in_offset
- Shoulder Tip to Waist Back, with 1in (2.54cm) offset
- Mark 1in (2.54cm) outward from Waist Back along Waist level. Measure from Shoulder Tip diagonal to mark.
- 0
K06:
- neck_back_to_waist_side
- Neck Back to Waist Side
- From Neck Back diagonal across back to Waist Side.
- 0
K07:
- neck_side_to_waist_side_b
- Neck Side to Waist Side, back
- From Neck Side diagonal across back to Waist Side.
- 0
K08:
- neck_side_to_armfold_f
- Neck Side to Armfold Front
- From Neck Side diagonal to Armfold Front.
- 0
K09:
- neck_side_to_armpit_f
- Neck Side to Highbust Side, front
- From Neck Side diagonal across front to Highbust Side (Armpit).
- 0
K10:
- neck_side_to_bust_side_f
- Neck Side to Bust Side, front
- Neck Side diagonal across front to Bust Side.
- 0
K11:
- neck_side_to_armfold_b
- Neck Side to Armfold Back
- From Neck Side diagonal to Armfold Back.
- 0
K12:
- neck_side_to_armpit_b
- Neck Side to Highbust Side, back
- From Neck Side diagonal across back to Highbust Side (Armpit).
- 0
K13:
- neck_side_to_bust_side_b
- Neck Side to Bust Side, back
- Neck Side diagonal across back to Bust Side.
- 0
L:
description: Hand
measurements:
L01:
- arm_shoulder_tip_to_wrist_bent
- 'Arm: Shoulder Tip to Wrist, bent'
- Bend Arm, measure from Shoulder Tip around Elbow to radial Wrist bone.
- 0
L02:
- arm_shoulder_tip_to_elbow_bent
- 'Arm: Shoulder Tip to Elbow, bent'
- Bend Arm, measure from Shoulder Tip to Elbow Tip.
- 0
L03:
- arm_elbow_to_wrist_bent
- 'Arm: Elbow to Wrist, bent'
- 'Elbow tip to wrist. (''Arm: Shoulder Tip to Wrist, bent'' - ''Arm: Shoulder Tip to Elbow, bent'').'
- arm_shoulder_tip_to_wrist_bent - arm_shoulder_tip_to_elbow_bent
L04:
- arm_elbow_circ_bent
- 'Arm: Elbow circumference, bent'
- Elbow circumference, arm is bent.
- 0
L05:
- arm_shoulder_tip_to_wrist
- 'Arm: Shoulder Tip to Wrist'
- From Shoulder Tip to Wrist bone, arm straight.
- 0
L06:
- arm_shoulder_tip_to_elbow
- 'Arm: Shoulder Tip to Elbow'
- From Shoulder tip to Elbow Tip, arm straight.
- 0
L07:
- arm_elbow_to_wrist
- 'Arm: Elbow to Wrist'
- 'From Elbow to Wrist, arm straight. (''Arm: Shoulder Tip to Wrist'' - ''Arm: Shoulder Tip to Elbow'').'
- arm_shoulder_tip_to_wrist - arm_shoulder_tip_to_elbow
L08:
- arm_armpit_to_wrist
- 'Arm: Armpit to Wrist, inside'
- From Armpit to ulna Wrist bone, arm straight.
- 0
L09:
- arm_armpit_to_elbow
- 'Arm: Armpit to Elbow, inside'
- From Armpit to inner Elbow, arm straight.
- 0
L10:
- arm_elbow_to_wrist_inside
- 'Arm: Elbow to Wrist, inside'
- 'From inside Elbow to Wrist. (''Arm: Armpit to Wrist, inside'' - ''Arm: Armpit to Elbow, inside'').'
- arm_armpit_to_wrist - arm_armpit_to_elbow
L11:
- arm_upper_circ
- 'Arm: Upper Arm circumference'
- Arm circumference at Armpit level.
- 0
L12:
- arm_above_elbow_circ
- 'Arm: Above Elbow circumference'
- Arm circumference at Bicep level.
- 0
L13:
- arm_elbow_circ
- 'Arm: Elbow circumference'
- Elbow circumference, arm straight.
- 0
L14:
- arm_lower_circ
- 'Arm: Lower Arm circumference'
- Arm circumference where lower arm is widest.
- 0
L15:
- arm_wrist_circ
- 'Arm: Wrist circumference'
- Wrist circumference.
- 0
L16:
- arm_shoulder_tip_to_armfold_line
- 'Arm: Shoulder Tip to Armfold line'
- From Shoulder Tip down to Armpit level.
- 0
L17:
- arm_neck_side_to_wrist
- 'Arm: Neck Side to Wrist'
- 'From Neck Side to Wrist. (''Shoulder Length'' + ''Arm: Shoulder Tip to Wrist'').'
- shoulder_length + arm_shoulder_tip_to_wrist
L18:
- arm_neck_side_to_finger_tip
- 'Arm: Neck Side to Finger Tip'
- 'From Neck Side down arm to tip of middle finger. (''Shoulder Length'' + ''Arm: Shoulder Tip toWrist'' + ''Hand: Length'').'
- shoulder_length + arm_shoulder_tip_to_wrist + hand_length
L19:
- armscye_circ
- 'Armscye: Circumference'
- Let arm hang at side. Measure Armscye circumference through Shoulder Tip and Armpit.
- 0
L20:
- armscye_length
- 'Armscye: Length'
- Vertical distance from Shoulder Tip to Armpit.
- 0
L21:
- armscye_width
- 'Armscye: Width'
- Horizontal distance between Armscye Front and Armscye Back.
- 0
L22:
- arm_neck_side_to_outer_elbow
- 'Arm: Neck side to Elbow'
- 'From Neck Side over Shoulder Tip down to Elbow. (Shoulder length + Arm: Shoulder Tip to Elbow).'
- shoulder_length + arm_shoulder_tip_to_elbow
M:
description: Foot
measurements:
M01:
- leg_crotch_to_floor
- 'Leg: Crotch to floor'
- Stand feet close together. Measure from crotch level (touching body, no extra space) down to floor.
- 0
M02:
- leg_waist_side_to_floor
- 'Leg: Waist Side to floor'
- From Waist Side along curve to Hip level then straight down to floor.
- 0
M03:
- leg_thigh_upper_circ
- 'Leg: Thigh Upper circumference'
- Thigh circumference at the fullest part of the upper Thigh near the Crotch.
- 0
M04:
- leg_thigh_mid_circ
- 'Leg: Thigh Middle circumference'
- Thigh circumference about halfway between Crotch and Knee.
- 0
M05:
- leg_knee_circ
- 'Leg: Knee circumference'
- Knee circumference with straight leg.
- 0
M06:
- leg_knee_small_circ
- 'Leg: Knee Small circumference'
- Leg circumference just below the knee.
- 0
M07:
- leg_calf_circ
- 'Leg: Calf circumference'
- Calf circumference at the largest part of lower leg.
- 0
M08:
- leg_ankle_high_circ
- 'Leg: Ankle High circumference'
- Ankle circumference where the indentation at the back of the ankle is the deepest.
- 0
M09:
- leg_ankle_circ
- 'Leg: Ankle circumference'
- Ankle circumference where front of leg meets the top of the foot.
- 0
M10:
- leg_knee_circ_bent
- 'Leg: Knee circumference, bent'
- Knee circumference with leg bent.
- 0
M11:
- leg_ankle_diag_circ
- 'Leg: Ankle diagonal circumference'
- Ankle circumference diagonal from top of foot to bottom of heel.
- 0
M12:
- leg_crotch_to_ankle
- 'Leg: Crotch to Ankle'
- 'From Crotch to Ankle. (''Leg: Crotch to Floor'' - ''Height: Ankle'').'
- leg_crotch_to_floor - height_ankle
M13:
- leg_waist_side_to_ankle
- 'Leg: Waist Side to Ankle'
- 'From Waist Side to Ankle. (''Leg: Waist Side to Floor'' - ''Height: Ankle'').'
- leg_waist_side_to_floor - height_ankle
M14:
- leg_waist_side_to_knee
- 'Leg: Waist Side to Knee'
- 'From Waist Side along curve to Hip level then straight down to Knee level. (''Leg: Waist Side toFloor'' - ''Height Knee'').'
- leg_waist_side_to_floor - height_knee
N:
description: Head
measurements:
N01:
- crotch_length
- Crotch length
- Put tape across gap between buttocks at Hip level. Measure from Waist Front down betwen legs and up to Waist Back.
- 0
N02:
- crotch_length_b
- Crotch length, back
- Put tape across gap between buttocks at Hip level. Measure from Waist Back to mid-Crotch, either at the vagina or between testicles and anus).
- 0
N03:
- crotch_length_f
- Crotch length, front
- From Waist Front to start of vagina or end of testicles. ('Crotch length' - 'Crotch length, back').
- crotch_length - crotch_length_b
N04:
- rise_length_side_sitting
- ''
- From Waist Side around hip curve down to surface, while seated on hard surface.
- 0
N05:
- rise_length_diag
- Rise length, diagonal
- Measure from Waist Side diagonally to a string tied at the top of the leg, seated on a hard surface.
- 0
N06:
- rise_length_b
- Rise length, back
- 'Vertical distance from Waist Back to Crotch level. (''Height: Waist Back'' - ''Leg: Crotch to Floor'')'
- height_waist_back - leg_crotch_to_floor
N07:
- rise_length_f
- Rise length, front
- 'Vertical Distance from Waist Front to Crotch level. (''Height: Waist Front'' - ''Leg: Crotch to Floor'')'
- height_waist_front - leg_crotch_to_floor
N08:
- rise_length_side
- Rise length, side
- 'Vertical distance from Waist side down to Crotch level. Use formula (Height: Waist side - Leg: Crotch to floor).'
- 0
O:
description: Men & Tailoring
measurements:
O01:
- neck_back_to_waist_front
- Neck Back to Waist Front
- From Neck Back around Neck Side down to Waist Front.
- 0
O02:
- waist_to_waist_halter
- Waist to Waist Halter, around Neck Back
- From Waist level around Neck Back to Waist level.
- 0
O03:
- waist_natural_circ
- Natural Waist circumference
- Torso circumference at men's natural side Abdominal Obliques indentation, if Oblique indentation isn't found then just below the Navel level.
- 0
O04:
- waist_natural_arc_f
- Natural Waist arc, front
- From Side to Side at the Natural Waist level, across the front.
- 0
O05:
- waist_natural_arc_b
- Natural Waist arc, back
- From Side to Side at Natural Waist level, across the back. Calculate as ( Natural Waist circumference - Natural Waist arc (front) ).
- waist_natural_circ - waist_natural_arc_f
O06:
- waist_to_natural_waist_f
- Waist Front to Natural Waist Front
- Length from Waist Front to Natural Waist Front.
- 0
O07:
- waist_to_natural_waist_b
- Waist Back to Natural Waist Back
- Length from Waist Back to Natural Waist Back.
- 0
O08:
- arm_neck_back_to_elbow_bent
- 'Arm: Neck Back to Elbow, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Neck Back to Elbow Tip.
- 0
O09:
- arm_neck_back_to_wrist_bent
- 'Arm: Neck Back to Wrist, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Neck Back to Elbow Tip to Wrist bone.
- 0
O10:
- arm_neck_side_to_elbow_bent
- 'Arm: Neck Side to Elbow, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Neck Side to Elbow Tip.
- 0
O11:
- arm_neck_side_to_wrist_bent
- 'Arm: Neck Side to Wrist, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Neck Side to Elbow Tip to Wrist bone.
- 0
O12:
- arm_across_back_center_to_elbow_bent
- 'Arm: Across Back Center to Elbow, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Middle of Back to Elbow Tip.
- 0
O13:
- arm_across_back_center_to_wrist_bent
- 'Arm: Across Back Center to Wrist, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Middle of Back to Elbow Tip to Wrist bone.
- 0
O14:
- arm_armscye_back_center_to_wrist_bent
- 'Arm: Armscye Back Center to Wrist, high bend'
- Bend Arm with Elbow out, hand in front. Measure from Armscye Back to Elbow Tip.
- 0
P:
description: Historical & Specialty
measurements:
P01:
- neck_back_to_bust_front
- Neck Back to Bust Front
- From Neck Back, over Shoulder, to Bust Front.
- 0
P02:
- neck_back_to_armfold_front
- Neck Back to Armfold Front
- From Neck Back over Shoulder to Armfold Front.
- 0
P03:
- neck_back_to_armfold_front_to_waist_side
- Neck Back, over Shoulder, to Waist Side
- From Neck Back, over Shoulder, down chest to Waist Side.
- 0
P04:
- highbust_back_over_shoulder_to_armfold_front
- Highbust Back, over Shoulder, to Armfold Front
- From Highbust Back over Shoulder to Armfold Front.
- 0
P05:
- highbust_back_over_shoulder_to_waist_front
- Highbust Back, over Shoulder, to Waist Front
- From Highbust Back, over Shoulder touching Neck Side, to Waist Front.
- 0
P06:
- neck_back_to_armfold_front_to_neck_back
- Neck Back, to Armfold Front, to Neck Back
- From Neck Back, over Shoulder to Armfold Front, under arm and return to start.
- 0
P07:
- across_back_center_to_armfold_front_to_across_back_center
- Across Back Center, circled around Shoulder
- From center of Across Back, over Shoulder, under Arm, and return to start.
- 0
P08:
- neck_back_to_armfold_front_to_highbust_back
- Neck Back, to Armfold Front, to Highbust Back
- From Neck Back over Shoulder to Armfold Front, under arm to Highbust Back.
- 0
P09:
- armfold_to_armfold_bust
- Armfold to Armfold, front, curved through Bust Front
- Measure in a curve from Armfold Left Front through Bust Front curved back up to Armfold Right Front.
- 0
P10:
- armfold_to_bust_front
- Armfold to Bust Front
- Measure from Armfold Front to Bust Front, shortest distance between the two, as straight as possible.
- 0
P11:
- highbust_b_over_shoulder_to_highbust_f
- Highbust Back, over Shoulder, to Highbust level
- From Highbust Back, over Shoulder, then aim at Bustpoint, stopping measurement at Highbust level.
- 0
P12:
- armscye_arc
- 'Armscye: Arc'
- From Armscye at Across Chest over ShoulderTip to Armscye at Across Back.
- 0
Q:
description: Patternmaking measurements
measurements:
Q01:
- dart_width_shoulder
- 'Dart Width: Shoulder'
- This information is pulled from pattern charts in some patternmaking systems, e.g. Winifred P.Aldrich's "Metric Pattern Cutting".
- 0
Q02:
- dart_width_bust
- 'Dart Width: Bust'
- This information is pulled from pattern charts in some patternmaking systems, e.g. Winifred P.Aldrich's "Metric Pattern Cutting".
- 0
Q03:
- dart_width_waist
- 'Dart Width: Waist'
- This information is pulled from pattern charts in some patternmaking systems, e.g. Winifred P.Aldrich's "Metric Pattern Cutting".
- 0
...@@ -564,7 +564,7 @@ class NormalPoint(Point, LinePropertiesMixin, FirstSecondPointMixin, LengthAngle ...@@ -564,7 +564,7 @@ class NormalPoint(Point, LinePropertiesMixin, FirstSecondPointMixin, LengthAngle
vector = self._second_point.vector - self._first_point.vector vector = self._second_point.vector - self._first_point.vector
self._pattern.calculator.set_current_segment(vector) self._pattern.calculator.set_current_segment(vector)
direction = vector.to_normalised() direction = vector.to_normalised()
direction = direction.normal() direction = direction.normal
angle = self._angle.value angle = self._angle.value
if angle: if angle:
direction = direction.rotate(angle) direction = direction.rotate(angle)
......
...@@ -168,10 +168,10 @@ class Calculator: ...@@ -168,10 +168,10 @@ class Calculator:
def _function_AngleLine(self, point_name1, point_name2): def _function_AngleLine(self, point_name1, point_name2):
point1, point2 = self._names_to_vector_points(point_name1, point_name2) point1, point2 = self._names_to_vector_points(point_name1, point_name2)
return (point2 - point1).orientation() return (point2 - point1).orientation
def _function_CurrentLength(self): def _function_CurrentLength(self):
return self._current_segment.magnitude() return self._current_segment.magnitude
def _function_C1LengthSpl(self, point_name1, point_name2): def _function_C1LengthSpl(self, point_name1, point_name2):
point1, point2 = self._names_to_vector_points(point_name1, point_name2) point1, point2 = self._names_to_vector_points(point_name1, point_name2)
...@@ -183,7 +183,7 @@ class Calculator: ...@@ -183,7 +183,7 @@ class Calculator:
def _function_Line(self, point_name1, point_name2): def _function_Line(self, point_name1, point_name2):
point1, point2 = self._names_to_vector_points(point_name1, point_name2) point1, point2 = self._names_to_vector_points(point_name1, point_name2)
return (point2 - point1).magnitude() return (point2 - point1).magnitude
def _function_Spl(self, point_name1, point_name2): def _function_Spl(self, point_name1, point_name2):
point1, point2 = self._names_to_vector_points(point_name1, point_name2) point1, point2 = self._names_to_vector_points(point_name1, point_name2)
......
...@@ -135,13 +135,15 @@ class Pattern: ...@@ -135,13 +135,15 @@ class Pattern:
############################################## ##############################################
@property
def bounding_box(self): def bounding_box(self):
"""Compute the bounding box of the pattern.""" """Compute the bounding box of the pattern."""
bounding_box = None bounding_box = None
for calculation in self._calculations: for calculation in self._calculations:
interval = calculation.geometry().bounding_box() interval = calculation.geometry().bounding_box
print(calculation.geometry(), interval)
if bounding_box is None: if bounding_box is None:
bounding_box = interval bounding_box = interval
else: else:
...@@ -167,7 +169,7 @@ class Pattern: ...@@ -167,7 +169,7 @@ class Pattern:
scene = GraphicScene() scene = GraphicScene()
# Fixme: scene bounding box # Fixme: scene bounding box
scene.bounding_box = self.bounding_box() scene.bounding_box = self.bounding_box
# Fixme: implement a transformer class to prevent if ... ? # Fixme: implement a transformer class to prevent if ... ?
......
...@@ -390,7 +390,7 @@ ul.auto-toc { ...@@ -390,7 +390,7 @@ ul.auto-toc {
<h1>Overview</h1> <h1>Overview</h1>
<div class="section" id="what-is-patro"> <div class="section" id="what-is-patro">
<h2>What is Patro ?</h2> <h2>What is Patro ?</h2>
<p>Patro is a Python module which implements pattern drafting for fashion design.</p> <p>Patro is a Python library which implements pattern drafting for fashion design.</p>
</div> </div>
<div class="section" id="where-is-the-documentation"> <div class="section" id="where-is-the-documentation">
<h2>Where is the Documentation ?</h2> <h2>Where is the Documentation ?</h2>
...@@ -398,21 +398,7 @@ ul.auto-toc { ...@@ -398,21 +398,7 @@ ul.auto-toc {
</div> </div>
<div class="section" id="what-are-the-main-features"> <div class="section" id="what-are-the-main-features">
<h2>What are the main features ?</h2> <h2>What are the main features ?</h2>
<!-- -*- Mode: rst -*- --> <!-- .. include:: features.txt -->
<p>The features of Patro are :</p>
<ul class="simple">
<li>read/write <em>.val</em> and <em>.vit</em> file</li>
<li><a class="reference external" href="http://beltoforion.de/article.php?a=muparser">QMuParser</a> expressions are translated to Python and evaluated on the fly</li>
<li>API to define patterns</li>
<li>compute the detail of a pattern</li>
<li>export the detail to latex/pgf as A0 or tiled A4 paper</li>
</ul>
<p>Missing features:</p>
<ul class="simple">
<li>full operation support</li>
<li>direct PDF export</li>
<li>SVG export</li>
</ul>
<img alt="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a0.png" src="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a0.png" style="height: 600px;" /> <img alt="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a0.png" src="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a0.png" style="height: 600px;" />
<img alt="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a4.png" src="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a4.png" style="height: 600px;" /> <img alt="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a4.png" src="https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a4.png" style="height: 600px;" />
</div> </div>
......
...@@ -66,18 +66,39 @@ ...@@ -66,18 +66,39 @@
.. https://img.shields.io/github/stars/badges/shields.svg?style=social&label=Star .. https://img.shields.io/github/stars/badges/shields.svg?style=social&label=Star
.. -*- Mode: rst -*- .. -*- Mode: rst -*-
.. |Python| replace:: Python .. |Inkscape| replace:: Inkscape
.. _Python: http://python.org .. _Inkscape: https://inkscape.org
.. |PyPI| replace:: PyPI .. |Matplotlib| replace:: Matplotlib
.. _PyPI: https://pypi.python.org/pypi .. _Matplotlib: https://matplotlib.org
.. |Numpy| replace:: Numpy .. |Numpy| replace:: Numpy
.. _Numpy: http://www.numpy.org .. _Numpy: http://www.numpy.org
.. |PyPI| replace:: PyPI
.. _PyPI: https://pypi.python.org/pypi
.. |Python| replace:: Python
.. _Python: https://python.org
.. |Qt| replace:: Qt
.. _Qt: https://www.qt.io
.. |PyQt| replace:: PyQt
.. _PyQt: https://riverbankcomputing.com/software/pyqt/intro>
.. |Reportlab| replace:: Reportlab
.. _Reportlab: https://www.reportlab.com/opensource
.. |Sphinx| replace:: Sphinx .. |Sphinx| replace:: Sphinx
.. _Sphinx: http://sphinx-doc.org .. _Sphinx: http://sphinx-doc.org
.. |Sympy| replace:: Sympy
.. _Sympy: http://www.sympy.org
.. |Valentina| replace:: Valentina
.. _Valentina: https://bitbucket.org/dismine/valentina
======= =======
Patro Patro
======= =======
...@@ -96,7 +117,7 @@ Overview ...@@ -96,7 +117,7 @@ Overview
What is Patro ? What is Patro ?
--------------- ---------------
Patro is a Python module which implements pattern drafting for fashion design. Patro is a Python library which implements pattern drafting for fashion design.
Where is the Documentation ? Where is the Documentation ?
---------------------------- ----------------------------
...@@ -106,21 +127,8 @@ The documentation is available on the |PatroHomePage|_. ...@@ -106,21 +127,8 @@ The documentation is available on the |PatroHomePage|_.
What are the main features ? What are the main features ?
---------------------------- ----------------------------
.. -*- Mode: rst -*- ..
.. include:: features.txt
The features of Patro are :
* read/write *.val* and *.vit* file
* `QMuParser <http://beltoforion.de/article.php?a=muparser>`_ expressions are translated to Python and evaluated on the fly
* API to define patterns
* compute the detail of a pattern
* export the detail to latex/pgf as A0 or tiled A4 paper
Missing features:
* full operation support
* direct PDF export
* SVG export
.. image:: https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a0.png .. image:: https://raw.github.com/FabriceSalvaire/Patro/master/test/output/pattern-a0.png
:height: 600px :height: 600px
......