Another set of reintegrated extensions

This commit is contained in:
2022-09-02 18:19:57 +02:00
parent a38a160484
commit 7cab1a92ea
88 changed files with 7769 additions and 1 deletions

View File

@ -0,0 +1,22 @@
[
{
"name": "Shirt Waist (Sara May Allington)",
"id": "fablabchemnitz.de.shirt_waist",
"path": "shirt_waist",
"dependent_extensions": null,
"original_name": "unknown",
"original_id": "unknown",
"license": "GNU GPL v2 / CC BY-NC 3.0",
"license_url": "https://docs.google.com/file/d/0BxsnjDIHW4yvZUl5SUxLTHZHems/edit",
"comment": "ported to Inkscape v1 by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/shirt_waist",
"fork_url": "http://www.taumeta.org/?page_id=247",
"documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=55018402",
"inkscape_gallery_url": null,
"main_authors": [
"taumeta.org/Susan Spencer",
"taumeta.org/Steve Conklin",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,820 @@
#!/usr/bin/env python3
#
# sewing_patterns.py
# Inkscape extension-Effects-Sewing Patterns
# Copyright (C) 2010, 2011, 2012 Susan Spencer, Steve Conklin < www.taumeta.org >
#
# 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 2 of the License, or
# (at your option) any later version. Attribution must be given in
# all derived works.
#
# 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 subprocess, math, inkex, gettext
from lxml import etree
def debug(errmsg, file = 'bell'):
#audio files directory: /usr/share/sounds/ubuntu/stereo
sounds(file)
inkex.errormsg(gettext.gettext(errmsg))
def sounds(file):
subprocess.call(['/usr/bin/canberra-gtk-play', '--id', file ])
#---
class Point():
'''Python object class to create variables that represent a named Cartesian point.
Accepts id, x, y. Returns object with .id, .x, .y attributes.
Example: a5 = Point('a5', 10.0, 15.32) returns variable a5 where a5.id = 'a5', a5.x = 10.0, a5.y = 15.32xt
If called with no parameters the defaults are id = '', x = 0, y = 0
The python object's id attribute enables it to be represented on the canvas as an svg object with the same id.
'''
def __init__(self, id = '', x = 0.0, y = 0.0): #if no parameters are passed in then the default values id = '', x = 0, y = 0 are used
self.id = id
self.x = x
self.y = y
self.y = y
def patternPointXY(parent, id, x, y):
'''Accepts parent, id, x, y. Returns object of class Point. Calls addPoint() & addText() to create a pattern point on canvas.'''
# create python variable
pnt = Point(id, x, y)
# create svg circle red 5px radius
addCircle(parent, id, x, y, radius = 5, fill = 'red', stroke = 'red', stroke_width = '1', reference = 'true')
#draw label 8px right and 8px above circle
addText(parent, id + '_text', x + 8, y-8, id, fontsize = '30', textalign = 'left', textanchor = 'start', reference = 'true') #the id is used for two things here: the text object's id and the text object's content.
return pnt # return python variable for use in remainder of pattern
def patternPoint(parent, id, pnt):
"""Wrapper for patternPointXY. Accepts a Point object instead of X & Y values."""
return patternPointXY(parent, id, pnt.x, pnt.y)
def controlPointXY(parent, id, x, y):
'''Accepts parent, id, x, y. Returns object of class Point. Calls addPoint() & addText() to create a pattern point with label on canvas.'''
# create python variable
pnt = Point(id, x, y)
# create unfilled grey circle 5px radius
addCircle(parent, id, x, y, radius = 5, fill = 'none', stroke = 'gray', stroke_width = '1', reference = 'true')
#draw label 8px right and 8px above circle
addText(parent, id + '_text', x + 8, y-8, id, fontsize = '30', textalign = 'left', textanchor = 'start', reference = 'true') #the id is used twice: the text object id and the text object content.
return pnt # return python variable for use in remainder of pattern
def controlPoint(parent, id, pnt):
"""Wrapper for controlPointXY. Accepts a Point object instead of X & Y values."""
return controlPointXY(parent, id, pnt.x, pnt.y)
def pointList( * args):
"""Accepts list of args. Returns array of args."""
list = []
for arg in args:
list.append(arg)
return list
#---tests for position---
def isRight(pnt1, pnt2):
'''returns 1 if pnt2 is to the right of pnt1'''
right = 0
if pnt2.x > pnt1.x:
right = 1
return right
def isLeft(pnt1, pnt2):
'''returns 1 if pnt2 is to the left of pnt1'''
left = 0
if pnt2.x < pnt1.x:
left = 1
return left
def isAbove(pnt1, pnt2):
'''returns 1 if pnt2 is above pnt1'''
up = 0
if pnt2.y < pnt1.y:
up = 1
return up
def isBelow(pnt1, pnt2):
'''returns 1 if pnt2 is below pnt1'''
down = 0
if pnt2.y > pnt1.y:
down = 1
return down
def lowest(pnts):
"""Accepts array pnts[]. Returns lowest point in array."""
low = Point('', pnts[0].x, pnts[0].y)
for item in pnts:
if isBelow(low, item): #if item is below current low
updatePoint('', low, item)
return low
def highest(pnts):
"""Accepts array pnts[]. Returns highest point in array."""
high = Point('', pnts[0].x, pnts[0].y)
for item in pnts:
if isAbove(high, item): #if item is above current high
updatePoint(high, item)
return high
def leftmost(pnts):
"""Accepts array pnts[]. Returns leftmost point in array."""
left = Point('', pnts[0].x, pnts[0].y)
for item in pnts:
if isLeft(left, item):
updatePoint(left, item)
return left
def rightmost(pnts):
"""Accepts array pnts[]. Returns rightmost point in array."""
right = Point('', pnts[0].x, pnts[0].y)
for item in pnts:
if isRight(right, item):
updatePoint(right, item)
return right
#---functions to calculate points. These functions do not create SVG objects---
def updatePoint(p1, p2):
'''Accepts p1 and p2 of class Point. Updates p1 with x & y values from p2'''
p1.x = p2.x
p1.y = p2.y
return
def right(p1, n):
'''Accepts point p1 and float n. Returns (x,y) to the right of p1 at (p1.x + n, p1.y)'''
return Point('', p1.x + n, p1.y)
def left(p1, n):
'''Accepts point p1 and float n. Returns p2 to the left of p1 at (p1.x-n, p1.y)'''
return Point('', p1.x-n, p1.y)
def up(p1, n):
'''Accepts point p1 and float n. Returns p2 above p1 at (p1.x, p1.y-n)'''
return Point('', p1.x, p1.y-n)
def down(p1, n):
'''Accepts point p1 and float n. Returns p2 below p1 at (p1.x, p1.y + n)'''
return Point('', p1.x, p1.y + n)
def symmetric(p1, p2, type = 'vertical'):
"""
Accepts p1 and p2 of class Point, and optional type is either 'vertical' or 'horizontal with default 'vertical'.
Returns p3 of class Point as "mirror image" of p1 relative to p2
If type == 'vertical': pnt is on opposite side of vertical line x = p2.x from p1
If type == 'horizontal': pnt is on opposite side of horizontal line y = p2.y from p1
"""
p3 = Point()
dx = p2.x - p1.x
dy = p2.y - p1.y
if (type == 'vertical'):
p3.x = p2.x + dx
p3.y = p1.y
elif (type == 'horizontal'):
p3.x = p1.x
p3.y = p2.y + dy
return p3
def polar(p1, length, angle):
'''
Adapted from http://www.teacherschoice.com.au/maths_library/coordinates/polar_-_rectangular_conversion.htm
Accepts p1 as type Point, length as float, angle as float. angle is in radians
Returns p2 as type Point, calculated at length and angle from p1,
Angles start at position 3:00 and move clockwise due to y increasing downwards on Cairo Canvas
'''
id = ''
r = length
x = p1.x + (r * math.cos(angle))
y = p1.y + (r * math.sin(angle))
p2 = Point(id, x, y)
return p2
def midPoint(p1, p2, n = 0.5):
'''Accepts points p1 & p2, and n where 0 < n < 1. Returns point p3 as midpoint b/w p1 & p2'''
p3 = Point('', (p1.x + p2.x) * n, (p1.y + p2.y) * n)
return p3
#---measurements
def distance(p1, p2):
'''Accepts two points p1 & p2. Returns the distance between p1 & p2.'''
return ( ((p2.x-p1.x) ** 2) + ((p2.y-p1.y) ** 2) ) ** 0.5
def angleOfDegree(degree):
'''Accepts degree. Returns radians.'''
return degree * math.pi/180.0
def angleOfLine(p1, p2):
""" Accepts points p1 & p2. Returns the angle of the vector between them. Uses atan2."""
return math.atan2(p2.y-p1.y, p2.x-p1.x)
def angleOfVector(p1, v, p2):
'''Accepts three points o1, v, and p2. Returns radians of the angle formed between the three points.'''
return abs(angleOfLine(v, p1)-angleOfLine(v, p2))
def slopeOfLine(p1, p2):
""" Accepts two point objects and returns the slope """
if ((p2.x-p1.x) != 0):
m = (p2.y-p1.y)/(p2.x-p1.x)
else:
#TODO: better error handling here
debug('Vertical Line in slopeOfLine')
m = None
return m
def slopeOfAngle(radians):
'''
Accepts angle (radians)
Returns the slope as tangent radians
'''
#get tangent of radians
return math.tan(radians)
#---intersections & extensions
def extendLine(p1, p2, length, rotation=0):
"""
Accepts two directed points of a line, and a length to extend the line
Finds point along line at length from p2 in direction p1->p2
"""
return onLineAtLength(p2, p1, -length)
def onLineAtLength(p1, p2, length, rotation=0):
"""
Accepts points p1 and p2, distance, and an optional rotation angle.
Returns coordinate pair on the line at length measured from p1 towards p2
If length is negative, will return a coordinate pair at length measured
from p1 in opposite direction from p2.
The result is optionally rotated about the first point by the rotation angle in degrees
"""
lineangle = angleOfLine(p1, p2)
angle = lineangle + rotation * (math.pi/180)
x = (length * math.cos(angle)) + p1.x
y = (length * math.sin(angle)) + p1.y
return Point('', x, y)
def onLineAtX(p1, p2, x):
#on line p1-p2, given x find y
if (p1.x == p2.x):# vertical line
raise ValueError('Points form a vertical line, infinite answers possible')
return None
else:
m = (p2.y - p1.y)/(p2.x - p1.x)
b = p2.y - (m * p2.x)
return Point('', x, (m * x) + b)
def onLineAtY(p1, p2, y):
#on line p1-p2, find x given y
if (p1.y == p2.y): #if horizontal line
raise ValueError('Points form a horizontal line, infinite answers possible')
return None
elif (p1.x == p2.x): # if vertical line
return Point('', p1.x, y)
else:
m = (p1.y - p2.y)/(p1.x - p2.x)
b = p2.y - (m * p2.x)
return Point('', (y - b)/m, y)
def intersectLines(p1, p2, p3, p4):
"""
Find intersection between two lines. Accepts p1, p2, p3, p4 as class Point. Returns p5 as class Point
Intersection does not have to be within the supplied line segments
"""
x, y = 0.0, 0.0
if (p1.x == p2.x): #if 1st line vertical, use slope of 2nd line
x = p1.x
m2 = slopeOfLine(p3, p4)
b2 = p3.y-m2 * p3.x
y = m2 * x + b2
elif (p3.x == p4.x): #if 2nd line vertical, use slope of 1st line
x = p3.x
m1 = slopeOfLine(p1, p2)
b1 = p1.y-m1 * p1.x
y = m1 * x + b1
else: #otherwise use ratio of difference between points
m1 = (p2.y-p1.y)/(p2.x-p1.x)
m2 = (p4.y-p3.y)/(p4.x-p3.x)
b1 = p1.y-m1 * p1.x
b2 = p3.y-m2 * p3.x
#if (abs(b1-b2) < 0.01) and (m1 == m2):
#x = p1.x
#else:
#x = (b2-b1)/(m1-m2)
if (m1 == m2):
#TODO: better error handling here
debug(' ** ** * Parallel lines in intersectLines ** ** * ')
else:
x = (b2-b1)/(m1-m2)
y = (m1 * x) + b1 # arbitrary choice, could have used m2 & b2
p5 = Point("", x, y)
return p5
def intersectLineRay(P1, P2, R1, angle):
'''
Accepts two points defining a line, and a point and angle defining a ray.
Returns point where they intersect.
'''
#define a line R1-R2 by finding point R2 along ray 25 pixels (arbitary) from R1
R2 = polar(R1, 1 * 25, angle)
pnt = intersectLines(P1, P2, R1, R2)
return pnt
def onRayAtX(P, angle, x):
'''
Accepts point P and angle of line.
Returns point along ray at x
'''
#convert degrees to slope
m = slopeOfAngle(angle)
#solve for y
#(P.y - y)/(P.x - x) = m
y = P.y - m * (P.x - x)
return Point('', x, y)
def onRayAtY(P, angle, y):
'''
Accepts point P and angle of line.
Returns point along ray at y
'''
#convert degrees to slope
m = slopeOfAngle(angle)
#solve for x
#(P.y - y)/(P.x - x) = m
x = P.x - (P.y - y)/m
return Point('', x, y)
def intersectCircles(C1, r1, C2, r2):
"""
Accepts C1, r1, C2, r2 where C1 & C2 are center points of each circle, and r1 & r2 are the radius of each circle.
Returns an array of points of intersection.
"""
x0, y0 = C1.x, C1.y
x1, y1 = C2.x, C2.y
d = distance(C1, C2) # distance b/w circle centers
dx, dy = (x1-x0), (y1-y0) # negate y b/c canvas increases top to bottom
pnts = []
if (d == 0):
#intersections = 0
#TODO: better error handling here
debug('center of both circles are the same in intersectCircles()')
debug('C1 = ', C1.x, C1.y, 'radius1 = ', r1)
debug('C2 = ', C2.x, C2.y, 'radius1 = ', r2)
return
elif (d < abs(r1-r2)):
#intersections = 0
#TODO: better error handling here
debug('one circle is within the other in intersectCircles()')
debug('d = ', d)
debug('r1 - r2 = ', (r1-r2))
debug('d < abs(r1 - r2) ?', (d < abs(r1-r2)))
debug('C1 = ', C1.x, C1.y, 'radius1 = ', r1)
debug('C2 = ', C2.x, C2.y, 'radius1 = ', r2)
return
elif (d > (r1 + r2)):
#intersections = 0
#TODO: better error handling here
debug('circles do not intersect in intersectCircles()')
debug('d = ', d)
debug('r1 + r2 = ', (r1 + r2))
debug('d > abs(r1 + r2) ?', (d > abs(r1 + r2)))
debug('C1 = ', C1.x, C1.y, 'radius1 = ', r1)
debug('C2 = ', C2.x, C2.y, 'radius1 = ', r2)
# TODO:possible kluge -check if this is acceptable using a small margin of error between r1 & r2 (0.5 * CM)?:
#r2 = d-r1
return
else:
#intersections = 2 or intersections = 1
a = ((r1 * r1)-(r2 * r2) + (d * d))/(2.0 * d)
x2 = x0 + (dx * a/d)
y2 = y0 + (dy * a/d)
h = math.sqrt((r1 * r1)-(a * a))
rx = -dy * (h/d)
ry = dx * (h/d)
X1 = x2 + rx
Y1 = y2 + ry
X2 = x2-rx
Y2 = y2-ry
pnts.append(Point("", X1, Y1))
pnts.append(Point("", X2, Y2))
return pnts
def onCircleAtX(C, r, x):
"""
Finds points on circle where p.x=x
Accepts C as an object of class Point or xy coords for the center of the circle,
r as the radius, and x to find the points on the circle
Returns an array P
Based on paulbourke.net/geometry/sphereline/sphere_line_intersection.py
"""
#print 'C =', C.x, C.y
#print 'r =', r
#print 'x =', x
P = []
if abs(x - C.x) > r:
print('abs(x - C.x) > r ...', abs(x - C.x), ' > ', r)
print('x is outside radius of circle in intersections.onCircleAtX()')
else:
translated_x = x - C.x # center of translated circle is (0, 0) as translated_x is the difference b/w C.x & x
translated_y1 = abs(math.sqrt(r**2 - translated_x**2))
translated_y2 = -(translated_y1)
y1 = translated_y1 + C.y # translate back to C.y
y2 = translated_y2 + C.y # translate back to C.y
P.append(Point('', x, y1))
P.append(Point('', x, y2))
return P
def onCircleAtY(C, r, y):
"""
Finds points one or two points on circle where P.y=y
Accepts C of class Point or coords as circle center, r of type float as radius, and y of type float)
Returns an array P
Based on paulbourke.net/geometry/sphereline/sphere_line_intersection.py
"""
#print('C =', C.x, C.y)
#print('r =', r)
#print('x = ', y))
P = []
if abs(y - C.y) > r:
print('abs(y - C.y) > r ...', abs(y - C.y), ' > ', r)
print('y is outside radius in onCircleAtY() -- no intersection')
else:
translated_y = y - C.y
translated_x1 = abs(math.sqrt(r**2 - translated_y**2))
translated_x2 = -translated_x1
x1 = translated_x1 + C.x
x2 = translated_x2 + C.x
P.append(Point('', x1, y))
P.append(Point('', x2, y))
return P
def intersectLineCircle(P1, P2, C, r):
"""
Finds intersection of a line segment and a circle.
Accepts circle center point object C, radius r, and two line point objects P1 & P2
Returns an array P with up to two coordinate pairs as P.intersections P[0] & P[1]
Based on paulbourke.net/geometry/sphereline/sphere_line_intersection.py
"""
#print('C =', C.x, C.y)
#print('P1 =', P1.x, P1.y)
#print('P2 =', P2.x, P2.y)
#print('r =', r, 'pts', ', ', r / CM, 'cm')
p1, p2 = Point('', '', ''), Point('', '', '')
P = []
if P1.x == P2.x: #vertical line
if abs(P1.x - C.x) > r:
print('no intersections for vertical line P1', P1.name, P1.x, P1.y, ', P2', P2.name, P2.x, P2.y, ', and Circle', C.name, C.x, C.y, ', radius', r)
return None
else:
#print('Vertical line')
p1.x = P1.x
p2.x = P1.x
p1.y = C.y + sqrt(r**2 - (P1.x - C.x)**2)
p2.y = C.y - sqrt(r**2 - (P1.x - C.x)**2)
elif P1.y == P2.y: #horizontal line
if abs(P1.y-C.y) > r:
print('no intersections for horizontal line P1', P1.name, P1.x, P1.y, ', P2', P2.name, P2.x, P2.y, ', and Circle', C.name, C.x, C.y, ', radius', r)
return None
else:
#print('Horizontal line')
p1.y = P1.y
p2.y = P1.y
p1.x = C.x + sqrt(r**2 - (P1.y - C.y)**2)
p2.x = C.x - sqrt(r**2 - (P1.y - C.y)**2)
else:
a = (P2.x - P1.x)**2 + (P2.y - P1.y)**2
b = 2.0 * ((P2.x - P1.x) * (P1.x - C.x)) + ((P2.y - P1.y) * (P1.y - C.y))
c = C.x**2 + C.y**2 + P1.x**2 + P1.y**2 - (2.0 * (C.x * P1.x + C.y * P1.y)) - r**2
i = b**2 - 4.0 * a * c
if i < 0.0:
print('no intersections b/w line', P1.name, P1.x, P1.y, '--', P2.name, P2.x, P2.y, 'and Circle', C.name, C.x, C.y, 'with radius', r)
return None
elif i == 0.0:
# one intersection
#print('one intersection')
mu = -b/(2.0 * a)
p1.x, p1.y = P1.x + mu * (P2.x - P1.x), P1.y + mu * (P2.y - P1.y)
elif i > 0.0:
# two intersections
#print('two intersections')
# first intersection
mu1 = (-b + math.sqrt(i)) / (2.0*a)
p1.x, p1.y = P1.x + mu1 * (P2.x - P1.x), P1.y + mu1 * (P2.y - P1.y)
# second intersection
mu2 = (-b - math.sqrt(i)) / (2.0*a)
p2.x, p2.y = P1.x + mu2 * (P2.x - P1.x), P1.y + mu2 * (P2.y - P1.y)
P.append(p1)
P.append(p2)
return P
def intersectChordCircle(C, r, P, chord_length):
''' Accepts center of circle, radius of circle, a point on the circle, and chord length.
Returns an array of two points on the circle at chord_length distance away from original point'''
d = chord_length
# point on circle given chordlength & starting point = 2 * asin(d/2r)
d_div_2r = d/(2.0 * r)
angle = 2 * asin(d_div_2r)
pnts = []
pnts.append(polar(C, r, angle))
pnts.append(polar(C, r, - angle))
return pnts
def intersectLineCurve(P1, P2, curve, n = 100):
'''
Accepts two points of a line P1 & P2, and an array of connected bezier curves [P11, C11, C12, P12, C21, C22, P22, C31, C32, P32, ...]
Returns an array points_found[] of point objects where line intersected with the curve, and tangents_found[] of tangent angle at that point
'''
# get polar equation for line for P1-P2
# point furthest away from 1st point in curve[] is the fixed point & sets the direction of the angle towards the curve
#if distance(P1, curve[0]) > = distance(P2, curve[0] ):
# fixed_pnt = P1
# angle = angleOfLine(P1, P2)
#else:
# fixed_pnt = P2
# angle = angleOfLine(P2, P1)
#debug('intersectLineCurve...')
#debug('....P1 = ' + P1.id + ' ' + str(P1.x) + ', ' + str(P1.y))
#debug('....P2 = ' + P2.id + ' ' + str(P2.x) + ', ' + str(P2.y))
#for pnt in curve:
#debug( '....curve = ' + pnt.id + ' ' + str(pnt.x) + ', ' + str(pnt.y))
fixed_pnt = P1
angle = angleOfLine(P1, P2)
intersections = 0
points_found = []
tangents_found = []
pnt = Point()
j = 0
while j <= len(curve) -4: # for each bezier curve in curveArray
intersection_estimate = intersectLines(P1, P2, curve[j], curve[j + 3]) # is there an intersection?
if intersection_estimate != None or intersection_estimate != '':
interpolatedPoints = interpolateCurve(curve[j], curve[j + 1], curve[j + 2], curve[j + 3], n) #interpolate this bezier curve, n = 100
k = 0
while k < len(interpolatedPoints)-1:
length = distance(fixed_pnt, interpolatedPoints[k])
pnt_on_line = polar(fixed_pnt, length, angle)
range = distance(interpolatedPoints[k], interpolatedPoints[k + 1]) # TODO:improve margin of error
length = distance(pnt_on_line, interpolatedPoints[k])
#debug(str(k) + 'pntOnCurve = ' + \
# str(interpolatedPoints[k].x) + ', ' + str(interpolatedPoints[k].y) + \
# 'intersectLineAtLength = ' + str(pnt_on_line.x) + ', ' + str( pnt_on_line.y)\
# + 'length = ' + str(length) + 'range = ' + str(range))
if ( length <= range):
#debug('its close enough!')
if k > 1:
if (interpolatedPoints[k-1] not in points_found) and (interpolatedPoints[k-2] not in points_found):
points_found.append(interpolatedPoints[k])
tangents_found.append(angleOfLine(interpolatedPoints[k-1], interpolatedPoints[k + 1]))
intersections += 1
elif k == 1:
if (curve[0] not in intersections):
points_found.append(interpolatedPoints[1])
tangents_found.append(angleOfLine(curve[0], interpolatedPoints[2]))
intersections += 1
else:
intersections.append(curve[0])
tangents_found.append(angleOfLine(curve[0], curve[1]))
k += 1
j += 3 # skip j up to P3 of the current curve to be used as P0 start of next curve
if intersections == 0:
#TODO: better error handling here
debug('no intersections found in intersectLineCurve(' + P1.id + ', ' + P2.id + ' and curve')
#return points_found, tangents_found
return points_found
def interpolateCurve(P0, C1, C2, P1, t = 100):
'''
Accepts curve points P0, C1, C2, P1 & number of interpolations t
Returns array of interpolated points of class Point
Adapted from http://www.planetclegg.com/projects/WarpingTextToSplines.htm
'''
# calculate coefficients for two knot points P0 & P1 ; C1 & C2 are the controlpoints.
# x coefficients
A = P1.x-(3 * C2.x) + (3 * C1.x)-P0.x
B = (3 * C2.x)-(6 * C1.x) + (3 * P0.x)
C = (3 * C1.x)-(3 * P0.x)
D = P0.x
# y coefficients
E = P1.y-(3 * C2.y) + (3 * C1.y)-P0.y
F = (3 * C2.y)-(6 * C1.y) + (3 * P0.y)
G = (3 * C1.y)-(3 * P0.y)
H = P0.y
# calculate interpolated points
interpolatedPoints = []
maxPoint = float(t)
i = 0
while ( i <= t):
j = i/maxPoint # j can't be an integer, i/t is an integer..always 0.
x = A * (j ** 3) + B * (j ** 2) + C * j + D
y = E * (j ** 3) + F * (j ** 2) + G * j + H
interpolatedPoints.append(Point('', x, y))
i += 1
return interpolatedPoints
#---rotations
def slashAndSpread(pivot, angle, *args):
"""
Accepts pivot point, angle of rotation, and the points to be rotated.
Accepts positive & negative angles.
"""
if (angle == 0.0):
print('Angle = 0 -- Slash and Spread not possible')
else:
list = []
for arg in args:
list.append(arg)
i = 0
for pnt in list:
length = distance(pivot, pnt)
rotated_pnt = polar(pivot, length, angleOfLine(pivot, pnt) + angle) # if angle > 0 spread clockwise. if angle < 0 spread counterclockwise
updatePoint(pnt, rotated_pnt)
return
#---darts
def foldDart(dart, inside_pnt, seam_allowance):
'''
Accepts dart, and the nearest point in the direction dart will be folded
Returns dart.m, dart.oc, dart.ic, dart.angle
dart.m = middle dart leg at seamline (to be included in seamline path)
dart.oc = inside dart leg at cuttingline (to be included in dartline path)
dart.oc = outside dart leg at cuttingline (to be included in dartline path)
'''
mid_pnt = midPoint(dart.i, dart.o)
dart_length = distance(dart, dart.i)
i_angle = angleOfLine(dart, dart.i)
c_angle = angleOfLine(dart, inside_pnt)
dart_angle = abs(angleOfVector(dart.i, dart, dart.o))
dart_half_angle = dart_angle/2.0
#determine which direction the dart will be folded
#if ((dart.i.x > dart.x) and (dart.i.y > dart.y)) or ((dart.i.x < dart.x) and (dart.i.y > dart.y)):
#x & y vectors not the same sign
#dart_half_angle = -dart_half_angle
if i_angle > c_angle:
dart_half_angle = -dart_half_angle
elif dart_angle < c_angle:
#dart straddles 0 angle
dart_half_angle = -dart_half_angle
fold_angle = i_angle + dart_half_angle
fold_pnt = intersectLineRay(dart.i, inside_pnt, dart, fold_angle)
dart.m = onLineAtLength(dart, mid_pnt, distance(dart, fold_pnt)) #dart midpoint at seamline
dart.oc = polar(dart, distance(dart, dart.o) + seam_allowance, angleOfLine(dart, dart.o)) #dart outside leg at cuttingline
dart.ic = extendLine(dart, dart.i, seam_allowance) #dart inside leg at cuttingline
#create or update dart.angles
dart.angle = angleOfVector(dart.i, dart, dart.o)
return
#---base, pattern & patternpiece groups
def base(parent, id):
'''Create a base group to contain all patterns, parent should be the document'''
newBase = addGroup(parent, id)
newBase.set(inkex.addNS('label', 'inkscape'), id)
newBase.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
return newBase
def pattern(parent, id):
'''Create a pattern group to hold a single pattern, parent should be the base group'''
newPattern = addGroup(parent, id)
newPattern.set(inkex.addNS('label', 'inkscape'), id)
newPattern.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
return newPattern
def patternPiece(parent, id, name, fabric = 2, interfacing = 0, lining = 0):
'''Create a pattern piece group to hold a single pattern piece, parent should be a pattern group'''
newPatternPiece = addGroup(parent, id)
newPatternPiece.set(inkex.addNS('label', 'inkscape'), name)
newPatternPiece.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
return newPatternPiece
#---svg
def addCircle(parent, id, x, y, radius = 5, fill = 'red', stroke = 'red', stroke_width = '1', reference = 'false'):
'''create & write a circle to canvas & set it's attributes'''
circ = etree.SubElement(parent, inkex.addNS('circle', 'svg'))
circ.set('id', id)
circ.set('cx', str(x))
circ.set('cy', str(y))
circ.set('r', str(radius))
circ.set('fill', fill)
circ.set('stroke', stroke)
circ.set('stroke-width', stroke_width)
if reference == 'true':
circ.attrib['reference'] = 'true'
return
def addSquare(parent, id, w, h, x, y, reference='false'):
# create & write a square element, set its attributes
square = etree.SubElement(parent, inkex.addNS('rect', 'svg'))
square.set('id', id)
square.set('width', str(w))
square.set('height', str(h))
square.set('x', str(x))
square.set('y', str(y))
square.set('stroke', 'none')
square.set('fill', '#000000')
square.set('stroke-width', '1')
if (reference == 'true'):
square.attrib['reference'] = 'true'
return
def addText(parent, id, x, y, text, fontsize = '12', textalign = 'left', textanchor = 'start', reference = 'false'):
'''Create a text element, set its attributes, then write to canvas.
The text element is different than other elements -- > Set attributes first then write to canvas using append method.
There is no etree.SubElement() method for creating a text element & placing it into the document in one step.
Use inkex's etree.Element() method to create an unattached text svg object,
then use a document object's append() method to place it on the document canvas'''
#create a text element with inkex's Element()
txt = etree.Element(inkex.addNS('text', 'svg'))
#set attributes of the text element
txt.set('id', id)
txt.set('x', str(x))
txt.set('y', str(y))
txt.text = text
style = {'text-align':textalign, 'text-anchor':textanchor, 'font-size':fontsize}
txt.set('style', str(inkex.Style(style)))
if reference == 'true':
txt.attrib['reference'] = 'true'
#txt.setAttribute('reference', 'true') #alternative syntax
#add to canvas in the parent group
parent.append(txt)
return
def addLayer(parent, id):
'''Create & write an inkscape group-layer to canvas'''
new_layer = etree.SubElement(parent, 'g')
new_layer.set('id', id)
new_layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
new_layer.set(inkex.addNS('label', 'inkscape'), '%s layer' % (id))
return new_layer
def addGroup(parent, id):
'''Create & write an svg group to canvas'''
new_layer = etree.SubElement(parent, 'g')
new_layer.set('id', id)
return new_layer
def addPath(parent, id, path_str, path_type):
'''Accepts parent, id, path string, path type. Creates attribute dictionary. Creates & writes path.'''
reference = 'false'
if path_type == 'seamline':
path_style = {'stroke':'green', 'stroke-width':'4', 'fill':'none', 'opacity':'1', 'stroke-dasharray':'15, 5', 'stroke-dashoffset':'0'}
elif path_type == 'cuttingline':
path_style = {'stroke':'green', 'stroke-width':'4', 'fill':'none', 'opacity':'1'}
elif path_type == 'gridline':
path_style = {'stroke':'gray', 'stroke-width':'4', 'fill':'none', 'opacity':'1', 'stroke-dasharray':'6, 6', 'stroke-dashoffset':'0'}
reference = 'true'
elif path_type == 'dartline':
path_style = {'stroke':'gray', 'stroke-width':'4', 'fill':'none', 'opacity':'1'}
elif path_type == 'grainline':
path_style = {'stroke':'DimGray', 'stroke-width':'3', 'fill':'none', 'opacity':'1', \
'marker-start':'url(#ArrowStart)', 'marker-end':'url(#ArrowEnd)'}
elif path_type == 'slashline':
path_style = {'stroke':'green', 'stroke-width':'4', 'fill':'none', 'opacity':'1'}
svg_path = etree.SubElement(parent, inkex.addNS('path', 'svg'))
svg_path.set('id', id)
svg_path.set('d', path_str)
svg_path.set('style', str(inkex.Style(path_style)))
if reference == 'true':
svg_path.attrib['reference'] = 'true'
return svg_path
def formatPath( * args):
"""
Accepts a series of pseudo svg path arguments 'M', 'L', 'C' , and point objects.
Returns path_string which is a string formatted for use as the 'd' path attribute in an svg object.
"""
tokens = [] # initialize an empty array
# put all the parameters in * args into the array
for arg in args:
tokens.append(arg)
com = ', '
path_string = ''
i = 0
while (i < len(tokens)):
cmd = tokens[i]
if (cmd == 'M') or (cmd == 'L'):
path_string += " %s %g %g" % (cmd, tokens[i + 1].x, tokens[i + 1].y)
i = i + 2
elif (cmd == 'C'):
path_string += " %s %g %g %g %g %g %g" % (cmd, tokens[i + 1].x, \
tokens[i + 1].y, tokens[i + 2].x, tokens[i + 2].y, tokens[i + 3].x, tokens[i + 3].y)
i = i + 4
return path_string
def addDefs(doc):
'''Add defs group with markers to the document'''
defs = etree.SubElement(doc, inkex.addNS('defs', 'svg'))
#add start arrow
marker = etree.SubElement(defs, 'marker', {'id':'ArrowStart', 'orient':'auto', 'refX':'0.0', 'refY':'0.0', 'style':'overflow:visible'})
etree.SubElement(marker, 'path', {'d':'M 0, 0 L 0, 5 L -20, 0 L 0, -5 z', 'style':'fill:DimGray; stroke:DimGray; stroke-width:0.5'})
#add end arrow
marker = etree.SubElement(defs, 'marker', {'id':'ArrowEnd', 'orient':'auto', 'refX':'0.0', 'refY':'0.0', 'style':'overflow:visible'})
etree.SubElement(marker, 'path', {'d':'M 0, 0 L 0, 5 L 20, 0 L 0, -5 z', 'style':'fill:DimGray; stroke:DimGray; stroke-width:0.5'})

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Shirt Waist (Sara May Allington)</name>
<id>fablabchemnitz.de.shirt_waist</id>
<param name="m_unit" type="optiongroup" appearance="combo" gui-text="Select measurement: ">
<option value="Inches">Inches</option>
<option value="Centimeters">Centimeters</option>
</param>
<param name="m_front_waist_length" type="float" min="1.0" max="100.0" gui-text="Front Waist Length">15.0</param>
<param name="m_back_waist_length" type="float" min="1.0" max="100.0" gui-text="Back Waist Length">15.5</param>
<param name="m_neck_circumference" type="float" min="1.0" max="100.0" gui-text="Neck Circumference">13.5</param>
<param name="m_bust_circumference" type="float" min="1.0" max="100.0" gui-text="Bust Circumference">39.0</param>
<param name="m_waist_circumference" type="float" min="1.0" max="100.0" gui-text="Waist Circumference">25.0</param>
<param name="m_armscye_circumference" type="float" min="1.0" max="100.0" gui-text="Armscye Circumference">15.0</param>
<param name="m_across_back" type="float" min="1.0" max="100.0" gui-text="Across Back">13.5</param>
<param name="m_side" type="float" min="1.0" max="100.0" gui-text="Side">7.75</param>
<param name="m_upper_front_height" type="float" min="1.0" max="100.0" gui-text="Upper Front Height">10.75</param>
<param name="m_overarm_length" type="float" min="1.0" max="100.0" gui-text="Overarm Length">20.00</param>
<param name="m_elbow_height" type="float" min="1.0" max="100.0" gui-text="Elbow height - from wrist to elbow">9.50</param>
<param name="m_elbow_circumference" type="float" min="1.0" max="100.0" gui-text="Elbow Circumference">12.50</param>
<param name="m_hand_circumference" type="float" min="1.0" max="100.0" gui-text="Hand Circumference">8.00</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Dimensioning/Measuring"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">shirt_waist.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,459 @@
#!/usr/bin/env python3
#
# shirt_waist_allington.py
# Inkscape extension-Effects-Sewing Patterns-Shirt Waist Allington
# Copyright (C) 2010, 2011, 2012 Susan Spencer, Steve Conklin <www.taumeta.org>
'''
Licensing paragraph:
1. CODE LICENSE: GPL 2.0+
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2. PATTERN LICENSE: CC BY-NC 3.0
The output of this code is a pattern and is considered a
visual artwork. The pattern is licensed under
Attribution-NonCommercial 3.0 (CC BY-NC 3.0)
<http://creativecommons.org/licenses/by-nc/3.0/>
Items made from the pattern may be sold;
the pattern may not be sold.
End of Licensing paragraph.
'''
import math, inkex
from sewing_patterns import *
class ShirtWaist(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument('--m_unit', default = 'Inches', help = 'Centimeters or Inches?')
pars.add_argument('--m_front_waist_length', type = float, default = '15.0', help = 'Front Waist Length')
pars.add_argument('--m_back_waist_length', type = float, default = '15.5', help = 'Back Waist Length')
pars.add_argument('--m_neck_circumference', type = float, default = '13.5', help = 'Neck Circumference')
pars.add_argument('--m_bust_circumference', type = float, default = '39.0', help = 'Bust Circumference')
pars.add_argument('--m_waist_circumference', type = float, default = '25.0', help = 'Waist Circumference')
pars.add_argument('--m_armscye_circumference', type = float, default = '15.0', help = 'Armscye circumference')
pars.add_argument('--m_across_back', type = float, default = '13.5', help = 'Across Back')
pars.add_argument('--m_shoulder', type = float, default = '6.5', help = 'Shoulder')
pars.add_argument('--m_side', type = float, default = '7.75', help = 'Side')
pars.add_argument('--m_upper_front_height', type = float, default = '10.75', help = 'Upper Front Height')
pars.add_argument('--m_overarm_length', type = float, default = '20.00', help = 'Overarm Length')
pars.add_argument('--m_elbow_height', type = float, default = '9.50', help = 'Elbow Height - from wrist to elbow')
pars.add_argument('--m_elbow_circumference', type = float, default = '12.50', help = 'Elbow Circumference - arm bent')
pars.add_argument('--m_hand_circumference', type = float, default = '8.00', help = 'Hand Circumference')
def effect(self):
def printPoint(pnt):
debug(' %s = %f, %f')%pnt.id, pnt.x, pnt.y
INCH_to_PX = 96.0 #inkscape 1.0 uses 96 pixels per 1 inch
CM_to_INCH = 1/2.54
CM_to_PX = CM_to_INCH*INCH_to_PX
CM = CM_to_PX # CM - shorthand when using centimeters
IN = INCH_to_PX # IN - shorthand when using inches
#all measurements must be converted to px
munit = self.options.m_unit
if munit == 'Centimeters':
MEASUREMENT_CONVERSION = CM
else:
MEASUREMENT_CONVERSION = IN
#convert measurements
front_waist_length = self.options.m_front_waist_length * MEASUREMENT_CONVERSION
neck_circumference = self.options.m_neck_circumference * MEASUREMENT_CONVERSION
bust_circumference = self.options.m_bust_circumference * MEASUREMENT_CONVERSION
waist_circumference = self.options.m_waist_circumference * MEASUREMENT_CONVERSION
armscye_circumference = self.options.m_armscye_circumference * MEASUREMENT_CONVERSION
across_back = self.options.m_across_back * MEASUREMENT_CONVERSION
shoulder = self.options.m_shoulder * MEASUREMENT_CONVERSION
side = self.options.m_side * MEASUREMENT_CONVERSION
upper_front_height = self.options.m_upper_front_height * MEASUREMENT_CONVERSION
overarm_length = self.options.m_overarm_length * MEASUREMENT_CONVERSION
elbow_height = self.options.m_elbow_height * MEASUREMENT_CONVERSION
elbow_circumference = self.options.m_elbow_circumference * MEASUREMENT_CONVERSION
hand_circumference = self.options.m_hand_circumference * MEASUREMENT_CONVERSION
#constants
ANGLE90 = angleOfDegree(90)
ANGLE180 = angleOfDegree(180)
SEAM_ALLOWANCE = (5/8.0)*IN
BORDER = 1*IN
NOTE_HEIGHT = 1*IN
#get the document, set initial width & height
doc = self.document.getroot() # self.document is the canvas seen in Inkscape
#add defs group with markers to document
addDefs(doc)
#width_orig = inkex.unittouu(doc.get('width'))
#height_orig = inkex.unittouu(doc.get('height'))
#doc_width = 4*BORDER+4*SEAM_ALLOWANCE + bust_circumference/2.0
#doc_height = 2*BORDER+3*SEAM_ALLOWANCE+(upper_front_height+side)
#doc.set('width', str(doc_width)) #temporary document width, doc is resized near end of pattern file
#doc.set('height', str(doc_height)) #temporary document height, doc is resized near end of pattern file
#create a base group in the document to hold all patterns
pattern_base = base(doc, 'pattern_base')
#create a pattern group for each pattern, put pattern group in base group - there can be multiple patterns
bodice = pattern(pattern_base, 'bodice')
# create a group for each pattern piece, put pattern piece group in pattern group
A = patternPiece(bodice, 'A', 'bodice_front', fabric = 2, interfacing = 0, lining = 0)
B = patternPiece(bodice, 'B', 'bodice_back', fabric = 2, interfacing = 0, lining = 0)
C = patternPiece(bodice, 'C', 'bodice_sleeve', fabric = 2, interfacing = 0, lining = 0)
D = patternPiece(bodice, 'D', 'bodice_cuff', fabric = 2, interfacing = 0, lining = 0)
#pattern notes
notes = []
notes.append('Define Seam Allowances: Select File/Inkscape Preferences/Steps and set Outset to 56.25px (5/8" seam allowance)')
notes.append('Create Seam Allowances: Press CTRL-F, type "cuttingline" in the ID field, click the Find button, press CTRL-)')
notes.append('Remove Points & Gridlines: Press CTRL-F, type "reference" in the Attribute field, click Find button, press DELETE')
notes.append('Print: Save as a PDF file, open PDF with PDF viewer (Adobe, Evince, Okular, xPDF), print from Print Preview option')
#pattern points
b1 = patternPointXY(B, 'b1', 0, 0) #B
b2 = patternPoint(B, 'b2', down(b1, front_waist_length)) #A
b3 = patternPoint(B, 'b3', up(b2, side)) #C
a1 = patternPoint(A, 'a1', left(b3, bust_circumference/2.0)) #D
b4 = patternPoint(B, 'b4', left(b3, across_back/2.0)) #E
b5 = patternPoint(B, 'b5', up(b4, armscye_circumference/3.0)) #F
b6 = patternPoint(B, 'b6', up(b1, 0.5*IN)) #G
b7 = patternPoint(B, 'b7', left(b6, 1.5*IN)) #H
b8 = patternPoint(B, 'b8', onLineAtLength(b5, b7, -0.5*IN)) #I
a2 = patternPoint(A, 'a2', left(b4, armscye_circumference/4.0)) #J
a3 = patternPoint(A, 'a3', midPoint(a2, b4)) #K
a4 = patternPoint(A, 'a4', up(a2, 2.5*IN)) #L
a5 = patternPoint(A, 'a5', up(b5, 1.5*IN)) #M
a6 = patternPoint(A, 'a6', left(a5, 2*IN)) #N
a7 = patternPoint(A, 'a7', left(a6, distance(b7, b8))) #O
a8 = patternPointXY(A, 'a8', a7.x, b3.y - (upper_front_height - distance(b1, b7))) #P
a9 = patternPoint(A, 'a9', down(a8, neck_circumference/4.0)) #Q
a10 = patternPoint(A, 'a10', up(a9, 0.5*IN)) #R
a11 = patternPoint(A, 'a11', left(a10, (neck_circumference/6.0)+0.25*IN )) #S
b9 = patternPoint(B, 'b9', midPoint(a3, b4)) #T on back bodice B
a12 = patternPoint(A, 'a12', b9) #T on front bodice A
b10 = patternPoint(B, 'b10', down(b9, side)) #U
b11 = patternPoint(B , 'b11', right(b10, 1*IN)) #V
a13 = patternPoint(A, 'a13', left(b10, 1*IN)) #W
a14 = patternPoint(A, 'a14', onLineAtLength(a11, a1, front_waist_length)) #X
a15 = patternPoint(A, 'a15', down(a8, distance(a8, a14))) #Y - new point at front waist
b12 = patternPoint(B, 'b12', up(b4, distance(b5, b4)/3.0)) #Z - new point at back armscye
#temporary armscye curve from a3 to b12 to find top point of side seam
length = distance(a3, b12)/3.0
temp_b12_c1 = right(a3, length) #don't create an svg controlpoint circle for this point
temp_b12_c2 = down(b12, length) #or for this point
#find top point of side seam with intersection of side and armscye curve, save to two points a16 and b13
curve1 = pointList(a3, temp_b12_c1, temp_b12_c2, b12)
intersections = intersectLineCurve(b10, b9, curve1) #this line is directional from b10 to b9
b13 = patternPoint(B, 'b13', intersections[0]) # AA on bodice back B -use 1st intersection found, in this case there's only one intersection
a16 = patternPoint(A, 'a16', b13) #AA on bodice back A
#front control points - path runs counterclockwise from front neck center a11
#front neck control points from a8 to a11
length = distance(a8, a11)/3.0
a11.c2 = controlPoint(A, 'a11.c2', right(a11, 1.5*length))
a11.c1 = controlPoint(A, 'a11.c1', polar(a8, length, angleOfLine(a8, a11.c2)))
#front waist control points from a14 to a15
length = distance(a14, a15)/3.0
a15.c1 = controlPoint(A, 'a15.c1', polar(a14, length, angleOfLine(a14, a11)+ANGLE90)) #control handle line is perpendicular to line a14-a11
a15.c2 = controlPoint(A, 'a15.c2', left(a15, length))
#front waist control points from a15 to a13
length = distance(a15, a13)/3.0
a13.c1 = controlPoint(A, 'a13.c1', right(a15, 1.5*length))
a13.c2 = controlPoint(A, 'a13.c2', polar(a13, length, angleOfLine(a13, a13.c1))) #second control aimed at first control point
#front side control points from a13 to a12
length = distance(a13, a12)/3.0
a12.c1 = controlPoint(A, 'a12.c1', up(a13, length))
a12.c2 = controlPoint(A, 'a12.c2', down(a12, length))
#front armscye control points from a16 to a3 to a4 to 16
length1 = distance(a16, a3)/3.0
length2 = distance(a3, a4)/3.0
length3 = distance(a4, a6)/3.0
angle1 = angleOfLine(a16, a3)
angle2 = ANGLE180
angle3 = (angle1+angle2)/2.0
a3.c1 = controlPoint(A, 'a3.c1', polar(a16, length1, angle1))
a3.c2 = controlPoint(A, 'a3.c2', polar(a3, length1, angle3-ANGLE180))
a4.c1 = controlPoint(A, 'a4.c1', polar(a3, length2, angle3))
angle4 = angleOfLine(a3, a6)
angle5 = angleOfLine(a4, a6)
angle6 = (angle4+angle5)/2.0
a4.c2 = controlPoint(A, 'a4.c2', polar(a4, 1.5*length2, angle6-ANGLE180))
a6.c1 = controlPoint(A, 'a6.c1', polar(a4, length3, angle6))
a6.c2 = controlPoint(A, 'a6.c2', polar(a6, length3/2.0, angleOfLine(a8, a6)+ANGLE90))
#back control points - path runs clockwise from back nape b1
#back neck control points from b7 to b1
length = distance(b7, b1)/3.0
b1.c1 = controlPoint(B, 'b1.c1', down(b7, length/2.0)) #short control point handle
b1.c2 = controlPoint(B, 'b1.c2', left(b1, length*2)) #long control point handle
#back side control points from b11 to b9
length = distance(b11, b9)/3.0
b9.c1 = controlPoint(B, 'b9.c1', up(b11, length))
b9.c2 = controlPoint(B, 'b9.c2', down(b9, length))
#back armscye points from b13 to b12 to b8
length1 = distance(b13, b12)/3.0
length2 = distance(b12, b8)/3.0
angle1 = angleOfLine(b13, b8)
b12.c1 = controlPoint(B, 'b12.c1', polar(b13, length1, angleOfLine(a3.c1, a16)))
b12.c2 = controlPoint(B, 'b12.c2', polar(b12, length1, angle1-ANGLE180))
b8.c1 = controlPoint(B, 'b8.c1', polar(b12, length2, angle1))
b8.c2 = controlPoint(B, 'b8.c2', polar(b8, length2/2.0, angleOfLine(b7, b8)-ANGLE90))
#sleeve C
c1 = patternPointXY(C, 'c1', 0.0, 0.0) #A
c2 = patternPoint(C, 'c2', down(c1, overarm_length)) #B
c3 = patternPoint(C, 'c3', up(c2, elbow_height)) #C
c4 = patternPoint(C, 'c4', right(c2, 1*IN)) #D
c5 = patternPoint(C, 'c5', right(c3, 0.5*IN)) #E
c6 = patternPoint(C, 'c6', left(c1, 1*IN)) #F
c7 = patternPoint(C, 'c7', right(c4, 1*IN)) #G
c8 = patternPoint(C, 'c8', right(c7, hand_circumference+2*IN)) #H
c9 = patternPoint(C, 'c9', right(c8, 1*IN)) #I
c10 = patternPoint(C, 'c10', right(c5, 1*IN) )#J
c11 = patternPoint(C, 'c11', right(c10, elbow_circumference)) #K
c12 = patternPoint(C, 'c12', right(c11, 0.5*IN)) #L
c13 = patternPoint(C, 'c13', right(c1, armscye_circumference)) #M
c14 = patternPoint(C, 'c14', right(c13, 2*IN)) #N
c15 = patternPoint(C, 'c15', up(c1, 2.5*IN)) #O
c16 = patternPoint(C, 'c16', right(c1, 1.5*IN)) #P
c17 = patternPoint(C, 'c17', left(c13, 3*IN)) #Q
c18 = patternPointXY(C, 'c18', c16.x, c15.y) #R
c19 = patternPointXY(C, 'c19', c17.x, c15.y) #S
c20 = patternPoint(C, 'c20', midPoint(c16, c17)) #T
c21 = patternPoint(C, 'c21', up(c20, distance(c20, c18))) #U - above T
c22 = patternPoint(C, 'c22', down(midPoint(c7, c8), 0.75*IN)) #V - was U
c23 = patternPoint(C, 'c23', right(c4, distance(c4, c8)*3/5.0)) #W
c24 = patternPoint(C, 'c24', up(c23, distance(c4, c3)/3.0)) #X - was V
c25 = patternPoint(C, 'c25', down(c23, 0.75*IN)) #Y - new point
# sleeve C control points
# sleevecap c6 to c18 to c21 to c19 to c13 to c14
length1 = distance(c6, c18)/3.0
length2 = distance(c18, c21)/3.0
c21.c2 = controlPoint(C, 'c21.c2', left(c21, length2))
c21.c1 = controlPoint(C, 'c21.c1', polar(c18, length2, angleOfLine(c18, c21.c2)))
angle = angleOfLine(c6, c18)+angleOfVector(c18, c6, c1)/2.0
c18.c1 = controlPoint(C, 'c18.c1', polar(c6, length1, angle))
c18.c2 = controlPoint(C, 'c18.c2', polar(c18, length1, angleOfLine(c21.c1, c18)))
length1 = distance(c21, c19)/3.0
length2 = distance(c19, c13)/3.0
length3 = distance(c13, c14)/3.0
c19.c1 = controlPoint(C, 'c19.c1', right(c21, length1))
c19.c2 = controlPoint(C, 'c19.c2', polar(c19, length1, angleOfLine(c19, c19.c1)))
c13.c1 = controlPoint(C, 'c13.c1', polar(c19, length2, angleOfLine(c19.c2, c19)))
angle1 = angleOfLine(c13.c1, c13)/2.0
c13.c2 = controlPoint(C, 'c13.c2', polar(c13, length2, angle1+ANGLE180))
c14.c1 = controlPoint(C, 'c14.c1', polar(c13, length3, angle1))
c14.c2 = controlPoint(C, 'c14.c2', polar(c14, length3, angleOfLine(c18.c1, c6)))
# c14 to c12
length = distance(c14, c12)/3.0
c12.c2 = controlPoint(C, 'c12.c2', polar(c12, length, angleOfLine(c9, c12)))
c12.c1 = controlPoint(C, 'c12.c1', polar(c14, length, angleOfLine(c14, c12.c2)))
# c9 to c25
length = distance(c9, c25)/3.0
c25.c2 = controlPoint(C, 'c25.c2', right(c25, length))
c25.c1 = controlPoint(C, 'c25.c1', polar(c9, length, angleOfLine(c9, c25.c2)))
#c22 to c4
length = distance(c22, c4)/3.0
c4.c1 = controlPoint(C, 'c4.c1', left(c22, length))
c4.c2 = controlPoint(C, 'c4.c2', polar(c4, length, angleOfLine(c4, c4.c1)))
#c5 to c6
length = distance(c5, c6)/3.0
c6.c1 = controlPoint(C, 'c6.c1', polar(c5, length, angleOfLine(c4, c5)))
c6.c2 = controlPoint(C, 'c6.c2', polar(c6, length, angleOfLine(c6, c6.c1)))
#cuff D
d1 = patternPointXY(D, 'd1', 0, 0)
d2 = patternPoint(D, 'd2', right(d1, hand_circumference+2*IN))
d3 = patternPoint(D, 'd3', down(d2, 3*IN))
d4 = patternPoint(D, 'd4', up(d3, 0.75*IN))
d5 = patternPoint(D, 'd5', left(d3, 1*IN))
d6 = patternPoint(D, 'd6', down(d1, 3*IN))
d7 = patternPoint(D, 'd7', right(d6, 1*IN))
d8 = patternPoint(D, 'd8', up(d6, 0.75*IN))
length1 = 0.7*distance(d1, d6)
length2 = 0.75*IN
d9 = patternPointXY(D, 'd9', d1.x+0.5*IN, d1.y+length1)
d10 = patternPoint(D, 'd10', right(d9, length2))
d11 = patternPointXY(D, 'd11', d2.x-0.5*IN, d2.y+length1)
d12 = patternPoint(D, 'd12', left(d11, length2))
#cuff D control points
length = distance(d4, d5)/3.0
d5.c1 = controlPoint(D, 'd5.c1', down(d4, length))
d5.c2 = controlPoint(D, 'd5.c2', right(d5, length))
d8.c1 = controlPoint(D, 'd8.c1', left(d7, length))
d8.c2 = controlPoint(D, 'd8.c2', down(d8, length))
# all points are defined, now create paths with them...
# pattern marks, labels, grainlines, seamlines, cuttinglines, darts, etc.
#bodice front A
#letter
pnt1 = Point('', a8.x, a6.c1.y)
addText(A, 'A_letter', pnt1.x, pnt1.y, 'A', fontsize = '72')
#label
pnt2 = down(pnt1, 0.5*IN)
addText(A, 'A_label', pnt2.x, pnt2.y, 'Bodice Front', fontsize = '48')
#label
pnt3 = down(pnt2, 0.5*IN)
addText(A, 'A_fabric', pnt3.x, pnt3.y, 'Cut 2 of fabric', fontsize = '38')
#grainline points
aG1 = down(a11, front_waist_length/3.0)
aG2 = polar(aG1, front_waist_length/2.0, angleOfLine(a11, a14))
path_str = formatPath('M', aG1, 'L', aG2)
A_grainline = addPath(A, 'A_grainline', path_str, 'grainline')
# gridline - helpful for troubleshooting during design phase
path_str = formatPath('M', a1, 'L', a3, 'M', a4, 'L', a2, 'M', a8, 'L', a15, 'M', a11, 'L', a10, 'M', a7, 'L', a5)
A_gridline = addPath(A, 'A_gridline', path_str, 'gridline')
#seamline & cuttingline
path_str = formatPath('M', a11, 'L', a14, 'C', a15.c1, a15.c2, a15, 'C', a13.c1, a13.c2, a13, 'C', a12.c1, a12.c2, a12)
path_str = path_str+formatPath('L', a16, 'C', a3.c1, a3.c2, a3, 'C', a4.c1, a4.c2, a4, 'C', a6.c1, a6.c2, a6, 'L', a8, 'C', a11.c1, a11.c2, a11)
A_seamline = addPath(A, 'A_seamline', path_str, 'seamline')
A_cuttingline = addPath(A, 'A_cuttingline', path_str, 'cuttingline')
#bodice back B
#letter
pnt1 = Point('', b8.x*2/3.0, b8.c2.y)
addText(B, 'B_letter', pnt1.x, pnt1.y, 'B', fontsize = '72') #
#label
pnt2 = down(pnt1, 0.5*IN)
addText(B, 'B_name', pnt2.x, pnt2.y, 'Bodice Back', fontsize = '48')
#label
pnt3 = down(pnt2, 0.5*IN)
addText(B, 'B_fabric', pnt3.x, pnt3.y, 'Cut 2 of fabric', fontsize = '38')
#grainline points
bG1 = down(b7, front_waist_length/3.0)
bG2 = down(bG1, front_waist_length/2.0)
path_str = formatPath('M', bG1, 'L', bG2)
B_grainline = addPath(B, 'B_grainline', path_str, 'grainline')
# gridline
path_str = formatPath('M', b1, 'L', b2, 'M', b11, 'L', b9, 'M', b9, 'L', b10, 'M', b7, 'L', b6, 'L', b1, 'M', b11, 'L', b10)
B_gridline = addPath(B, 'B_gridline', path_str, 'gridline')
#seamline & cuttingline
path_str = formatPath('M', b1, 'L', b2, 'L', b11, 'C', b9.c1, b9.c2, b9, 'L', b13, 'C', b12.c1, b12.c2, b12, 'C', b8.c1, b8.c2, b8, 'L', b7, 'C', b1.c1, b1.c2, b1)
B_seamline = addPath(B, 'B_seamline', path_str, 'seamline')
B_cuttingline = addPath(B, 'B_cuttingline', path_str, 'cuttingline')
#bodice sleeve C
#letter
pnt1 = Point('', c19.c1.x, c12.c1.y)
addText(C, 'C_letter', pnt1.x, pnt1.y, 'C', fontsize = '72') #
#label
pnt2 = down(pnt1, 0.5*IN)
addText(C, 'C_name', pnt2.x, pnt2.y, 'Bodice Sleeve', fontsize = '48')
#label
pnt3 = down(pnt2, 0.5*IN)
addText(C, 'C_fabric', pnt3.x, pnt3.y, 'Cut 2 of fabric', fontsize = '38')
#grainline points
cG1 = c20
cG2 = down(cG1, overarm_length/2.0)
path_str = formatPath('M', cG1, 'L', cG2)
C_grainline = addPath(C, 'C_grainline', path_str, 'grainline')
# gridline
path_str = formatPath('M', c15, 'L', c2, 'M', c15, 'L', c19, 'M', c2, 'L', c9, 'M', c3, 'L', c12, 'M', c6, 'L', c14, 'M', c18, 'L', c16, 'M', c19, 'L', c17)
C_gridline = addPath(C, 'C_gridline', path_str, 'gridline')
# slashline
path_str = formatPath('M', c24, 'L', c25)
C_slashline = addPath(C, 'C_slashline', path_str, 'slashline')
#seamline & cuttingline
path_str = formatPath('M', c6, 'C', c18.c1, c18.c2, c18, 'C', c21.c1, c21.c2, c21, 'C', c19.c1, c19.c2, c19, 'C', c13.c1, c13.c2, c13, 'C', c14.c1, c14.c2, c14)
path_str += formatPath('C', c12.c1, c12.c2, c12, 'L', c9, 'C', c25.c1, c25.c2, c25, 'L', c22, 'C', c4.c1, c4.c2, c4, 'L', c5, 'C', c6.c1, c6.c2, c6)
C_seamline = addPath(C, 'C_seamline', path_str, 'seamline')
C_cuttingline = addPath(C, 'C_cuttingline', path_str, 'cuttingline')
#bodice cuff D
#letter
pnt1 = Point('', d7.x, d6.y/4.0)
addText(D, 'D_letter', pnt1.x, pnt1.y, 'D', fontsize = '38') #
#label
pnt2 = right(pnt1, 1*IN)
addText(D, 'C_name', pnt2.x, pnt2.y, 'Bodice Sleeve Cuff', fontsize = '30')
#label
pnt3 = right(pnt2, 4*IN)
addText(D, 'C_fabric', pnt3.x, pnt3.y, 'Cut 2 of fabric', fontsize = '24')
pnt3 = down(pnt3, 0.3*IN)
addText(D, 'C_finterfacing', pnt3.x, pnt3.y, 'Cut 2 of interfacing', fontsize = '24')
#grainline points
pnt1 = midPoint(d1, d6)
dG1 = right(pnt1, distance(d1, d2)/4.0)
dG2 = right(dG1, distance(d1, d2)/2.0)
path_str = formatPath('M', dG1, 'L', dG2)
D_grainline = addPath(D, 'D_grainline', path_str, 'grainline')
# gridline
path_str = formatPath('M', d1, 'L', d2, 'L', d4, 'L', d5, 'L', d7, 'L', d8, 'L', d1)
D_gridline = addPath(D, 'D_gridline', path_str, 'gridline')
# slashline
path_str = formatPath('M', d9, 'L', d10, 'M', d11, 'L', d12)
D_slashline = addPath(D, 'D_slashline', path_str, 'slashline')
#seamline & cuttingline
path_str = formatPath('M', d1, 'L', d2, 'L', d4, 'C', d5.c1, d5.c2, d5, 'L', d7, 'C', d8.c1, d8.c2, d8, 'L', d1)
D_seamline = addPath(D, 'D_seamline', path_str, 'seamline')
D_cuttingline = addPath(D, 'D_cuttingline', path_str, 'cuttingline')
#layout patterns on document in rows
dx = BORDER+SEAM_ALLOWANCE #left border, allow width for seam allowance
dy = BORDER+NOTE_HEIGHT+2*SEAM_ALLOWANCE # print pattern under the note header, allow height for seam allowance plus extra space
pattern_buffer = 3*SEAM_ALLOWANCE #between any two patterns need 2 seam allowances plus additional space
# first row
pattern_offset = dx
row_offset = dy
#layout bodice front A
adx = pattern_offset-a14.x #left border offset dx, translate leftmost A point a14 to this offset
ady = row_offset-a8.y #upper height offset dy, translate highest A point a8
A.set('transform', 'translate('+str(adx)+' '+str(ady)+')')
pattern_offset = adx+a12.x+pattern_buffer
#layout bodice front B
bdx = pattern_offset-b9.x #translate leftmost B point
bdy = row_offset-b6.y #translate highest B point
B.set('transform', 'translate('+str(bdx)+' '+str(bdy)+')')
#2nd row
pattern_offset = dx
row_offset = ady+a15.y+pattern_offset # row_offset + lowest point from previous row, plus pattern_offset
#layout sleeve C
cdx = pattern_offset-c6.x
cdy = row_offset-c21.y
C.set('transform', 'translate('+str(cdx)+' '+str(cdy)+')')
pattern_offset = cdx+c14.x+pattern_buffer
#layout cuff D
ddx = pattern_offset-d1.x
ddy = row_offset-d1.y
D.set('transform', 'translate('+str(ddx)+' '+str(ddy)+')')
#3rd row, use this to calculate document height
row_offset = cdy+c25.y
#resize document to fit pattern piece layout
width = ddx+d2.x # use pattern piece that appears farthest to the right in Inkscape canvas
doc_width = width+2*SEAM_ALLOWANCE+2*BORDER
doc_height = row_offset+SEAM_ALLOWANCE+BORDER
root = self.svg.getElement('//svg:svg');
root.set('viewBox', '%f %f %f %f' % (0,0,doc_width,doc_height))
root.set('width', str(doc_width))
root.set('height', str(doc_height))
#Place notes on document after pattern pieces are transformed so that notes are centered on correct width
x = doc_width/2.0
y = BORDER
i = 0
for item in notes:
addText(bodice, 'note'+str(i), x, y, item, fontsize = '28', textalign = 'center', textanchor = 'middle', reference = 'false')
y = y+0.33*IN
if __name__ == '__main__':
ShirtWaist().run()