init_code
This commit is contained in:
312
assets/garment_programs/pants.py
Normal file
312
assets/garment_programs/pants.py
Normal file
@@ -0,0 +1,312 @@
|
||||
from copy import deepcopy
|
||||
import numpy as np
|
||||
|
||||
import pygarment as pyg
|
||||
from assets.garment_programs.base_classes import BaseBottoms
|
||||
from assets.garment_programs import bands
|
||||
|
||||
|
||||
class PantPanel(pyg.Panel):
|
||||
def __init__(
|
||||
self, name, body, design,
|
||||
length,
|
||||
waist,
|
||||
hips,
|
||||
hips_depth,
|
||||
crotch_width,
|
||||
dart_position,
|
||||
match_top_int_to=None,
|
||||
hipline_ext=1,
|
||||
double_dart=False) -> None:
|
||||
"""
|
||||
Basic pant panel with option to be fitted (with darts)
|
||||
"""
|
||||
super().__init__(name)
|
||||
|
||||
flare = body['leg_circ'] * (design['flare']['v'] - 1) / 4
|
||||
hips_depth = hips_depth * hipline_ext
|
||||
|
||||
hip_side_incl = np.deg2rad(body['_hip_inclination'])
|
||||
dart_depth = hips_depth * 0.8
|
||||
|
||||
# Crotch cotrols
|
||||
crotch_depth_diff = body['crotch_hip_diff']
|
||||
crotch_extention = crotch_width
|
||||
|
||||
# eval pants shape
|
||||
# TODO Return ruffle opportunity?
|
||||
|
||||
# amount of extra fabric at waist
|
||||
w_diff = hips - waist # Assume its positive since waist is smaller then hips
|
||||
# We distribute w_diff among the side angle and a dart
|
||||
hw_shift = np.tan(hip_side_incl) * hips_depth
|
||||
# Small difference
|
||||
if hw_shift > w_diff:
|
||||
hw_shift = w_diff
|
||||
|
||||
# --- Edges definition ---
|
||||
# Right
|
||||
if pyg.utils.close_enough(design['flare']['v'], 1): # skip optimization
|
||||
right_bottom = pyg.Edge(
|
||||
[-flare, 0],
|
||||
[0, length]
|
||||
)
|
||||
else:
|
||||
right_bottom = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
[-flare, 0],
|
||||
[0, length],
|
||||
target_tan1=np.array([0, 1]),
|
||||
# initial guess places control point closer to the hips
|
||||
initial_guess=[0.75, 0]
|
||||
)
|
||||
right_top = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
right_bottom.end,
|
||||
[hw_shift, length + hips_depth],
|
||||
target_tan0=np.array([0, 1]),
|
||||
initial_guess=[0.5, 0]
|
||||
)
|
||||
|
||||
top = pyg.Edge(
|
||||
right_top.end,
|
||||
[w_diff + waist, length + hips_depth]
|
||||
)
|
||||
|
||||
crotch_top = pyg.Edge(
|
||||
top.end,
|
||||
[hips, length + 0.45 * hips_depth] # A bit higher than hip line
|
||||
# NOTE: The point should be lower than the minimum rise value (0.5)
|
||||
)
|
||||
crotch_bottom = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
crotch_top.end,
|
||||
[hips + crotch_extention, length - crotch_depth_diff],
|
||||
target_tan0=np.array([0, -1]),
|
||||
target_tan1=np.array([1, 0]),
|
||||
initial_guess=[0.5, -0.5]
|
||||
)
|
||||
|
||||
left = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
crotch_bottom.end,
|
||||
[
|
||||
# NOTE "Magic value" (-2 cm) which we use to define default width:
|
||||
# just a little behing the crotch point
|
||||
# NOTE: Ensuring same distance from the crotch point in both
|
||||
# front and back for matching curves
|
||||
crotch_bottom.end[0] - 2 + flare,
|
||||
# NOTE: The inside edge either matches the length of the outside (0, normal case)
|
||||
# or when the inteded length is smaller than crotch depth,
|
||||
# inside edge covers of the inside leg a bit below the crotch (panties-like shorts)
|
||||
y:=min(0, length - crotch_depth_diff * 1.5)
|
||||
],
|
||||
target_tan1=[flare, y - crotch_bottom.end[1]],
|
||||
initial_guess=[0.3, 0]
|
||||
)
|
||||
|
||||
self.edges = pyg.EdgeSequence(
|
||||
right_bottom, right_top, top, crotch_top, crotch_bottom, left
|
||||
).close_loop()
|
||||
bottom = self.edges[-1]
|
||||
|
||||
# Default placement
|
||||
self.set_pivot(crotch_bottom.end)
|
||||
self.translation = [-0.5, - hips_depth - crotch_depth_diff + 5, 0]
|
||||
|
||||
# Out interfaces (easier to define before adding a dart)
|
||||
self.interfaces = {
|
||||
'outside': pyg.Interface(
|
||||
self,
|
||||
pyg.EdgeSequence(right_bottom, right_top),
|
||||
ruffle=[1, hipline_ext]),
|
||||
'crotch': pyg.Interface(self, pyg.EdgeSequence(crotch_top, crotch_bottom)),
|
||||
'inside': pyg.Interface(self, left),
|
||||
'bottom': pyg.Interface(self, bottom)
|
||||
}
|
||||
|
||||
# Add top dart
|
||||
# NOTE: Ruffle indicator to match to waistline proportion for correct balance line matching
|
||||
dart_width = w_diff - hw_shift
|
||||
if w_diff > hw_shift:
|
||||
top_edges, int_edges = self.add_darts(
|
||||
top, dart_width, dart_depth, dart_position, double_dart=double_dart)
|
||||
self.interfaces['top'] = pyg.Interface(
|
||||
self, int_edges,
|
||||
ruffle=waist / match_top_int_to if match_top_int_to is not None else 1.
|
||||
)
|
||||
self.edges.substitute(top, top_edges)
|
||||
else:
|
||||
self.interfaces['top'] = pyg.Interface(
|
||||
self, top,
|
||||
ruffle=waist / match_top_int_to if match_top_int_to is not None else 1.
|
||||
)
|
||||
|
||||
|
||||
|
||||
def add_darts(self, top, dart_width, dart_depth, dart_position, double_dart=False):
|
||||
|
||||
if double_dart:
|
||||
# TODOLOW Avoid hardcoding for matching with the top?
|
||||
dist = dart_position * 0.5 # Dist between darts -> dist between centers
|
||||
offsets_mid = [
|
||||
- (dart_position + dist / 2 + dart_width / 2 + dart_width / 4),
|
||||
- (dart_position - dist / 2) - dart_width / 4,
|
||||
]
|
||||
|
||||
darts = [
|
||||
pyg.EdgeSeqFactory.dart_shape(dart_width / 2, dart_depth * 0.9), # smaller
|
||||
pyg.EdgeSeqFactory.dart_shape(dart_width / 2, dart_depth)
|
||||
]
|
||||
else:
|
||||
offsets_mid = [
|
||||
- dart_position - dart_width / 2,
|
||||
]
|
||||
darts = [
|
||||
pyg.EdgeSeqFactory.dart_shape(dart_width, dart_depth)
|
||||
]
|
||||
top_edges, int_edges = pyg.EdgeSequence(top), pyg.EdgeSequence(top)
|
||||
|
||||
for off, dart in zip(offsets_mid, darts):
|
||||
left_edge_len = top_edges[-1].length()
|
||||
top_edges, int_edges = self.add_dart(
|
||||
dart,
|
||||
top_edges[-1],
|
||||
offset=left_edge_len + off,
|
||||
edge_seq=top_edges,
|
||||
int_edge_seq=int_edges
|
||||
)
|
||||
|
||||
return top_edges, int_edges
|
||||
|
||||
|
||||
class PantsHalf(BaseBottoms):
|
||||
def __init__(self, tag, body, design, rise=None) -> None:
|
||||
super().__init__(body, design, tag, rise=rise)
|
||||
design = design['pants']
|
||||
self.rise = design['rise']['v'] if rise is None else rise
|
||||
waist, hips_depth, waist_back = self.eval_rise(self.rise)
|
||||
|
||||
# NOTE: min value = full sum > leg curcumference
|
||||
# Max: pant leg falls flat from the back
|
||||
# Mostly from the back side
|
||||
# => This controls the foundation width of the pant
|
||||
min_ext = body['leg_circ'] - body['hips'] / 2 + 5 # 2 inch ease: from pattern making book
|
||||
front_hip = (body['hips'] - body['hip_back_width']) / 2
|
||||
crotch_extention = min_ext * design['width']['v']
|
||||
front_extention = front_hip / 4 # From pattern making book
|
||||
back_extention = crotch_extention - front_extention
|
||||
|
||||
length, cuff_len = design['length']['v'], design['cuff']['cuff_len']['v']
|
||||
if design['cuff']['type']['v']:
|
||||
if length - cuff_len < design['length']['range'][0]: # Min length from paramss
|
||||
# Cannot be longer then a pant
|
||||
cuff_len = length - design['length']['range'][0]
|
||||
# Include the cuff into the overall length,
|
||||
# unless the requested length is too short to fit the cuff
|
||||
# (to avoid negative length)
|
||||
length -= cuff_len
|
||||
length *= body['_leg_length']
|
||||
cuff_len *= body['_leg_length']
|
||||
|
||||
self.front = PantPanel(
|
||||
f'pant_f_{tag}', body, design,
|
||||
length=length,
|
||||
waist=(waist - waist_back) / 2,
|
||||
hips=(body['hips'] - body['hip_back_width']) / 2,
|
||||
hips_depth=hips_depth,
|
||||
dart_position = body['bust_points'] / 2,
|
||||
crotch_width=front_extention,
|
||||
match_top_int_to=(body['waist'] - body['waist_back_width']) / 2
|
||||
).translate_by([0, body['_waist_level'] - 5, 25])
|
||||
self.back = PantPanel(
|
||||
f'pant_b_{tag}', body, design,
|
||||
length=length,
|
||||
waist=waist_back / 2,
|
||||
hips=body['hip_back_width'] / 2,
|
||||
hips_depth=hips_depth,
|
||||
hipline_ext=1.1,
|
||||
dart_position = body['bum_points'] / 2,
|
||||
crotch_width=back_extention,
|
||||
match_top_int_to=body['waist_back_width'] / 2,
|
||||
double_dart=True
|
||||
).translate_by([0, body['_waist_level'] - 5, -20])
|
||||
|
||||
self.stitching_rules = pyg.Stitches(
|
||||
(self.front.interfaces['outside'], self.back.interfaces['outside']),
|
||||
(self.front.interfaces['inside'], self.back.interfaces['inside'])
|
||||
)
|
||||
|
||||
# add a cuff
|
||||
# TODOLOW This process is the same for sleeves -- make a function?
|
||||
if design['cuff']['type']['v']:
|
||||
|
||||
pant_bottom = pyg.Interface.from_multiple(
|
||||
self.front.interfaces['bottom'],
|
||||
self.back.interfaces['bottom'])
|
||||
|
||||
# Copy to avoid editing original design dict
|
||||
cdesign = deepcopy(design)
|
||||
cdesign['cuff']['b_width'] = {}
|
||||
cdesign['cuff']['b_width']['v'] = pant_bottom.edges.length() / design['cuff']['top_ruffle']['v']
|
||||
cdesign['cuff']['cuff_len']['v'] = cuff_len
|
||||
|
||||
# Init
|
||||
cuff_class = getattr(bands, cdesign['cuff']['type']['v'])
|
||||
self.cuff = cuff_class(f'pant_{tag}', cdesign)
|
||||
|
||||
# Position
|
||||
self.cuff.place_by_interface(
|
||||
self.cuff.interfaces['top'],
|
||||
pant_bottom,
|
||||
gap=5,
|
||||
alignment='left'
|
||||
)
|
||||
|
||||
# Stitch
|
||||
self.stitching_rules.append((
|
||||
pant_bottom,
|
||||
self.cuff.interfaces['top'])
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'crotch_f': self.front.interfaces['crotch'],
|
||||
'crotch_b': self.back.interfaces['crotch'],
|
||||
'top_f': self.front.interfaces['top'],
|
||||
'top_b': self.back.interfaces['top']
|
||||
}
|
||||
|
||||
def length(self):
|
||||
if self.design['pants']['cuff']['type']['v']:
|
||||
return self.front.length() + self.cuff.length()
|
||||
|
||||
return self.front.length()
|
||||
|
||||
class Pants(BaseBottoms):
|
||||
def __init__(self, body, design, rise=None) -> None:
|
||||
super().__init__(body, design)
|
||||
|
||||
self.right = PantsHalf('r', body, design, rise)
|
||||
self.left = PantsHalf('l', body, design, rise).mirror()
|
||||
|
||||
self.stitching_rules = pyg.Stitches(
|
||||
(self.right.interfaces['crotch_f'], self.left.interfaces['crotch_f']),
|
||||
(self.right.interfaces['crotch_b'], self.left.interfaces['crotch_b']),
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'top_f': pyg.Interface.from_multiple(
|
||||
self.right.interfaces['top_f'], self.left.interfaces['top_f']),
|
||||
'top_b': pyg.Interface.from_multiple(
|
||||
self.right.interfaces['top_b'], self.left.interfaces['top_b']),
|
||||
# Some are reversed for correct connection
|
||||
'top': pyg.Interface.from_multiple( # around the body starting from front right
|
||||
self.right.interfaces['top_f'].flip_edges(),
|
||||
self.left.interfaces['top_f'].reverse(with_edge_dir_reverse=True),
|
||||
self.left.interfaces['top_b'].flip_edges(),
|
||||
self.right.interfaces['top_b'].reverse(with_edge_dir_reverse=True), # Flips the edges and restores the direction
|
||||
)
|
||||
}
|
||||
|
||||
def get_rise(self):
|
||||
return self.right.get_rise()
|
||||
|
||||
def length(self):
|
||||
return self.right.length()
|
||||
|
||||
Reference in New Issue
Block a user