init_code

This commit is contained in:
sky
2025-07-03 17:03:00 +08:00
parent a710c87a2b
commit 89766fe3d1
220 changed files with 479903 additions and 77 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,732 @@
{
"pattern": {
"panels": {
"left_btorso": {
"translation": [
0.0,
91.4448950181744,
-20.0
],
"rotation": [
0.0,
0.0,
0.0
],
"vertices": [
[
0.0,
0.0
],
[
0.0,
44.26956007084882
],
[
10.8188,
49.91845847814037
],
[
17.2284,
47.37065774294582
],
[
25.0299525,
28.92917774294582
],
[
25.0299525,
0.0
]
],
"edges": [
{
"endpoints": [
0,
1
]
},
{
"endpoints": [
1,
2
],
"curvature": {
"type": "circle",
"params": [
13.184560591816423,
0,
1
]
}
},
{
"endpoints": [
2,
3
]
},
{
"endpoints": [
3,
4
],
"curvature": {
"type": "cubic",
"params": [
[
0.5,
-0.2
],
[
0.8,
-0.35
]
]
}
},
{
"endpoints": [
4,
5
]
},
{
"endpoints": [
5,
0
]
}
],
"label": "body"
},
"left_ftorso": {
"translation": [
0.0,
91.40029082350912,
25.0
],
"rotation": [
0.0,
0.0,
0.0
],
"vertices": [
[
0.0,
0.0
],
[
27.386415000000003,
0.0
],
[
27.386415000000003,
28.973782566623598
],
[
17.228399999999997,
47.415262566623596
],
[
10.818799999999998,
49.963062523006585
],
[
0.0,
34.31450269441045
]
],
"edges": [
{
"endpoints": [
0,
1
]
},
{
"endpoints": [
1,
2
]
},
{
"endpoints": [
2,
3
],
"curvature": {
"type": "cubic",
"params": [
[
0.19999999999999996,
0.35
],
[
0.5,
0.2
]
]
}
},
{
"endpoints": [
3,
4
]
},
{
"endpoints": [
4,
5
],
"curvature": {
"type": "circle",
"params": [
11.564126734773824,
0,
0
]
}
},
{
"endpoints": [
5,
0
]
}
],
"label": "body"
},
"left_hood": {
"translation": [
10.818800000000001,
149.36600318541687,
-11.637599999999999
],
"rotation": [
0.0,
90.0,
0.0
],
"vertices": [
[
0.8187999999999995,
-0.6488984072915587
],
[
-12.16376,
36.7356
],
[
-22.98256,
36.7356
],
[
-21.37098745180261,
-11.459953817131435
],
[
-10.124915390760208,
5.065222977771245
]
],
"edges": [
{
"endpoints": [
0,
1
],
"curvature": {
"type": "quadratic",
"params": [
[
0.8,
-0.5
]
]
}
},
{
"endpoints": [
1,
2
]
},
{
"endpoints": [
2,
3
]
},
{
"endpoints": [
3,
4
],
"curvature": {
"type": "cubic",
"params": [
[
0.2809593298782206,
-0.37546453034800403
],
[
0.7190492879150145,
0.3755072247003444
]
]
}
},
{
"endpoints": [
4,
0
],
"curvature": {
"type": "cubic",
"params": [
[
0.377233550352168,
0.18074617772869186
],
[
0.6225672655140111,
-0.18160966127735645
]
]
}
}
],
"label": "body"
},
"right_hood": {
"translation": [
-10.818800000000001,
149.36600318541687,
-11.637599999999999
],
"rotation": [
0.0,
-90.0,
0.0
],
"vertices": [
[
-0.8187999999999995,
-0.6488984072915587
],
[
10.124915390760208,
5.065222977771245
],
[
21.37098745180261,
-11.459953817131435
],
[
22.98256,
36.7356
],
[
12.16376,
36.7356
]
],
"edges": [
{
"endpoints": [
0,
1
],
"curvature": {
"type": "cubic",
"params": [
[
0.37743273448598885,
-0.18160966127735645
],
[
0.622766449647832,
0.18074617772869186
]
]
}
},
{
"endpoints": [
1,
2
],
"curvature": {
"type": "cubic",
"params": [
[
0.28095071208498545,
0.3755072247003444
],
[
0.7190406701217794,
-0.37546453034800403
]
]
}
},
{
"endpoints": [
2,
3
]
},
{
"endpoints": [
3,
4
]
},
{
"endpoints": [
4,
0
],
"curvature": {
"type": "quadratic",
"params": [
[
0.2,
-0.5
]
]
}
}
],
"label": "body"
},
"right_ftorso": {
"translation": [
0.0,
91.40029082350912,
25.0
],
"rotation": [
0.0,
0.0,
0.0
],
"vertices": [
[
0,
0
],
[
0.0,
34.31450269441045
],
[
-10.818799999999998,
49.963062523006585
],
[
-17.228399999999997,
47.415262566623596
],
[
-27.386415000000003,
28.973782566623598
],
[
-27.386415000000003,
0
]
],
"edges": [
{
"endpoints": [
0,
1
]
},
{
"endpoints": [
1,
2
],
"curvature": {
"type": "circle",
"params": [
11.564126734773824,
0,
0
]
}
},
{
"endpoints": [
2,
3
]
},
{
"endpoints": [
3,
4
],
"curvature": {
"type": "cubic",
"params": [
[
0.5,
0.2
],
[
0.8,
0.35
]
]
}
},
{
"endpoints": [
4,
5
]
},
{
"endpoints": [
5,
0
]
}
],
"label": "body"
},
"right_btorso": {
"translation": [
0.0,
91.4448950181744,
-20.0
],
"rotation": [
0.0,
0.0,
0.0
],
"vertices": [
[
0,
0
],
[
-25.0299525,
0
],
[
-25.0299525,
28.92917774294582
],
[
-17.2284,
47.37065774294582
],
[
-10.8188,
49.91845847814037
],
[
0.0,
44.26956007084882
]
],
"edges": [
{
"endpoints": [
0,
1
]
},
{
"endpoints": [
1,
2
]
},
{
"endpoints": [
2,
3
],
"curvature": {
"type": "cubic",
"params": [
[
0.19999999999999996,
-0.35
],
[
0.5,
-0.2
]
]
}
},
{
"endpoints": [
3,
4
]
},
{
"endpoints": [
4,
5
],
"curvature": {
"type": "circle",
"params": [
13.184560591816423,
0,
1
]
}
},
{
"endpoints": [
5,
0
]
}
],
"label": "body"
}
},
"stitches": [
[
{
"panel": "left_ftorso",
"edge": 4
},
{
"panel": "left_hood",
"edge": 3
}
],
[
{
"panel": "left_btorso",
"edge": 1
},
{
"panel": "left_hood",
"edge": 4
}
],
[
{
"panel": "left_ftorso",
"edge": 3
},
{
"panel": "left_btorso",
"edge": 2
}
],
[
{
"panel": "left_ftorso",
"edge": 1
},
{
"panel": "left_btorso",
"edge": 4
}
],
[
{
"panel": "right_ftorso",
"edge": 1
},
{
"panel": "right_hood",
"edge": 1
}
],
[
{
"panel": "right_btorso",
"edge": 4
},
{
"panel": "right_hood",
"edge": 0
}
],
[
{
"panel": "right_ftorso",
"edge": 2
},
{
"panel": "right_btorso",
"edge": 3
}
],
[
{
"panel": "right_ftorso",
"edge": 4
},
{
"panel": "right_btorso",
"edge": 1
}
],
[
{
"panel": "right_ftorso",
"edge": 0
},
{
"panel": "left_ftorso",
"edge": 5
}
],
[
{
"panel": "right_btorso",
"edge": 5
},
{
"panel": "left_btorso",
"edge": 0
}
],
[
{
"panel": "right_hood",
"edge": 3
},
{
"panel": "left_hood",
"edge": 1
}
],
[
{
"panel": "right_hood",
"edge": 4
},
{
"panel": "left_hood",
"edge": 0
}
]
]
},
"parameters": {},
"parameter_order": [],
"properties": {
"curvature_coords": "relative",
"normalize_panel_translation": false,
"normalized_edge_loops": true,
"units_in_meter": 100
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
sim:
config:
optimize_storage: true
max_sim_steps: 2400
max_frame_time: 60
max_meshgen_time: 60
max_sim_time: 600
static_threshold: 0.03
non_static_percent: 1.5
max_body_collisions: 35
max_self_collisions: 300
zero_gravity_steps: 10
resolution_scale: 1.0
ground: false
material:
garment_tri_ka: 10000.0
garment_edge_ke: 1.0 # Very soft fabric
garment_tri_ke: 10000.0
spring_ke: 50000.0
garment_edge_kd: 10.0
garment_tri_kd: 1.0
spring_kd: 10.0
fabric_density: 1.0
fabric_thickness: 0.1
fabric_friction: 0.5
options:
enable_particle_particle_collisions: false
enable_triangle_particle_collisions: true
enable_edge_edge_collisions: true
enable_body_collision_filters: true
enable_global_collision_filter: true
global_damping_factor: 0.25
global_damping_effective_velocity: 0.0
global_max_velocity: 25.0
enable_attachment_constraint: true
attachment_stiffness:
- 1000.
- 1000.
- 1000.
- 1000.
attachment_damping:
- 10.
- 0.
- 0.
- 0.
attachment_frames: 400
attachment_label_names:
- lower_interface
- right_collar
- left_collar
- strapless_top
enable_cloth_reference_drag: true
cloth_reference_margin: 0.1
enable_body_smoothing: false
smoothing_total_smoothing_factor: 1.0
smoothing_recover_start_frame: 150
smoothing_num_steps: 100
smoothing_frame_gap_between_steps: 1
body_collision_thickness: 0.25
body_friction: 0.5
stats:
fails: {}
sim_time: {}
spf: {}
fin_frame: {}
self_collisions: {}
body_collisions: {}
render:
config:
resolution:
- 800
- 800
sides:
- front
- back
front_camera_location:
- 0
- 0.97
- 4.15
uv_texture:
seam_width: 0.5
dpi: 1500
fabric_grain_texture_path: ./assets/img/fabric_texture.png
fabric_grain_resolution: 5
stats:
render_time: {}

View File

@@ -0,0 +1,93 @@
sim:
config:
optimize_storage: true
max_sim_steps: 2400
max_frame_time: null # NOTE: Important for GUI!!
# GUI runs the sim in a thread, which is not supported by timeouts on Linux/MacOS
max_meshgen_time: 60
max_sim_time: 600
static_threshold: 0.03
non_static_percent: 1.5
max_body_collisions: 35
max_self_collisions: 300
zero_gravity_steps: 10
resolution_scale: 1.0
ground: false
material:
garment_tri_ka: 10000.0
garment_edge_ke: 100.0 # Mid-softness fabric
garment_tri_ke: 10000.0
spring_ke: 50000.0
garment_edge_kd: 10.0
garment_tri_kd: 1.0
spring_kd: 10.0
fabric_density: 1.0
fabric_thickness: 0.1
fabric_friction: 0.5
options:
enable_particle_particle_collisions: false
enable_triangle_particle_collisions: true
enable_edge_edge_collisions: true
enable_body_collision_filters: true
enable_global_collision_filter: true
global_damping_factor: 0.25
global_damping_effective_velocity: 0.0
global_max_velocity: 25.0
enable_attachment_constraint: true
attachment_stiffness:
- 1000.
- 1000.
- 1000.
- 1000.
attachment_damping:
- 10.
- 0.
- 0.
- 0.
attachment_frames: 400
attachment_label_names:
- lower_interface
- right_collar
- left_collar
- strapless_top
enable_cloth_reference_drag: true
cloth_reference_margin: 0.1
enable_body_smoothing: false
smoothing_total_smoothing_factor: 1.0
smoothing_recover_start_frame: 150
smoothing_num_steps: 100
smoothing_frame_gap_between_steps: 1
body_collision_thickness: 0.25
body_friction: 0.5
stats:
fails: {}
sim_time: {}
spf: {}
fin_frame: {}
self_collisions: {}
body_collisions: {}
render:
config:
resolution:
- 800
- 800
sides:
- front
- back
front_camera_location:
- 0
- 0.97
- 4.15
uv_texture:
seam_width: 0.5
dpi: 1500
fabric_grain_texture_path: ./assets/img/fabric_texture.png
fabric_grain_resolution: 5
stats:
render_time: {}

View File

@@ -0,0 +1,92 @@
sim:
config:
optimize_storage: true
max_sim_steps: 2400
max_frame_time: 60
max_meshgen_time: 60
max_sim_time: 600
static_threshold: 0.03
non_static_percent: 1.5
max_body_collisions: 35
max_self_collisions: 300
zero_gravity_steps: 10
resolution_scale: 1.0
ground: false
material:
garment_tri_ka: 10000.0
garment_edge_ke: 100.0
garment_tri_ke: 10000.0
spring_ke: 50000.0
garment_edge_kd: 10.0
garment_tri_kd: 1.0
spring_kd: 10.0
fabric_density: 1.0
fabric_thickness: 0.1
fabric_friction: 0.5
options:
enable_particle_particle_collisions: false
enable_triangle_particle_collisions: true
enable_edge_edge_collisions: true
enable_body_collision_filters: true
enable_global_collision_filter: true
global_damping_factor: 0.25
global_damping_effective_velocity: 0.0
global_max_velocity: 25.0
enable_attachment_constraint: true
attachment_stiffness:
- 1000.
- 1000.
- 1000.
- 1000.
attachment_damping:
- 10.
- 0.
- 0.
- 0.
attachment_frames: 400
attachment_label_names:
- lower_interface
- right_collar
- left_collar
- strapless_top
enable_cloth_reference_drag: true
cloth_reference_margin: 0.1
enable_body_smoothing: false
smoothing_total_smoothing_factor: 1.0
smoothing_recover_start_frame: 150
smoothing_num_steps: 100
smoothing_frame_gap_between_steps: 1
body_collision_thickness: 0.25
body_friction: 0.5
stats:
fails: {}
sim_time: {}
spf: {}
fin_frame: {}
self_collisions: {}
body_collisions: {}
render:
config:
resolution:
- 800
- 800
sides:
- front
- back
front_camera_location:
- 0
- 0.97
- 4.15
uv_texture:
seam_width: 0.5
dpi: 1500
fabric_grain_texture_path: ./assets/img/fabric_texture.png
fabric_grain_resolution: 5
stats:
render_time: {}

View File

@@ -0,0 +1,92 @@
sim:
config:
optimize_storage: true
max_sim_steps: 2400
max_frame_time: 60
max_meshgen_time: 60
max_sim_time: 600
static_threshold: 0.03
non_static_percent: 1.5
max_body_collisions: 35
max_self_collisions: 300
zero_gravity_steps: 10
resolution_scale: 1.0
ground: false
material:
garment_tri_ka: 10000.0
garment_edge_ke: 500.0
garment_tri_ke: 10000.0
spring_ke: 50000.0
garment_edge_kd: 10.0
garment_tri_kd: 1.0
spring_kd: 10.0
fabric_density: 1.0
fabric_thickness: 0.1
fabric_friction: 0.5
options:
enable_particle_particle_collisions: false
enable_triangle_particle_collisions: true
enable_edge_edge_collisions: true
enable_body_collision_filters: true
enable_global_collision_filter: true
global_damping_factor: 0.25
global_damping_effective_velocity: 0.0
global_max_velocity: 25.0
enable_attachment_constraint: true
attachment_stiffness:
- 1000.
- 1000.
- 1000.
- 1000.
attachment_damping:
- 10.
- 0.
- 0.
- 0.
attachment_frames: 400
attachment_label_names:
- lower_interface
- right_collar
- left_collar
- strapless_top
enable_cloth_reference_drag: true
cloth_reference_margin: 0.1
enable_body_smoothing: false
smoothing_total_smoothing_factor: 1.0
smoothing_recover_start_frame: 150
smoothing_num_steps: 100
smoothing_frame_gap_between_steps: 1
body_collision_thickness: 0.25
body_friction: 0.5
stats:
fails: {}
sim_time: {}
spf: {}
fin_frame: {}
self_collisions: {}
body_collisions: {}
render:
config:
resolution:
- 800
- 800
sides:
- front
- back
front_camera_location:
- 0
- 0.97
- 4.15
uv_texture:
seam_width: 0.5
dpi: 1500
fabric_grain_texture_path: ./assets/img/fabric_texture.png
fabric_grain_resolution: 5
stats:
render_time: {}

View File

@@ -0,0 +1,75 @@
{
"body": "f_average_A40.obj",
"render": {
"config": {
"garment_color": [
0.6359999775886536,
0.43511512875556946,
0.04769999161362648
],
"resolution": [
800,
800
]
},
"stats": {
"render_time": {}
}
},
"scan_imitation": {
"config": {
"test_rays_num": 30,
"visible_rays_num": 4
},
"stats": {}
},
"sim": {
"config": {
"body_friction": 0.25,
"collision_thickness": 0.04,
"material": {
"air_drag": 0.01,
"bend_angle_dropoff": 0.11,
"bend_damp": 0.1,
"bend_damp_dropoff": 0.0,
"bend_plasticity": 0.0,
"bend_resistance": 0.03,
"bend_yield": 0.0,
"compression_resistance": 100.0,
"density": 0.015,
"friction": 0.01,
"length_scale": 1.0,
"pressure": 0.0,
"rubber": 1,
"shear_resistance": 0.4,
"stretch_damp": 0.2,
"stretch_resistance": 25.0,
"viscous_damp": 0.0,
"warp_resistance_scale": 1.0,
"warp_rubber_scale": 1.0,
"weft_resistance_scale": 1.0,
"weft_rubber_scale": 1.0
},
"max_sim_steps": 1500,
"non_static_percent": 1.5,
"object_intersect_border_threshold": 100,
"resolution_scale": 9.0,
"self_intersect_hit_threshold": 0,
"static_threshold": 0.01,
"zero_gravity_steps": 5
},
"stats": {
"fails": {
"crashes": [],
"fast_finish": [],
"intersect_colliders": [],
"intersect_self": [],
"pattern_loading": [],
"static_equilibrium": []
},
"fin_frame": {},
"sim_time": {},
"spf": {}
}
}
}

View File

@@ -0,0 +1,75 @@
{
"body": "f_average_A40.obj",
"render": {
"config": {
"garment_color": [
0.6359999775886536,
0.43511512875556946,
0.04769999161362648
],
"resolution": [
800,
800
]
},
"stats": {
"render_time": {}
}
},
"scan_imitation": {
"config": {
"test_rays_num": 30,
"visible_rays_num": 4
},
"stats": {}
},
"sim": {
"config": {
"body_friction": 0.25,
"collision_thickness": 0.04,
"material": {
"air_drag": 0.01,
"bend_angle_dropoff": 0.11,
"bend_damp": 0.1,
"bend_damp_dropoff": 0.0,
"bend_plasticity": 0.0,
"bend_resistance": 0.03,
"bend_yield": 0.0,
"compression_resistance": 100.0,
"density": 0.015,
"friction": 0.01,
"length_scale": 1.0,
"pressure": 0.0,
"rubber": 1,
"shear_resistance": 0.4,
"stretch_damp": 0.2,
"stretch_resistance": 400.0,
"viscous_damp": 0.0,
"warp_resistance_scale": 1.0,
"warp_rubber_scale": 1.0,
"weft_resistance_scale": 1.0,
"weft_rubber_scale": 1.0
},
"max_sim_steps": 1500,
"non_static_percent": 1.5,
"object_intersect_border_threshold": 100,
"resolution_scale": 9.0,
"self_intersect_hit_threshold": 0,
"static_threshold": 0.01,
"zero_gravity_steps": 5
},
"stats": {
"fails": {
"crashes": [],
"fast_finish": [],
"intersect_colliders": [],
"intersect_self": [],
"pattern_loading": [],
"static_equilibrium": []
},
"fin_frame": {},
"sim_time": {},
"spf": {}
}
}
}

View File

@@ -0,0 +1,75 @@
{
"body": "f_average_A40.obj",
"render": {
"config": {
"garment_color": [
0.6359999775886536,
0.43511512875556946,
0.04769999161362648
],
"resolution": [
800,
800
]
},
"stats": {
"render_time": {}
}
},
"scan_imitation": {
"config": {
"test_rays_num": 30,
"visible_rays_num": 4
},
"stats": {}
},
"sim": {
"config": {
"body_friction": 0.25,
"collision_thickness": 0.04,
"material": {
"air_drag": 0.01,
"bend_angle_dropoff": 0.11,
"bend_damp": 0.1,
"bend_damp_dropoff": 0.0,
"bend_plasticity": 0.0,
"bend_resistance": 2.0,
"bend_yield": 0.0,
"compression_resistance": 100.0,
"density": 0.015,
"friction": 0.01,
"length_scale": 1.0,
"pressure": 0.0,
"rubber": 1,
"shear_resistance": 0.4,
"stretch_damp": 0.2,
"stretch_resistance": 400.0,
"viscous_damp": 0.0,
"warp_resistance_scale": 1.0,
"warp_rubber_scale": 1.0,
"weft_resistance_scale": 1.0,
"weft_rubber_scale": 1.0
},
"max_sim_steps": 1500,
"non_static_percent": 1.5,
"object_intersect_border_threshold": 100,
"resolution_scale": 4.0,
"self_intersect_hit_threshold": 0,
"static_threshold": 0.01,
"zero_gravity_steps": 10
},
"stats": {
"fails": {
"crashes": [],
"fast_finish": [],
"intersect_colliders": [],
"intersect_self": [],
"pattern_loading": [],
"static_equilibrium": []
},
"fin_frame": {},
"sim_time": {},
"spf": {}
}
}
}

7
assets/bodies/Readme.md Normal file
View File

@@ -0,0 +1,7 @@
# Attribution
Body models in this folder with "smpl" in the name are based on the female and male average body models of [SMPL](https://smpl.is.tue.mpg.de/) (licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)).
Body models without "smpl" in the name are based on our own body model, see https://github.com/mbotsch/GarmentMeasurements
### Disclaimer
Due to the restrictions of the SMPL license, we cannot share all 3D models of the body shapes used in GarmentCode paper, except for the base average bodies for male and female versions of SMPL.

View File

@@ -0,0 +1,41 @@
import numpy as np
import pygarment as pyg
class BodyParameters(pyg.BodyParametrizationBase):
"""Custom class that defines calculated body parameters"""
def __init__(self, param_file='') -> None:
super().__init__(param_file)
def eval_dependencies(self, key=None):
super().eval_dependencies(key)
if key in ['height', 'head_l', 'waist_line', 'hips_line', None]:
self.params['_waist_level'] = self.params['height'] - self.params['head_l'] - self.params['waist_line']
self.params['_leg_length'] = self.params['_waist_level'] - self.params['hips_line']
if key in ['shoulder_w', None]:
# Correct sleeve line location is a little closer to the neck
# than the true shoulder width
self.params['_base_sleeve_balance'] = self.params['shoulder_w'] - 2
# Balance for the bust dart location
if key in ['bust_line', 'vert_bust_line', None]:
if 'vert_bust_line' in self.params:
self.params['_bust_line'] = (1 - 1/3) * self.params['vert_bust_line'] + 1/3 * self.params['bust_line']
else:
self.params['_bust_line'] = self.params['bust_line']
# Half of the slopes for use in garment (smoother fabric distribution)
if key in ['hip_inclination', None]:
self.params['_hip_inclination'] = self.params['hip_inclination'] / 2
if key in ['shoulder_incl', None]:
self.params['_shoulder_incl'] = self.params['shoulder_incl']
# Add ease to armhole
if key in ['armscye_depth', None]:
self.params['_armscye_depth'] = self.params['armscye_depth'] + 2.5

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
# Measurments of the body
body:
height: 165
head_l: 25 # nape of the neck to the top -- ok to use some average value
neck_w: 15
shoulder_w: 38
armscye_depth: 15 # Nape of the Neck to armscye depth on the back
shoulder_incl: 10 # degrees
arm_pose_angle: 40 # Degrees
arm_length: 80
wrist: 17
waist: 80 # 82 adjusted for 'dips'
waist_line: 36 # Nape of the Neck to waist (on the back)
waist_over_bust_line: 43 # -- measurement + addition for the shoulder
waist_back_width: 37
bust_line: 23
bust: 90 # adjust for the 'dips' from 96
bust_points: 21 #
underbust: 83
back_width: 45 # 32 + 2 * between-size
hips: 107
hip_back_width: 55
hips_line: 22 # From the waist
hip_inclination: 6
bum_points: 16
crotch_hip_diff: 10.5 # NOTE Waist-crotch = 25 # NOTE vertical projection of the level
leg_circ: 64

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
body:
arm_pose_angle: 40
arm_length: 80
wrist: 25
armscye_depth: 17.5
back_width: 52 # 46
bum_points: 19.3
bust: 100.0
bust_line: 24.4
bust_points: 17
crotch_hip_diff: 13.0
head_l: 25.6
height: 178.1
hips: 100.0
hips_line: 17.0
hip_back_width: 52
hip_inclination: 4
leg_circ: 63
neck_w: 17
shoulder_w: 38.2
shoulder_incl: 10
underbust: 98
waist: 80.0
waist_line: 44.2
waist_over_bust_line: 47
waist_back_width: 38

71252
assets/bodies/mean_all.obj Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/bodies/mean_all.stl Normal file

Binary file not shown.

View File

@@ -0,0 +1,27 @@
body:
arm_length: 53.9697
arm_pose_angle: 45.483
armscye_depth: 12.8679
back_width: 47.6761
bum_points: 18.2342
bust: 99.8407
bust_line: 25.6947
bust_points: 16.9463
crotch_hip_diff: 8.81363
head_l: 26.3262
height: 171.99
hip_back_width: 54.8237
hip_inclination: 9.86489
hips: 103.478
hips_line: 23.4837
leg_circ: 60.2039
neck_w: 18.9328
shoulder_incl: 21.6777
shoulder_w: 36.4568
underbust: 86.2455
vert_bust_line: 21.1388
waist: 84.3338
waist_back_width: 39.1358
waist_line: 36.8913
waist_over_bust_line: 40.5603
wrist: 16.5945

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
#python/object:assets.bodies.body_params.BodyParameters
body:
_armscye_depth: 15.4
_base_sleeve_balance: 34.5
_bust_line: 22.7
_hip_inclination: 4.93
_leg_length: 85.3
_shoulder_incl: 21.7
_waist_level: 108.0
arm_length: 53.0
arm_pose_angle: 45.5
armscye_depth: 12.9
back_width: 47.7
bum_points: 18.2
bust: 99.8
bust_line: 25.7
bust_points: 16.9
crotch_hip_diff: 8.81
head_l: 26.3
height: 171.0
hip_back_width: 54.8
hip_inclination: 9.86
hips: 103.0
hips_line: 23.5
leg_circ: 60.2
neck_w: 18.9
shoulder_incl: 21.7
shoulder_w: 36.5
underbust: 86.2
vert_bust_line: 21.1
waist: 84.3
waist_back_width: 39.1
waist_line: 36.9
waist_over_bust_line: 40.6
wrist: 16.6

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
body:
arm_length: 53.9697
arm_pose_angle: 0.0
armscye_depth: 12.8679
back_width: 47.6761
bum_points: 18.2342
bust: 99.8407
bust_line: 25.6947
bust_points: 16.9463
crotch_hip_diff: 8.81363
head_l: 26.3262
height: 171.99
hip_back_width: 54.8237
hip_inclination: 9.86489
hips: 103.478
hips_line: 23.4837
leg_circ: 60.2039
neck_w: 18.9328
shoulder_incl: 21.6777
shoulder_w: 36.4568
underbust: 86.2455
vert_bust_line: 21.1388
waist: 84.3338
waist_back_width: 39.1358
waist_line: 36.8913
waist_over_bust_line: 40.5603
wrist: 16.5945

71252
assets/bodies/mean_female.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
body:
arm_length: 51.594
arm_pose_angle: 45.487
armscye_depth: 12.6489
back_width: 46.1652
bum_points: 17.6707
bust: 97.4076
bust_line: 25.5656
bust_points: 16.212
crotch_hip_diff: 7.8731
head_l: 25.5328
height: 166.194
hip_back_width: 55.6788
hip_inclination: 12.6783
hips: 104.091
hips_line: 22.6309
leg_circ: 60.6592
neck_w: 17.772
shoulder_incl: 20.905
shoulder_w: 34.7129
underbust: 80.7483
vert_bust_line: 20.7686
waist: 80.3442
waist_back_width: 37.7747
waist_line: 35.7549
waist_over_bust_line: 39.9902
wrist: 15.8636

71252
assets/bodies/mean_male.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
body:
arm_length: 56.8064
arm_pose_angle: 45.4775
armscye_depth: 13.1301
back_width: 50.7175
bum_points: 17.736
bust: 102.787
bust_line: 25.9217
bust_points: 17.8195
crotch_hip_diff: 9.92969
head_l: 27.2653
height: 178.873
hip_back_width: 53.9145
hip_inclination: 6.7451
hips: 102.85
hips_line: 24.4948
leg_circ: 59.9882
neck_w: 20.3361
shoulder_incl: 22.5059
shoulder_w: 38.5276
underbust: 93.0277
vert_bust_line: 21.5796
waist: 89.003
waist_back_width: 40.7068
waist_line: 38.3492
waist_over_bust_line: 41.6141
wrist: 17.4648

File diff suppressed because it is too large Load Diff

926
assets/design_params/default.yaml Executable file
View File

@@ -0,0 +1,926 @@
design:
meta:
upper:
v: null
range:
- FittedShirt
- Shirt
- null
type: select_null
default_prob: 0.3
wb:
v: null
range:
- StraightWB
- FittedWB
- null
type: select_null
default_prob: 0.5
bottom:
v: null
range:
- SkirtCircle
- AsymmSkirtCircle
- GodetSkirt
- Pants
- Skirt2
- SkirtManyPanels
- PencilSkirt
- SkirtLevels
- SkirtLayers
- null
type: select_null
default_prob: 0.3
connected:
v: false
range:
- true
- false
type: bool
waistband:
waist:
v: 1.0
range:
- 1.0
- 2
type: float
default_prob: 0.7
width:
v: 0.2
range:
- 0.1
- 1.0
type: float
default_prob: 0.5
fitted_shirt:
strapless:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
shirt:
length:
v: 1.2
range:
- 0.5
- 3.5
type: float
default_prob: 0.7
width:
v: 1.0
range:
- 1.0
- 1.3
type: float
default_prob: 0.4
flare:
v: 1.0
range:
- 0.7
- 1.6
type: float
default_prob: 0.4
collar:
f_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.4
b_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.8
width:
v: 0.5
range:
- -0.5
- 1
type: float
default_prob: 0.4
fc_depth:
v: 0.15
range:
- 0.3
- 2
type: float
default_prob: 0.3
bc_depth:
v: 0.05
range:
- 0
- 2
type: float
default_prob: 0.4
fc_angle:
v: 95
range:
- 70
- 110
type: int
bc_angle:
v: 95
range:
- 70
- 110
type: int
f_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
b_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
component:
style:
v: null
range:
- Turtle
- SimpleLapel
- Hood2Panels
- null
type: select_null
default_prob: 0.6
depth:
v: 5
range:
- 2
- 8
type: int
lapel_standing:
v: false
range:
- true
- false
type: bool
hood_depth:
v: 1
range:
- 1
- 2
type: float
default_prob: 0.6
hood_length:
v: 1
range:
- 1
- 1.5
type: float
default_prob: 0.6
sleeve:
sleeveless:
v: true
range:
- true
- false
type: bool
default_prob: 0.7
armhole_shape:
v: ArmholeCurve
range:
- ArmholeSquare
- ArmholeAngle
- ArmholeCurve
type: select
default_prob: 0.7
length:
v: 0.2
range:
- 0.1
- 1.15
type: float
connecting_width:
v: 0.2
range:
- 0
- 2
type: float
default_prob: 0.6
end_width:
v: 1.0
range:
- 0.2
- 2
type: float
default_prob: 0.4
sleeve_angle:
v: 11
range:
- 10
- 50
type: int
opening_dir_mix:
v: 0.1
range:
- -0.9
- 0.8
type: float
default_prob: 1.0
standing_shoulder:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
standing_shoulder_len:
v: 4.0
range:
- 4
- 10
type: float
connect_ruffle:
v: 1.0
range:
- 1
- 2
type: float
default_prob: 0.4
smoothing_coeff:
v: 0.25
range:
- 0.1
- 0.4
type: float
default_prob: 0.8
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
top_ruffle:
v: 1
range:
- 1
- 3
type: float
cuff_len:
v: 0.225
range:
- 0.05
- 0.9
type: float
default_prob: 0.7
skirt_fraction:
v: 0.425
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
skirt_flare:
v: 1.0
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float
default_prob: 0.3
left:
enable_asym:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
fitted_shirt:
strapless:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
shirt:
width:
v: 1.0
range:
- 1.0
- 1.3
type: float
default_prob: 0.4
flare:
v: 1.0
range:
- 0.7
- 1.6
type: float
default_prob: 0.4
collar:
f_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.4
b_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.8
width:
v: 0.5
range:
- 0
- 1
type: float
default_prob: 0.4
fc_angle:
v: 95
range:
- 70
- 110
type: int
bc_angle:
v: 95
range:
- 70
- 110
type: int
f_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
b_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
f_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
b_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
sleeve:
sleeveless:
v: true
range:
- true
- false
type: bool
default_prob: 0.7
armhole_shape:
v: ArmholeCurve
range:
- ArmholeSquare
- ArmholeAngle
- ArmholeCurve
type: select
default_prob: 0.7
length:
v: 0.2
range:
- 0.1
- 1.15
type: float
connecting_width:
v: 0.2
range:
- 0
- 2
type: float
default_prob: 0.6
end_width:
v: 1.0
range:
- 0.2
- 2
type: float
default_prob: 0.4
sleeve_angle:
v: 11
range:
- 10
- 50
type: int
opening_dir_mix:
v: 0.1
range:
- -0.9
- 0.8
type: float
default_prob: 1.0
standing_shoulder:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
standing_shoulder_len:
v: 4.0
range:
- 4
- 10
type: float
connect_ruffle:
v: 1.0
range:
- 1
- 2
type: float
default_prob: 0.4
smoothing_coeff:
v: 0.25
range:
- 0.1
- 0.4
type: float
default_prob: 0.8
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
top_ruffle:
v: 1
range:
- 1
- 2
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.7
skirt_fraction:
v: 0.5
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
skirt_flare:
v: 1.2
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float
default_prob: 0.3
skirt:
length:
v: 0.1
range:
- -0.2
- 0.95
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
ruffle:
v: 1.4
range:
- 1
- 2
type: float
default_prob: 0.3
bottom_cut:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.3
flare:
v: 1
range:
- 0
- 20
type: int
default_prob: 0.5
flare-skirt:
length:
v: 0.2
range:
- -0.2
- 0.95
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
suns:
v: 0.75
range:
- 0.1
- 1.95
type: float
skirt-many-panels:
n_panels:
v: 4
range:
- 4
- 15
type: int
panel_curve:
v: 0.15
range:
- -0.35
- -0.25
- -0.15
- 0
- 0.15
- 0.25
- 0.35
- 0.45
type: select
asymm:
front_length:
v: 0.675
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
cut:
add:
v: false
range:
- true
- false
type: bool
default_prob: 0.6
depth:
v: 0.5
range:
- 0.05
- 0.95
type: float
default_prob: 0.6
width:
v: 0.1
range:
- 0.05
- 0.4
type: float
place:
v: -0.5
range:
- -1
- 1
type: float
godet-skirt:
base:
v: PencilSkirt
range:
- Skirt2
- PencilSkirt
type: select
default_prob: 0.7
insert_w:
v: 15
range:
- 10
- 50
type: int
insert_depth:
v: 20
range:
- 10
- 50
type: int
num_inserts:
v: 4
range:
- 4
- 6
- 8
- 10
- 12
type: select
cuts_distance:
v: 5
range:
- 0
- 10
type: int
pencil-skirt:
length:
v: 0.4
range:
- 0.2
- 0.95
type: float
rise:
v: 1
range:
- 0.5
- 1
type: float
default_prob: 0.3
flare:
v: 1.0
range:
- 0.6
- 1.5
type: float
default_prob: 0.3
low_angle:
v: 0
range:
- -30
- 30
type: int
default_prob: 0.7
front_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.4
back_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.4
left_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.6
right_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.6
style_side_cut:
v: null
range:
- Sun
- SIGGRAPH_logo
- null
type: select_null
default_prob: 1.0
levels-skirt:
base:
v: PencilSkirt
range:
- Skirt2
- PencilSkirt
- SkirtCircle
- AsymmSkirtCircle
type: select
level:
v: Skirt2
range:
- Skirt2
- SkirtCircle
- AsymmSkirtCircle
type: select
num_levels:
v: 1
range:
- 1
- 5
type: int
level_ruffle:
v: 1
range:
- 1
- 1.7
type: float
length:
v: 0.5
range:
- 0.2
- 0.95
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
base_length_frac:
v: 0.375
range:
- 0.2
- 0.8
type: float
layers-skirt:
base:
v: SkirtCircle
range:
- SkirtCircle
type: select
num_layers:
v: 2
range:
- 2
- 5
type: int
layer_ruffle:
v: 1
range:
- 1
- 1.7
type: float
length:
v: 0.5
range:
- 0.2
- 5
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
pants:
length:
v: 0.3
range:
- 0.2
- 0.9
type: float
width:
v: 1.0
range:
- 1.0
- 1.5
type: float
default_prob: 0.5
flare:
v: 1.0
range:
- 0.5
- 1.2
type: float
default_prob: 0.3
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
default_prob: 0.5
top_ruffle:
v: 1
range:
- 1
- 2
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.3
skirt_fraction:
v: 0.425
range:
- 0.1
- 0.9
type: float
skirt_flare:
v: 1.0
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float

View File

@@ -0,0 +1,895 @@
design:
meta:
upper:
v: null
range:
- FittedShirt
- Shirt
- null
type: select_null
default_prob: 0.3
wb:
v: null
range:
- StraightWB
- FittedWB
- null
type: select_null
default_prob: 0.5
bottom:
v: null
range:
- SkirtCircle
- AsymmSkirtCircle
- GodetSkirt
- Pants
- Skirt2
- SkirtManyPanels
- PencilSkirt
- SkirtLevels
- SkirtLayers
- null
type: select_null
default_prob: 0.3
connected:
v: false
range:
- true
- false
type: bool
waistband:
waist:
v: 1.0
range:
- 1.0
- 2
type: float
default_prob: 0.7
width:
v: 0.2
range:
- 0.1
- 1.0
type: float
default_prob: 0.5
fitted_shirt:
strapless:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
shirt:
length:
v: 1.2
range:
- 0.5
- 3.5
type: float
default_prob: 0.7
width:
v: 1.0
range:
- 1.0
- 1.3
type: float
default_prob: 0.4
flare:
v: 1.0
range:
- 0.7
- 1.6
type: float
default_prob: 0.4
collar:
f_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.4
b_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.8
width:
v: 0.5
range:
- -0.5
- 1
type: float
default_prob: 0.4
fc_depth:
v: 0.15
range:
- 0.3
- 2
type: float
default_prob: 0.3
bc_depth:
v: 0.05
range:
- 0
- 2
type: float
default_prob: 0.4
fc_angle:
v: 95
range:
- 70
- 110
type: int
bc_angle:
v: 95
range:
- 70
- 110
type: int
f_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
b_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
component:
style:
v: null
range:
- Turtle
- SimpleLapel
- Hood2Panels
- null
type: select_null
default_prob: 0.6
depth:
v: 5
range:
- 2
- 8
type: int
lapel_standing:
v: false
range:
- true
- false
type: bool
hood_depth:
v: 1
range:
- 1
- 2
type: float
default_prob: 0.6
hood_length:
v: 1
range:
- 1
- 1.5
type: float
default_prob: 0.6
sleeve:
sleeveless:
v: true
range:
- true
- false
type: bool
default_prob: 0.7
armhole_shape:
v: ArmholeCurve
range:
- ArmholeSquare
- ArmholeAngle
- ArmholeCurve
type: select
default_prob: 0.7
length:
v: 0.2
range:
- 0.1
- 1.15
type: float
connecting_width:
v: 0.2
range:
- 0
- 2
type: float
default_prob: 0.6
end_width:
v: 1.0
range:
- 0.2
- 2
type: float
default_prob: 0.4
sleeve_angle:
v: 11
range:
- 10
- 50
type: int
opening_dir_mix:
v: 0.1
range:
- -0.9
- 0.8
type: float
default_prob: 1.0
standing_shoulder:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
standing_shoulder_len:
v: 4.0
range:
- 4
- 10
type: float
connect_ruffle:
v: 1.0
range:
- 1
- 2
type: float
default_prob: 0.4
smoothing_coeff:
v: 0.25
range:
- 0.1
- 0.4
type: float
default_prob: 0.8
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
top_ruffle:
v: 1
range:
- 1
- 3
type: float
cuff_len:
v: 0.225
range:
- 0.05
- 0.9
type: float
default_prob: 0.7
skirt_fraction:
v: 0.425
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
skirt_flare:
v: 1.0
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float
default_prob: 0.3
left:
enable_asym:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
fitted_shirt:
strapless:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
shirt:
width:
v: 1.0
range:
- 1.0
- 1.3
type: float
default_prob: 0.4
flare:
v: 1.0
range:
- 0.7
- 1.6
type: float
default_prob: 0.4
collar:
f_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.4
b_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.8
width:
v: 0.5
range:
- 0
- 1
type: float
default_prob: 0.4
fc_angle:
v: 95
range:
- 70
- 110
type: int
bc_angle:
v: 95
range:
- 70
- 110
type: int
f_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
b_bezier_x:
v: 0.175
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_y:
v: 0.175
range:
- 0.05
- 0.95
type: float
f_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
b_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
sleeve:
sleeveless:
v: true
range:
- true
- false
type: bool
default_prob: 0.7
armhole_shape:
v: ArmholeCurve
range:
- ArmholeSquare
- ArmholeAngle
- ArmholeCurve
type: select
default_prob: 0.7
length:
v: 0.2
range:
- 0.1
- 1.15
type: float
connecting_width:
v: 0.2
range:
- 0
- 2
type: float
default_prob: 0.6
end_width:
v: 1.0
range:
- 0.2
- 2
type: float
default_prob: 0.4
sleeve_angle:
v: 11
range:
- 10
- 50
type: int
opening_dir_mix:
v: 0.1
range:
- -0.9
- 0.8
type: float
default_prob: 1.0
standing_shoulder:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
standing_shoulder_len:
v: 4.0
range:
- 4
- 10
type: float
connect_ruffle:
v: 1.0
range:
- 1
- 2
type: float
default_prob: 0.4
smoothing_coeff:
v: 0.25
range:
- 0.1
- 0.4
type: float
default_prob: 0.8
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
top_ruffle:
v: 1
range:
- 1
- 2
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.7
skirt_fraction:
v: 0.5
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
skirt_flare:
v: 1.2
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float
default_prob: 0.3
skirt:
length:
v: 0.1
range:
- -0.2
- 0.95
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
ruffle:
v: 1.4
range:
- 1
- 2
type: float
default_prob: 0.3
bottom_cut:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.3
flare:
v: 1
range:
- 0
- 20
type: int
default_prob: 0.5
flare-skirt:
length:
v: 0.2
range:
- -0.2
- 0.95
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
suns:
v: 0.75
range:
- 0.1
- 1.95
type: float
skirt-many-panels:
n_panels:
v: 4
range:
- 4
- 15
type: int
panel_curve:
v: 0.15
range:
- -0.35
- -0.25
- -0.15
- 0
- 0.15
- 0.25
- 0.35
- 0.45
type: select
asymm:
front_length:
v: 0.675
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
cut:
add:
v: false
range:
- true
- false
type: bool
default_prob: 0.6
depth:
v: 0.5
range:
- 0.05
- 0.95
type: float
default_prob: 0.6
width:
v: 0.1
range:
- 0.05
- 0.4
type: float
place:
v: -0.5
range:
- -1
- 1
type: float
godet-skirt:
base:
v: PencilSkirt
range:
- Skirt2
- PencilSkirt
type: select
default_prob: 0.7
insert_w:
v: 15
range:
- 10
- 50
type: int
insert_depth:
v: 20
range:
- 10
- 50
type: int
num_inserts:
v: 4
range:
- 4
- 6
- 8
- 10
- 12
type: select
cuts_distance:
v: 5
range:
- 0
- 10
type: int
pencil-skirt:
length:
v: 0.4
range:
- 0.2
- 0.95
type: float
rise:
v: 1
range:
- 0.5
- 1
type: float
default_prob: 0.3
flare:
v: 1.0
range:
- 0.6
- 1.5
type: float
default_prob: 0.3
low_angle:
v: 0
range:
- -30
- 30
type: int
default_prob: 0.7
front_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.4
back_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.4
left_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.6
right_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.6
style_side_cut:
v: null
range:
- Sun
- SIGGRAPH_logo
- null
type: select_null
default_prob: 1.0
levels-skirt:
base:
v: PencilSkirt
range:
- Skirt2
- PencilSkirt
- SkirtCircle
- AsymmSkirtCircle
type: select
level:
v: Skirt2
range:
- Skirt2
- SkirtCircle
- AsymmSkirtCircle
type: select
num_levels:
v: 1
range:
- 1
- 5
type: int
level_ruffle:
v: 1
range:
- 1
- 1.7
type: float
length:
v: 0.5
range:
- 0.2
- 0.95
type: float
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
base_length_frac:
v: 0.375
range:
- 0.2
- 0.8
type: float
pants:
length:
v: 0.3
range:
- 0.2
- 0.9
type: float
width:
v: 1.0
range:
- 1.0
- 1.5
type: float
default_prob: 0.5
flare:
v: 1.0
range:
- 0.5
- 1.2
type: float
default_prob: 0.3
rise:
v: 0.5
range:
- 0.5
- 1
type: float
default_prob: 0.3
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
default_prob: 0.5
top_ruffle:
v: 1
range:
- 1
- 2
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.3
skirt_fraction:
v: 0.425
range:
- 0.1
- 0.9
type: float
skirt_flare:
v: 1.0
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,885 @@
design:
meta:
upper:
v: Shirt
range:
- FittedShirt
- Shirt
- null
type: select_null
default_prob: 0.3
wb:
v: null
range:
- StraightWB
- FittedWB
- null
type: select_null
default_prob: 0.5
bottom:
v: null
range:
- SkirtCircle
- AsymmSkirtCircle
- GodetSkirt
- Pants
- Skirt2
- SkirtManyPanels
- PencilSkirt
- SkirtLevels
- null
type: select_null
default_prob: 0.3
waistband:
waist:
v: 1.0
range:
- 1.0
- 2
type: float
default_prob: 0.7
width:
v: 0.2
range:
- 0.1
- 1.
type: float
default_prob: 0.5
shirt:
strapless:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
length:
v: 1.2
range:
- 0.5
- 3.5
type: float
default_prob: 0.7
width:
v: 1.05
range:
- 1.0
- 1.3
type: float
default_prob: 0.4
flare:
v: 1.0
range:
- 0.7
- 1.6
type: float
default_prob: 0.4
collar:
f_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.4
b_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.8
width:
v: 0.2
range:
- -0.5
- 1
type: float
default_prob: 0.4
fc_depth:
v: 0.4
range:
- 0.3
- 2
type: float
default_prob: 0.3
bc_depth:
v: 0
range:
- 0
- 2
type: float
default_prob: 0.4
fc_angle:
v: 95
range:
- 70
- 110
type: int
bc_angle:
v: 95
range:
- 70
- 110
type: int
f_bezier_x:
v: 0.3
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_bezier_y:
v: 0.55
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_x:
v: 0.15
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_y:
v: 0.1
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
b_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
component:
style:
v: null
range:
- Turtle
- SimpleLapel
- Hood2Panels
- null
type: select_null
default_prob: 0.6
depth:
v: 7
range:
- 2
- 8
type: int
lapel_standing:
v: false
range:
- true
- false
type: bool
hood_depth:
v: 1
range:
- 1
- 2
type: float
default_prob: 0.6
hood_length:
v: 1
range:
- 1
- 1.5
type: float
default_prob: 0.6
sleeve:
sleeveless:
v: false
range:
- true
- false
type: bool
default_prob: 0.7
armhole_shape:
v: ArmholeCurve
range:
- ArmholeSquare
- ArmholeAngle
- ArmholeCurve
type: select
default_prob: 0.7
length:
v: 0.3
range:
- 0.1
- 1.15
type: float
connecting_width:
v: 0.2
range:
- 0
- 2
type: float
default_prob: 0.6
end_width:
v: 1.0
range:
- 0.2
- 2
type: float
default_prob: 0.4
sleeve_angle:
v: 10
range:
- 10
- 50
type: int
opening_dir_mix:
v: 0.1
range:
- -0.9
- 0.8
type: float
default_prob: 1.
standing_shoulder:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
standing_shoulder_len:
v: 5.0
range:
- 4
- 10
type: float
connect_ruffle:
v: 1
range:
- 1
- 2
type: float
default_prob: 0.4
smoothing_coeff:
v: 0.25
range:
- 0.1
- 0.4
type: float
default_prob: 0.8
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
top_ruffle:
v: 1
range:
- 1
- 3
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.7
skirt_fraction:
v: 0.5
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
skirt_flare:
v: 1.2
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float
default_prob: 0.3
left:
enable_asym:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
shirt:
strapless:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
width:
v: 1.0
range:
- 1.0
- 1.3
type: float
default_prob: 0.4
flare:
v: 1.0
range:
- 0.7
- 1.6
type: float
default_prob: 0.4
collar:
f_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.4
b_collar:
v: CircleNeckHalf
range:
- CircleNeckHalf
- CurvyNeckHalf
- VNeckHalf
- SquareNeckHalf
- TrapezoidNeckHalf
- CircleArcNeckHalf
- Bezier2NeckHalf
type: select
default_prob: 0.8
width:
v: 0.5
range:
- 0
- 1
type: float
default_prob: 0.4
fc_angle:
v: 95
range:
- 70
- 110
type: int
bc_angle:
v: 95
range:
- 70
- 110
type: int
f_bezier_x:
v: 0.5
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
f_bezier_y:
v: 0.3
range:
- 0.05
- 0.95
type: float
b_bezier_x:
v: 0.5
range:
- 0.05
- 0.95
type: float
default_prob: 0.4
b_bezier_y:
v: 0.3
range:
- 0.05
- 0.95
type: float
f_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
b_flip_curve:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
sleeve:
sleeveless:
v: true
range:
- true
- false
type: bool
default_prob: 0.7
armhole_shape:
v: ArmholeCurve
range:
- ArmholeSquare
- ArmholeAngle
- ArmholeCurve
type: select
default_prob: 0.7
length:
v: 0.3
range:
- 0.1
- 1.15
type: float
connecting_width:
v: 0.2
range:
- 0
- 2
type: float
default_prob: 0.6
end_width:
v: 1.0
range:
- 0.2
- 2
type: float
default_prob: 0.4
sleeve_angle:
v: 10
range:
- 10
- 50
type: int
opening_dir_mix:
v: 0.2
range:
- -0.9
- 0.8
type: float
default_prob: 1.
standing_shoulder:
v: false
range:
- true
- false
type: bool
default_prob: 0.8
standing_shoulder_len:
v: 5.0
range:
- 4
- 10
type: float
connect_ruffle:
v: 1
range:
- 1
- 2
type: float
default_prob: 0.4
smoothing_coeff:
v: 0.25
range:
- 0.1
- 0.4
type: float
default_prob: 0.8
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
top_ruffle:
v: 1
range:
- 1
- 2
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.7
skirt_fraction:
v: 0.5
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
skirt_flare:
v: 1.2
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float
default_prob: 0.3
skirt:
length:
v: 0.2
range:
- -0.2
- 0.95
type: float
rise:
v: 1
range:
- 0.5
- 1
type: float
default_prob: 0.3
ruffle:
v: 1.3
range:
- 1
- 2
type: float
default_prob: 0.3
bottom_cut:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.3
flare:
v: 0
range:
- 0
- 20
type: int
default_prob: 0.5
flare-skirt:
length:
v: 0.2
range:
- -0.2
- 0.95
type: float
rise:
v: 1
range:
- 0.5
- 1
type: float
default_prob: 0.3
suns:
v: 0.75
range:
- 0.1
- 1.95
type: float
skirt-many-panels:
n_panels:
v: 4
range:
- 4
- 15
type: int
panel_curve:
v: 0
range:
- -0.35
- -0.25
- -0.15
- 0
- 0.15
- 0.25
- 0.35
- 0.45
type: select
asymm:
front_length:
v: 0.5
range:
- 0.1
- 0.9
type: float
default_prob: 0.5
cut:
add:
v: false
range:
- true
- false
type: bool
default_prob: 0.6
depth:
v: 0.5
range:
- 0.05
- 0.95
type: float
default_prob: 0.6
width:
v: 0.1
range:
- 0.05
- 0.4
type: float
place:
v: -0.5
range:
- -1
- 1
type: float
godet-skirt:
base:
v: PencilSkirt
range:
- Skirt2
- PencilSkirt
type: select
default_prob: 0.7
insert_w:
v: 15
range:
- 10
- 50
type: int
insert_depth:
v: 20
range:
- 10
- 50
type: int
num_inserts:
v: 4
range:
- 4
- 6
- 8
- 10
- 12
type: select
cuts_distance:
v: 5
range:
- 0
- 10
type: int
pencil-skirt:
length:
v: 0.4
range:
- 0.2
- 0.95
type: float
rise:
v: 1
range:
- 0.5
- 1
type: float
default_prob: 0.3
flare:
v: 1.
range:
- 0.6
- 1.5
type: float
default_prob: 0.3
low_angle:
v: 0
range:
- -30
- 30
type: int
default_prob: 0.7
front_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.4
back_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.4
left_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.6
right_slit:
v: 0
range:
- 0
- 0.9
type: float
default_prob: 0.6
style_side_cut:
v: null
range:
- Sun
- SIGGRAPH_logo
type: select_null
default_prob: 1.
levels-skirt:
base:
v: PencilSkirt
range:
- Skirt2
- PencilSkirt
- SkirtCircle
- AsymmSkirtCircle
type: select
level:
v: Skirt2
range:
- Skirt2
- SkirtCircle
- AsymmSkirtCircle
type: select
num_levels:
v: 1
range:
- 1
- 5
type: int
level_ruffle:
v: 1.0
range:
- 1
- 1.7
type: float
length:
v: 0.5
range:
- 0.2
- 0.95
type: float
rise:
v: 1
range:
- 0.5
- 1
type: float
default_prob: 0.3
base_length_frac:
v: 0.5
range:
- 0.2
- 0.8
type: float
pants:
length:
v: 0.3
range:
- 0.2
- 0.9
type: float
width:
v: 1.0
range:
- 1.0
- 1.5
type: float
default_prob: 0.5
flare:
v: 1.0
range:
- 0.5
- 1.2
type: float
default_prob: 0.3
rise:
v: 1.0
range:
- 0.5
- 1
type: float
default_prob: 0.3
cuff:
type:
v: null
range:
- CuffBand
- CuffSkirt
- CuffBandSkirt
- null
type: select_null
default_prob: 0.5
top_ruffle:
v: 1.0
range:
- 1
- 2
type: float
cuff_len:
v: 0.1
range:
- 0.05
- 0.9
type: float
default_prob: 0.3
skirt_fraction:
v: 0.5
range:
- 0.1
- 0.9
type: float
skirt_flare:
v: 1.2
range:
- 1
- 2
type: float
skirt_ruffle:
v: 1.0
range:
- 1
- 1.5
type: float

View File

@@ -0,0 +1,262 @@
import pygarment as pyg
from assets.garment_programs.circle_skirt import CircleArcPanel
from assets.garment_programs import skirt_paneled
from assets.garment_programs.base_classes import BaseBand
class StraightBandPanel(pyg.Panel):
"""One panel for a panel skirt"""
def __init__(self, name, width, depth, match_int_proportion=None) -> None:
super().__init__(name)
# define edge loop
self.edges = pyg.EdgeSeqFactory.from_verts(
[0, 0], [0, depth], [width, depth], [width, 0], loop=True)
# define interface
self.interfaces = {
'right': pyg.Interface(self, self.edges[0]),
'top': pyg.Interface(self,
self.edges[1],
ruffle=width / match_int_proportion if match_int_proportion is not None else 1.
).reverse(True),
'left': pyg.Interface(self, self.edges[2]),
'bottom': pyg.Interface(self,
self.edges[3],
ruffle=width / match_int_proportion if match_int_proportion is not None else 1.
)
}
# Default translation
self.top_center_pivot()
self.center_x()
class StraightWB(BaseBand):
"""Simple 2 panel waistband"""
def __init__(self, body, design, rise=1.) -> None:
"""Simple 2 panel waistband
* rise -- the rise value of the bottoms that the WB is attached to
Adapts the shape of the waistband to sit tight on top
of the given rise level (top measurement). If 1. or anything less than waistband width,
the rise is ignored and the StraightWB is created to sit well on the waist
"""
super().__init__(body, design, rise=rise)
# Measurements
self.waist = design['waistband']['waist']['v'] * body['waist']
self.waist_back_frac = body['waist_back_width'] / body['waist']
self.hips = body['hips'] * design['waistband']['waist']['v']
self.hips_back_frac = body['hip_back_width'] / body['hips']
# Params
self.width = design['waistband']['width']['v']
self.rise = rise
# Check correct values
if self.rise + self.width > 1:
self.rise = 1 - self.width
self.top_width = pyg.utils.lin_interpolation(
self.hips, self.waist, self.rise + self.width)
self.top_back_fraction = pyg.utils.lin_interpolation(
self.hips_back_frac, self.waist_back_frac, self.rise + self.width)
self.width = self.width * body['hips_line']
self.define_panels()
self.front.translate_by([0, body['_waist_level'] + 10, 20])
self.back.translate_by([0, body['_waist_level'] + 10, -20])
self.stitching_rules = pyg.Stitches(
(self.front.interfaces['right'], self.back.interfaces['right']),
(self.front.interfaces['left'], self.back.interfaces['left'])
)
self.interfaces = {
'bottom_f': self.front.interfaces['bottom'],
'bottom_b': self.back.interfaces['bottom'],
'top_f': self.front.interfaces['top'],
'top_b': self.back.interfaces['top'],
'bottom': pyg.Interface.from_multiple(
self.front.interfaces['bottom'],
self.back.interfaces['bottom']),
'top': pyg.Interface.from_multiple(
self.front.interfaces['top'],
self.back.interfaces['top']),
}
def define_panels(self):
back_width = self.top_width * self.top_back_fraction
self.front = StraightBandPanel(
'wb_front',
self.top_width - back_width,
self.width,
match_int_proportion=self.body['waist'] - self.body['waist_back_width']
)
self.back = StraightBandPanel(
'wb_back',
back_width,
self.width,
match_int_proportion=self.body['waist_back_width']
)
class FittedWB(StraightWB):
"""Also known as Yoke: a waistband that ~follows the body curvature,
and hence sits tight
Made out of two circular arc panels
"""
def __init__(self, body, design, rise=1.) -> None:
"""A waistband that ~follows the body curvature, and hence sits tight
* rise -- the rise value of the bottoms that the WB is attached to
Adapts the shape of the waistband to sit tight on top
of the given rise level. If 1. or anything less than waistband width,
the rise is ignored and the FittedWB is created to sit well on the waist
"""
self.bottom_width = None
self.bottom_back_fraction = None
super().__init__(body, design, rise)
def define_panels(self):
self.bottom_width = pyg.utils.lin_interpolation(
self.hips, self.waist, self.rise)
self.bottom_back_fraction = pyg.utils.lin_interpolation(
self.hips_back_frac, self.waist_back_frac, self.rise)
self.front = CircleArcPanel.from_all_length(
'wb_front',
self.width,
self.top_width * (1 - self.top_back_fraction),
self.bottom_width * (1 - self.bottom_back_fraction),
match_top_int_proportion=self.body['waist'] - self.body['waist_back_width'],
match_bottom_int_proportion=self.body['waist'] - self.body['waist_back_width']
)
self.back = CircleArcPanel.from_all_length(
'wb_back',
self.width,
self.top_width * self.top_back_fraction,
self.bottom_width * self.bottom_back_fraction,
match_top_int_proportion=self.body['waist_back_width'],
match_bottom_int_proportion=self.body['waist_back_width']
)
class CuffBand(BaseBand):
""" Cuff class for sleeves or pants
band-like piece of fabric with optional "skirt"
"""
def __init__(self, tag, design, length=None) -> None:
super().__init__(body=None, design=design, tag=tag)
self.design = design['cuff']
if length is None:
length = self.design['cuff_len']['v']
self.front = StraightBandPanel(
f'{tag}_cuff_f', self.design['b_width']['v'] / 2, length)
self.front.translate_by([0, 0, 15])
self.back = StraightBandPanel(
f'{tag}_cuff_b', self.design['b_width']['v'] / 2, length)
self.back.translate_by([0, 0, -15])
self.stitching_rules = pyg.Stitches(
(self.front.interfaces['right'], self.back.interfaces['right']),
(self.front.interfaces['left'], self.back.interfaces['left'])
)
self.interfaces = {
'bottom': pyg.Interface.from_multiple(
self.front.interfaces['bottom'],
self.back.interfaces['bottom']),
'top_front': self.front.interfaces['top'],
'top_back': self.back.interfaces['top'],
'top': pyg.Interface.from_multiple(
self.front.interfaces['top'],
self.back.interfaces['top']),
}
class CuffSkirt(BaseBand):
"""A skirt-like flared cuff """
def __init__(self, tag, design, length=None) -> None:
super().__init__(body=None, design=design, tag=tag)
self.design = design['cuff']
width = self.design['b_width']['v']
flare_diff = (self.design['skirt_flare']['v'] - 1) * width / 2
if length is None:
length = self.design['cuff_len']['v']
self.front = skirt_paneled.SkirtPanel(
f'{tag}_cuff_skirt_f', ruffles=self.design['skirt_ruffle']['v'],
waist_length=width / 2, length=length,
flare=flare_diff)
self.front.translate_by([0, 0, 15])
self.back = skirt_paneled.SkirtPanel(
f'{tag}_cuff_skirt_b', ruffles=self.design['skirt_ruffle']['v'],
waist_length=width / 2, length=length,
flare=flare_diff)
self.back.translate_by([0, 0, -15])
self.stitching_rules = pyg.Stitches(
(self.front.interfaces['right'], self.back.interfaces['right']),
(self.front.interfaces['left'], self.back.interfaces['left'])
)
self.interfaces = {
'top': pyg.Interface.from_multiple(
self.front.interfaces['top'], self.back.interfaces['top']),
'top_front': self.front.interfaces['top'],
'top_back': self.back.interfaces['top'],
'bottom': pyg.Interface.from_multiple(
self.front.interfaces['bottom'],
self.back.interfaces['bottom']),
}
class CuffBandSkirt(pyg.Component):
""" Cuff class for sleeves or pants
band-like piece of fabric with optional "skirt"
"""
def __init__(self, tag, design) -> None:
super().__init__(self.__class__.__name__)
self.cuff = CuffBand(
tag,
design,
length=design['cuff']['cuff_len']['v'] * (1 - design['cuff']['skirt_fraction']['v'])
)
self.skirt = CuffSkirt(
tag,
design,
length=design['cuff']['cuff_len']['v'] * design['cuff']['skirt_fraction']['v']
)
# Align
self.skirt.place_below(self.cuff)
self.stitching_rules = pyg.Stitches(
(self.cuff.interfaces['bottom'], self.skirt.interfaces['top']),
)
self.interfaces = {
'top': self.cuff.interfaces['top'],
'top_front': self.cuff.interfaces['top_front'],
'top_back': self.cuff.interfaces['top_back'],
'bottom': self.skirt.interfaces['bottom']
}
def length(self):
return self.cuff.length() + self.skirt.length()

View File

@@ -0,0 +1,122 @@
import pygarment as pyg
class BaseBodicePanel(pyg.Panel):
"""Base class for bodice panels that defines expected interfaces and common functions"""
def __init__(self, name, body, design) -> None:
super().__init__(name)
self.body = body
self.design = design
self.interfaces = {
'outside': object(),
'inside': object(),
'shoulder': object(),
'bottom': object(),
'shoulder_corner': object(),
'collar_corner': object(),
}
def get_width(self, level):
"""Return the panel width at a given level (excluding darts)
* Level is counted from the top of the panel
NOTE: for fitted bodice, the request is only valid for values between 0 and bust_level
"""
# NOTE: this evaluation assumes that the top edge width is the same as bodice shoulder width
side_edge = self.interfaces['outside'].edges[-1]
x = side_edge.end[0] - side_edge.start[0]
y = side_edge.end[1] - side_edge.start[1]
# If the orientation of the edge is "looking down"
# instead of "looking up" as calculations above expect, flip the values
if y < 0:
x, y = -x, -y
return (level * x / y) + self.body['shoulder_w'] / 2
class BaseBottoms(pyg.Component):
"""A base class for all the bottom components.
Defines common elements:
* List of interfaces
* Presence of the rise value
"""
def __init__(self, body, design, tag='', rise=None) -> None:
"""Base bottoms initialization
"""
super().__init__(
self.__class__.__name__ if not tag else f'{self.__class__.__name__}_{tag}')
self.body = body
self.design = design
self.rise = rise
# Set of interfaces that need to be implemented
self.interfaces = {
'top': object()
}
def get_rise(self):
"""Return a rise value for a given component"""
return self.rise
def eval_rise(self, rise):
"""Evaluate updated hip and waist-related measurements,
corresponding to the provided rise value
"""
waist, hips = self.body['waist'], self.body['hips']
hips_level = self.body['hips_line']
self.adj_hips_depth = rise * hips_level
self.adj_waist = pyg.utils.lin_interpolation(hips, waist, rise)
self_adj_back_waist = pyg.utils.lin_interpolation(
self.body['hip_back_width'], self.body['waist_back_width'], rise)
return self.adj_waist, self.adj_hips_depth, self_adj_back_waist
class StackableSkirtComponent(BaseBottoms):
"""
Abstract definition of a skirt that can be stacked with other stackable skirts
(connecting bottom to another StackableSkirtComponent())
"""
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, top_ruffles=True) -> None:
"""Skirt initialization
Extra parameters (length, sleets, top_ruffles)
can be used to overwrite parameters in design dictionary
"""
super().__init__(body, design, tag, rise=rise)
pass
# Set of interfaces that need to be implemented
self.interfaces = {
'top': object(),
'bottom_f': object(),
'bottom_b': object(),
'bottom': object()
}
class BaseBand(pyg.Component):
def __init__(self, body, design, tag='', rise=None) -> None:
"""Base band initialization
"""
super().__init__(
self.__class__.__name__ if not tag else f'{self.__class__.__name__}_{tag}')
self.body = body
self.design = design
self.rise = rise
# Set of interfaces that need to be implemented
self.interfaces = {
'top': object(),
'bottom': object()
}
def length(self):
"""Base length == Length of a first panel"""
return self._get_subcomponents()[0].length()

View File

@@ -0,0 +1,674 @@
from copy import deepcopy
import numpy as np
import pygarment as pyg
from assets.garment_programs.base_classes import BaseBodicePanel
from assets.garment_programs import sleeves
from assets.garment_programs import collars
from assets.garment_programs import tee
from scipy.spatial.transform import Rotation as R
class BodiceFrontHalf(BaseBodicePanel):
def __init__(self, name, body, design) -> None:
super().__init__(name, body, design)
m_bust = body['bust']
m_waist = body['waist']
# sizes
bust_point = body['bust_points'] / 2
front_frac = (body['bust'] - body['back_width']) / 2 / body['bust']
self.width = front_frac * m_bust
waist = (m_waist - body['waist_back_width']) / 2
sh_tan = np.tan(np.deg2rad(body['_shoulder_incl']))
shoulder_incl = sh_tan * self.width
bottom_d_width = (self.width - waist) * 2 / 3
adjustment = sh_tan * (self.width - body['shoulder_w'] / 2)
max_len = body['waist_over_bust_line'] - adjustment
# side length is adjusted due to shoulder inclination
# for the correct sleeve fitting
fb_diff = (front_frac - (0.5 - front_frac)) * body['bust']
back_adjustment = sh_tan * (body['back_width'] / 2 - body['shoulder_w'] / 2)
side_len = body['waist_line'] - back_adjustment - sh_tan * fb_diff
self.edges.append(pyg.EdgeSeqFactory.from_verts(
[0, 0],
[-self.width, 0],
[-self.width, max_len],
[0, max_len + shoulder_incl]
))
self.edges.close_loop()
# Side dart
bust_line = body['waist_line'] - body['_bust_line']
side_d_depth = 0.75 * (self.width - bust_point) # NOTE: calculated value
side_d_width = max_len - side_len
s_edge, side_interface = self.add_dart(
pyg.EdgeSeqFactory.dart_shape(side_d_width, side_d_depth),
self.edges[1],
offset=bust_line + side_d_width / 2)
self.edges.substitute(1, s_edge)
# Take some fabric from the top to match the shoulder width
s_edge[-1].end[0] += (x_upd:=self.width - body['shoulder_w'] / 2)
s_edge[-1].end[1] += (sh_tan * x_upd)
# Bottom dart
b_edge, b_interface = self.add_dart(
pyg.EdgeSeqFactory.dart_shape(bottom_d_width, 0.9 * bust_line),
self.edges[0],
offset=bust_point + bottom_d_width / 2
)
self.edges.substitute(0, b_edge)
# Take some fabric from side in the bottom (!: after side dart insertion)
b_edge[-1].end[0] = - (waist + bottom_d_width)
# Interfaces
self.interfaces = {
'outside': pyg.Interface(self, side_interface), # side_interface, # pyp.Interface(self, [side_interface]), #, self.edges[-3]]),
'inside': pyg.Interface(self, self.edges[-1]),
'shoulder': pyg.Interface(self, self.edges[-2]),
'bottom': pyg.Interface(self, b_interface),
# Reference to the corner for sleeve and collar projections
'shoulder_corner': pyg.Interface(
self, [self.edges[-3], self.edges[-2]]),
'collar_corner': pyg.Interface(
self, [self.edges[-2], self.edges[-1]])
}
# default placement
self.translate_by([0, body['height'] - body['head_l'] - max_len - shoulder_incl, 0])
class BodiceBackHalf(BaseBodicePanel):
"""Panel for the back of basic fitted bodice block"""
def __init__(self, name, body, design) -> None:
super().__init__(name, body, design)
# Overall measurements
self.width = body['back_width'] / 2
waist = body['waist_back_width'] / 2
# NOTE: no inclination on the side, since there is not much to begin with
waist_width = self.width if waist < self.width else waist
shoulder_incl = (sh_tan:=np.tan(np.deg2rad(body['_shoulder_incl']))) * self.width
# Adjust to make sure length is measured from the shoulder
# and not the de-fact side of the garment
back_adjustment = sh_tan * (self.width - body['shoulder_w'] / 2)
length = body['waist_line'] - back_adjustment
# Base edge loop
edge_0 = pyg.CurveEdgeFactory.curve_from_tangents(
start=[0, shoulder_incl / 4], # back a little shorter
end=[-waist_width, 0],
target_tan0=[-1, 0]
)
self.edges.append(edge_0)
self.edges.append(pyg.EdgeSeqFactory.from_verts(
edge_0.end,
[-self.width, body['waist_line'] - body['_bust_line']], # from the bottom
[-self.width, length],
[0, length + shoulder_incl], # Add some fabric for the neck (inclination of shoulders)
))
self.edges.close_loop()
# Take some fabric from the top to match the shoulder width
self.interfaces = {
'outside': pyg.Interface(
self, [self.edges[1], self.edges[2]]),
'inside': pyg.Interface(self, self.edges[-1]),
'shoulder': pyg.Interface(self, self.edges[-2]),
'bottom': pyg.Interface(self, self.edges[0]),
# Reference to the corners for sleeve and collar projections
'shoulder_corner': pyg.Interface(
self, pyg.EdgeSequence(self.edges[-3], self.edges[-2])),
'collar_corner': pyg.Interface(
self, pyg.EdgeSequence(self.edges[-2], self.edges[-1]))
}
# Bottom dart as cutout -- for straight line
if waist < self.get_width(self.edges[2].end[1] - self.edges[2].start[1]):
w_diff = waist_width - waist
side_adj = 0 if w_diff < 4 else w_diff / 6 # NOTE: don't take from sides if the difference is too small
bottom_d_width = w_diff - side_adj
bottom_d_width /= 2 # double darts
bottom_d_depth = 1. * (length - body['_bust_line']) # calculated value
bottom_d_position = body['bum_points'] / 2
# TODOLOW Avoid hardcoding for matching with the bottoms?
dist = bottom_d_position * 0.5 # Dist between darts -> dist between centers
b_edge, b_interface = self.add_dart(
pyg.EdgeSeqFactory.dart_shape(bottom_d_width, 0.9 * bottom_d_depth),
self.edges[0],
offset=bottom_d_position + dist / 2 + bottom_d_width + bottom_d_width / 2,
)
b_edge, b_interface = self.add_dart(
pyg.EdgeSeqFactory.dart_shape(bottom_d_width, bottom_d_depth),
b_edge[0],
offset=bottom_d_position - dist / 2 + bottom_d_width / 2,
edge_seq=b_edge,
int_edge_seq=b_interface,
)
self.edges.substitute(0, b_edge)
self.interfaces['bottom'] = pyg.Interface(self, b_interface)
# Remove fabric from the sides if the diff is big enough
b_edge[-1].end[0] += side_adj
# default placement
self.translate_by([0, body['height'] - body['head_l'] - length - shoulder_incl, 0])
def get_width(self, level):
return self.width
class BodiceHalf(pyg.Component):
"""Definition of a half of an upper garment with sleeves and collars"""
def __init__(self, name, body, design, fitted=True) -> None:
super().__init__(name)
design = deepcopy(design) # Recalculate freely!
# Torso
if fitted:
self.ftorso = BodiceFrontHalf(
f'{name}_ftorso', body, design).translate_by([0, 0, 30])
self.btorso = BodiceBackHalf(
f'{name}_btorso', body, design).translate_by([0, 0, -25])
else:
self.ftorso = tee.TorsoFrontHalfPanel(
f'{name}_ftorso', body, design).translate_by([0, 0, 30])
self.btorso = tee.TorsoBackHalfPanel(
f'{name}_btorso', body, design).translate_by([0, 0, -25])
# Interfaces
self.interfaces.update({
'f_bottom': self.ftorso.interfaces['bottom'],
'b_bottom': self.btorso.interfaces['bottom'],
'front_in': self.ftorso.interfaces['inside'],
'back_in': self.btorso.interfaces['inside']
})
# Sleeves/collar cuts
self.sleeve = None
self.collar_comp = None
self.eval_dep_params(body, design)
if design['fitted_shirt']['strapless']['v'] and fitted: # NOTE: Strapless design only for fitted tops
self.make_strapless(body, design)
else:
# Sleeves and collars
self.add_sleeves(name, body, design)
self.add_collars(name, body, design)
self.stitching_rules.append((
self.ftorso.interfaces['shoulder'],
self.btorso.interfaces['shoulder']
)) # tops
# Main connectivity
self.stitching_rules.append((
self.ftorso.interfaces['outside'], self.btorso.interfaces['outside'])) # sides
def eval_dep_params(self, body, design):
# Sleeves
# NOTE assuming the vertical side is the first argument
max_cwidth = self.ftorso.interfaces['shoulder_corner'].edges[0].length() - 1 # cm
min_cwidth = body['_armscye_depth']
v = design['sleeve']['connecting_width']['v']
design['sleeve']['connecting_width']['v'] = min(min_cwidth + min_cwidth * v, max_cwidth)
# Collars
# NOTE: Assuming the first is the top edge
# Width
# TODOLOW What if sleeve inclination is variable?
# NOTE: Back panel is more narrow, so using it
max_w = body['_base_sleeve_balance'] - 2 # 1 cm from default sleeve
min_w = body['neck_w']
if design['collar']['width']['v'] >= 0:
design['collar']['width']['v'] = width = pyg.utils.lin_interpolation(min_w, max_w, design['collar']['width']['v'])
else:
design['collar']['width']['v'] = width = pyg.utils.lin_interpolation(0, min_w, 1 + design['collar']['width']['v'])
# Depth
# Collar depth is given w.r.t. length.
# adjust for the shoulder inclination
tg = np.tan(np.deg2rad(body['_shoulder_incl']))
f_depth_adj = tg * (self.ftorso.get_width(0) - width / 2)
b_depth_adj = tg * (self.btorso.get_width(0) - width / 2)
max_f_len = self.ftorso.interfaces['collar_corner'].edges[1].length() - tg * self.ftorso.get_width(0) - 1 # cm
max_b_len = self.btorso.interfaces['collar_corner'].edges[1].length() - tg * self.btorso.get_width(0) - 1 # cm
design['collar']['f_strapless_depth'] = {}
design['collar']['f_strapless_depth']['v'] = min(
design['collar']['fc_depth']['v'] * body['_bust_line'],
max_f_len)
design['collar']['fc_depth']['v'] = design['collar']['f_strapless_depth']['v'] + f_depth_adj
design['collar']['b_strapless_depth'] = {}
design['collar']['b_strapless_depth']['v'] = min(
design['collar']['bc_depth']['v'] * body['_bust_line'],
max_b_len)
design['collar']['bc_depth']['v'] = design['collar']['b_strapless_depth']['v'] + b_depth_adj
def add_sleeves(self, name, body, design):
self.sleeve = sleeves.Sleeve(
name, body, design,
front_w=self.ftorso.get_width,
back_w=self.btorso.get_width
)
# self.sleeve = sleeves.OnePieceSleeve(
# name, body, design,
# front_w=self.ftorso.get_width,
# back_w=self.btorso.get_width
# )
_, f_sleeve_int = pyg.ops.cut_corner(
self.sleeve.interfaces['in_front_shape'].edges,
self.ftorso.interfaces['shoulder_corner'],
verbose=self.verbose
)
_, b_sleeve_int = pyg.ops.cut_corner(
self.sleeve.interfaces['in_back_shape'].edges,
self.btorso.interfaces['shoulder_corner'],
verbose=self.verbose
)
if not design['sleeve']['sleeveless']['v']:
# Ordering
bodice_sleeve_int = pyg.Interface.from_multiple(
f_sleeve_int.reverse(with_edge_dir_reverse=True),
b_sleeve_int.reverse(),
)
self.stitching_rules.append((
self.sleeve.interfaces['in'],
bodice_sleeve_int
))
# NOTE: This is a heuristic tuned for arm poses 30 deg-60 deg
# used in the dataset
# FIXME Needs a better general solution
gap = -1 - body['arm_pose_angle'] / 10
self.sleeve.place_by_interface(
self.sleeve.interfaces['in'],
bodice_sleeve_int,
gap=gap,
alignment='top',
)
# Add edge labels
f_sleeve_int.edges.propagate_label(f'{self.name}_armhole')
b_sleeve_int.edges.propagate_label(f'{self.name}_armhole')
def add_collars(self, name, body, design):
# Front
collar_type = getattr(
collars,
str(design['collar']['component']['style']['v']),
collars.NoPanelsCollar
)
self.collar_comp = collar_type(name, body, design)
# Project shape
_, fc_interface = pyg.ops.cut_corner(
self.collar_comp.interfaces['front_proj'].edges,
self.ftorso.interfaces['collar_corner'],
verbose=self.verbose
)
_, bc_interface = pyg.ops.cut_corner(
self.collar_comp.interfaces['back_proj'].edges,
self.btorso.interfaces['collar_corner'],
verbose=self.verbose
)
# Add stitches/interfaces
if 'bottom' in self.collar_comp.interfaces:
self.stitching_rules.append((
pyg.Interface.from_multiple(fc_interface, bc_interface),
self.collar_comp.interfaces['bottom']
))
# Upd front interfaces accordingly
if 'front' in self.collar_comp.interfaces:
self.interfaces['front_collar'] = self.collar_comp.interfaces['front']
self.interfaces['front_in'] = pyg.Interface.from_multiple(
self.ftorso.interfaces['inside'], self.interfaces['front_collar']
)
if 'back' in self.collar_comp.interfaces:
self.interfaces['back_collar'] = self.collar_comp.interfaces['back']
self.interfaces['back_in'] = pyg.Interface.from_multiple(
self.btorso.interfaces['inside'], self.interfaces['back_collar']
)
# Add edge labels
fc_interface.edges.propagate_label(f'{self.name}_collar')
bc_interface.edges.propagate_label(f'{self.name}_collar')
def make_strapless(self, body, design):
out_depth = design['sleeve']['connecting_width']['v']
f_in_depth = design['collar']['f_strapless_depth']['v']
b_in_depth = design['collar']['b_strapless_depth']['v']
# Shoulder adjustment for the back
# TODOLOW Shoulder adj evaluation should be a function
shoulder_angle = np.deg2rad(body['_shoulder_incl'])
sleeve_balance = body['_base_sleeve_balance'] / 2
back_w = self.btorso.get_width(0)
shoulder_adj = np.tan(shoulder_angle) * (back_w - sleeve_balance)
out_depth -= shoulder_adj
# Upd back
self._adjust_top_level(self.btorso, out_depth, b_in_depth)
# Front depth determined by ~compensating for lenght difference
len_back = self.btorso.interfaces['outside'].edges.length()
len_front = self.ftorso.interfaces['outside'].edges.length()
self._adjust_top_level(self.ftorso, out_depth, f_in_depth, target_remove=(len_front - len_back))
# Placement
# NOTE: The commented line places the top a bit higher, increasing the chanced of correct drape
# Surcumvented by attachment constraint, so removed for nicer alignment in asymmetric garments
# self.translate_by([0, out_depth - body['_armscye_depth'] * 0.75, 0]) # adjust for better localisation
# Add a label
self.ftorso.interfaces['shoulder'].edges.propagate_label('strapless_top')
self.btorso.interfaces['shoulder'].edges.propagate_label('strapless_top')
def _adjust_top_level(self, panel, out_level, in_level, target_remove=None):
"""Crops the top of the bodice front/back panel for strapless style
* out_length_diff -- if set, determined the length difference that should be compensates
after cutting the depth
"""
# TODOLOW Should this be the panel's function?
panel_top = panel.interfaces['shoulder'].edges[0]
min_y = min(panel_top.start[1], panel_top.end[1])
# Order vertices
ins, out = panel_top.start, panel_top.end
if panel_top.start[1] < panel_top.end[1]:
ins, out = out, ins
# Inside is a simple vertical line and can be adjusted by chaning Y value
ins[1] = min_y - in_level
# Outside could be inclined, so needs further calculations
outside_edge = panel.interfaces['outside'].edges[-1]
bot, top = outside_edge.start, outside_edge.end
if bot is out:
bot, top = top, bot
if target_remove is not None:
# Adjust the depth to remove this length exactly
angle_sin = abs(out[1] - bot[1]) / outside_edge.length()
curr_remove = out_level / angle_sin
length_diff = target_remove - curr_remove
adjustment = length_diff * angle_sin
out_level += adjustment
angle_cotan = abs(out[0] - bot[0]) / abs(out[1] - bot[1])
out[0] -= out_level * angle_cotan
out[1] = min_y - out_level
def length(self):
return self.btorso.length()
class Shirt(pyg.Component):
"""Panel for the front of upper garments with darts to properly fit it to
the shape"""
def __init__(self, body, design, fitted=False) -> None:
name_with_params = f"{self.__class__.__name__}"
super().__init__(name_with_params)
design = self.eval_dep_params(design)
self.right = BodiceHalf(f'right', body, design, fitted=fitted)
self.left = BodiceHalf(
f'left', body,
design['left'] if design['left']['enable_asym']['v'] else design,
fitted=fitted).mirror()
self.stitching_rules.append((self.right.interfaces['front_in'],
self.left.interfaces['front_in']))
self.stitching_rules.append((self.right.interfaces['back_in'],
self.left.interfaces['back_in']))
# Adjust interface ordering for correct connectivity
self.interfaces = { # Bottom connection
'bottom': pyg.Interface.from_multiple(
self.right.interfaces['f_bottom'].reverse(),
self.left.interfaces['f_bottom'],
self.left.interfaces['b_bottom'].reverse(),
self.right.interfaces['b_bottom'],)
}
def eval_dep_params(self, design):
# NOTE: Support for full collars with partially strapless top
# or combination of paneled collar styles
# requres further development
# TODOLOW enable this one to work
if design['left']['enable_asym']['v']:
# Force no collars since they are not compatible with each other
design = deepcopy(design)
design['collar']['component']['style']['v'] = None
design['left']['collar']['component'] = dict(style=dict(v=None))
# Left-right design compatibility
design['left']['shirt'].update(length={})
design['left']['shirt']['length']['v'] = design['shirt']['length']['v']
design['left']['collar'].update(fc_depth={}, bc_depth={})
design['left']['collar']['fc_depth']['v'] = design['collar']['fc_depth']['v']
design['left']['collar']['bc_depth']['v'] = design['collar']['bc_depth']['v']
return design
def length(self):
return self.right.length()
class FittedShirt(Shirt):
"""Creates fitted shirt
NOTE: Separate class is used for selection convenience.
Even though most of the processing is the same
(hence implemented with the same components except for panels),
design parametrization differs significantly.
With that, we decided to separate the top level names
"""
def __init__(self, body, design) -> None:
super().__init__(body, design, fitted=True)
class PrincessSeamShirt(pyg.Component):
"""Class representing a shirt with princess seams."""
def __init__(self, body, design):
# Initialize the base Component class
super().__init__("PrincessSeamShirt")
# Evaluate design parameters
self.eval_dep_params(design)
# Create front and back panels
self.front_panel = self.create_front_panel(body, design)
self.back_panel = self.create_back_panel(body, design)
# Add panels as components
self.add_component(self.front_panel)
self.add_component(self.back_panel)
# Define stitching rules
self.stitching_rules = [
# Side seams
(self.front_panel.interfaces['side_seam'], self.back_panel.interfaces['side_seam']),
# Shoulder seams
(self.front_panel.interfaces['shoulder_seam'], self.back_panel.interfaces['shoulder_seam']),
# Front princess seams
(self.front_panel.interfaces['princess_seam_left'], self.front_panel.left_princess_panel.interfaces['princess_seam']),
(self.front_panel.interfaces['princess_seam_right'], self.front_panel.right_princess_panel.interfaces['princess_seam']),
# Back princess seams
(self.back_panel.interfaces['princess_seam_left'], self.back_panel.left_princess_panel.interfaces['princess_seam']),
(self.back_panel.interfaces['princess_seam_right'], self.back_panel.right_princess_panel.interfaces['princess_seam']),
]
def eval_dep_params(self, design):
# Placeholder for evaluating dependent design parameters
pass
def create_front_panel(self, body, design):
# Create the front panel
front_panel = pyg.Panel("FrontPanel")
# Body measurements
bust = body['bust']
waist = body['waist']
hips = body['hips']
shoulder_width = body['shoulder_width']
neck_width = body['neck_width']
neck_depth = body['front_neck_depth']
# Calculate panel dimensions
panel_width = bust / 2
princess_seam_pos = bust / 8
# Define key points
points = [
[0, 0], # Bottom left
[0, hips], # Hip point
[0, waist], # Waist point
[princess_seam_pos, bust], # Left bust point
[shoulder_width / 2, bust + neck_depth], # Left shoulder
[panel_width - shoulder_width / 2, bust + neck_depth], # Right shoulder
[panel_width - princess_seam_pos, bust], # Right bust point
[panel_width, waist], # Right waist point
[panel_width, hips], # Right hip point
[panel_width, 0], # Bottom right
]
# Create edges
edges = pyg.EdgeSequence()
for i in range(len(points) - 1):
edges.append(pyg.Edge(points[i], points[i + 1]))
edges.append(pyg.Edge(points[-1], points[0])) # Close shape
# Assign edges to panel
front_panel.edges = edges
# Define interfaces
front_panel.interfaces = {
'side_seam': pyg.Interface(front_panel, [edges[0], edges[8]]),
'shoulder_seam': pyg.Interface(front_panel, [edges[4], edges[5]]),
'princess_seam_left': pyg.Interface(front_panel, [edges[2]]),
'princess_seam_right': pyg.Interface(front_panel, [edges[7]]),
}
# Create princess panels
front_panel.left_princess_panel = self.create_princess_panel(front_panel, edges[2], "FrontLeftPrincessPanel")
front_panel.right_princess_panel = self.create_princess_panel(front_panel, edges[7], "FrontRightPrincessPanel")
return front_panel
def create_back_panel(self, body, design):
# Create the back panel
back_panel = pyg.Panel("BackPanel")
# Body measurements
bust = body['bust']
waist = body['waist']
hips = body['hips']
shoulder_width = body['shoulder_width']
neck_width = body['neck_width']
neck_depth = body['back_neck_depth']
# Calculate panel dimensions
panel_width = bust / 2
princess_seam_pos = bust / 8
# Define key points
points = [
[0, 0], # Bottom left
[0, hips], # Hip point
[0, waist], # Waist point
[princess_seam_pos, bust], # Left bust point
[shoulder_width / 2, bust + neck_depth], # Left shoulder
[panel_width - shoulder_width / 2, bust + neck_depth], # Right shoulder
[panel_width - princess_seam_pos, bust], # Right bust point
[panel_width, waist], # Right waist point
[panel_width, hips], # Right hip point
[panel_width, 0], # Bottom right
]
# Create edges
edges = pyg.EdgeSequence()
for i in range(len(points) - 1):
edges.append(pyg.Edge(points[i], points[i + 1]))
edges.append(pyg.Edge(points[-1], points[0])) # Close shape
# Assign edges to panel
back_panel.edges = edges
# Define interfaces
back_panel.interfaces = {
'side_seam': pyg.Interface(back_panel, [edges[0], edges[8]]),
'shoulder_seam': pyg.Interface(back_panel, [edges[4], edges[5]]),
'princess_seam_left': pyg.Interface(back_panel, [edges[2]]),
'princess_seam_right': pyg.Interface(back_panel, [edges[7]]),
}
# Create princess panels
back_panel.left_princess_panel = self.create_princess_panel(back_panel, edges[2], "BackLeftPrincessPanel")
back_panel.right_princess_panel = self.create_princess_panel(back_panel, edges[7], "BackRightPrincessPanel")
return back_panel
def create_princess_panel(self, parent_panel, seam_edge, panel_name):
# Create a princess panel along the seam
princess_panel = pyg.Panel(panel_name)
# Seam edge points
start_point = seam_edge.start
end_point = seam_edge.end
# Panel offset
offset = 5 # Width of the princess panel
# Calculate new points
offset_start = [start_point[0] + offset, start_point[1]]
offset_end = [end_point[0] + offset, end_point[1]]
# Create edges
edges = pyg.EdgeSequence()
edges.append(pyg.Edge(start_point, end_point)) # Seam edge
edges.append(pyg.Edge(end_point, offset_end)) # Top edge
edges.append(pyg.Edge(offset_end, offset_start)) # Outer edge
edges.append(pyg.Edge(offset_start, start_point)) # Bottom edge
# Close shape
edges.close_loop()
# Assign edges to panel
princess_panel.edges = edges
# Define interfaces
princess_panel.interfaces = {
'princess_seam': pyg.Interface(princess_panel, [edges[0]]),
'outer_seam': pyg.Interface(princess_panel, [edges[2]]),
}
return princess_panel

View 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)

View 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()

View File

@@ -0,0 +1,121 @@
import math
import numpy as np
import pygarment as pyg
from assets.garment_programs.base_classes import BaseBottoms
from assets.garment_programs import skirt_paneled as skirts
class Insert(pyg.Panel):
def __init__(self, id, width=30, depth=30) -> None:
super().__init__(f'Insert_{id}')
self.edges = pyg.EdgeSeqFactory.from_verts(
[0, 0],
[width/2, depth],
[width, 0], loop=True)
self.interfaces = [
pyg.Interface(self, self.edges[:2])
]
self.top_center_pivot()
self.center_x()
class GodetSkirt(BaseBottoms):
def __init__(self, body, design, rise=None) -> None:
super().__init__(body, design, rise=rise)
gdesign = design['godet-skirt']
ins_w = gdesign['insert_w']['v']
ins_depth = gdesign['insert_depth']['v']
base_skirt = getattr(skirts, gdesign['base']['v'])
# NOTE: godets currently don't like slits on the front/back
# of the base skirt => Forcing to remove any slits
self.base = base_skirt(body, design, rise=rise, slit=False)
bintr = self.base.interfaces['bottom']
for edge, panel in zip(bintr.edges, bintr.panel):
self.inserts(
edge, panel, ins_w, ins_depth,
num_inserts=gdesign['num_inserts']['v'] / len(bintr),
cuts_dist=gdesign['cuts_distance']['v'])
self.interfaces = {
'top': self.base.interfaces['top']
}
def inserts(
self, bottom_edge, panel, ins_w, ins_depth,
num_inserts=3, cuts_dist=0):
"""Create insert panels, add cuts to the skirt panel,
and connect created insert panels with them
"""
num_inserts = int(num_inserts)
bottom_len = bottom_edge.length()
pbbox = panel.bbox3D()
z_transl = panel.translation[-1] + np.sign(panel.translation[-1]) * 5
y_base = pbbox[0][1] # min Y
x_shift = (pbbox[0][0] + pbbox[1][0]) / 2
cut_width = (bottom_len - cuts_dist * num_inserts) / num_inserts
if cut_width < 1:
cut_width = 1 # 1 cm
cuts_dist_req = cuts_dist
cuts_dist = (bottom_len - cut_width * num_inserts) / num_inserts
if self.verbose:
print(f'{self.__class__.__name__}::WARNING:: Cannot place {num_inserts} cuts '
f'with requested distance between cuts ({cuts_dist_req}). '
f'Using the maximum possible distance ({cuts_dist})')
# Insert panels
insert = Insert(0, width=ins_w, depth=ins_depth).translate_by([
x_shift - num_inserts * ins_w / 2 + ins_w / 2, y_base + ins_depth, z_transl])
self.subs += pyg.ops.distribute_horisontally(
insert, num_inserts, -ins_w, 'ins_' + panel.name)
# make appropriate cuts and stitches
side_len = math.sqrt((ins_w / 2)**2 + ins_depth**2) # should be the same on the skirt and the insert
if side_len > cut_width / 2: # Normal case
cut_depth = math.sqrt(side_len**2 - (cut_width / 2)**2)
else:
old_cut_width = cut_width
cut_depth = 1
cut_width = 2 * math.sqrt(side_len**2 - cut_depth**2)
if self.verbose:
print(f'{self.__class__.__name__}::WARNING::Requested cut_width ({old_cut_width:.2f}) '
'is too wide for given inserts. '
f'Using the maximum possible width ({cut_width:.2f})')
cut_shape = pyg.EdgeSeqFactory.from_verts(
[0, 0], [cut_width / 2, cut_depth], [cut_width, 0])
right = z_transl < 0 # NOTE: heuristic corresponding to skirts in our collection
for i in range(num_inserts):
offset = cut_width / 2 + (cuts_dist / 2 if i == 0 else cuts_dist) # start_offest + i * stride
new_bottom, cutted, _ = pyg.ops.cut_into_edge(
cut_shape, bottom_edge, offset=offset, right=right)
panel.edges.substitute(bottom_edge, new_bottom)
bottom_edge = new_bottom[-1] # New edge that needs to be cutted -- on the next step
cut_interface = pyg.Interface(panel, cutted)
if right:
cut_interface.reverse()
self.stitching_rules.append(
(self.subs[-1-i if right else -(num_inserts-i)].interfaces[0],
cut_interface))
def get_rise(self):
return self.base.get_rise()
def length(self):
return self.base.length()

View File

@@ -0,0 +1,195 @@
from assets.garment_programs.tee import *
from assets.garment_programs.godet import *
from assets.garment_programs.bodice import *
from assets.garment_programs.pants import *
from assets.garment_programs.bands import *
from assets.garment_programs.skirt_paneled import *
from assets.garment_programs.skirt_levels import *
from assets.garment_programs.circle_skirt import *
from assets.garment_programs.sleeves import *
import yaml
class TotalLengthError(BaseException):
"""Error indicating that the total length of a garment goes beyond
the floor length for a given person"""
pass
class IncorrectElementConfiguration(BaseException):
"""Error indicating that given pattern is an empty garment"""
pass
class MetaGarment(pyg.Component):
"""Meta garment component
Depending on parameter values it can generate sewing patterns
for various dresses and jumpsuit styles and fit them to the body
measurements
"""
def __init__(self, name, body, design) -> None:
super().__init__(name)
self.body = body
self.design = design
# with open('assets/bodies/mean_all_full.yaml', 'w') as f:
# yaml.dump(body, f, default_flow_style=False)
# Elements
self.upper_name = design['meta']['upper']['v']
self.lower_name = design['meta']['bottom']['v']
self.belt_name = design['meta']['wb']['v']
# Upper garment
if self.upper_name:
upper = globals()[self.upper_name]
self.subs = [upper(body, design)]
# Set a label
self.subs[-1].set_panel_label('body', overwrite=False)
#Pan up a bit
self.subs[-1].translate_by((0, 5, 0))
# Here are all the garments that are not connected to have a belt
if self.belt_name == None:
if design['meta']['connected']['v'] == False and design['meta']['bottom']['v'] != None:
if design['meta']['bottom']['v'] != 'Pants':
design['meta']['wb']['v'] = "FittedWB"
design['waistband']['waist']['v'] = 0.7
design['waistband']['width']['v'] = 0.1
if design['meta']['bottom']['v'] == 'Pants':
design['meta']['wb']['v'] = 'FittedWB'
design['waistband']['waist']['v'] = 1
design['waistband']['width']['v'] = 0.2
self.belt_name = design['meta']['wb']['v']
# Define Lower garment
if self.lower_name:
Lower_class = globals()[self.lower_name]
# NOTE: full rise for fitted tops
Lower = Lower_class(body, design, rise=1. if self.upper_name and 'Fitted' in self.upper_name else None)
else:
Lower = None
# Belt (or not)
# TODO Adapt the rise of the lower garment to the width of the belt for correct matching
if self.belt_name:
Belt_class = globals()[self.belt_name]
# Adjust rise to match the Lower garment if needed
Belt = Belt_class(body, design, Lower.get_rise() if Lower else 1.)
self.subs.append(Belt)
# Place below the upper garment
if len(self.subs) > 1:
self.subs[-1].place_by_interface(
self.subs[-1].interfaces['top'],
self.subs[-2].interfaces['bottom'],
gap=5
)
if design['meta']['connected']['v'] : # If you need to connect, you need to connect the belt to the top
self.stitching_rules.append(
(self.subs[-2].interfaces['bottom'],
self.subs[-1].interfaces['top']))
# Add waist label
self.subs[-1].interfaces['top'].edges.propagate_label('lower_interface')
# Set panel segmentation labels
self.subs[-1].set_panel_label('body', overwrite=False)
if self.lower_name:
self.subs.append(Lower)
# Place below the upper garment or self.wb
if len(self.subs) > 1:
self.subs[-1].place_by_interface(
self.subs[-1].interfaces['top'],
self.subs[-2].interfaces['bottom'],
gap=5
)
#There must be a belt now, so here's how to connect the belt to the bottom
self.stitching_rules.append(
(self.subs[-2].interfaces['bottom'],
self.subs[-1].interfaces['top']))
#To deal with the simulation impact caused by the lack of connection, the main thing is to adjust the position and all the next translationby is to simulate better and reduce the problem situation.
# The specific tranlateby values are mainly tried
if not design['meta']['connected']['v']:
self.handle_disconnected_position_influence(design)
# Add waist label
if not self.belt_name:
self.subs[-1].interfaces['top'].edges.propagate_label('lower_interface')
# Set panel segmentation labels
self.subs[-1].set_panel_label('leg', overwrite=False)
def handle_disconnected_position_influence(self, design):
'''deal with the influence of disconnected garments on the simulation by translateby .
which is value is tried out'''
self.subs[-1].translate_by((0, 6, 0))
# Gets a specific clothing object
pant_flag = False
pant = None
circleskirt = None
circleskirt_flag = False
shirt = None
shirt_flag = False
for sub in self.subs:
if isinstance(sub, Pants):
pant_flag = True
pant = sub
if isinstance(sub, SkirtCircle):
circleskirt = sub
circleskirt_flag = True
if isinstance(sub, Shirt):
shirt = sub
shirt_flag = True
if pant_flag and shirt_flag:
# If the top is short<=1.2, you need to pan the pants downward.
if design['shirt']['length']['v'] <= 1.2:
# pass
pant.translate_by((0, -13, 0))
# For clothes that are too long, pull them up
if design['shirt']['length'][
'v'] > 1.2:
pant.translate_by((0, 25, 0))
translate_d = 8
shirt.right.ftorso.translate_by((0, 0, translate_d))
shirt.right.btorso.translate_by((0, 0, -translate_d))
shirt.left.ftorso.translate_by((0, 0, translate_d))
shirt.left.btorso.translate_by((0, 0, -translate_d))
# For the handling that is not underwear
if design['meta']['bottom']['v'] is not None and design['meta']['bottom']['v'] != 'Pants':
translate_d = 10
bottom_garment = self.subs[-1]
# if design['meta']['bottom']['v'] != "SkirtManyPanels":
# bottom_garment.translate_by((0, 0, 0))
# Deal with a single class first, for multiple skirts such as level, this is not processed, and later, at present, multi-layer group skirts do not need to be processed
if (design['meta']['bottom']['v'] == "Skirt2 " or design['meta']['bottom']['v'] == "SkirtCircle"
or design['meta']['bottom']['v'] == "AssymmSkirtCircle"
or design['meta']['bottom']['v'] == "PencilSkirt"):
bottom_garment.front.translate_by((0, 0, translate_d))
bottom_garment.back.translate_by((0, 0, -translate_d))
bottom_garment.translate_by((0, -8, 0))
# For the handling of the pants
if pant_flag:
pant.translate_by((0, -8, 0))
def assert_total_length(self, tol=1):
"""Check the total length of components"""
# Check that the total length of the components are less that body height
length = self.length()
floor = self.body['height'] - self.body['head_l']
if length > floor + tol:
raise TotalLengthError(f'{self.__class__.__name__}::{self.name}::ERROR:'
f':Total length {length} exceeds the floor length {floor}')
# TODO these checks don't require initialization of the pattern!
def assert_non_empty(self, filter_belts=True):
"""Check that the garment is non-empty
* filter_wb -- if set, then garments consisting only of waistbands are considered empty
"""
if not self.upper_name and not self.lower_name:
if filter_belts or not self.belt_name:
raise IncorrectElementConfiguration()
def assert_skirt_waistband(self):
"""Check if a generated heavy skirt is created with a waistband"""
if self.lower_name and self.lower_name in ['SkirtCircle', 'AsymmSkirtCircle', 'SkirtManyPanels']:
if not (self.belt_name or self.upper_name):
raise IncorrectElementConfiguration()

View 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()

View File

@@ -0,0 +1,64 @@
"""A decorative shapes"""
import pygarment as pyg
def sample_arc(curve, length, stride, n_points, shift=0):
ts = [(shift + i*stride) / length for i in range(n_points)]
verts = [curve.point(t) for t in ts]
for i in range(len(verts)):
verts[i] = [verts[i].real, verts[i].imag]
return verts
def Sun(width, depth, n_rays=8, d_rays=5, **kwargs):
"""Sun-like mark"""
# Outer arc
out_arc = pyg.CircleEdgeFactory.from_three_points(
[0, 0], [width, 0], [width/2, depth]
)
in_arc = pyg.CircleEdgeFactory.from_three_points(
[d_rays, 0], [width - d_rays, 0], [width/2, depth - d_rays]
)
out_curve = out_arc.as_curve()
in_curve = in_arc.as_curve()
# Sample with stride
out_stride = out_arc.length() / n_rays
in_stride = in_arc.length() / n_rays
out_verts = sample_arc(out_curve, out_arc.length(),
out_stride, n_rays, out_stride / 2)
in_verts = sample_arc(in_curve, in_arc.length(), in_stride, n_rays + 1, 0)
# Mix the vertices in the right order
verts = out_verts
for i in range(len(in_verts)):
verts.insert(i*2, in_verts[i])
shape = pyg.EdgeSeqFactory.from_verts(*verts)
return shape, shape
def SIGGRAPH_logo(width, depth=None, **kwargs):
"""Shape of SIGGRAPH Logo (split vertically)"""
filename='./assets/img/siggraph_logo_thick_connection.svg' # NOTE assumes the script is run from the root
# TODOLOW path w.r.t. current file
left_seq, right_seq = pyg.EdgeSeqFactory.halfs_from_svg(
filename, target_height=width)
return left_seq, right_seq
def SVGFile(width, filename, depth=None, **kwargs):
"""Shape loaded from any svg file:
The shape is expected to consist of non-nested loops
each passing through OY once
"""
left_seq, right_seq = pyg.EdgeSeqFactory.halfs_from_svg(
filename, target_height=width)
return left_seq, right_seq

View File

@@ -0,0 +1,151 @@
from assets.garment_programs.circle_skirt import *
from assets.garment_programs.skirt_paneled import *
from copy import deepcopy
class SkirtLevels(BaseBottoms):
"""Skirt constiting of multuple stitched skirts"""
def __init__(self, body, design, rise=None) -> None:
super().__init__(body, design, rise=rise)
ldesign = design['levels-skirt']
lbody = deepcopy(body) # We will modify the values, so need a copy
n_levels = ldesign['num_levels']['v']
ruffle = ldesign['level_ruffle']['v']
# Adjust length to the common denominators
self.eval_length(ldesign, body)
# Definitions
self.rise = ldesign['rise']['v'] if rise is None else rise
base_skirt_class = globals()[ldesign['base']['v']]
self.subs.append(base_skirt_class(
body,
design,
length=self.base_len,
rise=self.rise,
slit=False))
if (hasattr(base := self.subs[0], 'design')
and 'low_angle' in base.design):
self.angle = base.design['low_angle']['v']
else:
self.angle = 0
# Place the levels
level_skirt_class = globals()[ldesign['level']['v']]
for i in range(n_levels):
# Adjust the mesurement to trick skirts into producing correct width
# TODOLOW More elegant overwrite
lbody['waist'] = ruffle * self.subs[-1].interfaces['bottom'].edges.length()
lbody['waist_back_width'] = ruffle * self.subs[-1].interfaces['bottom_b'].edges.length()
self.subs.append(level_skirt_class(
lbody,
design,
tag=str(i),
length=self.level_len,
slit=False,
top_ruffles=False))
# Placement
# Rotation if base is assymetric
self.subs[-1].rotate_by(R.from_euler(
'XYZ', [0, 0, -self.angle], degrees=True))
self.subs[-1].place_by_interface(
self.subs[-1].interfaces['top'],
self.subs[-2].interfaces['bottom'],
gap=5
)
# Stitch
self.stitching_rules.append((
self.subs[-2].interfaces['bottom'],
self.subs[-1].interfaces['top']
))
self.interfaces = {
'top': self.subs[0].interfaces['top']
}
def eval_length(self, ldesign, body):
# With convertion to absolute values
total_length = ldesign['length']['v'] * body['_leg_length']
self.base_len = total_length * ldesign['base_length_frac']['v']
self.level_len = (total_length - self.base_len) / ldesign['num_levels']['v']
# Add hip_line (== zero length)
self.base_len = body['hips_line'] * ldesign['rise']['v'] + self.base_len
class SkirtLayers(BaseBottoms):
"""Skirt consisting of multiple layered skirts stitched at the waistline"""
def __init__(self, body, design, rise=None):
super().__init__(body, design, rise=rise)
ldesign = design['layers-skirt']
lbody = deepcopy(body) # We will modify the values, so need a copy
n_layers = ldesign['num_layers']['v']
ruffle = ldesign['layer_ruffle']['v']
# Definitions
self.rise = ldesign['rise']['v'] if rise is None else rise
base_skirt_class = globals()[ldesign['base']['v']]
total_length = ldesign['length']['v'] * body['_leg_length']
layer_lengths = self.eval_layer_lengths(ldesign, total_length, n_layers)
# Place the levels
self.layers = []
for i in range(n_layers):
# Adjust the measurements to produce correct width with ruffle
waist_multiplier = 1 + ruffle * i
lbody['waist'] = self.body['waist'] * waist_multiplier
lbody['waist_back_width'] = self.body['waist_back_width'] * waist_multiplier
layer_length = layer_lengths[i]
skirt_layer = base_skirt_class(
lbody,
design,
tag=str(i),
length=layer_length,
rise=self.rise,
slit=False,
top_ruffles=False)
# Place the layer at the waistline
skirt_layer.translate_by([0, self.body['_waist_level'], 0])
self.layers.append(skirt_layer)
# Interfaces
self.interfaces = {
'top': self.layers[0].interfaces['top'],
'bottom': self.layers[-1].interfaces['bottom']
}
# Stitching rules: stitch each outer layer's top interface
# to the 'top' interface of the component (waistline)
for layer in self.layers[1:]:
self.stitching_rules.append(
(self.interfaces['top'], layer.interfaces['top'])
)
# Add layers to subcomponents
self.subs.extend(self.layers)
for id,layer in enumerate(self.layers):
layer.front.translate_by([0,0, 5*id])
layer.back.translate_by([0, 0, -5*id])
def eval_layer_lengths(self, ldesign, total_length, n_layers):
"""Calculate lengths for each layer"""
if 'layer_lengths' in ldesign:
# If specific lengths are provided for each layer
layer_lengths = [ldesign['layer_lengths'][i]['v'] * total_length for i in range(n_layers)]
else:
# Distribute the total length among layers, possibly making outer layers longer
base_length = total_length / n_layers
layer_lengths = [base_length * (1 + 0.1 * i) for i in range(n_layers)]
return layer_lengths

View File

@@ -0,0 +1,512 @@
import numpy as np
from scipy.spatial.transform import Rotation as R
import pygarment as pyg
from assets.garment_programs.base_classes import StackableSkirtComponent
from assets.garment_programs.base_classes import BaseBottoms
from assets.garment_programs import shapes
class SkirtPanel(pyg.Panel):
"""One panel of a panel skirt with ruffles on the waist"""
def __init__(self,
name,
waist_length=70, length=70,
ruffles=1,
match_top_int_to=None,
bottom_cut=0,
flare=0
) -> None:
super().__init__(name)
base_width = waist_length
top_width = base_width * ruffles
low_width = top_width + 2*flare
x_shift_top = (low_width - top_width) / 2 # to account for flare at the bottom
# define edge loop
self.right = pyg.EdgeSeqFactory.side_with_cut(
[0, 0],
[x_shift_top, length],
start_cut=bottom_cut / length) if bottom_cut else pyg.EdgeSequence(
pyg.Edge([0, 0], [x_shift_top, length]))
self.waist = pyg.Edge(
self.right[-1].end, [x_shift_top + top_width, length])
self.left = pyg.EdgeSeqFactory.side_with_cut(
self.waist.end, [low_width, 0],
end_cut=bottom_cut / length) if bottom_cut else pyg.EdgeSequence(
pyg.Edge(self.waist.end, [low_width, 0]))
self.bottom = pyg.Edge(self.left[-1].end, self.right[0].start)
# define interface
self.interfaces = {
'right': pyg.Interface(self, self.right[-1]),
'top': pyg.Interface(self, self.waist,
ruffle=self.waist.length() / match_top_int_to if match_top_int_to is not None else ruffles
).reverse(True),
'left': pyg.Interface(self, self.left[0]),
'bottom': pyg.Interface(self, self.bottom)
}
# Single sequence for correct assembly
self.edges = self.right
self.edges.append(self.waist) # on the waist
self.edges.append(self.left)
self.edges.append(self.bottom)
# default placement
self.top_center_pivot()
self.center_x() # Already know that this panel should be centered over Y
class ThinSkirtPanel(pyg.Panel):
"""One panel of a panel skirt"""
def __init__(self, name, top_width=10, bottom_width=20, length=70, b_curvature=0) -> None:
super().__init__(name)
# define edge loop
self.flare = (bottom_width - top_width) / 2
self.edges = pyg.EdgeSeqFactory.from_verts(
[0, 0], [self.flare, length], [self.flare + top_width, length],
[self.flare * 2 + top_width, 0])
if pyg.utils.close_enough(b_curvature, 0):
self.edges.close_loop()
else:
self.edges.append(
pyg.CircleEdgeFactory.from_three_points(
self.edges[-1].end,
self.edges[0].start,
[0.5, b_curvature],
relative=True
)
)
# w.r.t. top left point
self.set_pivot(self.edges[0].end)
self.interfaces = {
'right': pyg.Interface(self, self.edges[0]),
'top': pyg.Interface(self, self.edges[1]),
'left': pyg.Interface(self, self.edges[2]),
'bottom': pyg.Interface(self, self.edges[-1])
}
class FittedSkirtPanel(pyg.Panel):
"""Fitted panel for a pencil skirt"""
def __init__(
self, name, body, design,
waist, hips, hips_depth, # TODOLOW Half measurement instead of a quarter
length,
hipline_ext=1,
dart_position=None, dart_frac=0.5, double_dart=False,
match_top_int_to=None,
slit=0, left_slit=0, right_slit=0,
side_cut=None, flip_side_cut=False
) -> None:
""" Fitted panel for a pencil skirt
Body/design values that differ between front and back panels are supplied as parameters,
the rest are taken from the body and design dictionaries
"""
super().__init__(name)
# Shared params
low_angle = design['low_angle']['v']
hip_side_incl = np.deg2rad(body['_hip_inclination'])
flare = design['flare']['v']
low_width = body['hips'] * (flare - 1) / 4 + hips # Distribute the difference equally
# between front and back
# adjust for a rise
adj_hips_depth = hips_depth * hipline_ext
dart_depth = hips_depth * dart_frac
dart_depth = max(dart_depth - (hips_depth - adj_hips_depth), 0)
# amount of extra fabric
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) * adj_hips_depth
# Small difference
if hw_shift > w_diff:
hw_shift = w_diff
# Adjust the bottom edge to the desired angle
angle_shift = np.tan(np.deg2rad(low_angle)) * low_width
# --- Edges definition ---
# Right
if pyg.utils.close_enough(flare, 1): # skip optimization
right_bottom = pyg.Edge(
[hips - low_width, angle_shift],
[0, length]
)
else:
right_bottom = pyg.CurveEdgeFactory.curve_from_tangents(
[hips - low_width, angle_shift],
[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 + adj_hips_depth],
target_tan0=np.array([0, 1]),
initial_guess=[0.5, 0]
)
right = pyg.EdgeSequence(right_bottom, right_top)
# top
top = pyg.Edge(right[-1].end, [hips * 2 - hw_shift, length + adj_hips_depth])
# left
left_top = pyg.CurveEdgeFactory.curve_from_tangents(
top.end,
[hips * 2, length],
target_tan1=np.array([0, -1]),
initial_guess=[0.5, 0]
)
if pyg.utils.close_enough(flare, 1): # skip optimization for straight skirt
left_bottom = pyg.Edge(
left_top.end,
[hips + low_width, -angle_shift],
)
else:
left_bottom = pyg.CurveEdgeFactory.curve_from_tangents(
left_top.end,
[hips + low_width, -angle_shift],
target_tan0=np.array([0, -1]),
# initial guess places control point closer to the hips
initial_guess=[0.25, 0]
)
left = pyg.EdgeSequence(left_top, left_bottom)
# fin
self.edges = pyg.EdgeSequence(right, top, left).close_loop()
bottom = self.edges[-1]
if slit: # add a slit
# Use long and thin disconnected dart for a cutout
new_edges, _, int_edges = pyg.ops.cut_into_edge(
pyg.EdgeSeqFactory.dart_shape(2, depth=slit * length), # a very thin cutout
bottom,
offset=bottom.length() / 2,
right=True)
self.edges.substitute(bottom, new_edges)
bottom = int_edges
if left_slit:
frac = left_slit
new_left_bottom = left_bottom.subdivide_len([1 - frac, frac])
left.substitute(left_bottom, new_left_bottom[0])
self.edges.substitute(left_bottom, new_left_bottom)
left_bottom = new_left_bottom[0]
if right_slit:
frac = right_slit
new_rbottom = right_bottom.subdivide_len([frac, 1 - frac])
right.substitute(right_bottom, new_rbottom[1])
self.edges.substitute(right_bottom, new_rbottom)
right_bottom = new_rbottom[1]
if side_cut is not None:
try:
# Add a stylistic cutout to the skirt
new_edges, _, int_edges = pyg.ops.cut_into_edge(
side_cut, left_bottom,
offset=left_bottom.length() / 2,
right=True, flip_target=flip_side_cut)
except:
# Skip adding the cut if it doesn't fit (e.g. because of the slit)
pass
else:
self.edges.substitute(left_bottom, new_edges)
left.substitute(left_bottom, new_edges)
# Default placement
self.top_center_pivot()
self.translation = [-hips / 2, 5, 0]
# Out interfaces (easier to define before adding a dart)
# Adding ruffle factor on the hip line extention (used in back panel)
self.interfaces = {
'bottom': pyg.Interface(self, bottom),
'right': pyg.Interface(self, right, [1] * (len(right) - 1) + [hipline_ext]),
'left': pyg.Interface(self, left, [hipline_ext] + [1] * (len(left) - 1)),
}
self.interfaces['left'].edges_flipping[0] = True
self.interfaces['right'].edges_flipping[-1] = True
# Add top darts
if w_diff > hw_shift:
dart_width = 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=int_edges.length() / 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=top.length() / 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):
top_edge_len = top.length()
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,
dart_position - dist / 2 + dart_width / 4,
dart_position + dist / 2 + dart_width / 2 + dart_width / 4,
]
# dart_shape = pyp.EdgeSeqFactory.dart_shape(dart_width, dart_depth)
dart_shape_full = pyg.EdgeSeqFactory.dart_shape(dart_width / 2, dart_depth)
dart_shape_small = pyg.EdgeSeqFactory.dart_shape(dart_width / 2, dart_depth * 0.9)
darts = [
dart_shape_small,
dart_shape_full,
dart_shape_full,
dart_shape_small,
]
else:
offsets_mid = [
- dart_position - dart_width / 2,
dart_position + dart_width / 2,
]
dart_shape = pyg.EdgeSeqFactory.dart_shape(dart_width, dart_depth)
darts = [
dart_shape,
dart_shape,
]
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 - top_edge_len / 2) + off,
edge_seq=top_edges,
int_edge_seq=int_edges
)
return top_edges, int_edges
# Full garments - Components
class PencilSkirt(StackableSkirtComponent):
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, **kwargs) -> None:
super().__init__(body, design, tag)
design = design['pencil-skirt']
self.design = design # Make accessible from outside
# condition
if design['style_side_cut']['v'] is not None:
depth = 0.7 * (body['hips'] / 4 - body['bust_points'] / 2)
shape_class = getattr(shapes, design['style_side_cut']['v'])
style_shape_l, style_shape_r = shape_class(
width=depth * 1.5,
depth=depth, n_rays=6, d_rays=depth*0.2,
filename=design['style_side_file']['v'] if 'style_side_file' in design else None
)
else:
style_shape_l, style_shape_r = None, None
# Force from arguments if given
self.rise = design['rise']['v'] if rise is None else rise
waist, hips_depth, back_waist = self.eval_rise(self.rise)
if length is None:
length = design['length']['v'] * body['_leg_length'] # Depends on leg length
else:
length = length - hips_depth
self.front = FittedSkirtPanel(
'skirt_front',
body,
design,
(waist - back_waist) / 2,
(body['hips'] - body['hip_back_width']) / 2,
hips_depth=hips_depth,
length=length,
dart_position=body['bust_points'] / 2,
dart_frac=0.8, # Diff for front and back
match_top_int_to=(body['waist'] - body['waist_back_width']),
slit=design['front_slit']['v'] if slit else 0,
left_slit=design['left_slit']['v'] if slit else 0,
right_slit=design['right_slit']['v'] if slit else 0,
side_cut=style_shape_l
).translate_to([0, body['_waist_level'], 25])
self.back = FittedSkirtPanel(
'skirt_back',
body,
design,
back_waist / 2,
body['hip_back_width'] / 2,
length=length,
hips_depth=hips_depth,
hipline_ext=1.05,
dart_position=body['bum_points'] / 2,
dart_frac=0.85,
double_dart=True,
match_top_int_to=body['waist_back_width'],
slit=design['back_slit']['v'] if slit else 0,
left_slit=design['left_slit']['v'] if slit else 0,
right_slit=design['right_slit']['v'] if slit else 0,
side_cut=style_shape_r,
flip_side_cut=False,
).translate_to([0, body['_waist_level'], -20])
self.stitching_rules = pyg.Stitches(
(self.front.interfaces['right'], self.back.interfaces['right']),
(self.front.interfaces['left'], self.back.interfaces['left'])
)
# Reusing interfaces of sub-panels as interfaces of this component
self.interfaces = {
'top_f': self.front.interfaces['top'],
'top_b': self.back.interfaces['top'],
'top': pyg.Interface.from_multiple(
self.front.interfaces['top'].flip_edges(),
self.back.interfaces['top'].reverse(with_edge_dir_reverse=True)
),
'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 length(self):
return self.front.length()
class Skirt2(StackableSkirtComponent):
"""Simple 2 panel skirt"""
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, top_ruffles=True, min_len=5) -> None:
super().__init__(body, design, tag)
design = design['skirt']
self.rise = design['rise']['v'] if rise is None else rise
waist, hip_line, back_waist = self.eval_rise(self.rise)
# Force from arguments if given
if length is None:
length = hip_line + design['length']['v'] * body['_leg_length'] # Depends on 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)
self.front = SkirtPanel(
f'skirt_front_{tag}' if tag else 'skirt_front',
waist_length=waist - back_waist,
length=length,
ruffles=design['ruffle']['v'] if top_ruffles else 1, # Only if on waistband
flare=design['flare']['v'],
bottom_cut=design['bottom_cut']['v'] * design['length']['v'] if slit else 0,
match_top_int_to=(body['waist'] - body['waist_back_width'])
).translate_to([0, body['_waist_level'], 25])
self.back = SkirtPanel(
f'skirt_back_{tag}' if tag else 'skirt_back',
waist_length=back_waist,
length=length,
ruffles=design['ruffle']['v'] if top_ruffles else 1, # Only if on waistband
flare=design['flare']['v'],
bottom_cut=design['bottom_cut']['v'] * design['length']['v'] if slit else 0,
match_top_int_to=body['waist_back_width']
).translate_to([0, body['_waist_level'], -20])
self.stitching_rules = pyg.Stitches(
(self.front.interfaces['right'], self.back.interfaces['right']),
(self.front.interfaces['left'], self.back.interfaces['left'])
)
# Reusing interfaces of sub-panels as interfaces of this component
self.interfaces = {
'top_f': self.front.interfaces['top'],
'top_b': self.back.interfaces['top'],
'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 length(self):
return self.front.length()
class SkirtManyPanels(BaseBottoms):
"""Round Skirt with many panels"""
def __init__(self, body, design, tag='', rise=None, min_len=5) -> None:
tag_extra = str(design['flare-skirt']['skirt-many-panels']['n_panels']['v'])
tag = f'{tag}_{tag_extra}' if tag else tag_extra
super().__init__(body, design, tag=tag, rise=rise)
design = design['flare-skirt']
self.rise = design['rise']['v'] if rise is None else rise
waist, hip_line, _ = self.eval_rise(self.rise)
n_panels = design['skirt-many-panels']['n_panels']['v']
# Length is dependent on length of legs
length = hip_line + 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)
flare_coeff_pi = 1 + design['suns']['v'] * length * 2 * np.pi / waist
self.front = ThinSkirtPanel('front',
panel_w := waist / n_panels,
bottom_width=panel_w * flare_coeff_pi,
length=length,
b_curvature=design['skirt-many-panels']['panel_curve']['v'])
# Move far enough s.t. the widest part of the panels fit on the circle
dist = self.front.interfaces['bottom'].edges.length() / (2 * np.tan(np.pi / n_panels))
self.front.translate_to([-dist, body['_waist_level'], 0])
# Align orientation with a body
self.front.rotate_by(R.from_euler('XYZ', [0, -90, 0], degrees=True))
self.front.rotate_align([-dist, 0, panel_w / 2])
# Upd interface orientation
self.front.interfaces['top'].reverse(True)
# Create new panels
self.subs = pyg.ops.distribute_Y(self.front, n_panels, name_tag='skirt_panel')
# Stitch new components
for i in range(1, n_panels):
self.stitching_rules.append((self.subs[i - 1].interfaces['left'],
self.subs[i].interfaces['right']))
self.stitching_rules.append((self.subs[-1].interfaces['left'],
self.subs[0].interfaces['right']))
# Define the interface
self.interfaces = {
'top': pyg.Interface.from_multiple(*[sub.interfaces['top']
for sub in self.subs])
}
def length(self):
return self.front.length()

View File

@@ -0,0 +1,373 @@
from copy import deepcopy
import numpy as np
from scipy.spatial.transform import Rotation as R
from assets.garment_programs import bands
import pygarment as pyg
# ------ Armhole shapes ------
def ArmholeSquare(incl, width, angle, invert=True, **kwargs):
"""Simple square armhole cut-out
Not recommended to use for sleeves, stitching in 3D might be hard
if angle is provided, it also calculated the shape of the sleeve interface to attach
returns edge sequence and part to be preserved inverted
"""
edges = pyg.EdgeSeqFactory.from_verts([0, 0], [incl, 0], [incl, width])
if not invert:
return edges, None
sina, cosa = np.sin(angle), np.cos(angle)
l = edges[0].length()
sleeve_edges = pyg.EdgeSeqFactory.from_verts(
[incl + l*sina, - l*cosa],
[incl, 0], [incl, width])
# TODOLOW Bend instead of rotating to avoid sharp connection
sleeve_edges.rotate(angle=-angle)
return edges, sleeve_edges
def ArmholeAngle(incl, width, angle, incl_coeff=0.2, w_coeff=0.2,
invert=True, **kwargs):
"""Piece-wise smooth armhole shape"""
diff_incl = incl * (1 - incl_coeff)
edges = pyg.EdgeSeqFactory.from_verts(
[0, 0], [diff_incl, w_coeff * width], [incl, width])
if not invert:
return edges, None
sina, cosa = np.sin(angle), np.cos(angle)
l = edges[0].length()
sleeve_edges = pyg.EdgeSeqFactory.from_verts(
[diff_incl + l*sina, w_coeff * width - l*cosa],
[diff_incl, w_coeff * width], [incl, width])
# TODOLOW Bend instead of rotating to avoid sharp connection
sleeve_edges.rotate(angle=-angle)
return edges, sleeve_edges
def ArmholeCurve(incl, width, angle, bottom_angle_mix=0, invert=True, verbose=False, **kwargs):
""" Classic sleeve opening on Cubic Bezier curves
"""
# Curvature as parameters?
cps = [[0.5, 0.2], [0.8, 0.35]]
edge = pyg.CurveEdge([incl, width], [0, 0], cps)
edge_as_seq = pyg.EdgeSequence(edge.reverse())
if not invert:
return edge_as_seq, None
# Initialize inverse (initial guess)
# Agle == 0
down_direction = np.array([0, -1]) # Full opening is vertically aligned
inv_cps = deepcopy(cps)
inv_cps[-1][1] *= -1 # Invert the last
inv_edge = pyg.CurveEdge(
start=[incl, width],
end=(np.array([incl, width]) + down_direction * edge._straight_len()).tolist(),
control_points=inv_cps
)
# Rotate by desired angle (usually desired sleeve rest angle)
inv_edge.rotate(angle=-angle)
# Optimize the inverse shape to be nice
shortcut = inv_edge.shortcut()
rotated_direction = shortcut[-1] - shortcut[0]
rotated_direction /= np.linalg.norm(rotated_direction)
left_direction = np.array([-1, 0])
mix_factor = bottom_angle_mix
dir = (1 - mix_factor) * rotated_direction + (
mix_factor * down_direction if mix_factor > 0 else (- mix_factor * left_direction))
# TODOLOW Remember relative curvature results and reuse them? (speed)
fin_inv_edge = pyg.ops.curve_match_tangents(
inv_edge.as_curve(),
down_direction, # Full opening is vertically aligned
dir,
target_len=edge.length(),
return_as_edge=True,
verbose=verbose
)
return edge_as_seq, pyg.EdgeSequence(fin_inv_edge.reverse())
# -------- New sleeve definitions -------
class SleevePanel(pyg.Panel):
"""Trying proper sleeve panel"""
def __init__(self, name, body, design, open_shape, length_shift=0, _standing_margin=5):
"""Define a standard sleeve panel (half a sleeve)
* length_shift -- force upd sleeve length by this amount.
Can be used to adjust length evaluation to fit the cuff
"""
super().__init__(name)
MIN_LENGTH = 5 # Minimum sleeve length
shoulder_angle = np.deg2rad(body['_shoulder_incl'])
rest_angle = max(np.deg2rad(design['sleeve_angle']['v']),
shoulder_angle)
standing = design['standing_shoulder']['v']
# Calculating extension size & end size before applying ruffles
# Since ruffles add to pattern length & width, but not to de-facto
# sleeve length in 3D
end_width = design['end_width']['v'] * abs(open_shape[0].start[1] - open_shape[-1].end[1])
# Ensure it fits regardless of parameters
end_width = max(end_width, body['wrist'] / 2)
# Ruffles at opening
if not pyg.utils.close_enough(design['connect_ruffle']['v'], 1):
open_shape.extend(design['connect_ruffle']['v'])
# -- Main body of a sleeve --
opening_length = abs(open_shape[0].start[0] - open_shape[-1].end[0])
arm_width = abs(open_shape[0].start[1] - open_shape[-1].end[1])
# Length from the border of the opening to the end of the sleeve
length = design['length']['v'] * (body['arm_length'] - opening_length)
# NOTE: Asked to reduce by too much: reduce as much as possible
length = max(length + length_shift, MIN_LENGTH)
self.edges = pyg.EdgeSeqFactory.from_verts(
[0, 0], [0, -end_width], [length, -arm_width]
)
# Align the opening
open_shape.snap_to(self.edges[-1].end)
open_shape[0].start = self.edges[-1].end # chain
self.edges.append(open_shape)
# Fin
self.edges.close_loop()
if standing:
if rest_angle > (shoulder_angle + np.deg2rad(_standing_margin)): # Add a "shelve" to create square shoulder appearance
top_edge = self.edges[-1]
start = top_edge.start
len = design['standing_shoulder_len']['v']
x_shift = len * np.cos(rest_angle - shoulder_angle)
y_shift = len * np.sin(rest_angle - shoulder_angle)
standing_edge = pyg.Edge(
start=start,
end=[start[0] - x_shift, start[1] + y_shift]
)
top_edge.start = standing_edge.end
self.edges.substitute(top_edge, [standing_edge, top_edge])
else:
if self.verbose:
print(f'{self.__class__.__name__}::WARNING::'
f'Sleeve rest angle {np.rad2deg(rest_angle):.3f} should be '
f'larger than shoulder angle {body["_shoulder_incl"]} by '
f'at least {_standing_margin} deg to enable '
'standing shoulder. Standing shoulder ignored')
standing = False
# Interfaces
self.interfaces = {
# NOTE: interface needs reversing because the open_shape was reversed for construction
'in': pyg.Interface(self, open_shape, ruffle=design['connect_ruffle']['v']),
'out': pyg.Interface(self, self.edges[0], ruffle=design['cuff']['top_ruffle']['v']),
'top': pyg.Interface(self, self.edges[-2:] if standing else self.edges[-1]),
'bottom': pyg.Interface(self, self.edges[1])
}
# Default placement
self.set_pivot(self.edges[-1].start)
self.translate_to([
- body['shoulder_w'] / 2,
body['height'] - body['head_l'],
0,
])
def length(self, longest_dim=False):
return self.interfaces['bottom'].edges.length()
class Sleeve(pyg.Component):
"""Trying to do a proper sleeve"""
def __init__(self, tag, body, design, front_w, back_w):
"""Defintion of a sleeve:
* front_w, back_w: the width front and the back of the top
the sleeve will attach to -- needed for correct share calculations
They may be
* Specified as scalar numbers
* Specified as functions w.r.t. the requested vertical level (=>
calculated width of a horizontal slice)
"""
super().__init__(f'{self.__class__.__name__}_{tag}')
design = design['sleeve']
self.design = design
self.body = body
sleeve_balance = body['_base_sleeve_balance'] / 2
rest_angle = max(np.deg2rad(design['sleeve_angle']['v']),
np.deg2rad(body['_shoulder_incl']))
connecting_width = design['connecting_width']['v']
smoothing_coeff = design['smoothing_coeff']['v']
front_w = front_w(connecting_width) if callable(front_w) else front_w
back_w = back_w(connecting_width) if callable(back_w) else back_w
# --- Define sleeve opening shapes ----
# NOTE: Non-trad armholes only for sleeveless styles due to
# unclear inversion and stitching errors (see below)
armhole = globals()[design['armhole_shape']['v']] if design['sleeveless']['v'] else ArmholeCurve
front_project, front_opening = armhole(
front_w - sleeve_balance,
connecting_width,
angle=rest_angle,
incl_coeff=smoothing_coeff,
w_coeff=smoothing_coeff,
invert=not design['sleeveless']['v'],
bottom_angle_mix=design['opening_dir_mix']['v'],
verbose=self.verbose
)
back_project, back_opening = armhole(
back_w - sleeve_balance,
connecting_width,
angle=rest_angle,
incl_coeff=smoothing_coeff,
w_coeff=smoothing_coeff,
invert=not design['sleeveless']['v'],
bottom_angle_mix=design['opening_dir_mix']['v']
)
self.interfaces = {
'in_front_shape': pyg.Interface(self, front_project),
'in_back_shape': pyg.Interface(self, back_project)
}
if design['sleeveless']['v']:
# The rest is not needed!
return
if front_w != back_w:
front_opening, back_opening = pyg.ops.even_armhole_openings(
front_opening, back_opening,
tol=0.2 / front_opening.length(), # ~2mm tolerance as a fraction of length
verbose=self.verbose
)
# --- Eval length adjustment for cuffs (if any) ----
cuff_len_adj = self._cuff_len_adj()
# # ----- Get sleeve panels -------
self.f_sleeve = SleevePanel(
f'{tag}_sleeve_f', body, design, front_opening,
length_shift=-cuff_len_adj
).translate_by([5, 0, 15])
# self.f_sleeve = SleevePanel(
# f'{tag}_sleeve_f', body, design, front_opening,
# length_shift=-cuff_len_adj
# )
self.b_sleeve = SleevePanel(
f'{tag}_sleeve_b', body, design, back_opening,
length_shift=-cuff_len_adj
).translate_by([5, 0, -15])
# self.b_sleeve = SleevePanel(
# f'{tag}_sleeve_b', body, design, back_opening,
# length_shift=-cuff_len_adj
# )
# Connect panels
self.stitching_rules = pyg.Stitches(
(self.f_sleeve.interfaces['top'],
self.b_sleeve.interfaces['top']),
(self.f_sleeve.interfaces['bottom'],
self.b_sleeve.interfaces['bottom']),
)
# Interfaces
self.interfaces.update({
'in': pyg.Interface.from_multiple(
self.f_sleeve.interfaces['in'],
self.b_sleeve.interfaces['in'].reverse(with_edge_dir_reverse=True)
),
'out': pyg.Interface.from_multiple(
self.f_sleeve.interfaces['out'],
self.b_sleeve.interfaces['out']
),
})
# Cuff
if design['cuff']['type']['v']:
# Class
# Copy to avoid editing original design dict
cdesign = deepcopy(design)
cuff_circ = self.interfaces['out'].edges.length() / design['cuff']['top_ruffle']['v']
# Ensure it fits regardless of parameters
cuff_circ = max(cuff_circ, body['wrist'])
cdesign['cuff']['b_width'] = dict(v=cuff_circ)
cdesign['cuff']['cuff_len']['v'] = cuff_len_adj
cuff_class = getattr(bands, cdesign['cuff']['type']['v'])
self.cuff = cuff_class(f'sl_{tag}', cdesign)
# Position
self.cuff.rotate_by(
R.from_euler(
'XYZ',
[0, 0, -90], # from -Ox direction
degrees=True
)
)
self.cuff.place_by_interface(
self.cuff.interfaces['top'],
self.interfaces['out'],
gap=2,
alignment='top'
)
self.stitching_rules.append(
(
self.cuff.interfaces['top'],
self.interfaces['out']
)
)
# UPD out interface!
self.interfaces['out'] = self.cuff.interfaces['bottom']
# Final rotation of sleeve piece
# print('*** arm_pose_angle', body['arm_pose_angle'])
self.rotate_by(R.from_euler(
'XYZ', [0, 0, body['arm_pose_angle']], degrees=True))
# Set label
self.set_panel_label('arm')
def _cuff_len_adj(self):
"""Eval sleeve length adjustment due to cuffs (if any)"""
if not self.design['cuff']['type']['v']:
return 0
cuff_len_adj = self.design['cuff']['cuff_len']['v'] * self.body['arm_length']
max_len = self.design['length']['v'] * self.body['arm_length']
if cuff_len_adj > max_len * 0.7:
cuff_len_adj = max_len * 0.7
return cuff_len_adj
def length(self):
if self.design['sleeveless']['v']:
return 0
if self.design['cuff']['type']['v']:
return self.f_sleeve.length() + self.cuff.length()
return self.f_sleeve.length()

View File

@@ -0,0 +1,150 @@
"""Design analysis routines to supply intresting stats"""
import pygarment.pattern.core as pattern
import yaml
# panels
def count_panels(pattern:pattern.BasicPattern, props, verbose=False):
n_panels = len(pattern.pattern['panels'].keys())
if verbose:
print(pattern.name, ' Panel count ', n_panels)
props['generator']['stats']['panel_count'][pattern.name] = n_panels
# Type determination
def bottom_length(design):
meta = design['meta']
if meta['bottom']['v']:
bottom_section = None
if meta['bottom']['v'] in ['SkirtCircle', 'AsymmSkirtCircle', 'SkirtManyPanels']:
bottom_section = 'flare-skirt'
elif meta['bottom']['v'] == 'Pants':
bottom_section = 'pants'
elif meta['bottom']['v'] == 'Skirt2':
bottom_section = 'skirt'
elif meta['bottom']['v'] == 'PencilSkirt':
bottom_section = 'pencil-skirt'
elif meta['bottom']['v'] == 'SkirtLevels':
bottom_section = 'levels-skirt'
elif meta['bottom']['v'] == 'GodetSkirt':
base = design['godet-skirt']['base']['v']
if base == 'Skirt2':
bottom_section = 'skirt'
else: # One other option
bottom_section = 'pencil-skirt'
else:
raise(ValueError(f'Unknown bottoms type {meta["bottom"]["v"]}'))
return design[bottom_section]['length']['v']
else:
return 0
def sleeve_length(design):
# Sleeve length
sleeve_len_r = design['sleeve']['length']['v'] if not design['sleeve']['sleeveless']['v'] else 0
sleeve_len_l = design['left']['sleeve']['length']['v'] if design['left']['enable_asym']['v'] and not design['left']['sleeve']['sleeveless']['v'] else 0
return max(sleeve_len_r, sleeve_len_l)
def top_length(design):
if design['meta']['upper']['v'] == 'FittedShirt':
return 1.
elif design['meta']['upper']['v'] == 'Shirt':
return design['shirt']['length']['v']
else:
return 0.
def vertical_len(design):
# NOTE: this will give very approximate result since
# the units of mesurement are slightly different
wb_len = design['waistband']['width']['v'] if design['meta']['wb']['v'] else 0
return top_length(design) + wb_len + bottom_length(design)
def garment_type(el_name, design, props, verbose=False):
main_type = None
add_types = []
# Main:
# + upper garment (short skirt - top or dress?)
# + skirt
# + pants
# + dress
# + jumpsuit
# Additional labels:
# + asymmetrical top
# + Hoody?
# + maxi/midi/mini
# + sleeve/less
# + long sleeve / short sleeve?
meta = design['meta']
if meta['upper']['v']:
if meta['bottom']['v'] and 'Pants' in meta['bottom']['v']:
main_type = 'jumpsuit'
elif vertical_len(design) < 1.4: # NOTE: very approximate division
main_type = 'upper_garment'
else:
main_type = 'dress'
else:
if 'Pants' in meta['bottom']['v']:
main_type = 'pants'
else:
main_type = 'skirt'
# Additional types
if meta['upper']['v']:
if design['left']['enable_asym']['v']:
add_types.append('asymmetric_top')
if (not design['left']['enable_asym']['v']
and design['collar']['component']['style']['v']
and 'Hood' in design['collar']['component']['style']['v']):
add_types.append('hoodie')
if (design['sleeve']['sleeveless']['v']
and (design['left']['sleeve']['sleeveless']['v'] if design['left']['enable_asym']['v'] else True)):
add_types.append('sleeveless')
else:
add_types.append('with_sleeves')
sleeve_len = sleeve_length(design)
if sleeve_len < 0.5:
add_types.append('short_sleeve')
else:
add_types.append('long_sleeve')
# Mini/Midi/Maxi
if meta['bottom']['v']:
length = bottom_length(design)
if length < 0.3:
add_types.append('mini')
elif length < 0.5:
add_types.append('knee_len')
elif length < 0.65:
add_types.append('midi')
else:
add_types.append('maxi')
if verbose:
print(el_name, ' types ', main_type, add_types)
props['generator']['stats']['garment_types'][el_name] = {
'main': main_type,
'styles': add_types
}
# Summary
summary = props['generator']['stats']['garment_types_summary']
if main_type not in summary['main']:
summary['main'][main_type] = 1
else:
summary['main'][main_type] += 1
for style_t in add_types:
if style_t not in summary['style']:
summary['style'][style_t] = {'total': 0}
summary['style'][style_t]['total'] += 1
if main_type not in summary['style'][style_t]:
summary['style'][style_t][main_type] = 1
else:
summary['style'][style_t][main_type] += 1

View File

@@ -0,0 +1,116 @@
""" Panels for a straight upper garment (T-shirt)
Note that the code is very similar to Bodice.
"""
import numpy as np
import pygarment as pyg
from assets.garment_programs.base_classes import BaseBodicePanel
class TorsoFrontHalfPanel(BaseBodicePanel):
"""Half of a simple non-fitted upper garment (e.g. T-Shirt)
Fits to the bust size
"""
def __init__(self, name, body, design) -> None:
""" Front = True, provides the adjustments necessary for the front panel
"""
super().__init__(name, body, design)
design = design['shirt']
# width
m_width = design['width']['v'] * body['bust']
b_width = design['flare']['v'] * m_width
# sizes
body_width = (body['bust'] - body['back_width']) / 2
frac = body_width / body['bust']
self.width = frac * m_width
b_width = frac * b_width
sh_tan = np.tan(np.deg2rad(body['_shoulder_incl']))
shoulder_incl = sh_tan * self.width
length = design['length']['v'] * body['waist_line']
# length in the front panel is adjusted due to shoulder inclination
# for the correct sleeve fitting
fb_diff = (frac - (0.5 - frac)) * body['bust']
length = length - sh_tan * fb_diff
self.edges = pyg.EdgeSeqFactory.from_verts(
[0, 0],
[-b_width, 0],
[-self.width, length],
[0, length + shoulder_incl],
loop=True
)
# Interfaces
self.interfaces = {
'outside': pyg.Interface(self, self.edges[1]),
'inside': pyg.Interface(self, self.edges[-1]),
'shoulder': pyg.Interface(self, self.edges[-2]),
'bottom': pyg.Interface(self, self.edges[0], ruffle=self.width / ((body['waist'] - body['waist_back_width']) / 2)),
# Reference to the corner for sleeve and collar projections
'shoulder_corner': pyg.Interface(self, [self.edges[-3], self.edges[-2]]),
'collar_corner': pyg.Interface(self, [self.edges[-2], self.edges[-1]])
}
# default placement
self.translate_by([0, body['height'] - body['head_l'] - length - shoulder_incl, 0])
def get_width(self, level):
return super().get_width(level) + self.width - self.body['shoulder_w'] / 2
class TorsoBackHalfPanel(BaseBodicePanel):
"""Half of a simple non-fitted upper garment (e.g. T-Shirt)
Fits to the bust size
"""
def __init__(self, name, body, design) -> None:
""" Front = True, provides the adjustments necessary for the front panel
"""
super().__init__(name, body, design)
design = design['shirt']
# account for ease in basic measurements
m_width = design['width']['v'] * body['bust']
b_width = design['flare']['v'] * m_width
# sizes
body_width = body['back_width'] / 2
frac = body_width / body['bust']
self.width = frac * m_width
b_width = frac * b_width
shoulder_incl = (np.tan(np.deg2rad(body['_shoulder_incl']))) * self.width
length = design['length']['v'] * body['waist_line']
self.edges = pyg.EdgeSeqFactory.from_verts(
[0, 0],
[-b_width, 0],
[-self.width, length],
[0, length + shoulder_incl],
loop=True
)
# Interfaces
self.interfaces = {
'outside': pyg.Interface(self, self.edges[1]),
'inside': pyg.Interface(self, self.edges[-1]),
'shoulder': pyg.Interface(self, self.edges[-2]),
'bottom': pyg.Interface(self, self.edges[0], ruffle=self.width / (body['waist_back_width'] / 2)),
# Reference to the corner for sleeve and collar projections
'shoulder_corner': pyg.Interface(self, [self.edges[-3], self.edges[-2]]),
'collar_corner': pyg.Interface(self, [self.edges[-2], self.edges[-1]])
}
# default placement
self.translate_by([0, body['height'] - body['head_l'] - length - shoulder_incl, 0])
def get_width(self, level):
return super().get_width(level) + self.width - self.body['shoulder_w'] / 2

BIN
assets/img/artist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 KiB

BIN
assets/img/err_js.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 KiB

BIN
assets/img/err_regency.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
assets/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/img/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 184.75 187.06">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #231f20;
stroke-miterlimit: 10;
stroke-width: .25px;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<path class="cls-1" d="m2.94,111.46C-6.29,77.1,7.5,38.35,39.92,16.21c32.8-22.39,74.5-20.81,103.18,1.03-21-2.64-55.7,9.64-86.52,32.53C26.51,72.1,7.63,94.28,2.94,111.46Z"/>
<path class="cls-1" d="m171.8,53.37c-.17-.3-.41-.63-.6-.91-8.93-13.07-37.91-8.81-75.25,14.45,26.14-13.4,42.43-12.73,49.06-2.94,8.46,12.49-12.51,41.73-46.82,65.33-29.12,20.03-59.07,31.1-71.66,24.21-1.08-.59-2.03-1.42-2.74-2.38h-.03c.25.37.53.67.79,1.03h0c10.57,13.3,53.75,4.89,95.58-26.19,39.92-29.65,61.05-57.68,51.66-72.59"/>
<path class="cls-1" d="m13.42,134.09c.17.3.41.63.6.91,8.93,13.07,37.91,8.81,75.25-14.45-26.14,13.4-42.43,12.73-49.06,2.94-8.46-12.49,12.51-41.73,46.82-65.33,29.12-20.03,59.07-31.1,71.66-24.21,1.08.59,2.03,1.42,2.74,2.38h.03c-.25-.37-.53-.67-.79-1.03h0c-10.57-13.3-53.75-4.89-95.58,26.19C25.16,91.15,4.03,119.18,13.42,134.09"/>
<path class="cls-1" d="m181.81,75.6c9.23,34.36-4.56,73.12-36.98,95.25-32.8,22.39-74.5,20.81-103.18-1.03,21,2.64,55.7-9.64,86.52-32.53,30.06-22.33,48.95-44.51,53.64-61.69Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

View File

@@ -0,0 +1,8 @@
[
"shirt, sleeveless, regular length, crew neck",
"dress, A-line, knee-length, regular fit, boat neck, short sleeves",
"jumpsuit, straight leg, relaxed fit, turtleneck, regular length, long sleeves",
"dress, sleeveless, tight fit, level skirt, boat neck, mini-length",
"dress, with a hood",
"dress, one-shoulder"
]