init_code
This commit is contained in:
234
assets/garment_programs/circle_skirt.py
Normal file
234
assets/garment_programs/circle_skirt.py
Normal file
@@ -0,0 +1,234 @@
|
||||
import numpy as np
|
||||
import pygarment as pyg
|
||||
|
||||
from assets.garment_programs.base_classes import StackableSkirtComponent
|
||||
|
||||
|
||||
class CircleArcPanel(pyg.Panel):
|
||||
"""One panel circle skirt"""
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
top_rad, length, angle,
|
||||
match_top_int_proportion : bool = None,
|
||||
match_bottom_int_proportion=None
|
||||
) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
halfarc = angle / 2
|
||||
|
||||
dist_w = 2 * top_rad * np.sin(halfarc)
|
||||
dist_out = 2 * (top_rad + length) * np.sin(halfarc)
|
||||
|
||||
vert_len = length * np.cos(halfarc)
|
||||
|
||||
# top
|
||||
self.edges.append(pyg.CircleEdgeFactory.from_points_radius(
|
||||
[-dist_w/2, 0], [dist_w/2, 0],
|
||||
radius=top_rad, large_arc=halfarc > np.pi / 2))
|
||||
|
||||
self.edges.append(pyg.Edge(
|
||||
self.edges[-1].end, [dist_out / 2, -vert_len]))
|
||||
|
||||
# Bottom
|
||||
self.edges.append(pyg.CircleEdgeFactory.from_points_radius(
|
||||
self.edges[-1].end, [- dist_out / 2, -vert_len],
|
||||
radius=top_rad + length,
|
||||
large_arc=halfarc > np.pi / 2, right=False))
|
||||
|
||||
self.edges.close_loop()
|
||||
|
||||
# Interfaces
|
||||
self.interfaces = {
|
||||
'top': pyg.Interface(self, self.edges[0],
|
||||
ruffle=self.edges[0].length() / match_top_int_proportion if match_top_int_proportion is not None else 1.
|
||||
).reverse(True),
|
||||
'bottom': pyg.Interface(self, self.edges[2],
|
||||
ruffle=self.edges[2].length() / match_bottom_int_proportion if match_bottom_int_proportion is not None else 1.
|
||||
),
|
||||
'left': pyg.Interface(self, self.edges[1]),
|
||||
'right': pyg.Interface(self, self.edges[3])
|
||||
}
|
||||
|
||||
def length(self, *args):
|
||||
return self.interfaces['right'].edges.length()
|
||||
|
||||
@staticmethod
|
||||
def from_w_length_suns(name, length, top_width, sun_fraction, **kwargs):
|
||||
arc = sun_fraction * 2 * np.pi
|
||||
rad = top_width / arc
|
||||
|
||||
return CircleArcPanel(name, rad, length, arc, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def from_all_length(name, length, top_width, bottom_width, **kwargs):
|
||||
|
||||
diff = bottom_width - top_width
|
||||
arc = diff / length
|
||||
rad = top_width / arc
|
||||
|
||||
return CircleArcPanel(name, rad, length, arc, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def from_length_rad(name, length, top_width, rad, **kwargs):
|
||||
|
||||
arc = top_width / rad
|
||||
|
||||
return CircleArcPanel(name, rad, length, arc, **kwargs)
|
||||
|
||||
class AsymHalfCirclePanel(pyg.Panel):
|
||||
"""Panel for a asymmetrci circle skirt"""
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
top_rad, length_f, length_s,
|
||||
match_top_int_proportion=None,
|
||||
match_bottom_int_proportion=None
|
||||
) -> None:
|
||||
""" Half a shifted arc section
|
||||
"""
|
||||
super().__init__(name)
|
||||
|
||||
dist_w = 2 * top_rad
|
||||
dist_out = 2 * (top_rad + length_s)
|
||||
|
||||
# top
|
||||
self.edges.append(pyg.CircleEdgeFactory.from_points_radius(
|
||||
[-dist_w/2, 0], [dist_w/2, 0],
|
||||
radius=top_rad, large_arc=False))
|
||||
|
||||
self.edges.append(pyg.Edge(
|
||||
self.edges[-1].end, [dist_out / 2, 0]))
|
||||
|
||||
# Bottom
|
||||
self.edges.append(
|
||||
pyg.CircleEdgeFactory.from_three_points(
|
||||
self.edges[-1].end, [- dist_out / 2, 0],
|
||||
point_on_arc=[0, -(top_rad + length_f)]
|
||||
)
|
||||
)
|
||||
|
||||
self.edges.close_loop()
|
||||
|
||||
# Interfaces
|
||||
self.interfaces = {
|
||||
'top': pyg.Interface(self, self.edges[0],
|
||||
ruffle=self.edges[0].length() / match_top_int_proportion if match_top_int_proportion is not None else 1.
|
||||
).reverse(True),
|
||||
'bottom': pyg.Interface(self, self.edges[2],
|
||||
ruffle=self.edges[2].length() / match_bottom_int_proportion if match_bottom_int_proportion is not None else 1.
|
||||
),
|
||||
'left': pyg.Interface(self, self.edges[1]),
|
||||
'right': pyg.Interface(self, self.edges[3])
|
||||
}
|
||||
|
||||
def length(self, *args):
|
||||
return self.interfaces['right'].edges.length()
|
||||
|
||||
class SkirtCircle(StackableSkirtComponent):
|
||||
"""Simple circle skirt"""
|
||||
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, asymm=False, min_len=5, **kwargs) -> None:
|
||||
super().__init__(body, design, tag)
|
||||
|
||||
design = design['flare-skirt']
|
||||
suns = design['suns']['v']
|
||||
self.rise = design['rise']['v'] if rise is None else rise
|
||||
waist, hips_depth, _ = self.eval_rise(self.rise)
|
||||
|
||||
if length is None: # take from design parameters
|
||||
length = hips_depth + design['length']['v'] * body['_leg_length']
|
||||
|
||||
# NOTE: with some combinations of rise and length parameters length may become too small/negative
|
||||
# Hence putting a min positive value here
|
||||
length = max(length, min_len)
|
||||
|
||||
# panels
|
||||
if not asymm: # Typical symmetric skirt
|
||||
self.front = CircleArcPanel.from_w_length_suns(
|
||||
f'skirt_front_{tag}' if tag else 'skirt_front',
|
||||
length, waist / 2, suns / 2,
|
||||
match_top_int_proportion=self.body['waist'] - self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], 15])
|
||||
|
||||
self.back = CircleArcPanel.from_w_length_suns(
|
||||
f'skirt_back_{tag}' if tag else 'skirt_back',
|
||||
length, waist / 2, suns / 2,
|
||||
match_top_int_proportion=self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], -15])
|
||||
else:
|
||||
# NOTE: Asymmetic front/back is only defined on full skirt (1 sun)
|
||||
w_rad = waist / 2 / np.pi
|
||||
f_length = design['asymm']['front_length']['v'] * length
|
||||
tot_len = w_rad * 2 + length + f_length
|
||||
del_r = tot_len / 2 - f_length - w_rad
|
||||
s_length = np.sqrt((tot_len / 2)**2 - del_r**2) - w_rad
|
||||
|
||||
self.front = AsymHalfCirclePanel(
|
||||
f'skirt_front_{tag}' if tag else 'skirt_front',
|
||||
w_rad, f_length, s_length,
|
||||
match_top_int_proportion=self.body['waist'] - self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], 15])
|
||||
|
||||
self.back = AsymHalfCirclePanel(
|
||||
f'skirt_back_{tag}' if tag else 'skirt_back',
|
||||
w_rad, length, s_length,
|
||||
match_top_int_proportion=self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], -15])
|
||||
|
||||
# Add a cut
|
||||
if design['cut']['add']['v'] and slit:
|
||||
self.add_cut(
|
||||
self.front if design['cut']['place']['v'] > 0 else self.back,
|
||||
design, length)
|
||||
|
||||
# Stitches
|
||||
self.stitching_rules = pyg.Stitches(
|
||||
(self.front.interfaces['right'], self.back.interfaces['right']),
|
||||
(self.front.interfaces['left'], self.back.interfaces['left'])
|
||||
)
|
||||
|
||||
# Interfaces
|
||||
self.interfaces = {
|
||||
'top': pyg.Interface.from_multiple(self.front.interfaces['top'], self.back.interfaces['top']),
|
||||
'bottom_f': self.front.interfaces['bottom'],
|
||||
'bottom_b': self.back.interfaces['bottom'],
|
||||
'bottom': pyg.Interface.from_multiple(self.front.interfaces['bottom'], self.back.interfaces['bottom'])
|
||||
}
|
||||
|
||||
def add_cut(self, panel, design, sk_length):
|
||||
"""Add a cut to the skirt"""
|
||||
width, depth = design['cut']['width']['v'] * sk_length, design['cut']['depth']['v'] * sk_length
|
||||
|
||||
target_edge = panel.interfaces['bottom'].edges[0]
|
||||
t_len = target_edge.length()
|
||||
offset = abs(design['cut']['place']['v'] * t_len)
|
||||
|
||||
# Respect the placement boundaries
|
||||
offset = max(offset, width / 2)
|
||||
offset = min(offset, t_len - width / 2)
|
||||
|
||||
# NOTE: heuristic is specific for the panels that we use
|
||||
right = target_edge.start[0] > target_edge.end[0]
|
||||
|
||||
# Make a cut
|
||||
cut_shape = pyg.EdgeSeqFactory.dart_shape(width, depth=depth)
|
||||
|
||||
new_edges, _, interf_edges = pyg.ops.cut_into_edge(
|
||||
cut_shape, target_edge,
|
||||
offset=offset,
|
||||
right=right
|
||||
)
|
||||
|
||||
panel.edges.substitute(target_edge, new_edges)
|
||||
panel.interfaces['bottom'].substitute(
|
||||
target_edge, interf_edges,
|
||||
[panel for _ in range(len(interf_edges))])
|
||||
|
||||
def length(self, *args):
|
||||
return self.front.length()
|
||||
|
||||
|
||||
class AsymmSkirtCircle(SkirtCircle):
|
||||
"""Front/back asymmetric skirt"""
|
||||
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, **kwargs):
|
||||
super().__init__(body, design, tag, length, rise, slit, asymm=True)
|
||||
Reference in New Issue
Block a user