init_code
This commit is contained in:
370
assets/garment_programs/collars.py
Normal file
370
assets/garment_programs/collars.py
Normal file
@@ -0,0 +1,370 @@
|
||||
import numpy as np
|
||||
from scipy.spatial.transform import Rotation as R
|
||||
|
||||
import pygarment as pyg
|
||||
|
||||
from assets.garment_programs.bands import StraightBandPanel
|
||||
from assets.garment_programs.circle_skirt import CircleArcPanel
|
||||
|
||||
|
||||
# # ------ Collar shapes withough extra panels ------
|
||||
|
||||
def VNeckHalf(depth, width, **kwargs):
|
||||
"""Simple VNeck design"""
|
||||
|
||||
edges = pyg.EdgeSequence(pyg.Edge([0, 0], [width / 2, -depth]))
|
||||
return edges
|
||||
|
||||
def SquareNeckHalf(depth, width, **kwargs):
|
||||
"""Square design"""
|
||||
|
||||
edges = pyg.EdgeSeqFactory.from_verts([0, 0], [0, -depth], [width / 2, -depth])
|
||||
return edges
|
||||
|
||||
def TrapezoidNeckHalf(depth, width, angle=90, verbose=True, **kwargs):
|
||||
"""Trapesoid neck design"""
|
||||
|
||||
# Special case when angle = 180 (sin = 0)
|
||||
if (pyg.utils.close_enough(angle, 180, tol=1)
|
||||
or pyg.utils.close_enough(angle, 0, tol=1)):
|
||||
# degrades into VNeck
|
||||
return VNeckHalf(depth, width)
|
||||
|
||||
rad_angle = np.deg2rad(angle)
|
||||
|
||||
bottom_x = -depth * np.cos(rad_angle) / np.sin(rad_angle)
|
||||
if bottom_x > width / 2: # Invalid angle/depth/width combination resulted in invalid shape
|
||||
if verbose:
|
||||
print('TrapezoidNeckHalf::WARNING::Parameters are invalid and create overlap: '
|
||||
f'{bottom_x} > {width / 2}. '
|
||||
'The collar is reverted to VNeck')
|
||||
|
||||
return VNeckHalf(depth, width)
|
||||
|
||||
edges = pyg.EdgeSeqFactory.from_verts([0, 0], [bottom_x, -depth], [width / 2, -depth])
|
||||
return edges
|
||||
|
||||
def CurvyNeckHalf(depth, width, flip=False, **kwargs):
|
||||
"""Testing Curvy Collar design"""
|
||||
|
||||
sign = -1 if flip else 1
|
||||
edges = pyg.EdgeSequence(pyg.CurveEdge(
|
||||
[0, 0], [width / 2,-depth],
|
||||
[[0.4, sign * 0.3], [0.8, sign * -0.3]]))
|
||||
|
||||
return edges
|
||||
|
||||
def CircleArcNeckHalf(depth, width, angle=90, flip=False, **kwargs):
|
||||
"""Collar with a side represented by a circle arc"""
|
||||
# 1/4 of a circle
|
||||
edges = pyg.EdgeSequence(pyg.CircleEdgeFactory.from_points_angle(
|
||||
[0, 0], [width / 2,-depth], arc_angle=np.deg2rad(angle),
|
||||
right=(not flip)
|
||||
))
|
||||
|
||||
return edges
|
||||
|
||||
|
||||
def CircleNeckHalf(depth, width, **kwargs):
|
||||
"""Collar that forms a perfect circle arc when halfs are stitched"""
|
||||
|
||||
# Take a full desired arc and half it!
|
||||
circle = pyg.CircleEdgeFactory.from_three_points(
|
||||
[0, 0],
|
||||
[width, 0],
|
||||
[width / 2, -depth])
|
||||
subdiv = circle.subdivide_len([0.5, 0.5])
|
||||
return pyg.EdgeSequence(subdiv[0])
|
||||
|
||||
def Bezier2NeckHalf(depth, width, flip=False, x=0.5, y=0.3, **kwargs):
|
||||
"""2d degree Bezier curve as neckline"""
|
||||
|
||||
sign = 1 if flip else -1
|
||||
edges = pyg.EdgeSequence(pyg.CurveEdge(
|
||||
[0, 0], [width / 2,-depth],
|
||||
[[x, sign*y]]))
|
||||
|
||||
return edges
|
||||
|
||||
# # ------ Collars with panels ------
|
||||
|
||||
class NoPanelsCollar(pyg.Component):
|
||||
"""Face collar class that only forms the projected shapes """
|
||||
|
||||
def __init__(self, name, body, design) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
# Front
|
||||
collar_type = globals()[design['collar']['f_collar']['v']]
|
||||
f_collar = collar_type(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'],
|
||||
angle=design['collar']['fc_angle']['v'],
|
||||
flip=design['collar']['f_flip_curve']['v'],
|
||||
x=design['collar']['f_bezier_x']['v'],
|
||||
y=design['collar']['f_bezier_y']['v'],
|
||||
verbose=self.verbose
|
||||
)
|
||||
|
||||
# Back
|
||||
collar_type = globals()[design['collar']['b_collar']['v']]
|
||||
b_collar = collar_type(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'],
|
||||
angle=design['collar']['bc_angle']['v'],
|
||||
flip=design['collar']['b_flip_curve']['v'],
|
||||
x=design['collar']['b_bezier_x']['v'],
|
||||
y=design['collar']['b_bezier_y']['v'],
|
||||
verbose=self.verbose
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
def length(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Turtle(pyg.Component):
|
||||
|
||||
def __init__(self, tag, body, design) -> None:
|
||||
super().__init__(f'Turtle_{tag}')
|
||||
|
||||
depth = design['collar']['component']['depth']['v']
|
||||
|
||||
# --Projecting shapes--
|
||||
f_collar = CircleNeckHalf(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
b_collar = CircleNeckHalf(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
# -- Panels --
|
||||
length_f, length_b = f_collar.length(), b_collar.length()
|
||||
height_p = body['height'] - body['head_l'] + depth
|
||||
|
||||
self.front = StraightBandPanel(
|
||||
f'{tag}_turtle_front', length_f, depth).translate_by(
|
||||
[-length_f / 2, height_p, 10])
|
||||
self.back = StraightBandPanel(
|
||||
f'{tag}_turtle_back', length_b, depth).translate_by(
|
||||
[-length_b / 2, height_p, -10])
|
||||
|
||||
self.stitching_rules.append((
|
||||
self.front.interfaces['right'],
|
||||
self.back.interfaces['right']
|
||||
))
|
||||
|
||||
self.interfaces.update({
|
||||
'front': self.front.interfaces['left'],
|
||||
'back': self.back.interfaces['left'],
|
||||
'bottom': pyg.Interface.from_multiple(
|
||||
self.front.interfaces['bottom'],
|
||||
self.back.interfaces['bottom']
|
||||
)
|
||||
})
|
||||
|
||||
def length(self):
|
||||
return self.interfaces['back'].edges.length()
|
||||
|
||||
|
||||
class SimpleLapelPanel(pyg.Panel):
|
||||
"""A panel for the front part of simple Lapel"""
|
||||
def __init__(self, name, length, max_depth) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
self.edges = pyg.EdgeSeqFactory.from_verts(
|
||||
[0, 0], [max_depth, 0], [max_depth, -length]
|
||||
)
|
||||
|
||||
self.edges.append(
|
||||
pyg.CurveEdge(
|
||||
self.edges[-1].end,
|
||||
self.edges[0].start,
|
||||
[[0.7, 0.2]]
|
||||
)
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'to_collar': pyg.Interface(self, self.edges[0]),
|
||||
'to_bodice': pyg.Interface(self, self.edges[1])
|
||||
}
|
||||
|
||||
|
||||
class SimpleLapel(pyg.Component):
|
||||
|
||||
def __init__(self, tag, body, design) -> None:
|
||||
super().__init__(f'Turtle_{tag}')
|
||||
|
||||
depth = design['collar']['component']['depth']['v']
|
||||
standing = design['collar']['component']['lapel_standing']['v']
|
||||
|
||||
# --Projecting shapes--
|
||||
# Any front one!
|
||||
collar_type = globals()[design['collar']['f_collar']['v']]
|
||||
f_collar = collar_type(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'],
|
||||
angle=design['collar']['fc_angle']['v'],
|
||||
flip=design['collar']['f_flip_curve']['v'])
|
||||
|
||||
b_collar = CircleNeckHalf(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
# -- Panels --
|
||||
length_f, length_b = f_collar.length(), b_collar.length()
|
||||
height_p = body['height'] - body['head_l'] + depth * 2
|
||||
|
||||
self.front = SimpleLapelPanel(
|
||||
f'{tag}_lapel_front', length_f, depth).translate_by(
|
||||
[-depth * 2, height_p, 35]) # TODOLOW This should be related with the bodice panels' placement
|
||||
|
||||
if standing:
|
||||
self.back = StraightBandPanel(
|
||||
f'{tag}_lapel_back', length_b, depth).translate_by(
|
||||
[-length_b / 2, height_p, -10])
|
||||
else:
|
||||
# A curved back panel that follows the collar opening
|
||||
rad, angle, _ = b_collar[0].as_radius_angle()
|
||||
self.back = CircleArcPanel(
|
||||
f'{tag}_lapel_back', rad, depth, angle
|
||||
).translate_by([-length_b, height_p, -10])
|
||||
self.back.rotate_by(R.from_euler('XYZ', [90, 45, 0], degrees=True))
|
||||
|
||||
if standing:
|
||||
self.back.interfaces['right'].set_right_wrong(True)
|
||||
|
||||
self.stitching_rules.append((
|
||||
self.front.interfaces['to_collar'],
|
||||
self.back.interfaces['right']
|
||||
))
|
||||
|
||||
self.interfaces.update({
|
||||
#'front': NOTE: no front interface here
|
||||
'back': self.back.interfaces['left'],
|
||||
'bottom': pyg.Interface.from_multiple(
|
||||
self.front.interfaces['to_bodice'].set_right_wrong(True),
|
||||
self.back.interfaces['bottom'] if standing else self.back.interfaces['top'].set_right_wrong(True),
|
||||
)
|
||||
})
|
||||
|
||||
def length(self):
|
||||
return self.interfaces['back'].edges.length()
|
||||
|
||||
class HoodPanel(pyg.Panel):
|
||||
"""A panel for the side of the hood"""
|
||||
def __init__(self, name, f_depth, b_depth, f_length, b_length, width, in_length, depth) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
width = width / 2 # Panel covers one half only
|
||||
length = in_length + width / 2
|
||||
|
||||
# Bottom-back
|
||||
bottom_back_in = pyg.CurveEdge(
|
||||
[-width, -b_depth],
|
||||
[0, 0],
|
||||
[[0.3, -0.2], [0.6, 0.2]]
|
||||
)
|
||||
bottom_back = pyg.ops.curve_match_tangents(
|
||||
bottom_back_in.as_curve(),
|
||||
[1, 0], # Full opening is vertically aligned
|
||||
[1, 0],
|
||||
target_len=b_length,
|
||||
return_as_edge=True,
|
||||
verbose=self.verbose
|
||||
)
|
||||
self.edges.append(bottom_back)
|
||||
|
||||
# Bottom front
|
||||
bottom_front_in = pyg.CurveEdge(
|
||||
self.edges[-1].end,
|
||||
[width, -f_depth],
|
||||
[[0.3, 0.2], [0.6, -0.2]]
|
||||
)
|
||||
bottom_front = pyg.ops.curve_match_tangents(
|
||||
bottom_front_in.as_curve(),
|
||||
[1, 0], # Full opening is vertically aligned
|
||||
[1, 0],
|
||||
target_len=f_length,
|
||||
return_as_edge=True,
|
||||
verbose=self.verbose
|
||||
)
|
||||
self.edges.append(bottom_front)
|
||||
|
||||
# Front-top straight section
|
||||
self.edges.append(pyg.EdgeSeqFactory.from_verts(
|
||||
self.edges[-1].end,
|
||||
[width * 1.2, length], [width * 1.2 - depth, length]
|
||||
))
|
||||
# Back of the hood
|
||||
self.edges.append(
|
||||
pyg.CurveEdge(
|
||||
self.edges[-1].end,
|
||||
self.edges[0].start,
|
||||
[[0.2, -0.5]]
|
||||
)
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'to_other_side': pyg.Interface(self, self.edges[-2:]),
|
||||
'to_bodice': pyg.Interface(self, self.edges[0:2]).reverse()
|
||||
}
|
||||
|
||||
self.rotate_by(R.from_euler('XYZ', [0, -90, 0], degrees=True))
|
||||
self.translate_by([-width*2, 0, 0])
|
||||
|
||||
class Hood2Panels(pyg.Component):
|
||||
|
||||
def __init__(self, tag, body, design) -> None:
|
||||
super().__init__(f'Hood_{tag}')
|
||||
|
||||
# --Projecting shapes--
|
||||
width = design['collar']['width']['v']
|
||||
f_collar = CircleNeckHalf(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
b_collar = CircleNeckHalf(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
# -- Panel --
|
||||
self.panel = HoodPanel(
|
||||
f'{tag}_hood',
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['bc_depth']['v'],
|
||||
f_length=f_collar.length(),
|
||||
b_length=b_collar.length(),
|
||||
width=width,
|
||||
in_length=body['head_l'] * design['collar']['component']['hood_length']['v'],
|
||||
depth=width / 2 * design['collar']['component']['hood_depth']['v']
|
||||
).translate_by(
|
||||
[0, body['height'] - body['head_l'] + 10, 0])
|
||||
|
||||
self.interfaces.update({
|
||||
#'front': NOTE: no front interface here
|
||||
'back': self.panel.interfaces['to_other_side'],
|
||||
'bottom': self.panel.interfaces['to_bodice']
|
||||
})
|
||||
|
||||
def length(self):
|
||||
return self.panel.length()
|
||||
|
||||
Reference in New Issue
Block a user