init_code
189
README.md
@@ -1,92 +1,127 @@
|
||||
# design2garmentcode
|
||||
|
||||
# Design2GarmentCode: Programmatic Garment Patterns from Text and Images
|
||||
|
||||
[arXiv](https://arxiv.org/abs/2412.08603) | [Project Page](https://style3d.github.io/design2garmentcode/)
|
||||
|
||||
## Getting started
|
||||
Feng Zhou, Ruiyang Liu, Chen Liu, Gaofeng He, Yong‑Lu Li, Xiaogang Jin, Huamin Wang. *CVPR 2025 .*
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin http://gitlab.linctex.com/ZhouDeal/design2garmentcode.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
- [ ] [Set up project integrations](http://gitlab.linctex.com/ZhouDeal/design2garmentcode/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||

|
||||
we propose a novel
|
||||
sewing pattern generation approach Design2GarmentCode
|
||||
based on Large Multimodal Models (LMMs), to generate parametric pattern-making programs from multi-modal
|
||||
design concepts
|
||||
---
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
### 1. Clone the repository
|
||||
```bash
|
||||
git clone https://github.com/your-org/design2garmentcode.git # ← replace with the real URL
|
||||
cd design2garmentcode
|
||||
```
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
### 2. Create the Conda environment
|
||||
An `environment.yml` file is provided in the project root with all required Conda and PyPI dependencies (Python 3.9.19, Torch 2.4.0 + CUDA 12.1, etc.).
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
```bash
|
||||
conda env create -f environment.yml
|
||||
conda activate d2g
|
||||
python -m pip install --upgrade pip # optional: upgrade pip
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
### 3. (Optional) Enable 3‑D simulation
|
||||
If you need local cloth simulation and 3‑D visualization, follow the installation instructions for **GarmentCode Warp Simulator**:
|
||||
<https://github.com/maria-korosteleva/NvidiaWarp-GarmentCode>
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
---
|
||||
### 4. Language‑Model API
|
||||
`Design2GarmentCode` communicates with large multimodal models.
|
||||
Follow the steps **in the given order**:
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
1. **Provide API credentials**
|
||||
- **Environment variable (recommended)** – defaults to *ChatGPT‑4o*
|
||||
```bash
|
||||
export OPENAI_API_KEY="sk‑..."
|
||||
```
|
||||
- **Edit `system.json`** (project root) – manually specify `api_key`, `base_url`, and `model` if you prefer a file‑based approach.
|
||||
2. **You should first download the base model**
|
||||
[`Qwen2-VL-2B-Instruct`]<https://huggingface.co/Qwen/Qwen2-VL-2B-Instruct/tree/main>
|
||||
and place its full folder in:
|
||||
`lmm_utils/Qwen/Qwen2-VL-2B-Instruct/`
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
Then make sure that [`model.pth`]<lmm_utils/Qwen/qwen2vl_lora_mlp/model.pth> is placed in the `lmm_utils/Qwen/qwen2vl_lora_mlp/` directory.
|
||||
This file contains the LoRA+MLP fine-tuned weights.
|
||||
---
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
## Quick GUI Demo
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
```bash
|
||||
python gui.py
|
||||
```
|
||||
- Input: free‑form prompt or an image/sketch
|
||||
- Output: GarmentCode JSON, preview image, and (optionally) physics simulation
|
||||
---
|
||||
## Model Inference
|
||||
### 1. Text Guided Generation
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
Use `test_text_batch.py` to process a list of text descriptions from a JSON file.
|
||||
|
||||
```bash
|
||||
python lmm_utils/test_text_batch.py \
|
||||
--input assets/test_text/examples.json \
|
||||
--output assets/test_text_result \
|
||||
--sim
|
||||
```
|
||||
|
||||
- `--input`: Path to your input JSON file containing multiple garment description texts.
|
||||
- `--output`: Directory where the output `.json` files will be saved.
|
||||
- `--sim`: Enable or disable physical simulation output.
|
||||
Supports physical simulation (enabled by default in script).
|
||||
|
||||
---
|
||||
|
||||
### 2. Image Guided Generation
|
||||
|
||||
Use `test_picture_batch.py` to process all image files in a directory.
|
||||
|
||||
```bash
|
||||
python lmm_utils/test_picture_batch.py \
|
||||
--input assets/test_img/examples \
|
||||
--output assets/test_image_result/examples \
|
||||
--sim
|
||||
```
|
||||
- `--input`: Folder containing multiple image files.
|
||||
- `--output`: Output folder where results will be saved.
|
||||
- `--sim`: Enable or disable physical simulation output.
|
||||
|
||||
---
|
||||
### 3. Modify Patterns in the GUI
|
||||
Once a pattern is generated in GUI, you can refine them directly inside the GUI:
|
||||
|
||||
1. Focus the **input box** at the bottom.
|
||||
2. Type `modify: <your-instruction>` .
|
||||
3. Press **Enter** – the system will regenerate the pattern to reflect your changes.
|
||||
|
||||
|
||||
## Get 3D Garment Patterns
|
||||
### 1. Generate from a pattern.json
|
||||
After generating the pattern data, you can simulate the corresponding 3D output directly from the pattern's JSON file.
|
||||
```bash
|
||||
python lmm_utils/test_garment_sim.py --pattern_spec $OUTPUT_JSON
|
||||
```
|
||||
### 2. Generate from gui
|
||||
You can also run the simulation directly on the GUI to obtain 3D data.
|
||||
```bash
|
||||
python gui.py
|
||||
```
|
||||
### Citation
|
||||
```bash
|
||||
If you find this work useful, please cite:
|
||||
|
||||
```bibtex
|
||||
@article{zhou2024design2garmentcode,
|
||||
title={Design2GarmentCode: Turning Design Concepts to Tangible Garments Through Program Synthesis},
|
||||
author={Zhou, Feng and Liu, Ruiyang and Liu, Chen and He, Gaofeng and Li, Yong-Lu and Jin, Xiaogang and Wang, Huamin},
|
||||
journal={arXiv preprint arXiv:2412.08603},
|
||||
year={2024}
|
||||
}
|
||||
```
|
||||
1161
assets/Patterns/dress_pencil_specification.json
Normal file
732
assets/Patterns/hoody_mean_specification.json
Normal 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
|
||||
}
|
||||
}
|
||||
2505
assets/Patterns/js_mean_all_specification.json
Normal file
1058
assets/Patterns/shirt_mean_specification.json
Normal file
92
assets/Sim_props/default_sim_props.yaml
Normal 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: {}
|
||||
93
assets/Sim_props/gui_sim_props.yaml
Normal 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: {}
|
||||
92
assets/Sim_props/mid_bending.yaml
Normal 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: {}
|
||||
92
assets/Sim_props/minimal_bending.yaml
Normal 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: {}
|
||||
75
assets/Sim_props/soft_flexy_ochra 30s.json
Normal 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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
assets/Sim_props/soft_ochra.json
Normal 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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
assets/Sim_props/stiff_ochra.json
Normal 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
@@ -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.
|
||||
41
assets/bodies/body_params.py
Normal 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
|
||||
|
||||
|
||||
28259
assets/bodies/f_smpl_average_A40.obj
Normal file
35
assets/bodies/f_smpl_average_A40.yaml
Normal 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
|
||||
|
||||
|
||||
23749
assets/bodies/ggg_body_segmentation.json
Normal file
28259
assets/bodies/m_smpl_average_A40.obj
Normal file
26
assets/bodies/m_smpl_average_A40.yaml
Normal 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
BIN
assets/bodies/mean_all.stl
Normal file
27
assets/bodies/mean_all.yaml
Normal 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
|
||||
71252
assets/bodies/mean_all_apart.obj
Normal file
35
assets/bodies/mean_all_full.yaml
Normal 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
|
||||
71256
assets/bodies/mean_all_tpose.obj
Normal file
27
assets/bodies/mean_all_tpose.yaml
Normal 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
27
assets/bodies/mean_female.yaml
Normal 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
27
assets/bodies/mean_male.yaml
Normal 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
|
||||
7440
assets/bodies/smpl_vert_segmentation.json
Normal file
926
assets/design_params/default.yaml
Executable 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
|
||||
895
assets/design_params/default_template.yaml
Executable 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
|
||||
1008
assets/design_params/default_text_value.yaml
Executable file
885
assets/design_params/t-shirt.yaml
Normal 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
|
||||
262
assets/garment_programs/bands.py
Normal 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()
|
||||
122
assets/garment_programs/base_classes.py
Normal 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()
|
||||
674
assets/garment_programs/bodice.py
Normal 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
|
||||
234
assets/garment_programs/circle_skirt.py
Normal file
@@ -0,0 +1,234 @@
|
||||
import numpy as np
|
||||
import pygarment as pyg
|
||||
|
||||
from assets.garment_programs.base_classes import StackableSkirtComponent
|
||||
|
||||
|
||||
class CircleArcPanel(pyg.Panel):
|
||||
"""One panel circle skirt"""
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
top_rad, length, angle,
|
||||
match_top_int_proportion : bool = None,
|
||||
match_bottom_int_proportion=None
|
||||
) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
halfarc = angle / 2
|
||||
|
||||
dist_w = 2 * top_rad * np.sin(halfarc)
|
||||
dist_out = 2 * (top_rad + length) * np.sin(halfarc)
|
||||
|
||||
vert_len = length * np.cos(halfarc)
|
||||
|
||||
# top
|
||||
self.edges.append(pyg.CircleEdgeFactory.from_points_radius(
|
||||
[-dist_w/2, 0], [dist_w/2, 0],
|
||||
radius=top_rad, large_arc=halfarc > np.pi / 2))
|
||||
|
||||
self.edges.append(pyg.Edge(
|
||||
self.edges[-1].end, [dist_out / 2, -vert_len]))
|
||||
|
||||
# Bottom
|
||||
self.edges.append(pyg.CircleEdgeFactory.from_points_radius(
|
||||
self.edges[-1].end, [- dist_out / 2, -vert_len],
|
||||
radius=top_rad + length,
|
||||
large_arc=halfarc > np.pi / 2, right=False))
|
||||
|
||||
self.edges.close_loop()
|
||||
|
||||
# Interfaces
|
||||
self.interfaces = {
|
||||
'top': pyg.Interface(self, self.edges[0],
|
||||
ruffle=self.edges[0].length() / match_top_int_proportion if match_top_int_proportion is not None else 1.
|
||||
).reverse(True),
|
||||
'bottom': pyg.Interface(self, self.edges[2],
|
||||
ruffle=self.edges[2].length() / match_bottom_int_proportion if match_bottom_int_proportion is not None else 1.
|
||||
),
|
||||
'left': pyg.Interface(self, self.edges[1]),
|
||||
'right': pyg.Interface(self, self.edges[3])
|
||||
}
|
||||
|
||||
def length(self, *args):
|
||||
return self.interfaces['right'].edges.length()
|
||||
|
||||
@staticmethod
|
||||
def from_w_length_suns(name, length, top_width, sun_fraction, **kwargs):
|
||||
arc = sun_fraction * 2 * np.pi
|
||||
rad = top_width / arc
|
||||
|
||||
return CircleArcPanel(name, rad, length, arc, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def from_all_length(name, length, top_width, bottom_width, **kwargs):
|
||||
|
||||
diff = bottom_width - top_width
|
||||
arc = diff / length
|
||||
rad = top_width / arc
|
||||
|
||||
return CircleArcPanel(name, rad, length, arc, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def from_length_rad(name, length, top_width, rad, **kwargs):
|
||||
|
||||
arc = top_width / rad
|
||||
|
||||
return CircleArcPanel(name, rad, length, arc, **kwargs)
|
||||
|
||||
class AsymHalfCirclePanel(pyg.Panel):
|
||||
"""Panel for a asymmetrci circle skirt"""
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
top_rad, length_f, length_s,
|
||||
match_top_int_proportion=None,
|
||||
match_bottom_int_proportion=None
|
||||
) -> None:
|
||||
""" Half a shifted arc section
|
||||
"""
|
||||
super().__init__(name)
|
||||
|
||||
dist_w = 2 * top_rad
|
||||
dist_out = 2 * (top_rad + length_s)
|
||||
|
||||
# top
|
||||
self.edges.append(pyg.CircleEdgeFactory.from_points_radius(
|
||||
[-dist_w/2, 0], [dist_w/2, 0],
|
||||
radius=top_rad, large_arc=False))
|
||||
|
||||
self.edges.append(pyg.Edge(
|
||||
self.edges[-1].end, [dist_out / 2, 0]))
|
||||
|
||||
# Bottom
|
||||
self.edges.append(
|
||||
pyg.CircleEdgeFactory.from_three_points(
|
||||
self.edges[-1].end, [- dist_out / 2, 0],
|
||||
point_on_arc=[0, -(top_rad + length_f)]
|
||||
)
|
||||
)
|
||||
|
||||
self.edges.close_loop()
|
||||
|
||||
# Interfaces
|
||||
self.interfaces = {
|
||||
'top': pyg.Interface(self, self.edges[0],
|
||||
ruffle=self.edges[0].length() / match_top_int_proportion if match_top_int_proportion is not None else 1.
|
||||
).reverse(True),
|
||||
'bottom': pyg.Interface(self, self.edges[2],
|
||||
ruffle=self.edges[2].length() / match_bottom_int_proportion if match_bottom_int_proportion is not None else 1.
|
||||
),
|
||||
'left': pyg.Interface(self, self.edges[1]),
|
||||
'right': pyg.Interface(self, self.edges[3])
|
||||
}
|
||||
|
||||
def length(self, *args):
|
||||
return self.interfaces['right'].edges.length()
|
||||
|
||||
class SkirtCircle(StackableSkirtComponent):
|
||||
"""Simple circle skirt"""
|
||||
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, asymm=False, min_len=5, **kwargs) -> None:
|
||||
super().__init__(body, design, tag)
|
||||
|
||||
design = design['flare-skirt']
|
||||
suns = design['suns']['v']
|
||||
self.rise = design['rise']['v'] if rise is None else rise
|
||||
waist, hips_depth, _ = self.eval_rise(self.rise)
|
||||
|
||||
if length is None: # take from design parameters
|
||||
length = hips_depth + design['length']['v'] * body['_leg_length']
|
||||
|
||||
# NOTE: with some combinations of rise and length parameters length may become too small/negative
|
||||
# Hence putting a min positive value here
|
||||
length = max(length, min_len)
|
||||
|
||||
# panels
|
||||
if not asymm: # Typical symmetric skirt
|
||||
self.front = CircleArcPanel.from_w_length_suns(
|
||||
f'skirt_front_{tag}' if tag else 'skirt_front',
|
||||
length, waist / 2, suns / 2,
|
||||
match_top_int_proportion=self.body['waist'] - self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], 15])
|
||||
|
||||
self.back = CircleArcPanel.from_w_length_suns(
|
||||
f'skirt_back_{tag}' if tag else 'skirt_back',
|
||||
length, waist / 2, suns / 2,
|
||||
match_top_int_proportion=self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], -15])
|
||||
else:
|
||||
# NOTE: Asymmetic front/back is only defined on full skirt (1 sun)
|
||||
w_rad = waist / 2 / np.pi
|
||||
f_length = design['asymm']['front_length']['v'] * length
|
||||
tot_len = w_rad * 2 + length + f_length
|
||||
del_r = tot_len / 2 - f_length - w_rad
|
||||
s_length = np.sqrt((tot_len / 2)**2 - del_r**2) - w_rad
|
||||
|
||||
self.front = AsymHalfCirclePanel(
|
||||
f'skirt_front_{tag}' if tag else 'skirt_front',
|
||||
w_rad, f_length, s_length,
|
||||
match_top_int_proportion=self.body['waist'] - self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], 15])
|
||||
|
||||
self.back = AsymHalfCirclePanel(
|
||||
f'skirt_back_{tag}' if tag else 'skirt_back',
|
||||
w_rad, length, s_length,
|
||||
match_top_int_proportion=self.body['waist_back_width'],
|
||||
).translate_by([0, body['_waist_level'], -15])
|
||||
|
||||
# Add a cut
|
||||
if design['cut']['add']['v'] and slit:
|
||||
self.add_cut(
|
||||
self.front if design['cut']['place']['v'] > 0 else self.back,
|
||||
design, length)
|
||||
|
||||
# Stitches
|
||||
self.stitching_rules = pyg.Stitches(
|
||||
(self.front.interfaces['right'], self.back.interfaces['right']),
|
||||
(self.front.interfaces['left'], self.back.interfaces['left'])
|
||||
)
|
||||
|
||||
# Interfaces
|
||||
self.interfaces = {
|
||||
'top': pyg.Interface.from_multiple(self.front.interfaces['top'], self.back.interfaces['top']),
|
||||
'bottom_f': self.front.interfaces['bottom'],
|
||||
'bottom_b': self.back.interfaces['bottom'],
|
||||
'bottom': pyg.Interface.from_multiple(self.front.interfaces['bottom'], self.back.interfaces['bottom'])
|
||||
}
|
||||
|
||||
def add_cut(self, panel, design, sk_length):
|
||||
"""Add a cut to the skirt"""
|
||||
width, depth = design['cut']['width']['v'] * sk_length, design['cut']['depth']['v'] * sk_length
|
||||
|
||||
target_edge = panel.interfaces['bottom'].edges[0]
|
||||
t_len = target_edge.length()
|
||||
offset = abs(design['cut']['place']['v'] * t_len)
|
||||
|
||||
# Respect the placement boundaries
|
||||
offset = max(offset, width / 2)
|
||||
offset = min(offset, t_len - width / 2)
|
||||
|
||||
# NOTE: heuristic is specific for the panels that we use
|
||||
right = target_edge.start[0] > target_edge.end[0]
|
||||
|
||||
# Make a cut
|
||||
cut_shape = pyg.EdgeSeqFactory.dart_shape(width, depth=depth)
|
||||
|
||||
new_edges, _, interf_edges = pyg.ops.cut_into_edge(
|
||||
cut_shape, target_edge,
|
||||
offset=offset,
|
||||
right=right
|
||||
)
|
||||
|
||||
panel.edges.substitute(target_edge, new_edges)
|
||||
panel.interfaces['bottom'].substitute(
|
||||
target_edge, interf_edges,
|
||||
[panel for _ in range(len(interf_edges))])
|
||||
|
||||
def length(self, *args):
|
||||
return self.front.length()
|
||||
|
||||
|
||||
class AsymmSkirtCircle(SkirtCircle):
|
||||
"""Front/back asymmetric skirt"""
|
||||
def __init__(self, body, design, tag='', length=None, rise=None, slit=True, **kwargs):
|
||||
super().__init__(body, design, tag, length, rise, slit, asymm=True)
|
||||
370
assets/garment_programs/collars.py
Normal file
@@ -0,0 +1,370 @@
|
||||
import numpy as np
|
||||
from scipy.spatial.transform import Rotation as R
|
||||
|
||||
import pygarment as pyg
|
||||
|
||||
from assets.garment_programs.bands import StraightBandPanel
|
||||
from assets.garment_programs.circle_skirt import CircleArcPanel
|
||||
|
||||
|
||||
# # ------ Collar shapes withough extra panels ------
|
||||
|
||||
def VNeckHalf(depth, width, **kwargs):
|
||||
"""Simple VNeck design"""
|
||||
|
||||
edges = pyg.EdgeSequence(pyg.Edge([0, 0], [width / 2, -depth]))
|
||||
return edges
|
||||
|
||||
def SquareNeckHalf(depth, width, **kwargs):
|
||||
"""Square design"""
|
||||
|
||||
edges = pyg.EdgeSeqFactory.from_verts([0, 0], [0, -depth], [width / 2, -depth])
|
||||
return edges
|
||||
|
||||
def TrapezoidNeckHalf(depth, width, angle=90, verbose=True, **kwargs):
|
||||
"""Trapesoid neck design"""
|
||||
|
||||
# Special case when angle = 180 (sin = 0)
|
||||
if (pyg.utils.close_enough(angle, 180, tol=1)
|
||||
or pyg.utils.close_enough(angle, 0, tol=1)):
|
||||
# degrades into VNeck
|
||||
return VNeckHalf(depth, width)
|
||||
|
||||
rad_angle = np.deg2rad(angle)
|
||||
|
||||
bottom_x = -depth * np.cos(rad_angle) / np.sin(rad_angle)
|
||||
if bottom_x > width / 2: # Invalid angle/depth/width combination resulted in invalid shape
|
||||
if verbose:
|
||||
print('TrapezoidNeckHalf::WARNING::Parameters are invalid and create overlap: '
|
||||
f'{bottom_x} > {width / 2}. '
|
||||
'The collar is reverted to VNeck')
|
||||
|
||||
return VNeckHalf(depth, width)
|
||||
|
||||
edges = pyg.EdgeSeqFactory.from_verts([0, 0], [bottom_x, -depth], [width / 2, -depth])
|
||||
return edges
|
||||
|
||||
def CurvyNeckHalf(depth, width, flip=False, **kwargs):
|
||||
"""Testing Curvy Collar design"""
|
||||
|
||||
sign = -1 if flip else 1
|
||||
edges = pyg.EdgeSequence(pyg.CurveEdge(
|
||||
[0, 0], [width / 2,-depth],
|
||||
[[0.4, sign * 0.3], [0.8, sign * -0.3]]))
|
||||
|
||||
return edges
|
||||
|
||||
def CircleArcNeckHalf(depth, width, angle=90, flip=False, **kwargs):
|
||||
"""Collar with a side represented by a circle arc"""
|
||||
# 1/4 of a circle
|
||||
edges = pyg.EdgeSequence(pyg.CircleEdgeFactory.from_points_angle(
|
||||
[0, 0], [width / 2,-depth], arc_angle=np.deg2rad(angle),
|
||||
right=(not flip)
|
||||
))
|
||||
|
||||
return edges
|
||||
|
||||
|
||||
def CircleNeckHalf(depth, width, **kwargs):
|
||||
"""Collar that forms a perfect circle arc when halfs are stitched"""
|
||||
|
||||
# Take a full desired arc and half it!
|
||||
circle = pyg.CircleEdgeFactory.from_three_points(
|
||||
[0, 0],
|
||||
[width, 0],
|
||||
[width / 2, -depth])
|
||||
subdiv = circle.subdivide_len([0.5, 0.5])
|
||||
return pyg.EdgeSequence(subdiv[0])
|
||||
|
||||
def Bezier2NeckHalf(depth, width, flip=False, x=0.5, y=0.3, **kwargs):
|
||||
"""2d degree Bezier curve as neckline"""
|
||||
|
||||
sign = 1 if flip else -1
|
||||
edges = pyg.EdgeSequence(pyg.CurveEdge(
|
||||
[0, 0], [width / 2,-depth],
|
||||
[[x, sign*y]]))
|
||||
|
||||
return edges
|
||||
|
||||
# # ------ Collars with panels ------
|
||||
|
||||
class NoPanelsCollar(pyg.Component):
|
||||
"""Face collar class that only forms the projected shapes """
|
||||
|
||||
def __init__(self, name, body, design) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
# Front
|
||||
collar_type = globals()[design['collar']['f_collar']['v']]
|
||||
f_collar = collar_type(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'],
|
||||
angle=design['collar']['fc_angle']['v'],
|
||||
flip=design['collar']['f_flip_curve']['v'],
|
||||
x=design['collar']['f_bezier_x']['v'],
|
||||
y=design['collar']['f_bezier_y']['v'],
|
||||
verbose=self.verbose
|
||||
)
|
||||
|
||||
# Back
|
||||
collar_type = globals()[design['collar']['b_collar']['v']]
|
||||
b_collar = collar_type(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'],
|
||||
angle=design['collar']['bc_angle']['v'],
|
||||
flip=design['collar']['b_flip_curve']['v'],
|
||||
x=design['collar']['b_bezier_x']['v'],
|
||||
y=design['collar']['b_bezier_y']['v'],
|
||||
verbose=self.verbose
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
def length(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Turtle(pyg.Component):
|
||||
|
||||
def __init__(self, tag, body, design) -> None:
|
||||
super().__init__(f'Turtle_{tag}')
|
||||
|
||||
depth = design['collar']['component']['depth']['v']
|
||||
|
||||
# --Projecting shapes--
|
||||
f_collar = CircleNeckHalf(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
b_collar = CircleNeckHalf(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
# -- Panels --
|
||||
length_f, length_b = f_collar.length(), b_collar.length()
|
||||
height_p = body['height'] - body['head_l'] + depth
|
||||
|
||||
self.front = StraightBandPanel(
|
||||
f'{tag}_turtle_front', length_f, depth).translate_by(
|
||||
[-length_f / 2, height_p, 10])
|
||||
self.back = StraightBandPanel(
|
||||
f'{tag}_turtle_back', length_b, depth).translate_by(
|
||||
[-length_b / 2, height_p, -10])
|
||||
|
||||
self.stitching_rules.append((
|
||||
self.front.interfaces['right'],
|
||||
self.back.interfaces['right']
|
||||
))
|
||||
|
||||
self.interfaces.update({
|
||||
'front': self.front.interfaces['left'],
|
||||
'back': self.back.interfaces['left'],
|
||||
'bottom': pyg.Interface.from_multiple(
|
||||
self.front.interfaces['bottom'],
|
||||
self.back.interfaces['bottom']
|
||||
)
|
||||
})
|
||||
|
||||
def length(self):
|
||||
return self.interfaces['back'].edges.length()
|
||||
|
||||
|
||||
class SimpleLapelPanel(pyg.Panel):
|
||||
"""A panel for the front part of simple Lapel"""
|
||||
def __init__(self, name, length, max_depth) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
self.edges = pyg.EdgeSeqFactory.from_verts(
|
||||
[0, 0], [max_depth, 0], [max_depth, -length]
|
||||
)
|
||||
|
||||
self.edges.append(
|
||||
pyg.CurveEdge(
|
||||
self.edges[-1].end,
|
||||
self.edges[0].start,
|
||||
[[0.7, 0.2]]
|
||||
)
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'to_collar': pyg.Interface(self, self.edges[0]),
|
||||
'to_bodice': pyg.Interface(self, self.edges[1])
|
||||
}
|
||||
|
||||
|
||||
class SimpleLapel(pyg.Component):
|
||||
|
||||
def __init__(self, tag, body, design) -> None:
|
||||
super().__init__(f'Turtle_{tag}')
|
||||
|
||||
depth = design['collar']['component']['depth']['v']
|
||||
standing = design['collar']['component']['lapel_standing']['v']
|
||||
|
||||
# --Projecting shapes--
|
||||
# Any front one!
|
||||
collar_type = globals()[design['collar']['f_collar']['v']]
|
||||
f_collar = collar_type(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'],
|
||||
angle=design['collar']['fc_angle']['v'],
|
||||
flip=design['collar']['f_flip_curve']['v'])
|
||||
|
||||
b_collar = CircleNeckHalf(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
# -- Panels --
|
||||
length_f, length_b = f_collar.length(), b_collar.length()
|
||||
height_p = body['height'] - body['head_l'] + depth * 2
|
||||
|
||||
self.front = SimpleLapelPanel(
|
||||
f'{tag}_lapel_front', length_f, depth).translate_by(
|
||||
[-depth * 2, height_p, 35]) # TODOLOW This should be related with the bodice panels' placement
|
||||
|
||||
if standing:
|
||||
self.back = StraightBandPanel(
|
||||
f'{tag}_lapel_back', length_b, depth).translate_by(
|
||||
[-length_b / 2, height_p, -10])
|
||||
else:
|
||||
# A curved back panel that follows the collar opening
|
||||
rad, angle, _ = b_collar[0].as_radius_angle()
|
||||
self.back = CircleArcPanel(
|
||||
f'{tag}_lapel_back', rad, depth, angle
|
||||
).translate_by([-length_b, height_p, -10])
|
||||
self.back.rotate_by(R.from_euler('XYZ', [90, 45, 0], degrees=True))
|
||||
|
||||
if standing:
|
||||
self.back.interfaces['right'].set_right_wrong(True)
|
||||
|
||||
self.stitching_rules.append((
|
||||
self.front.interfaces['to_collar'],
|
||||
self.back.interfaces['right']
|
||||
))
|
||||
|
||||
self.interfaces.update({
|
||||
#'front': NOTE: no front interface here
|
||||
'back': self.back.interfaces['left'],
|
||||
'bottom': pyg.Interface.from_multiple(
|
||||
self.front.interfaces['to_bodice'].set_right_wrong(True),
|
||||
self.back.interfaces['bottom'] if standing else self.back.interfaces['top'].set_right_wrong(True),
|
||||
)
|
||||
})
|
||||
|
||||
def length(self):
|
||||
return self.interfaces['back'].edges.length()
|
||||
|
||||
class HoodPanel(pyg.Panel):
|
||||
"""A panel for the side of the hood"""
|
||||
def __init__(self, name, f_depth, b_depth, f_length, b_length, width, in_length, depth) -> None:
|
||||
super().__init__(name)
|
||||
|
||||
width = width / 2 # Panel covers one half only
|
||||
length = in_length + width / 2
|
||||
|
||||
# Bottom-back
|
||||
bottom_back_in = pyg.CurveEdge(
|
||||
[-width, -b_depth],
|
||||
[0, 0],
|
||||
[[0.3, -0.2], [0.6, 0.2]]
|
||||
)
|
||||
bottom_back = pyg.ops.curve_match_tangents(
|
||||
bottom_back_in.as_curve(),
|
||||
[1, 0], # Full opening is vertically aligned
|
||||
[1, 0],
|
||||
target_len=b_length,
|
||||
return_as_edge=True,
|
||||
verbose=self.verbose
|
||||
)
|
||||
self.edges.append(bottom_back)
|
||||
|
||||
# Bottom front
|
||||
bottom_front_in = pyg.CurveEdge(
|
||||
self.edges[-1].end,
|
||||
[width, -f_depth],
|
||||
[[0.3, 0.2], [0.6, -0.2]]
|
||||
)
|
||||
bottom_front = pyg.ops.curve_match_tangents(
|
||||
bottom_front_in.as_curve(),
|
||||
[1, 0], # Full opening is vertically aligned
|
||||
[1, 0],
|
||||
target_len=f_length,
|
||||
return_as_edge=True,
|
||||
verbose=self.verbose
|
||||
)
|
||||
self.edges.append(bottom_front)
|
||||
|
||||
# Front-top straight section
|
||||
self.edges.append(pyg.EdgeSeqFactory.from_verts(
|
||||
self.edges[-1].end,
|
||||
[width * 1.2, length], [width * 1.2 - depth, length]
|
||||
))
|
||||
# Back of the hood
|
||||
self.edges.append(
|
||||
pyg.CurveEdge(
|
||||
self.edges[-1].end,
|
||||
self.edges[0].start,
|
||||
[[0.2, -0.5]]
|
||||
)
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'to_other_side': pyg.Interface(self, self.edges[-2:]),
|
||||
'to_bodice': pyg.Interface(self, self.edges[0:2]).reverse()
|
||||
}
|
||||
|
||||
self.rotate_by(R.from_euler('XYZ', [0, -90, 0], degrees=True))
|
||||
self.translate_by([-width*2, 0, 0])
|
||||
|
||||
class Hood2Panels(pyg.Component):
|
||||
|
||||
def __init__(self, tag, body, design) -> None:
|
||||
super().__init__(f'Hood_{tag}')
|
||||
|
||||
# --Projecting shapes--
|
||||
width = design['collar']['width']['v']
|
||||
f_collar = CircleNeckHalf(
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
b_collar = CircleNeckHalf(
|
||||
design['collar']['bc_depth']['v'],
|
||||
design['collar']['width']['v'])
|
||||
|
||||
self.interfaces = {
|
||||
'front_proj': pyg.Interface(self, f_collar),
|
||||
'back_proj': pyg.Interface(self, b_collar)
|
||||
}
|
||||
|
||||
# -- Panel --
|
||||
self.panel = HoodPanel(
|
||||
f'{tag}_hood',
|
||||
design['collar']['fc_depth']['v'],
|
||||
design['collar']['bc_depth']['v'],
|
||||
f_length=f_collar.length(),
|
||||
b_length=b_collar.length(),
|
||||
width=width,
|
||||
in_length=body['head_l'] * design['collar']['component']['hood_length']['v'],
|
||||
depth=width / 2 * design['collar']['component']['hood_depth']['v']
|
||||
).translate_by(
|
||||
[0, body['height'] - body['head_l'] + 10, 0])
|
||||
|
||||
self.interfaces.update({
|
||||
#'front': NOTE: no front interface here
|
||||
'back': self.panel.interfaces['to_other_side'],
|
||||
'bottom': self.panel.interfaces['to_bodice']
|
||||
})
|
||||
|
||||
def length(self):
|
||||
return self.panel.length()
|
||||
|
||||
121
assets/garment_programs/godet.py
Normal 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()
|
||||
195
assets/garment_programs/meta_garment.py
Normal 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()
|
||||
312
assets/garment_programs/pants.py
Normal file
@@ -0,0 +1,312 @@
|
||||
from copy import deepcopy
|
||||
import numpy as np
|
||||
|
||||
import pygarment as pyg
|
||||
from assets.garment_programs.base_classes import BaseBottoms
|
||||
from assets.garment_programs import bands
|
||||
|
||||
|
||||
class PantPanel(pyg.Panel):
|
||||
def __init__(
|
||||
self, name, body, design,
|
||||
length,
|
||||
waist,
|
||||
hips,
|
||||
hips_depth,
|
||||
crotch_width,
|
||||
dart_position,
|
||||
match_top_int_to=None,
|
||||
hipline_ext=1,
|
||||
double_dart=False) -> None:
|
||||
"""
|
||||
Basic pant panel with option to be fitted (with darts)
|
||||
"""
|
||||
super().__init__(name)
|
||||
|
||||
flare = body['leg_circ'] * (design['flare']['v'] - 1) / 4
|
||||
hips_depth = hips_depth * hipline_ext
|
||||
|
||||
hip_side_incl = np.deg2rad(body['_hip_inclination'])
|
||||
dart_depth = hips_depth * 0.8
|
||||
|
||||
# Crotch cotrols
|
||||
crotch_depth_diff = body['crotch_hip_diff']
|
||||
crotch_extention = crotch_width
|
||||
|
||||
# eval pants shape
|
||||
# TODO Return ruffle opportunity?
|
||||
|
||||
# amount of extra fabric at waist
|
||||
w_diff = hips - waist # Assume its positive since waist is smaller then hips
|
||||
# We distribute w_diff among the side angle and a dart
|
||||
hw_shift = np.tan(hip_side_incl) * hips_depth
|
||||
# Small difference
|
||||
if hw_shift > w_diff:
|
||||
hw_shift = w_diff
|
||||
|
||||
# --- Edges definition ---
|
||||
# Right
|
||||
if pyg.utils.close_enough(design['flare']['v'], 1): # skip optimization
|
||||
right_bottom = pyg.Edge(
|
||||
[-flare, 0],
|
||||
[0, length]
|
||||
)
|
||||
else:
|
||||
right_bottom = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
[-flare, 0],
|
||||
[0, length],
|
||||
target_tan1=np.array([0, 1]),
|
||||
# initial guess places control point closer to the hips
|
||||
initial_guess=[0.75, 0]
|
||||
)
|
||||
right_top = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
right_bottom.end,
|
||||
[hw_shift, length + hips_depth],
|
||||
target_tan0=np.array([0, 1]),
|
||||
initial_guess=[0.5, 0]
|
||||
)
|
||||
|
||||
top = pyg.Edge(
|
||||
right_top.end,
|
||||
[w_diff + waist, length + hips_depth]
|
||||
)
|
||||
|
||||
crotch_top = pyg.Edge(
|
||||
top.end,
|
||||
[hips, length + 0.45 * hips_depth] # A bit higher than hip line
|
||||
# NOTE: The point should be lower than the minimum rise value (0.5)
|
||||
)
|
||||
crotch_bottom = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
crotch_top.end,
|
||||
[hips + crotch_extention, length - crotch_depth_diff],
|
||||
target_tan0=np.array([0, -1]),
|
||||
target_tan1=np.array([1, 0]),
|
||||
initial_guess=[0.5, -0.5]
|
||||
)
|
||||
|
||||
left = pyg.CurveEdgeFactory.curve_from_tangents(
|
||||
crotch_bottom.end,
|
||||
[
|
||||
# NOTE "Magic value" (-2 cm) which we use to define default width:
|
||||
# just a little behing the crotch point
|
||||
# NOTE: Ensuring same distance from the crotch point in both
|
||||
# front and back for matching curves
|
||||
crotch_bottom.end[0] - 2 + flare,
|
||||
# NOTE: The inside edge either matches the length of the outside (0, normal case)
|
||||
# or when the inteded length is smaller than crotch depth,
|
||||
# inside edge covers of the inside leg a bit below the crotch (panties-like shorts)
|
||||
y:=min(0, length - crotch_depth_diff * 1.5)
|
||||
],
|
||||
target_tan1=[flare, y - crotch_bottom.end[1]],
|
||||
initial_guess=[0.3, 0]
|
||||
)
|
||||
|
||||
self.edges = pyg.EdgeSequence(
|
||||
right_bottom, right_top, top, crotch_top, crotch_bottom, left
|
||||
).close_loop()
|
||||
bottom = self.edges[-1]
|
||||
|
||||
# Default placement
|
||||
self.set_pivot(crotch_bottom.end)
|
||||
self.translation = [-0.5, - hips_depth - crotch_depth_diff + 5, 0]
|
||||
|
||||
# Out interfaces (easier to define before adding a dart)
|
||||
self.interfaces = {
|
||||
'outside': pyg.Interface(
|
||||
self,
|
||||
pyg.EdgeSequence(right_bottom, right_top),
|
||||
ruffle=[1, hipline_ext]),
|
||||
'crotch': pyg.Interface(self, pyg.EdgeSequence(crotch_top, crotch_bottom)),
|
||||
'inside': pyg.Interface(self, left),
|
||||
'bottom': pyg.Interface(self, bottom)
|
||||
}
|
||||
|
||||
# Add top dart
|
||||
# NOTE: Ruffle indicator to match to waistline proportion for correct balance line matching
|
||||
dart_width = w_diff - hw_shift
|
||||
if w_diff > hw_shift:
|
||||
top_edges, int_edges = self.add_darts(
|
||||
top, dart_width, dart_depth, dart_position, double_dart=double_dart)
|
||||
self.interfaces['top'] = pyg.Interface(
|
||||
self, int_edges,
|
||||
ruffle=waist / match_top_int_to if match_top_int_to is not None else 1.
|
||||
)
|
||||
self.edges.substitute(top, top_edges)
|
||||
else:
|
||||
self.interfaces['top'] = pyg.Interface(
|
||||
self, top,
|
||||
ruffle=waist / match_top_int_to if match_top_int_to is not None else 1.
|
||||
)
|
||||
|
||||
|
||||
|
||||
def add_darts(self, top, dart_width, dart_depth, dart_position, double_dart=False):
|
||||
|
||||
if double_dart:
|
||||
# TODOLOW Avoid hardcoding for matching with the top?
|
||||
dist = dart_position * 0.5 # Dist between darts -> dist between centers
|
||||
offsets_mid = [
|
||||
- (dart_position + dist / 2 + dart_width / 2 + dart_width / 4),
|
||||
- (dart_position - dist / 2) - dart_width / 4,
|
||||
]
|
||||
|
||||
darts = [
|
||||
pyg.EdgeSeqFactory.dart_shape(dart_width / 2, dart_depth * 0.9), # smaller
|
||||
pyg.EdgeSeqFactory.dart_shape(dart_width / 2, dart_depth)
|
||||
]
|
||||
else:
|
||||
offsets_mid = [
|
||||
- dart_position - dart_width / 2,
|
||||
]
|
||||
darts = [
|
||||
pyg.EdgeSeqFactory.dart_shape(dart_width, dart_depth)
|
||||
]
|
||||
top_edges, int_edges = pyg.EdgeSequence(top), pyg.EdgeSequence(top)
|
||||
|
||||
for off, dart in zip(offsets_mid, darts):
|
||||
left_edge_len = top_edges[-1].length()
|
||||
top_edges, int_edges = self.add_dart(
|
||||
dart,
|
||||
top_edges[-1],
|
||||
offset=left_edge_len + off,
|
||||
edge_seq=top_edges,
|
||||
int_edge_seq=int_edges
|
||||
)
|
||||
|
||||
return top_edges, int_edges
|
||||
|
||||
|
||||
class PantsHalf(BaseBottoms):
|
||||
def __init__(self, tag, body, design, rise=None) -> None:
|
||||
super().__init__(body, design, tag, rise=rise)
|
||||
design = design['pants']
|
||||
self.rise = design['rise']['v'] if rise is None else rise
|
||||
waist, hips_depth, waist_back = self.eval_rise(self.rise)
|
||||
|
||||
# NOTE: min value = full sum > leg curcumference
|
||||
# Max: pant leg falls flat from the back
|
||||
# Mostly from the back side
|
||||
# => This controls the foundation width of the pant
|
||||
min_ext = body['leg_circ'] - body['hips'] / 2 + 5 # 2 inch ease: from pattern making book
|
||||
front_hip = (body['hips'] - body['hip_back_width']) / 2
|
||||
crotch_extention = min_ext * design['width']['v']
|
||||
front_extention = front_hip / 4 # From pattern making book
|
||||
back_extention = crotch_extention - front_extention
|
||||
|
||||
length, cuff_len = design['length']['v'], design['cuff']['cuff_len']['v']
|
||||
if design['cuff']['type']['v']:
|
||||
if length - cuff_len < design['length']['range'][0]: # Min length from paramss
|
||||
# Cannot be longer then a pant
|
||||
cuff_len = length - design['length']['range'][0]
|
||||
# Include the cuff into the overall length,
|
||||
# unless the requested length is too short to fit the cuff
|
||||
# (to avoid negative length)
|
||||
length -= cuff_len
|
||||
length *= body['_leg_length']
|
||||
cuff_len *= body['_leg_length']
|
||||
|
||||
self.front = PantPanel(
|
||||
f'pant_f_{tag}', body, design,
|
||||
length=length,
|
||||
waist=(waist - waist_back) / 2,
|
||||
hips=(body['hips'] - body['hip_back_width']) / 2,
|
||||
hips_depth=hips_depth,
|
||||
dart_position = body['bust_points'] / 2,
|
||||
crotch_width=front_extention,
|
||||
match_top_int_to=(body['waist'] - body['waist_back_width']) / 2
|
||||
).translate_by([0, body['_waist_level'] - 5, 25])
|
||||
self.back = PantPanel(
|
||||
f'pant_b_{tag}', body, design,
|
||||
length=length,
|
||||
waist=waist_back / 2,
|
||||
hips=body['hip_back_width'] / 2,
|
||||
hips_depth=hips_depth,
|
||||
hipline_ext=1.1,
|
||||
dart_position = body['bum_points'] / 2,
|
||||
crotch_width=back_extention,
|
||||
match_top_int_to=body['waist_back_width'] / 2,
|
||||
double_dart=True
|
||||
).translate_by([0, body['_waist_level'] - 5, -20])
|
||||
|
||||
self.stitching_rules = pyg.Stitches(
|
||||
(self.front.interfaces['outside'], self.back.interfaces['outside']),
|
||||
(self.front.interfaces['inside'], self.back.interfaces['inside'])
|
||||
)
|
||||
|
||||
# add a cuff
|
||||
# TODOLOW This process is the same for sleeves -- make a function?
|
||||
if design['cuff']['type']['v']:
|
||||
|
||||
pant_bottom = pyg.Interface.from_multiple(
|
||||
self.front.interfaces['bottom'],
|
||||
self.back.interfaces['bottom'])
|
||||
|
||||
# Copy to avoid editing original design dict
|
||||
cdesign = deepcopy(design)
|
||||
cdesign['cuff']['b_width'] = {}
|
||||
cdesign['cuff']['b_width']['v'] = pant_bottom.edges.length() / design['cuff']['top_ruffle']['v']
|
||||
cdesign['cuff']['cuff_len']['v'] = cuff_len
|
||||
|
||||
# Init
|
||||
cuff_class = getattr(bands, cdesign['cuff']['type']['v'])
|
||||
self.cuff = cuff_class(f'pant_{tag}', cdesign)
|
||||
|
||||
# Position
|
||||
self.cuff.place_by_interface(
|
||||
self.cuff.interfaces['top'],
|
||||
pant_bottom,
|
||||
gap=5,
|
||||
alignment='left'
|
||||
)
|
||||
|
||||
# Stitch
|
||||
self.stitching_rules.append((
|
||||
pant_bottom,
|
||||
self.cuff.interfaces['top'])
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'crotch_f': self.front.interfaces['crotch'],
|
||||
'crotch_b': self.back.interfaces['crotch'],
|
||||
'top_f': self.front.interfaces['top'],
|
||||
'top_b': self.back.interfaces['top']
|
||||
}
|
||||
|
||||
def length(self):
|
||||
if self.design['pants']['cuff']['type']['v']:
|
||||
return self.front.length() + self.cuff.length()
|
||||
|
||||
return self.front.length()
|
||||
|
||||
class Pants(BaseBottoms):
|
||||
def __init__(self, body, design, rise=None) -> None:
|
||||
super().__init__(body, design)
|
||||
|
||||
self.right = PantsHalf('r', body, design, rise)
|
||||
self.left = PantsHalf('l', body, design, rise).mirror()
|
||||
|
||||
self.stitching_rules = pyg.Stitches(
|
||||
(self.right.interfaces['crotch_f'], self.left.interfaces['crotch_f']),
|
||||
(self.right.interfaces['crotch_b'], self.left.interfaces['crotch_b']),
|
||||
)
|
||||
|
||||
self.interfaces = {
|
||||
'top_f': pyg.Interface.from_multiple(
|
||||
self.right.interfaces['top_f'], self.left.interfaces['top_f']),
|
||||
'top_b': pyg.Interface.from_multiple(
|
||||
self.right.interfaces['top_b'], self.left.interfaces['top_b']),
|
||||
# Some are reversed for correct connection
|
||||
'top': pyg.Interface.from_multiple( # around the body starting from front right
|
||||
self.right.interfaces['top_f'].flip_edges(),
|
||||
self.left.interfaces['top_f'].reverse(with_edge_dir_reverse=True),
|
||||
self.left.interfaces['top_b'].flip_edges(),
|
||||
self.right.interfaces['top_b'].reverse(with_edge_dir_reverse=True), # Flips the edges and restores the direction
|
||||
)
|
||||
}
|
||||
|
||||
def get_rise(self):
|
||||
return self.right.get_rise()
|
||||
|
||||
def length(self):
|
||||
return self.right.length()
|
||||
|
||||
64
assets/garment_programs/shapes.py
Normal 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
|
||||
151
assets/garment_programs/skirt_levels.py
Normal 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
|
||||
512
assets/garment_programs/skirt_paneled.py
Normal 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()
|
||||
|
||||
373
assets/garment_programs/sleeves.py
Normal 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()
|
||||
150
assets/garment_programs/stats_utils.py
Normal 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
|
||||
|
||||
116
assets/garment_programs/tee.py
Normal 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
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/img/err_dress_20s.png
Normal file
|
After Width: | Height: | Size: 864 KiB |
BIN
assets/img/err_dress_30s.png
Normal file
|
After Width: | Height: | Size: 911 KiB |
BIN
assets/img/err_dress_50s.png
Normal file
|
After Width: | Height: | Size: 867 KiB |
BIN
assets/img/err_js.png
Normal file
|
After Width: | Height: | Size: 832 KiB |
BIN
assets/img/err_red_modern.png
Normal file
|
After Width: | Height: | Size: 956 KiB |
BIN
assets/img/err_regency.png
Normal file
|
After Width: | Height: | Size: 899 KiB |
BIN
assets/img/fabric_texture.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
assets/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
20
assets/img/ggg_outline_mean_all.svg
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
4
assets/img/ggg_outline_nana.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/img/header.png
Normal file
|
After Width: | Height: | Size: 591 KiB |
BIN
assets/img/millimiter_paper_1500_900.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/img/millimiter_paper_1500_900_bk.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/img/neural_symbolic-teaser.png
Normal file
|
After Width: | Height: | Size: 5.1 MiB |
19
assets/img/siggraph_logo_thick_connection.svg
Normal 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 |
BIN
assets/test_img/examples/0001.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/test_img/examples/0002.jpeg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/test_img/examples/0003.jpg
Normal file
|
After Width: | Height: | Size: 270 KiB |
BIN
assets/test_img/examples/0004.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
assets/test_img/examples/0005.jpg
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
assets/test_img/examples/0006.jpeg
Normal file
|
After Width: | Height: | Size: 378 KiB |
8
assets/test_text/examples.json
Normal 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"
|
||||
]
|
||||
54
data_utils/align_bodies.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Aling body models s.t. they stand exactly on the plane y=0 and save as a new data"""
|
||||
import igl
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
import trimesh
|
||||
|
||||
from pygarment.data_config import Properties
|
||||
|
||||
def load_mesh(path):
|
||||
v, f = igl.read_triangle_mesh(str(path))
|
||||
return v, f.flatten(), f
|
||||
|
||||
def get_shift_param(body_vertices):
|
||||
v_body_arr = np.array(body_vertices)
|
||||
min_y = (min(v_body_arr[:, 1]))
|
||||
if min_y < 0:
|
||||
return abs(min_y)
|
||||
return 0.0
|
||||
|
||||
def save_mesh(path, v, f):
|
||||
igl.write_triangle_mesh(str(path), v=v, f=f, force_ascii=False)
|
||||
|
||||
|
||||
def process_body(path_in, path_out):
|
||||
|
||||
body_vertices, _, body_faces = load_mesh(path_in) # self.paths.in_body_obj)
|
||||
shift_y = get_shift_param(body_vertices)
|
||||
|
||||
# body_vertices = body_vertices * b_scale
|
||||
if shift_y:
|
||||
body_vertices[:, 1] = body_vertices[:, 1] + shift_y
|
||||
|
||||
save_mesh(path_out, body_vertices, body_faces)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
system_paths = Properties('./system.json')
|
||||
|
||||
# body_folder_path = Path(system_paths['body_samples_path']) / 'body_shapes_and_measures_2023-12-30'
|
||||
# body_objs_path = body_folder_path / 'meshes'
|
||||
body_objs_path = Path('./assets/bodies')
|
||||
# out_path = body_folder_path / 'meshes_aligned'
|
||||
out_path = Path('./assets/bodies_aligned')
|
||||
out_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# loop over all meshes
|
||||
for file in body_objs_path.iterdir():
|
||||
if '.obj' in file.name:
|
||||
process_body(file, out_path / file.name)
|
||||
|
||||
print(file.name)
|
||||
|
||||
|
||||
|
||||
25
data_utils/gather_renders.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""In simulated dataset, gather all the scene images in one folder"""
|
||||
|
||||
import pygarment.data_config as config
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
from pattern_data_sim import gather_renders
|
||||
|
||||
|
||||
system_props = config.Properties('./system.json')
|
||||
dataset = 'unpacking_test'
|
||||
datapaths = [
|
||||
Path(system_props['output']) / dataset / 'default_body',
|
||||
Path(system_props['output']) / dataset / 'random_body'
|
||||
]
|
||||
|
||||
for datapath in datapaths:
|
||||
# Check packing
|
||||
tar_path = datapath / 'data.tar.gz'
|
||||
if tar_path.exists():
|
||||
shutil.unpack_archive(tar_path, datapath)
|
||||
# Finally -- clean up
|
||||
tar_path.unlink()
|
||||
|
||||
gather_renders(datapath)
|
||||
117
data_utils/pattern_data_sim.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
Run or Resume simulation of a pattern dataset with MayaPy standalone mode
|
||||
Note that this module is executed in Maya (or by mayapy)
|
||||
|
||||
How to use:
|
||||
* fill out system.json with approppriate paths
|
||||
Running itself:
|
||||
./datasim.py --data <dataset folder name> --minibatch <size> --config <simulation_rendering_configuration.json>
|
||||
|
||||
"""
|
||||
import argparse
|
||||
import sys
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
# My modules
|
||||
import pygarment.data_config as data_config
|
||||
import pygarment.meshgen.datasim_utils as sim
|
||||
|
||||
|
||||
def get_command_args():
|
||||
"""command line arguments to control the run"""
|
||||
# https://stackoverflow.com/questions/40001892/reading-named-command-arguments
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--data', '-d', help='name of dataset folder', type=str)
|
||||
parser.add_argument('--config', '-c', help='name of .json file with desired simulation&rendering config', type=str,
|
||||
default=None)
|
||||
parser.add_argument('--minibatch', '-b', help='number of examples to simulate in this run', type=int, default=None)
|
||||
parser.add_argument('--default_body', action='store_true', help='run dataset on default body')
|
||||
parser.add_argument('--caching', action='store_true', help='cache intermediate simulation')
|
||||
parser.add_argument('--rewrite_config', action='store_true', help='cache intermediate simulation')
|
||||
|
||||
args = parser.parse_args()
|
||||
print(args)
|
||||
|
||||
return args
|
||||
|
||||
def gather_renders(out_data_path: Path, verbose=False):
|
||||
renders_path = out_data_path / 'renders'
|
||||
renders_path.mkdir(exist_ok=True)
|
||||
|
||||
render_files = list(out_data_path.glob('**/*render*.png'))
|
||||
for file in render_files:
|
||||
try:
|
||||
shutil.copy(str(file), str(renders_path))
|
||||
except shutil.SameFileError:
|
||||
if verbose:
|
||||
print(f'File {file} already exists')
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
command_args = get_command_args()
|
||||
system_config = data_config.Properties('./system.json')
|
||||
|
||||
# ------ Dataset ------
|
||||
dataset = command_args.data
|
||||
datapath = Path(system_config['datasets_path']) / dataset
|
||||
init_dataset_file = datapath / 'dataset_properties.yaml'
|
||||
|
||||
# Create dataset_file in correct folder (default_body or random_body)
|
||||
body_type = 'default_body' if command_args.default_body else 'random_body'
|
||||
datapath = datapath / body_type / 'data' # Overwrite datapath to specific body type
|
||||
|
||||
output_path = Path(system_config['datasets_sim']) / dataset / body_type
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
dataset_file_body = output_path / f'dataset_properties_{body_type}.yaml'
|
||||
if not dataset_file_body.exists():
|
||||
shutil.copy(str(init_dataset_file), str(dataset_file_body))
|
||||
dataset_file = dataset_file_body
|
||||
|
||||
props = data_config.Properties(dataset_file_body)
|
||||
if 'frozen' in props and props['frozen']: #Where is this set?
|
||||
# avoid accidential re-runs of data
|
||||
print('Warning: dataset is frozen, processing is skipped')
|
||||
sys.exit(0)
|
||||
|
||||
# ------- Defining sim props -----
|
||||
props.set_basic(data_folder=dataset) # in case data properties are from other dataset/folder, update info
|
||||
if command_args.config is not None:
|
||||
props.merge(
|
||||
Path(system_config['sim_configs_path']) / command_args.config,
|
||||
re_write=command_args.rewrite_config) # Re-write sim config only explicitly
|
||||
|
||||
# ----- Main loop ----------
|
||||
finished = sim.batch_sim(
|
||||
datapath,
|
||||
output_path,
|
||||
props,
|
||||
run_default_body=command_args.default_body,
|
||||
num_samples=command_args.minibatch, # run in mini-batch if requested
|
||||
caching=command_args.caching, force_restart=False)
|
||||
|
||||
# ----- Try and resim fails once -----
|
||||
if finished:
|
||||
# NOTE: Could be larger than a regular batch
|
||||
finished = sim.resim_fails(
|
||||
datapath,
|
||||
output_path,
|
||||
props,
|
||||
run_default_body=command_args.default_body,
|
||||
caching=command_args.caching)
|
||||
|
||||
props.add_sys_info() # Save system information
|
||||
props.serialize(dataset_file)
|
||||
|
||||
# ------ Gather renders -------
|
||||
gather_renders(output_path)
|
||||
|
||||
# -------- fin --------
|
||||
if finished:
|
||||
# finished processing the dataset
|
||||
print('Dataset processing finished')
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1) # not finished dataset processing
|
||||
34
data_utils/pattern_data_sim_runner.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# This script is needed to autorestart execution of simulating datapoints for a dataset
|
||||
# in case of crashes and/or using mini-batches
|
||||
# sh ./datasim_runner.sh 3>&1 2>&1 > C:\Users\out.txt (path to output file)
|
||||
|
||||
dataset_name=my_dataset
|
||||
config=default_sim_props.yaml
|
||||
sim_default_bodies=false
|
||||
batch_size=100
|
||||
|
||||
# -- Main calls --
|
||||
ret_code=1
|
||||
STARTTIME=$(date +%s)
|
||||
|
||||
while [ $ret_code != 0 ] # failed for any reason
|
||||
do
|
||||
if [ "$sim_default_bodies" = "true" ]; then
|
||||
python ./pattern_data_sim.py --data $dataset_name --default_body --config $config -b $batch_size
|
||||
else
|
||||
python ./pattern_data_sim.py --data $dataset_name --config $config -b $batch_size
|
||||
fi
|
||||
|
||||
ret_code=$?
|
||||
if [ $ret_code -eq 0 ]; then
|
||||
echo "The execution completed successfully."
|
||||
else
|
||||
echo "The execution failed with an error (ret_code: $ret_code)."
|
||||
fi
|
||||
|
||||
ENDTIME=$(date +%s)
|
||||
T=$(($ENDTIME - $STARTTIME))
|
||||
echo "It took ${T} seconds to complete this task so far..."
|
||||
printf "Pretty format: %02dd %02dh %02dm %02ds\n" "$(($T/86400))" "$(($T/3600%24))" "$(($T/60%60))" "$(($T%60))"
|
||||
done
|
||||
229
data_utils/pattern_fitter.py
Normal file
@@ -0,0 +1,229 @@
|
||||
"""
|
||||
Fitting one sewing pattern design to a set of various body shapes
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
import shutil
|
||||
import time
|
||||
import traceback
|
||||
import argparse
|
||||
|
||||
# Custom
|
||||
from pygarment.data_config import Properties
|
||||
from assets.garment_programs.meta_garment import MetaGarment
|
||||
from assets.bodies.body_params import BodyParameters
|
||||
|
||||
def get_command_args():
|
||||
"""command line arguments to control the run"""
|
||||
# https://stackoverflow.com/questions/40001892/reading-named-command-arguments
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('design_file', help='Path to design parameters file to be used to fit to the bodies', type=str)
|
||||
parser.add_argument('--batch_id', '-b', help='id of a sampling batch', type=int, default=None)
|
||||
parser.add_argument('--size', '-s', help='size of a sample', type=int, default=10)
|
||||
parser.add_argument('--name', '-n', help='Name of the dataset', type=str, default='design_fit')
|
||||
parser.add_argument('--replicate', '-re', help='Name of the dataset to re-generate. If set, other arguments are ignored', type=str, default=None)
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
print('Commandline arguments: ', args)
|
||||
|
||||
return args
|
||||
|
||||
def _create_data_folder(properties, path=Path('')):
|
||||
""" Create a new directory to put dataset in
|
||||
& generate appropriate name & update dataset properties
|
||||
"""
|
||||
if 'data_folder' in properties: # will this work?
|
||||
# => regenerating from existing data
|
||||
properties['name'] = properties['data_folder'] + '_regen'
|
||||
data_folder = properties['name']
|
||||
else:
|
||||
data_folder = properties['name']
|
||||
|
||||
# make unique
|
||||
data_folder += '_' + datetime.now().strftime('%y%m%d-%H-%M-%S')
|
||||
properties['data_folder'] = data_folder
|
||||
path_with_dataset = path / data_folder
|
||||
path_with_dataset.mkdir(parents=True)
|
||||
|
||||
default_folder = path_with_dataset / 'default_body'
|
||||
body_folder = path_with_dataset / 'random_body'
|
||||
|
||||
default_folder.mkdir(parents=True, exist_ok=True)
|
||||
body_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return path_with_dataset, default_folder, body_folder
|
||||
|
||||
|
||||
def _gather_body_options(body_path: Path):
|
||||
objs_path = body_path / 'measurements'
|
||||
|
||||
bodies = []
|
||||
for file in objs_path.iterdir():
|
||||
|
||||
# Get name
|
||||
b_name = file.stem.split('_')[0]
|
||||
bodies.append({})
|
||||
|
||||
# Get obj options
|
||||
bodies[-1]['objs'] = dict(
|
||||
straight=f'meshes/{b_name}_straight.obj',
|
||||
apart=f'meshes/{b_name}_apart.obj', )
|
||||
|
||||
# Get measurements
|
||||
bodies[-1]['mes'] = f'measurements/{b_name}.yaml'
|
||||
|
||||
return bodies
|
||||
|
||||
|
||||
def body_sample(idx, bodies: dict, path: Path, straight=True):
|
||||
|
||||
body_i = bodies[idx]
|
||||
|
||||
mes_file = body_i['mes']
|
||||
obj_file = body_i['objs']['straight'] if straight else body_i['objs']['apart']
|
||||
|
||||
body = BodyParameters(path / mes_file)
|
||||
body.params['body_sample'] = (path / obj_file).stem
|
||||
|
||||
return body
|
||||
|
||||
|
||||
def _save_sample(piece, body, new_design, folder, verbose=False):
|
||||
|
||||
pattern = piece.assembly()
|
||||
# Save as json file
|
||||
folder = pattern.serialize(
|
||||
folder,
|
||||
tag='',
|
||||
to_subfolder=True,
|
||||
with_3d=False, with_text=False, view_ids=False)
|
||||
|
||||
body.save(folder)
|
||||
with open(Path(folder) / 'design_params.yaml', 'w') as f:
|
||||
yaml.dump(
|
||||
{'design': new_design},
|
||||
f,
|
||||
default_flow_style=False,
|
||||
sort_keys=False
|
||||
)
|
||||
if verbose:
|
||||
print(f'Saved {piece.name}')
|
||||
|
||||
|
||||
|
||||
def generate(path, properties, sys_paths, verbose=False):
|
||||
"""Generates a synthetic dataset of patterns with given properties
|
||||
Params:
|
||||
path : path to folder to put a new dataset into
|
||||
props : an instance of DatasetProperties class
|
||||
requested properties of the dataset
|
||||
"""
|
||||
path = Path(path)
|
||||
gen_config = properties['generator']['config']
|
||||
gen_stats = properties['generator']['stats']
|
||||
body_samples_path = Path(sys_paths['body_samples_path']) / properties['body_samples']
|
||||
body_options = _gather_body_options(body_samples_path)
|
||||
|
||||
# create data folder
|
||||
data_folder, default_path, body_sample_path = _create_data_folder(properties, path)
|
||||
default_sample_data = default_path / 'data'
|
||||
body_sample_data = body_sample_path / 'data'
|
||||
|
||||
# generate data
|
||||
start_time = time.time()
|
||||
|
||||
# Load design
|
||||
with open(properties['design_file'], 'r') as f:
|
||||
design = yaml.safe_load(f)['design']
|
||||
|
||||
# On default body
|
||||
default_body = BodyParameters(Path(sys_paths['bodies_default_path']) / (properties['body_default'] + '.yaml'))
|
||||
piece_default = MetaGarment(properties['body_default'], default_body, design)
|
||||
_save_sample(piece_default, default_body, design, default_sample_data, verbose=verbose)
|
||||
|
||||
|
||||
for i in range(properties['size']):
|
||||
# log properties every time
|
||||
properties.serialize(data_folder / 'dataset_properties.yaml')
|
||||
|
||||
try:
|
||||
# On random body shape
|
||||
rand_body = body_sample(
|
||||
i + properties['body_sample_start_id'],
|
||||
body_options,
|
||||
body_samples_path,
|
||||
straight='Pants' != design['meta']['bottom']['v'])
|
||||
name = rand_body.params['body_sample']
|
||||
|
||||
piece_shaped = MetaGarment(name, rand_body, design)
|
||||
|
||||
# Save samples
|
||||
_save_sample(piece_shaped, rand_body, design, body_sample_data, verbose=verbose)
|
||||
except KeyboardInterrupt: # Return immediately with whatever is ready
|
||||
return default_path, body_sample_path
|
||||
except BaseException as e:
|
||||
print(f'{name} failed')
|
||||
traceback.print_exc()
|
||||
print(e)
|
||||
|
||||
continue
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
gen_stats['generation_time'] = f'{elapsed:.3f} s'
|
||||
|
||||
# log properties
|
||||
properties.serialize(data_folder / 'dataset_properties.yaml')
|
||||
|
||||
return default_path, body_sample_path
|
||||
|
||||
|
||||
def gather_visuals(path, verbose=False):
|
||||
vis_path = Path(path) / 'patterns_vis'
|
||||
vis_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for p in path.rglob("*.png"):
|
||||
try:
|
||||
shutil.copy(p, vis_path)
|
||||
except shutil.SameFileError:
|
||||
if verbose:
|
||||
print('File {} already exists'.format(p.name))
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
system_props = Properties('./system.json')
|
||||
|
||||
args = get_command_args()
|
||||
|
||||
if args.replicate is not None:
|
||||
props = Properties(
|
||||
Path(system_props['datasets_path']) / args.replicate / 'dataset_properties.yaml',
|
||||
True)
|
||||
else:
|
||||
props = Properties()
|
||||
props.set_basic(
|
||||
design_file=args.design_file,
|
||||
body_default='mean_all',
|
||||
body_samples='5000_body_shapes_and_measures',
|
||||
body_sample_start_id=0,
|
||||
name=f'{args.name}_{args.size}' if not args.batch_id else f'{args.name}_{args.size}_{args.batch_id}',
|
||||
size=args.size,
|
||||
to_subfolders=True)
|
||||
props.set_section_config('generator')
|
||||
|
||||
# Generator
|
||||
default_path, body_sample_path = generate(
|
||||
system_props['datasets_path'], props, system_props, verbose=False)
|
||||
|
||||
# Gather the pattern images separately
|
||||
gather_visuals(default_path)
|
||||
gather_visuals(body_sample_path)
|
||||
|
||||
# At the end -- it takes some time to gather the info
|
||||
props.add_sys_info()
|
||||
|
||||
print('Data generation completed!')
|
||||
336
data_utils/pattern_sampler.py
Normal file
@@ -0,0 +1,336 @@
|
||||
"""
|
||||
Create a random sample of sewing pattern designs and fit each
|
||||
to a neutral and a random body shape
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
import shutil
|
||||
import time
|
||||
import random
|
||||
import string
|
||||
import traceback
|
||||
import argparse
|
||||
|
||||
# Custom
|
||||
from pygarment.data_config import Properties
|
||||
from assets.garment_programs.meta_garment import MetaGarment, IncorrectElementConfiguration
|
||||
from assets.bodies.body_params import BodyParameters
|
||||
import pygarment as pyg
|
||||
import assets.garment_programs.stats_utils as stats_utils
|
||||
|
||||
def get_command_args():
|
||||
"""command line arguments to control the run"""
|
||||
# https://stackoverflow.com/questions/40001892/reading-named-command-arguments
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--batch_id', '-b', help='id of a sampling batch', type=int, default=None)
|
||||
parser.add_argument('--size', '-s', help='size of a sample', type=int, default=10)
|
||||
parser.add_argument('--name', '-n', help='Name of the dataset', type=str, default='data')
|
||||
parser.add_argument('--replicate', '-re', help='Name of the dataset to re-generate. If set, other arguments are ignored', type=str, default=None)
|
||||
|
||||
args = parser.parse_args()
|
||||
print('Commandline arguments: ', args)
|
||||
|
||||
return args
|
||||
|
||||
# Utils
|
||||
def _create_data_folder(properties, path=Path('')):
|
||||
""" Create a new directory to put dataset in
|
||||
& generate appropriate name & update dataset properties
|
||||
"""
|
||||
if 'data_folder' in properties: # will this work?
|
||||
# => regenerating from existing data
|
||||
properties['name'] = properties['data_folder'] + '_regen'
|
||||
data_folder = properties['name']
|
||||
else:
|
||||
data_folder = properties['name']
|
||||
|
||||
# make unique
|
||||
data_folder += '_' + datetime.now().strftime('%y%m%d-%H-%M-%S')
|
||||
properties['data_folder'] = data_folder
|
||||
path_with_dataset = path / data_folder
|
||||
path_with_dataset.mkdir(parents=True)
|
||||
|
||||
default_folder = path_with_dataset / 'default_body'
|
||||
body_folder = path_with_dataset / 'random_body'
|
||||
|
||||
default_folder.mkdir(parents=True, exist_ok=True)
|
||||
body_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return path_with_dataset, default_folder, body_folder
|
||||
|
||||
def gather_body_options(body_path: Path):
|
||||
objs_path = body_path / 'measurements'
|
||||
|
||||
bodies = {}
|
||||
for file in objs_path.iterdir():
|
||||
|
||||
# Get name
|
||||
b_name = file.stem.split('_')[0]
|
||||
bodies[b_name] = {}
|
||||
|
||||
# Get obj options
|
||||
bodies[b_name]['objs'] = dict(
|
||||
straight=f'meshes/{b_name}_straight.obj',
|
||||
apart=f'meshes/{b_name}_apart.obj', )
|
||||
|
||||
# Get measurements
|
||||
bodies[b_name]['mes'] = f'measurements/{b_name}.yaml'
|
||||
|
||||
return bodies
|
||||
|
||||
def _id_generator(size=10, chars=string.ascii_uppercase + string.digits):
|
||||
"""Generate a random string of a given size, see
|
||||
https://stackoverflow.com/questions/2257441/random-string-generation-with-upper-case-letters-and-digits
|
||||
"""
|
||||
return ''.join(random.choices(chars, k=size))
|
||||
|
||||
def body_sample(bodies: dict, path: Path, straight=True):
|
||||
|
||||
rand_name = random.sample(list(bodies.keys()), k=1)
|
||||
body_i = bodies[rand_name[0]]
|
||||
|
||||
mes_file = body_i['mes']
|
||||
obj_file = body_i['objs']['straight'] if straight else body_i['objs']['apart']
|
||||
|
||||
body = BodyParameters(path / mes_file)
|
||||
body.params['body_sample'] = (path / obj_file).stem
|
||||
|
||||
return body
|
||||
|
||||
def _save_sample(piece, body, new_design, folder, verbose=False):
|
||||
|
||||
pattern = piece.assembly()
|
||||
# Save as json file
|
||||
folder = pattern.serialize(
|
||||
folder,
|
||||
tag='',
|
||||
to_subfolder=True,
|
||||
with_3d=False, with_text=False, view_ids=False)
|
||||
|
||||
body.save(folder)
|
||||
with open(Path(folder) / 'design_params.yaml', 'w') as f:
|
||||
yaml.dump(
|
||||
{'design': new_design},
|
||||
f,
|
||||
default_flow_style=False,
|
||||
sort_keys=False
|
||||
)
|
||||
if verbose:
|
||||
print(f'Saved {piece.name}')
|
||||
|
||||
return pattern
|
||||
|
||||
def has_pants(design):
|
||||
return 'Pants' == design['meta']['bottom']['v']
|
||||
|
||||
def gather_visuals(path, verbose=False):
|
||||
vis_path = Path(path) / 'patterns_vis'
|
||||
vis_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for p in path.rglob("*.png"):
|
||||
try:
|
||||
shutil.copy(p, vis_path)
|
||||
except shutil.SameFileError:
|
||||
if verbose:
|
||||
print('File {} already exists'.format(p.name))
|
||||
pass
|
||||
|
||||
# Quality filter
|
||||
def assert_param_combinations(design, filter_belts=True):
|
||||
"""Check for some known invalid parameter combinations cases"""
|
||||
upper_name = design['meta']['upper']['v']
|
||||
lower_name = design['meta']['bottom']['v']
|
||||
belt_name = design['meta']['wb']['v']
|
||||
|
||||
if upper_name: # No issues with garments that can hang on shoulders
|
||||
return
|
||||
|
||||
# Empty patterns and singular belts
|
||||
if not lower_name:
|
||||
if filter_belts or not belt_name:
|
||||
raise IncorrectElementConfiguration('ERROR::IncorrectParams::Empty pattern or singular belt')
|
||||
return
|
||||
|
||||
# Cases when lower name is present (and maybe a belt):
|
||||
# All pants and pencils are okay
|
||||
if lower_name in ['Pants', 'PencilSkirt']:
|
||||
return
|
||||
|
||||
# -- Sliding issues --
|
||||
# NOTE: Checks are conservative, so some sliding issues might be present nontheless
|
||||
# Skirt 2 & skirts of top of it -- uses ruffles and belt is too wide if even present
|
||||
if (lower_name == 'Skirt2'
|
||||
or lower_name == 'GodetSkirt' and design['godet-skirt']['base']['v'] == 'Skirt2'
|
||||
or lower_name == 'SkirtLevels' and design['levels-skirt']['base']['v'] == 'Skirt2'
|
||||
):
|
||||
|
||||
if (design['skirt']['ruffle']['v'] > 1 and (not belt_name or design['waistband']['waist']['v'] > 1.)):
|
||||
raise IncorrectElementConfiguration('ERROR::IncorrectParams::Skirt2 ruffles + belt')
|
||||
|
||||
# Flare skirts & skirts on top of it -- no belt + too wide / too long
|
||||
flare_skirts = ['SkirtCircle', 'AsymmSkirtCircle', 'SkirtManyPanels']
|
||||
if (lower_name in flare_skirts
|
||||
or lower_name == 'SkirtLevels' and design['levels-skirt']['base']['v'] in flare_skirts
|
||||
):
|
||||
# if Fitted belt of enough width not present -- check if "heavy"
|
||||
if (not belt_name
|
||||
or design['waistband']['waist']['v'] > 1.
|
||||
or design['waistband']['width']['v'] <= 0.25
|
||||
):
|
||||
length_param = design['levels-skirt' if lower_name == 'SkirtLevels' else 'flare-skirt']['length']['v']
|
||||
if length_param > 0.5 or design['flare-skirt']['suns']['v'] > 0.75:
|
||||
raise IncorrectElementConfiguration('ERROR::IncorrectParams::Flare skirts + belt')
|
||||
|
||||
|
||||
# Generation loop
|
||||
def generate(path, properties, sys_paths, verbose=False):
|
||||
"""Generates a synthetic dataset of patterns with given properties
|
||||
Params:
|
||||
path : path to folder to put a new dataset into
|
||||
props : an instance of DatasetProperties class
|
||||
requested properties of the dataset
|
||||
"""
|
||||
path = Path(path)
|
||||
gen_config = properties['generator']['config']
|
||||
gen_stats = properties['generator']['stats']
|
||||
body_samples_path = Path(sys_paths['body_samples_path']) / properties['body_samples']
|
||||
body_options = gather_body_options(body_samples_path)
|
||||
|
||||
# create data folder
|
||||
data_folder, default_path, body_sample_path = _create_data_folder(properties, path)
|
||||
default_sample_data = default_path / 'data'
|
||||
body_sample_data = body_sample_path / 'data'
|
||||
|
||||
# init random seed
|
||||
if 'random_seed' not in gen_config or gen_config['random_seed'] is None:
|
||||
gen_config['random_seed'] = int(time.time())
|
||||
print(f'Random seed is {gen_config["random_seed"]}')
|
||||
random.seed(gen_config['random_seed'])
|
||||
|
||||
# generate data
|
||||
start_time = time.time()
|
||||
|
||||
default_body = BodyParameters(Path(sys_paths['bodies_default_path']) / (properties['body_default'] + '.yaml'))
|
||||
sampler = pyg.DesignSampler(properties['design_file'])
|
||||
for i in range(properties['size']):
|
||||
# log properties every time
|
||||
properties.serialize(data_folder / 'dataset_properties.yaml')
|
||||
|
||||
# Redo sampling untill success
|
||||
for _ in range(100): # Putting a limit on re-tries to avoid infinite loops
|
||||
new_design = sampler.randomize()
|
||||
name = f'rand_{_id_generator()}'
|
||||
try:
|
||||
if verbose:
|
||||
print(f'{name} saving design params for debug')
|
||||
with open(Path('./Logs') / f'{name}_design_params.yaml', 'w') as f:
|
||||
yaml.dump(
|
||||
{'design': new_design},
|
||||
f,
|
||||
default_flow_style=False,
|
||||
sort_keys=False
|
||||
)
|
||||
|
||||
# Preliminary checks
|
||||
assert_param_combinations(new_design)
|
||||
|
||||
# On default body
|
||||
piece_default = MetaGarment(name, default_body, new_design)
|
||||
piece_default.assert_total_length() # Check final length correctnesss
|
||||
|
||||
# Straight/apart legs pose
|
||||
def_obj_name = properties['body_default']
|
||||
if has_pants(new_design):
|
||||
def_obj_name += '_apart'
|
||||
default_body.params['body_sample'] = def_obj_name
|
||||
|
||||
# On random body shape
|
||||
rand_body = body_sample(
|
||||
body_options,
|
||||
body_samples_path,
|
||||
straight=not has_pants(new_design))
|
||||
piece_shaped = MetaGarment(name, rand_body, new_design)
|
||||
piece_shaped.assert_total_length() # Check final length correctness
|
||||
|
||||
if piece_default.is_self_intersecting() or piece_shaped.is_self_intersecting():
|
||||
if verbose:
|
||||
print(f'{piece_default.name} is self-intersecting!!')
|
||||
continue # Redo the randomization
|
||||
|
||||
# Save samples
|
||||
pattern = _save_sample(piece_default, default_body, new_design, default_sample_data, verbose=verbose)
|
||||
_save_sample(piece_shaped, rand_body, new_design, body_sample_data, verbose=verbose)
|
||||
|
||||
stats_utils.count_panels(pattern, props)
|
||||
stats_utils.garment_type(name, new_design, props)
|
||||
|
||||
break # Stop generation
|
||||
except KeyboardInterrupt: # Return immediately with whatever is ready
|
||||
return default_path, body_sample_path
|
||||
except BaseException as e:
|
||||
print(f'{name} failed')
|
||||
if verbose:
|
||||
traceback.print_exc()
|
||||
print(e)
|
||||
|
||||
# Check empty folder
|
||||
if (default_sample_data / name).exists():
|
||||
print('Generate::Info::Removed empty folder after unsuccessful sampling attempt', default_sample_data / name)
|
||||
shutil.rmtree(default_sample_data / name, ignore_errors=True)
|
||||
|
||||
if (body_sample_data / name).exists():
|
||||
print('Generate::Info::Removed empty folder after unsuccessful sampling attempt', body_sample_data / name)
|
||||
shutil.rmtree(body_sample_data / name, ignore_errors=True)
|
||||
|
||||
continue
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
gen_stats['generation_time'] = f'{elapsed:.3f} s'
|
||||
|
||||
# log properties
|
||||
props.stats_summary()
|
||||
properties.serialize(data_folder / 'dataset_properties.yaml')
|
||||
|
||||
return default_path, body_sample_path
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
system_props = Properties('./system.json')
|
||||
|
||||
args = get_command_args()
|
||||
|
||||
if args.replicate is not None:
|
||||
props = Properties(
|
||||
Path(system_props['datasets_path']) / args.replicate / 'dataset_properties.yaml',
|
||||
True)
|
||||
else: # New sample
|
||||
props = Properties()
|
||||
props.set_basic(
|
||||
design_file='./assets/design_params/default.yaml',
|
||||
body_default='mean_all',
|
||||
body_samples='5000_body_shapes_and_measures',
|
||||
size=args.size,
|
||||
name=f'{args.name}_{args.size}' if not args.batch_id else f'{args.name}_{args.size}_{args.batch_id}',
|
||||
to_subfolders=True)
|
||||
props.set_section_config('generator')
|
||||
props.set_section_stats(
|
||||
'generator',
|
||||
panel_count={},
|
||||
garment_types={},
|
||||
garment_types_summary=dict(main={}, style={})
|
||||
)
|
||||
|
||||
|
||||
# Generator
|
||||
default_path, body_sample_path = generate(
|
||||
system_props['datasets_path'], props, system_props, verbose=False)
|
||||
|
||||
# Gather the pattern images separately
|
||||
gather_visuals(default_path)
|
||||
gather_visuals(body_sample_path)
|
||||
|
||||
print('Data generation completed!')
|
||||
40
environment.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
name: d2g
|
||||
channels:
|
||||
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
|
||||
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
|
||||
dependencies:
|
||||
- python=3.9.19
|
||||
- numpy=1.26.4
|
||||
- scipy=1.13.1
|
||||
- pyyaml=6.0.2
|
||||
- svgwrite=1.4.3
|
||||
- psutil=6.0.0
|
||||
- matplotlib
|
||||
- svgpathtools
|
||||
- cairosvg=2.7.1
|
||||
- nicegui=2.15.0
|
||||
- trimesh
|
||||
- cgal
|
||||
- pip
|
||||
- pip:
|
||||
- --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
- torch==2.4.0+cu121
|
||||
- torchvision==0.19.0+cu121
|
||||
- torchaudio==2.4.0+cu121
|
||||
- transformers==4.46.2
|
||||
- tokenizers==0.20.3
|
||||
- accelerate==1.1.1
|
||||
- datasets==2.18.0
|
||||
- huggingface-hub==0.29.2
|
||||
- safetensors==0.5.3
|
||||
- tiktoken==0.9.0
|
||||
- peft==0.13.2
|
||||
- qwen-vl-utils==0.0.8
|
||||
- modelscope==1.18.0
|
||||
- pyrender==0.1.45
|
||||
- libigl==2.5.1
|
||||
- cgal==6.0.1.post202410241521
|
||||
- openai==1.54.4
|
||||
|
||||
|
||||
|
||||
46
gui.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import argparse
|
||||
from nicegui import ui, Client
|
||||
|
||||
# Custom
|
||||
from gui.callbacks import GUIState
|
||||
import gui.error_pages
|
||||
# GPT
|
||||
from lmm_utils.agent import Agent
|
||||
import asyncio
|
||||
agent=Agent(model_init=True)
|
||||
@ui.page('/')
|
||||
async def index(client: Client):
|
||||
global agent
|
||||
# Start the interface!
|
||||
gui_st = GUIState()
|
||||
gui_st.pattern_state.agent=agent
|
||||
# Connection end
|
||||
# https://github.com/zauberzeug/nicegui/discussions/1379
|
||||
|
||||
await client.disconnected()
|
||||
|
||||
print('Closed connection ', gui_st.pattern_state.id, '. Deleting files...')
|
||||
gui_st.release()
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser=argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'--host',
|
||||
help='Host address to start the gui server ,defaults to "127.0.0.0"',
|
||||
type=str,
|
||||
default='0.0.0.0'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--port',
|
||||
help='Use this port',
|
||||
type=str,
|
||||
default="8080"
|
||||
)
|
||||
args=parser.parse_args()
|
||||
ui.run(
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
reload=False,
|
||||
favicon='assets/img/favicon.ico',
|
||||
title='Design2GarmentCode: Turning Design Concepts to Tangible Garments Through Program Synthesis'
|
||||
)
|
||||
0
gui/__init__.py
Normal file
918
gui/callbacks.py
Normal file
@@ -0,0 +1,918 @@
|
||||
"""Callback functions & State info for Sewing Pattern Configurator """
|
||||
|
||||
# NOTE: NiceGUI reference: https://nicegui.io/
|
||||
|
||||
import asyncio
|
||||
import shutil
|
||||
import traceback
|
||||
from argparse import Namespace
|
||||
# Async execution of regular functions
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
from uuid import uuid4
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
from nicegui import ui, app, events
|
||||
|
||||
# Custom
|
||||
from .gui_pattern import GUIPattern
|
||||
|
||||
icon_github = """
|
||||
<svg viewbox="0 0 98 96" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0
|
||||
21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362
|
||||
0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015
|
||||
4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283
|
||||
0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571
|
||||
12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015
|
||||
13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89
|
||||
2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/>
|
||||
</svg>
|
||||
"""
|
||||
icon_arxiv = """<svg id="primary_logo_-_single_color_-_white" data-name="primary logo - single color - white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 246.978 110.119"><path d="M492.976,269.5l24.36-29.89c1.492-1.989,2.2-3.03,1.492-4.723a5.142,5.142,0,0,0-4.481-3.161h0a4.024,4.024,0,0,0-3.008,1.108L485.2,261.094Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M526.273,325.341,493.91,287.058l-.972,1.033-7.789-9.214-7.743-9.357-4.695,5.076a4.769,4.769,0,0,0,.015,6.53L520.512,332.2a3.913,3.913,0,0,0,3.137,1.192,4.394,4.394,0,0,0,4.027-2.818C528.4,328.844,527.6,327.133,526.273,325.341Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M479.215,288.087l6.052,6.485L458.714,322.7a2.98,2.98,0,0,1-2.275,1.194,3.449,3.449,0,0,1-3.241-2.144c-.513-1.231.166-3.15,1.122-4.168l.023-.024.021-.026,24.851-29.448m-.047-1.882-25.76,30.524c-1.286,1.372-2.084,3.777-1.365,5.5a4.705,4.705,0,0,0,4.4,2.914,4.191,4.191,0,0,0,3.161-1.563l27.382-29.007-7.814-8.372Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M427.571,255.154c1.859,0,3.1,1.24,3.985,3.453,1.062-2.213,2.568-3.453,4.694-3.453h14.878a4.062,4.062,0,0,1,4.074,4.074v7.828c0,2.656-1.327,4.074-4.074,4.074-2.656,0-4.074-1.418-4.074-4.074V263.3H436.515a2.411,2.411,0,0,0-2.656,2.745v27.188h10.007c2.658,0,4.074,1.329,4.074,4.074s-1.416,4.074-4.074,4.074h-26.39c-2.659,0-3.986-1.328-3.986-4.074s1.327-4.074,3.986-4.074h8.236V263.3h-7.263c-2.656,0-3.985-1.329-3.985-4.074,0-2.658,1.329-4.074,3.985-4.074Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M539.233,255.154c2.656,0,4.074,1.416,4.074,4.074v34.007h10.1c2.746,0,4.074,1.329,4.074,4.074s-1.328,4.074-4.074,4.074H524.8c-2.656,0-4.074-1.328-4.074-4.074s1.418-4.074,4.074-4.074h10.362V263.3h-8.533c-2.744,0-4.073-1.329-4.073-4.074,0-2.658,1.329-4.074,4.073-4.074Zm4.22-17.615a5.859,5.859,0,1,1-5.819-5.819A5.9,5.9,0,0,1,543.453,237.539Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M605.143,259.228a4.589,4.589,0,0,1-.267,1.594L590,298.9a3.722,3.722,0,0,1-3.721,2.48h-5.933a3.689,3.689,0,0,1-3.808-2.48l-15.055-38.081a3.23,3.23,0,0,1-.355-1.594,4.084,4.084,0,0,1,4.164-4.074,3.8,3.8,0,0,1,3.718,2.656l14.348,36.134,13.9-36.134a3.8,3.8,0,0,1,3.72-2.656A4.084,4.084,0,0,1,605.143,259.228Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M390.61,255.154c5.018,0,8.206,3.312,8.206,8.4v37.831H363.308a4.813,4.813,0,0,1-5.143-4.929V283.427a8.256,8.256,0,0,1,7-8.148l25.507-3.572v-8.4H362.306a4.014,4.014,0,0,1-4.141-4.074c0-2.87,2.143-4.074,4.355-4.074Zm.059,38.081V279.942l-24.354,3.4v9.9Z" transform="translate(-358.165 -223.27)" fill="#fff"/><path d="M448.538,224.52h.077c1,.024,2.236,1.245,2.589,1.669l.023.028.024.026,46.664,50.433a3.173,3.173,0,0,1-.034,4.336l-4.893,5.2-6.876-8.134L446.652,230.4c-1.508-2.166-1.617-2.836-1.191-3.858a3.353,3.353,0,0,1,3.077-2.02m0-1.25a4.606,4.606,0,0,0-4.231,2.789c-.705,1.692-.2,2.88,1.349,5.1l39.493,47.722,7.789,9.214,5.853-6.221a4.417,4.417,0,0,0,.042-6.042L452.169,225.4s-1.713-2.08-3.524-2.124Z" transform="translate(-358.165 -223.27)" fill="#fff"/></svg>"""
|
||||
|
||||
|
||||
# # Dracula color theme
|
||||
# theme_colors = Namespace(
|
||||
# primary='#BD93F9', # Light purple (primary accent in Dracula theme)
|
||||
# secondary='#FF79C6', # Pink
|
||||
# accent='#FFB86C', # Orange
|
||||
# dark='#282A36', # Dracula background (dark)
|
||||
# positive='#50FA7B', # Green
|
||||
# negative='#FF5555', # Red
|
||||
# info='#8BE9FD', # Cyan (info color)
|
||||
# warning='#F1FA8C' # Yellow
|
||||
# )
|
||||
|
||||
theme_colors = Namespace(
|
||||
primary='#5C5D71',
|
||||
secondary='#333333',
|
||||
accent='#a82c64',
|
||||
dark='#4d1f48',
|
||||
positive='#22ba38',
|
||||
negative='#f50000',
|
||||
info='#31CCEC',
|
||||
warning='#9333ea'
|
||||
)
|
||||
|
||||
|
||||
messages: List[Tuple[str, str, str, str, str]] = []
|
||||
|
||||
|
||||
@ui.refreshable
|
||||
def chat_messages(own_id: str) -> None:
|
||||
if messages:
|
||||
for user_id, avatar, text, stamp, img_url in messages:
|
||||
if img_url:
|
||||
with ui.chat_message(text=text, stamp=stamp, avatar=avatar, sent=own_id==user_id).classes('w-full'):
|
||||
ui.image(img_url).classes('w-64')
|
||||
else:
|
||||
ui.chat_message(text=text, stamp=stamp, avatar=avatar, sent=own_id==user_id).classes('w-full')
|
||||
else:
|
||||
ui.label('').classes('mx-auto my-36')
|
||||
ui.run_javascript('window.scrollTo(0, document.body.scrollHeight)')
|
||||
|
||||
|
||||
# State of GUI
|
||||
class GUIState:
|
||||
"""State of GUI-related objects
|
||||
|
||||
NOTE: "#" is used as a separator in GUI keys to avoid confusion with
|
||||
symbols that can be (typically) used in body/design parameter names
|
||||
('_', '-', etc.)
|
||||
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
self.window = None
|
||||
|
||||
# Pattern
|
||||
self.pattern_state = GUIPattern()
|
||||
|
||||
# Pattern display constants
|
||||
self.canvas_aspect_ratio = 1500. / 900 # Millimiter paper
|
||||
self.w_rel_body_size = 0.5 # Body size as fraction of horisontal canvas axis
|
||||
self.h_rel_body_size = 0.95
|
||||
self.background_body_scale = 1 / 171.99 # Inverse of the mean_all body height from GGG
|
||||
self.background_body_canvas_center = 0.273 # Fraction of the canvas (millimiter paper)
|
||||
self.w_canvas_pad, self.h_canvas_pad = 0.011, 0.04
|
||||
self.body_outline_classes = '' # Application of pattern&body scaling when it overflows
|
||||
|
||||
# Paths setup
|
||||
# Static images for GUI
|
||||
self.path_static_img = '/img'
|
||||
app.add_static_files(self.path_static_img, './assets/img')
|
||||
|
||||
# 3D updates
|
||||
self.path_static_3d = '/geo'
|
||||
self.garm_3d_filename = f'garm_3d_{self.pattern_state.id}.glb'
|
||||
self.local_path_3d = Path('./tmp_gui/garm_3d')
|
||||
self.local_path_3d.mkdir(parents=True, exist_ok=True)
|
||||
app.add_static_files(self.path_static_3d, self.local_path_3d)
|
||||
app.add_static_files('/body', './assets/bodies')
|
||||
#model api
|
||||
self.baseurl = None
|
||||
self.api = None
|
||||
self.model = None
|
||||
self.text_model = None
|
||||
# Elements
|
||||
self.ui_design_subtabs = {}
|
||||
self.ui_pattern_display = None
|
||||
self._async_executor = ThreadPoolExecutor(1)
|
||||
|
||||
self.pattern_state.reload_garment()
|
||||
self.stylings()
|
||||
self.layout()
|
||||
|
||||
def release(self):
|
||||
"""Clean-up after the sesssion"""
|
||||
self.pattern_state.release()
|
||||
(self.local_path_3d / self.garm_3d_filename).unlink(missing_ok=True)
|
||||
|
||||
# Initial definitions
|
||||
def stylings(self):
|
||||
"""Theme definition"""
|
||||
# Theme
|
||||
# Here: https://quasar.dev/style/theme-builder
|
||||
ui.colors(
|
||||
primary=theme_colors.primary,
|
||||
secondary=theme_colors.secondary,
|
||||
accent=theme_colors.accent,
|
||||
dark=theme_colors.dark,
|
||||
positive=theme_colors.positive,
|
||||
negative=theme_colors.negative,
|
||||
info=theme_colors.info,
|
||||
warning=theme_colors.warning
|
||||
)
|
||||
|
||||
# SECTION Top level layout
|
||||
def layout(self):
|
||||
"""Overall page layout"""
|
||||
|
||||
# as % of viewport width/height
|
||||
self.h_header = 5
|
||||
self.h_params_content = 88
|
||||
self.h_garment_display = 74
|
||||
self.w_garment_display = 65
|
||||
self.w_splitter_design = 32
|
||||
self.scene_base_resoltion = (1024, 800)
|
||||
|
||||
# Helpers
|
||||
self.def_pattern_waiting()
|
||||
# TODOLOW One dialog for both?
|
||||
self.def_design_file_dialog()
|
||||
self.def_body_file_dialog()
|
||||
|
||||
# Configurator GUI
|
||||
with ui.row(wrap=False).classes(f'w-full h-[{self.h_params_content}dvh] p-0 m-0 '):
|
||||
# Tabs
|
||||
self.def_param_tabs_layout()
|
||||
|
||||
# Pattern visual
|
||||
self.view_tabs_layout()
|
||||
|
||||
# Overall wrapping
|
||||
# NOTE: https://nicegui.io/documentation/section_pages_routing#page_layout
|
||||
with ui.header(elevated=True, fixed=False).classes(f'h-[{self.h_header}vh] items-center justify-end py-0 px-4 m-0'):
|
||||
ui.label('Design2GarmentCode').classes('mr-auto').style('font-size: 150%; font-weight: 400')
|
||||
ui.button(
|
||||
'Project',
|
||||
on_click=lambda: ui.navigate.to('https://style3d.github.io/design2garmentcode/', new_tab=True)
|
||||
).props('flat color=white')
|
||||
with ui.link(target='http://arxiv.org/abs/2412.08603', new_tab=True):
|
||||
ui.html(icon_arxiv).classes('w-16 bg-transparent')
|
||||
with ui.link(target='https://github.com/Style3D/SXDGarmentCode', new_tab=True):
|
||||
ui.html(icon_github).classes('w-8 bg-transparent')
|
||||
# NOTE No ui.left_drawer(), no ui.right_drawer()
|
||||
with ui.footer(fixed=False, elevated=True).classes('items-center justify-center p-0 m-0'):
|
||||
# https://www.termsfeed.com/blog/sample-copyright-notices/
|
||||
ui.link(
|
||||
'© 2025 Style3D Research',
|
||||
'https://www.linctex.com/',
|
||||
new_tab=True
|
||||
).classes('text-white')
|
||||
|
||||
def view_tabs_layout(self):
|
||||
"""2D/3D view tabs"""
|
||||
with ui.column(wrap=False).classes(f'h-[{self.h_params_content}vh] w-full items-center'):
|
||||
with ui.tabs() as tabs:
|
||||
self.ui_2d_tab = ui.tab('Sewing Pattern')
|
||||
self.ui_3d_tab = ui.tab('3D view')
|
||||
with ui.tab_panels(tabs, value=self.ui_2d_tab, animated=True).classes('w-full h-full items-center'):
|
||||
with ui.tab_panel(self.ui_2d_tab).classes('w-full h-full items-center justify-center p-0 m-0'):
|
||||
self.def_pattern_display()
|
||||
with ui.tab_panel(self.ui_3d_tab).classes('w-full h-full items-center p-0 m-0'):
|
||||
self.def_3d_scene()
|
||||
|
||||
ui.button('Download Current Garment', on_click=lambda: self.state_download()).classes('justify-self-end')
|
||||
|
||||
|
||||
def def_param_tabs_layout(self):
|
||||
"""Layout of tabs with parameters"""
|
||||
with ui.column(wrap=False).classes(f'h-[{self.h_params_content}vh]'):
|
||||
with ui.tabs().classes('w-full').props('dense') as tabs:
|
||||
self.ui_lmm_tab = ui.tab('Parse Design').props('icon=chat').classes('text-sm')
|
||||
self.ui_design_tab = ui.tab('Design Parameters').props('icon=checkroom').classes('text-sm')
|
||||
self.ui_body_tab = ui.tab('Body Measurement').props('icon=accessibility_new').classes('text-sm')
|
||||
|
||||
with ui.tab_panels(tabs, value=self.ui_design_tab, animated=True).classes('w-full h-full items-center'):
|
||||
with ui.tab_panel(self.ui_lmm_tab).classes('w-full h-full items-center p-0 m-0'):
|
||||
self.def_lmm_tab()
|
||||
with ui.tab_panel(self.ui_design_tab).classes('w-full h-full items-center p-0 m-0'):
|
||||
self.def_design_tab()
|
||||
with ui.tab_panel(self.ui_body_tab).classes('w-full h-full items-center p-0 m-0'):
|
||||
self.def_body_tab()
|
||||
|
||||
|
||||
def def_lmm_tab(self):
|
||||
self.own_id = str(uuid4())
|
||||
self.chat_msg = ''
|
||||
self.chat_img_url = ''
|
||||
|
||||
def toggle_inputs():
|
||||
if 'hidden' in self.api_input_fields.classes:
|
||||
self.api_input_fields.classes.remove('hidden') # display
|
||||
else:
|
||||
self.api_input_fields.classes.append('hidden') # hidden
|
||||
self.api_input_fields.update() # Update the UI
|
||||
def submit_api_data():
|
||||
self.baseurl = self.baseurl_input.value
|
||||
self.api = self.api_input.value
|
||||
self.model = self.model_input.value
|
||||
self.text_model = self.text_model_input.value
|
||||
toggle_inputs()
|
||||
with ui.column().classes('w-full h-full'):
|
||||
with ui.element('div').classes('w-full h-full overflow-auto'):
|
||||
chat_messages(self.own_id)
|
||||
|
||||
with ui.row().classes('w-full mt-2'):
|
||||
self.chat_input = ui.input(
|
||||
placeholder='Describe your design or upload a reference image...').props('autofocus').classes('flex-grow').on('keydown.enter', self.handle_chat_input)
|
||||
ui.button('Send', on_click=self.handle_chat_input).props('icon=send flat').classes('mt-2')
|
||||
with ui.column().classes('hidden').style('margin-left: auto;') as self.api_input_fields:
|
||||
self.baseurl_input = ui.input(placeholder='Base URL').classes('w-full')
|
||||
self.api_input = ui.input(placeholder='API Key').classes('w-full')
|
||||
self.model_input = ui.input(placeholder='Model Name').classes('w-full')
|
||||
self.text_model_input = ui.input(placeholder='Text Model Name').classes('w-full')
|
||||
ui.button('Submit', on_click=submit_api_data).classes('icon=send')
|
||||
|
||||
ui.button('open/close input baseurl api model', on_click=toggle_inputs).classes('icon=send')
|
||||
|
||||
with self.chat_input.add_slot('prepend'):
|
||||
ui.icon('add_photo_alternate').on('click', self.open_image_upload_dialog).classes('cursor-pointer').style('margin-left: 8px;')
|
||||
|
||||
with ui.dialog() as self.image_upload_dialog:
|
||||
with ui.card():
|
||||
ui.upload(on_upload=self.handle_image_upload).props('accept="image/*"').classes('w-full')
|
||||
ui.button('Close', on_click=self.image_upload_dialog.close).classes('mt-2')
|
||||
|
||||
|
||||
async def handle_chat_input(self, e=None):
|
||||
self.chat_msg = self.chat_input.value.strip()
|
||||
|
||||
if self.chat_msg:
|
||||
timestamp = datetime.now().strftime('%H:%M')
|
||||
messages.append((self.own_id, f'{self.path_static_img}/artist.png', self.chat_msg, timestamp, self.chat_img_url))
|
||||
chat_messages.refresh(self.own_id)
|
||||
|
||||
await self.parse_design(self.chat_msg, self.chat_img_url,api_key=self.api, base_url=self.baseurl, model=self.model,text_model=self.text_model)
|
||||
|
||||
self.chat_input.value = ''
|
||||
self.chat_img_url = ''
|
||||
|
||||
|
||||
def open_image_upload_dialog(self):
|
||||
self.image_upload_dialog.open()
|
||||
|
||||
|
||||
async def handle_image_upload(self, e: events.UploadEventArguments):
|
||||
image_name = e.name
|
||||
image_path = f'{self.path_static_img}/{image_name}'
|
||||
with open(f'./assets/img/{image_name}', 'wb') as f:
|
||||
f.write(e.content.read())
|
||||
|
||||
self.chat_img_url = image_path
|
||||
self.image_upload_dialog.close()
|
||||
self.chat_msg = self.chat_input.value.strip()
|
||||
|
||||
timestamp = datetime.now().strftime('%H:%M')
|
||||
messages.append((self.own_id, f'{self.path_static_img}/artist.png', self.chat_msg, timestamp, self.chat_img_url))
|
||||
chat_messages.refresh(self.own_id)
|
||||
await self.parse_design(self.chat_msg, f'./assets/img/{image_name}',api_key=self.api, base_url=self.baseurl, model=self.model,text_model=self.text_model)
|
||||
|
||||
self.chat_input.value = ''
|
||||
self.chat_img_url = ''
|
||||
|
||||
|
||||
def def_body_tab(self):
|
||||
|
||||
# Set of buttons
|
||||
with ui.row():
|
||||
ui.button('Upload', on_click=self.ui_body_dialog.open)
|
||||
|
||||
self.ui_active_body_refs = {}
|
||||
self.ui_passive_body_refs = {}
|
||||
with ui.scroll_area().classes('w-full h-full p-0 m-0'): # NOTE: p-0 m-0 gap-0 dont' seem to have effect
|
||||
body = self.pattern_state.body_params
|
||||
for param in body:
|
||||
param_name = param.replace('_', ' ').capitalize()
|
||||
elem = ui.number(
|
||||
label=param_name,
|
||||
value=str(body[param]),
|
||||
format='%.2f',
|
||||
precision=2,
|
||||
step=0.5,
|
||||
).classes('text-[0.85rem]')
|
||||
|
||||
if param[0] == '_': # Info elements for calculatable parameters
|
||||
elem.disable()
|
||||
self.ui_passive_body_refs[param] = elem
|
||||
else: # active elements accepting input
|
||||
# NOTE: e.sender == UI object, e.value == new value
|
||||
elem.on_value_change(lambda e, dic=body, param=param: self.update_pattern_ui_state(
|
||||
dic, param, e.value, body_param=True
|
||||
))
|
||||
self.ui_active_body_refs[param] = elem
|
||||
|
||||
def def_flat_design_subtab(self, ui_elems, design_params, use_collapsible=False):
|
||||
"""Group of design parameters"""
|
||||
for param in design_params:
|
||||
param_name = param.replace('_', ' ').capitalize()
|
||||
if 'v' not in design_params[param]:
|
||||
ui_elems[param] = {}
|
||||
if use_collapsible:
|
||||
with ui.expansion().classes('w-full p-0 m-0') as expansion:
|
||||
with expansion.add_slot('header'):
|
||||
ui.label(f'{param_name}').classes('text-base self-center w-full h-full p-0 m-0')
|
||||
with ui.row().classes('w-full h-full p-0 m-0'): # Ensures correct application of style classes for children
|
||||
self.def_flat_design_subtab(ui_elems[param], design_params[param])
|
||||
else:
|
||||
with ui.card().classes('w-full shadow-md border m-0 rounded-md'):
|
||||
ui.label(f'{param_name}').classes('text-base self-center w-full h-full p-0 m-0')
|
||||
self.def_flat_design_subtab(ui_elems[param], design_params[param])
|
||||
else:
|
||||
# Leaf value
|
||||
p_type = design_params[param]['type']
|
||||
val = design_params[param]['v']
|
||||
p_range = design_params[param]['range']
|
||||
if 'select' in p_type:
|
||||
values = design_params[param]['range']
|
||||
if 'null' in p_type and None not in values:
|
||||
values.append(None) # NOTE: Displayable value
|
||||
ui.label(param_name).classes('p-0 m-0 mt-2 text-stone-500 text-[0.85rem]')
|
||||
ui_elems[param] = ui.select(
|
||||
values, value=val,
|
||||
on_change=lambda e, dic=design_params, param=param: self.update_pattern_ui_state(dic, param, e.value)
|
||||
).classes('w-full')
|
||||
elif p_type == 'bool':
|
||||
ui_elems[param] = ui.switch(
|
||||
param_name, value=val,
|
||||
on_change=lambda e, dic=design_params, param=param: self.update_pattern_ui_state(dic, param, e.value)
|
||||
).classes('text-stone-500')
|
||||
elif p_type == 'float' or p_type == 'int':
|
||||
ui.label(param_name).classes('p-0 m-0 mt-2 text-stone-500 text-[0.85rem]')
|
||||
ui_elems[param] = ui.slider(
|
||||
value=val,
|
||||
min=p_range[0],
|
||||
max=p_range[1],
|
||||
step=0.025 if p_type == 'float' else 1,
|
||||
).props('snap label').classes('w-full') \
|
||||
.on('update:model-value',
|
||||
lambda e, dic=design_params, param=param: self.update_pattern_ui_state(dic, param, e.args),
|
||||
throttle=0.5, leading_events=False)
|
||||
|
||||
# NOTE Events control: https://nicegui.io/documentation/slider#throttle_events_with_leading_and_trailing_options
|
||||
elif 'file' in p_type:
|
||||
print(f'GUI::NotImplementedERROR::{param}::'
|
||||
'"file" parameter type is not yet supported in Web GarmentCode. '
|
||||
'Creation of corresponding UI element skipped'
|
||||
)
|
||||
else:
|
||||
print(f'GUI::WARNING::Unknown parameter type: {p_type}')
|
||||
ui_elems[param] = ui.input(label=param_name, value=val, placeholder='Type the value',
|
||||
validation={'Input too long': lambda value: len(value) < 20},
|
||||
on_change=lambda e, dic=design_params, param=param: self.update_pattern_ui_state(dic, param, e.value)
|
||||
).classes('w-full')
|
||||
|
||||
def def_design_tab(self):
|
||||
# Set of buttons
|
||||
with ui.row():
|
||||
ui.button('Random', on_click=self.random)
|
||||
ui.button('Default', on_click=self.default)
|
||||
ui.button('Upload', on_click=self.ui_design_dialog.open)
|
||||
|
||||
# Design parameters
|
||||
design_params = self.pattern_state.design_params
|
||||
self.ui_design_refs = {}
|
||||
if self.pattern_state.is_design_sectioned():
|
||||
# Use tabs to represent top-level sections
|
||||
with ui.splitter(value=self.w_splitter_design).classes('w-full h-full p-0 m-0') as splitter:
|
||||
with splitter.before:
|
||||
with ui.tabs().props('vertical').classes('w-full h-full') as tabs:
|
||||
for param in design_params:
|
||||
# Tab
|
||||
self.ui_design_subtabs[param] = ui.tab(param)
|
||||
self.ui_design_refs[param] = {}
|
||||
|
||||
with splitter.after:
|
||||
with ui.tab_panels(tabs, value=self.ui_design_subtabs['meta']).props('vertical').classes('w-full h-full p-0 m-0'):
|
||||
for param, tab_elem in self.ui_design_subtabs.items():
|
||||
with ui.tab_panel(tab_elem).classes('w-full h-full p-0 m-0').style('gap: 0px'):
|
||||
with ui.scroll_area().classes('w-full h-full p-0 m-0').style('gap: 0px'):
|
||||
self.def_flat_design_subtab(
|
||||
self.ui_design_refs[param],
|
||||
design_params[param],
|
||||
use_collapsible=(param == 'left')
|
||||
)
|
||||
else:
|
||||
# Simplified display of designs
|
||||
with ui.scroll_area().classes('w-full h-full p-0 m-0'):
|
||||
self.def_flat_design_subtab(
|
||||
self.ui_design_refs,
|
||||
design_params,
|
||||
use_collapsible=True
|
||||
)
|
||||
|
||||
# !SECTION
|
||||
# SECTION -- Pattern visuals
|
||||
def def_pattern_display(self):
|
||||
"""Prepare pattern display area"""
|
||||
with ui.column().classes('h-full p-0 m-0'):
|
||||
with ui.row().classes('w-full p-0 m-0 justify-between'):
|
||||
switch = ui.switch(
|
||||
'Body Silhouette', value=True,
|
||||
).props('dense left-label').classes('text-stone-800')
|
||||
|
||||
self.ui_self_intersect = ui.label(
|
||||
'WARNING: Garment panels are self-intersecting!'
|
||||
).classes('font-semibold text-purple-600 border-purple-600 border py-0 px-1.5 rounded-md') \
|
||||
.bind_visibility(self.pattern_state, 'is_self_intersecting')
|
||||
|
||||
with ui.image(
|
||||
f'{self.path_static_img}/millimiter_paper_1500_900.png'
|
||||
).classes(f'aspect-[{self.canvas_aspect_ratio}] h-[95%] p-0 m-0') as self.ui_pattern_bg:
|
||||
# NOTE: Positioning: https://github.com/zauberzeug/nicegui/discussions/957
|
||||
with ui.row().classes('w-full h-full p-0 m-0 bg-transparent relative top-[0%] left-[0%]'):
|
||||
self.body_outline_classes = 'bg-transparent h-full absolute top-[0%] left-[0%] p-0 m-0'
|
||||
try:
|
||||
self.ui_body_outline = ui.image(f'{self.path_static_img}/ggg_outline_{self.pattern_state.body_id}.svg').classes(replace=self.body_outline_classes)
|
||||
switch.bind_value(self.ui_body_outline, 'visible')
|
||||
except Exception as e:
|
||||
print('GUI::WARNING::Body silhouette not found, using mean_all', str(e))
|
||||
self.ui_body_outline = ui.image(f'{self.path_static_img}/ggg_outline_mean_all.svg').classes(self.body_outline_classes)
|
||||
switch.bind_value(self.ui_body_outline, 'visible')
|
||||
|
||||
# NOTE: ui.row allows for correct classes application (e.g. no padding on svg pattern)
|
||||
with ui.row().classes('w-full h-full p-0 m-0 bg-transparent relative'):
|
||||
# Automatically updates from source
|
||||
self.ui_pattern_display = ui.interactive_image(
|
||||
''
|
||||
).classes('bg-transparent p-0 m-0')
|
||||
|
||||
# !SECTION
|
||||
# SECTION 3D view
|
||||
def create_lights(self, scene:ui.scene, intensity=30.0):
|
||||
light_positions = np.array([
|
||||
[1.60614, 1.23701, 1.5341,],
|
||||
[1.31844, -2.52238, 1.92831],
|
||||
[-2.80522, 2.34624, 1.2594],
|
||||
[0.160261, 3.52215, 1.81789],
|
||||
[-2.65752, -1.26328, 1.41194]
|
||||
])
|
||||
light_colors = [
|
||||
'#ffffff',
|
||||
'#ffffff',
|
||||
'#ffffff',
|
||||
'#ffffff',
|
||||
'#ffffff'
|
||||
]
|
||||
z_dirs = np.arctan2(light_positions[:, 1], light_positions[:, 0])
|
||||
|
||||
# Add lights to the scene
|
||||
for i in range(len(light_positions)):
|
||||
scene.spot_light(
|
||||
color=light_colors[i], intensity=intensity,
|
||||
angle=np.pi,
|
||||
).rotate(0., 0., -z_dirs[i]).move(light_positions[i][0], light_positions[i][1], light_positions[i][2])
|
||||
|
||||
def create_camera(self, cam_location, fov, scale=1.):
|
||||
camera = ui.scene.perspective_camera(fov=fov)
|
||||
camera.x = cam_location[0] * scale
|
||||
camera.y = cam_location[1] * scale
|
||||
camera.z = cam_location[2] * scale
|
||||
|
||||
# direction
|
||||
camera.look_at_x = 0
|
||||
camera.look_at_y = 0
|
||||
camera.look_at_z = cam_location[2] * scale * 2/3
|
||||
|
||||
return camera
|
||||
|
||||
def def_3d_scene(self):
|
||||
y_fov = 30 # Degrees == np.pi / 6. rad FOV
|
||||
camera_location = [0, -4.15, 1.25]
|
||||
bg_color='#ffffff'
|
||||
|
||||
def body_visibility(value):
|
||||
self.ui_body_3d.visible(value)
|
||||
|
||||
with ui.row().classes('w-full p-0 m-0 justify-between items-center'):
|
||||
self.ui_body_3d_switch = ui.switch(
|
||||
'Body Silhouette',
|
||||
value=True,
|
||||
on_change=lambda e: body_visibility(e.value)
|
||||
).props('dense left-label').classes('text-stone-800')
|
||||
|
||||
ui.button('Drape current design', on_click=lambda: self.update_3d_scene())
|
||||
|
||||
ui.label(
|
||||
'INFO: it takes a few minutes'
|
||||
).classes(f'font-semibold text-[{theme_colors.primary}] border-[{theme_colors.primary}] '
|
||||
'border py-0 px-1.5 rounded-md')
|
||||
|
||||
camera = self.create_camera(camera_location, y_fov)
|
||||
with ui.scene(
|
||||
width=self.scene_base_resoltion[0],
|
||||
height=self.scene_base_resoltion[1],
|
||||
camera=camera,
|
||||
grid=False,
|
||||
background_color=bg_color
|
||||
).classes(f'w-[{self.w_garment_display}vw] h-[90%] p-0 m-0') as self.ui_3d_scene:
|
||||
# Lights setup
|
||||
self.create_lights(self.ui_3d_scene, intensity=60.)
|
||||
# NOTE: texture is there, just needs a better setup
|
||||
self.ui_garment_3d = None
|
||||
# TODOLOW Update body model to a correct shape
|
||||
try:
|
||||
self.ui_body_3d = self.ui_3d_scene.stl(
|
||||
f'/body/{self.pattern_state.body_id}.stl'
|
||||
).rotate(np.pi / 2, 0., 0.).material(color='#000000')
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
self.ui_body_3d = self.ui_3d_scene.stl(
|
||||
'/body/mean_all.stl'
|
||||
).rotate(np.pi / 2, 0., 0.).material(color='#000000')
|
||||
|
||||
# !SECTION
|
||||
# SECTION -- Other UI details
|
||||
def def_pattern_waiting(self):
|
||||
"""Define the waiting splashcreen with spinner
|
||||
(e.g. waiting for a pattern to update)"""
|
||||
|
||||
# NOTE: the screen darkens because of the shadow
|
||||
with ui.dialog(value=False).props(
|
||||
'persistent maximized'
|
||||
) as self.spin_dialog, ui.card().classes('bg-transparent'):
|
||||
# Styles https://quasar.dev/vue-components/spinners
|
||||
ui.spinner('hearts', size='15em').classes('fixed-center') # NOTE: 'dots' 'ball'
|
||||
|
||||
def def_body_file_dialog(self):
|
||||
""" Dialog for loading parameter files (body)
|
||||
"""
|
||||
async def handle_upload(e: events.UploadEventArguments):
|
||||
param_dict = yaml.safe_load(e.content.read())['body']
|
||||
|
||||
self.toggle_param_update_events(self.ui_active_body_refs)
|
||||
|
||||
self.pattern_state.body_id = e.name.split('.')[0]
|
||||
self.pattern_state.set_new_body_params(param_dict)
|
||||
self.update_body_params_ui_state(self.ui_active_body_refs)
|
||||
await self.update_pattern_ui_state()
|
||||
|
||||
if self.ui_body_3d is not None: self.ui_body_3d.delete()
|
||||
try:
|
||||
print(f'INFO::Body silhouette update ggg_outline_{self.pattern_state.body_id}.svg')
|
||||
self.ui_body_3d = self.ui_3d_scene.stl(
|
||||
f'/body/{self.pattern_state.body_id}.stl'
|
||||
).rotate(np.pi / 2, 0., 0.).material(color='#000000')
|
||||
except Exception as e:
|
||||
print('GUI::WARNING::3D Body mesh not found, using mean_all', str(e))
|
||||
self.ui_body_3d = self.ui_3d_scene.stl(
|
||||
'/body/mean_all.stl'
|
||||
).rotate(np.pi / 2, 0., 0.).material(color='#000000')
|
||||
|
||||
try:
|
||||
print(f'INFO::Body silhouette update ggg_outline_{self.pattern_state.body_id}.svg')
|
||||
print('*** img_url: ', self.ui_body_outline.source)
|
||||
self.ui_body_outline.set_source(f'{self.path_static_img}/ggg_outline_{self.pattern_state.body_id}.svg')
|
||||
self.ui_body_outline.update()
|
||||
except Exception as e:
|
||||
print('GUI::WARNING::Body silhouette not found, using mean_all', str(e))
|
||||
|
||||
self.toggle_param_update_events(self.ui_active_body_refs)
|
||||
|
||||
ui.notify(f'Successfully applied {e.name}')
|
||||
self.ui_body_dialog.close()
|
||||
|
||||
with ui.dialog() as self.ui_body_dialog, ui.card().classes('items-center'):
|
||||
# NOTE: https://www.reddit.com/r/nicegui/comments/1393i2f/file_upload_with_restricted_types/
|
||||
ui.upload(
|
||||
label='Body parameters .yaml or .json',
|
||||
on_upload=handle_upload
|
||||
).classes('max-w-full').props('accept=".yaml,.json"')
|
||||
|
||||
ui.button('Close without upload', on_click=self.ui_body_dialog.close)
|
||||
|
||||
def def_design_file_dialog(self):
|
||||
""" Dialog for loading parameter files (design)
|
||||
"""
|
||||
async def handle_upload(e: events.UploadEventArguments):
|
||||
param_dict = yaml.safe_load(e.content.read())['design']
|
||||
|
||||
self.toggle_param_update_events(self.ui_design_refs) # Don't react to value updates
|
||||
|
||||
self.pattern_state.set_new_design(param_dict)
|
||||
self.update_design_params_ui_state(self.ui_design_refs, self.pattern_state.design_params)
|
||||
await self.update_pattern_ui_state()
|
||||
|
||||
self.toggle_param_update_events(self.ui_design_refs) # Re-enable reaction to value updates
|
||||
|
||||
ui.notify(f'Successfully applied {e.name}')
|
||||
self.ui_design_dialog.close()
|
||||
|
||||
with ui.dialog() as self.ui_design_dialog, ui.card().classes('items-center'):
|
||||
# NOTE: https://www.reddit.com/r/nicegui/comments/1393i2f/file_upload_with_restricted_types/
|
||||
ui.upload(
|
||||
label='Design parameters .yaml or .json',
|
||||
on_upload=handle_upload
|
||||
).classes('max-w-full').props('accept=".yaml,.json"')
|
||||
|
||||
ui.button('Close without upload', on_click=self.ui_design_dialog.close)
|
||||
|
||||
# !SECTION
|
||||
# SECTION -- Event callbacks
|
||||
async def update_pattern_ui_state(self, param_dict=None, param=None, new_value=None, body_param=False):
|
||||
"""UI was updated -- update the state of the pattern parameters and visuals"""
|
||||
# NOTE: Fixing to the "same value" issue in lambdas
|
||||
# https://github.com/zauberzeug/nicegui/wiki/FAQs#why-have-all-my-elements-the-same-value
|
||||
|
||||
print('INFO::Updating pattern...')
|
||||
|
||||
# Update the values
|
||||
if param_dict is not None:
|
||||
if body_param:
|
||||
param_dict[param] = new_value
|
||||
else:
|
||||
param_dict[param]['v'] = new_value
|
||||
self.pattern_state.is_in_3D = False # Design param changes -> 3D model is not synced with the param
|
||||
|
||||
try:
|
||||
if not self.pattern_state.is_slow_design():
|
||||
# Quick update
|
||||
self._sync_update_state()
|
||||
return
|
||||
|
||||
# Display waiting spinner untill getting the result
|
||||
# NOTE Splashscreen solution to block users from modifying params while updating
|
||||
# https://github.com/zauberzeug/nicegui/discussions/1988
|
||||
|
||||
self.spin_dialog.open()
|
||||
# NOTE: Using threads for async call
|
||||
# https://stackoverflow.com/questions/49822552/python-asyncio-typeerror-object-dict-cant-be-used-in-await-expression
|
||||
self.loop = asyncio.get_event_loop()
|
||||
await self.loop.run_in_executor(self._async_executor, self._sync_update_state)
|
||||
|
||||
self.spin_dialog.close()
|
||||
|
||||
except KeyboardInterrupt as e:
|
||||
raise e
|
||||
except BaseException as e:
|
||||
traceback.print_exc()
|
||||
print(e)
|
||||
self.spin_dialog.close() # If open
|
||||
ui.notify(
|
||||
'Failed to generate pattern correctly. Try different parameter values',
|
||||
type='negative',
|
||||
close_button=True,
|
||||
position='center'
|
||||
)
|
||||
|
||||
def _sync_update_state(self):
|
||||
# Update derivative body values (just in case)
|
||||
# TODOLOW only do that on body value updates
|
||||
self.pattern_state.body_params.eval_dependencies()
|
||||
self.update_body_params_ui_state(self.ui_passive_body_refs) # Display evaluated dependencies
|
||||
|
||||
# Update the garment
|
||||
# Sync left-right for easier editing
|
||||
self.pattern_state.sync_left(with_check=True)
|
||||
|
||||
# NOTE This is the slow part
|
||||
self.pattern_state.reload_garment()
|
||||
|
||||
# TODOLOW the pattern is floating around when collars are added..
|
||||
# Update display
|
||||
if self.ui_pattern_display is not None:
|
||||
|
||||
if self.pattern_state.svg_filename:
|
||||
# Re-align the canvas and body with the new pattern
|
||||
p_bbox_size = self.pattern_state.svg_bbox_size
|
||||
p_bbox = self.pattern_state.svg_bbox
|
||||
|
||||
# Margin calculations w.r.t. canvas size
|
||||
# s.t. the pattern scales correctly
|
||||
w_shift = abs(p_bbox[0]) # Body feet location in width direction w.r.t top-left corner of the pattern
|
||||
m_top = (1. - abs(p_bbox[2]) * self.background_body_scale) * self.h_rel_body_size + (1. - self.h_rel_body_size) / 2
|
||||
m_left = self.background_body_canvas_center - w_shift * self.background_body_scale * self.w_rel_body_size
|
||||
m_right = 1 - m_left - p_bbox_size[0] * self.background_body_scale * self.w_rel_body_size
|
||||
m_bottom = 1 - m_top - p_bbox_size[1] * self.background_body_scale * self.h_rel_body_size
|
||||
|
||||
# Canvas padding adjustment
|
||||
m_top -= self.h_canvas_pad
|
||||
m_left -= self.w_canvas_pad
|
||||
m_right += self.w_canvas_pad # preserve evaluated width
|
||||
m_bottom -= self.h_canvas_pad
|
||||
|
||||
# New placement
|
||||
if m_top < 0 or m_bottom < 0 or m_left < 0 or m_right < 0:
|
||||
# Calculate the fraction
|
||||
scale_margin = 1.2
|
||||
y_top_scale = abs(min(m_top * scale_margin, 0.)) + 1.
|
||||
y_bot_scale = 1. + abs(min(m_bottom * scale_margin, 0.))
|
||||
x_left_scale = abs(min(m_left * scale_margin, 0.)) + 1.
|
||||
x_right_scale = abs(min(m_right * scale_margin, 0.)) + 1.
|
||||
scale = min(1. / y_top_scale, 1. / y_bot_scale, 1. / x_left_scale, 1. / x_right_scale)
|
||||
|
||||
# Rescale the body
|
||||
self.ui_body_outline.classes(
|
||||
replace=self.body_outline_classes + f' origin-center scale-[{scale}]'
|
||||
)
|
||||
|
||||
# Recalculate positioning & width
|
||||
body_center = 0.5 - self.background_body_canvas_center
|
||||
m_top = (1. - abs(p_bbox[2]) * self.background_body_scale) * self.h_rel_body_size * scale + (1. - self.h_rel_body_size * scale) / 2
|
||||
m_left = (0.5 - body_center * scale) - w_shift * self.background_body_scale * self.w_rel_body_size * scale
|
||||
m_right = 1 - m_left - p_bbox_size[0] * self.background_body_scale * self.w_rel_body_size * scale
|
||||
|
||||
# Canvas padding adjustment
|
||||
# TODOLOW For some reason top adjustment is not needed here: m_top -= self.h_canvas_pad * scale
|
||||
m_left -= self.w_canvas_pad * scale
|
||||
m_right += self.w_canvas_pad * scale
|
||||
|
||||
else: # Display normally
|
||||
# Remove body transforms if any were applied
|
||||
self.ui_body_outline.classes(replace=self.body_outline_classes)
|
||||
|
||||
# New pattern image
|
||||
self.ui_pattern_display.set_source(
|
||||
str(self.pattern_state.svg_path()) if self.pattern_state.svg_filename else '')
|
||||
self.ui_pattern_display.classes(
|
||||
replace=f"""bg-transparent p-0 m-0
|
||||
absolute
|
||||
left-[{m_left * 100}%]
|
||||
top-[{m_top * 100}%]
|
||||
w-[{(1. - m_right - m_left) * 100}%]
|
||||
height-auto
|
||||
""")
|
||||
|
||||
else:
|
||||
# Restore default state
|
||||
self.ui_pattern_display.set_source('')
|
||||
self.ui_body_outline.classes(replace=self.body_outline_classes)
|
||||
|
||||
def update_design_params_ui_state(self, ui_elems, design_params):
|
||||
"""Sync ui params with the current state of the design params"""
|
||||
for param in design_params:
|
||||
if 'v' not in design_params[param]:
|
||||
self.update_design_params_ui_state(ui_elems[param], design_params[param])
|
||||
else:
|
||||
ui_elems[param].value = design_params[param]['v']
|
||||
|
||||
def toggle_param_update_events(self, ui_elems):
|
||||
"""Enable/disable event handling on the ui elements related to GarmentCode parameters"""
|
||||
for param in ui_elems:
|
||||
if isinstance(ui_elems[param], dict):
|
||||
self.toggle_param_update_events(ui_elems[param])
|
||||
else:
|
||||
if ui_elems[param].is_ignoring_events: # -> disabled
|
||||
ui_elems[param].enable()
|
||||
else:
|
||||
ui_elems[param].disable()
|
||||
|
||||
def update_body_params_ui_state(self, ui_body_refs):
|
||||
"""Sync ui params with the current state of the body params"""
|
||||
for param in ui_body_refs:
|
||||
ui_body_refs[param].value = self.pattern_state.body_params[param]
|
||||
|
||||
|
||||
async def update_3d_scene(self):
|
||||
"""According the whatever pattern current state"""
|
||||
|
||||
print('INFO::Updating 3D...')
|
||||
|
||||
# Cleanup
|
||||
if self.ui_garment_3d is not None:
|
||||
self.ui_garment_3d.delete()
|
||||
self.ui_garment_3d = None
|
||||
|
||||
if not self.pattern_state.svg_filename:
|
||||
print('INFO::Current garment is empty, skipped 3D update')
|
||||
ui.notify('Current garment is empty. Chose a design to start simulating!')
|
||||
self.ui_body_3d.visible(True)
|
||||
self.ui_body_3d_switch.set_value(True)
|
||||
return
|
||||
|
||||
try:
|
||||
# Display waiting spinner untill getting the result
|
||||
# NOTE Splashscreen solution to block users from modifying params while updating
|
||||
# https://github.com/zauberzeug/nicegui/discussions/1988
|
||||
|
||||
self.spin_dialog.open()
|
||||
# NOTE: Using threads for async call
|
||||
# https://stackoverflow.com/questions/49822552/python-asyncio-typeerror-object-dict-cant-be-used-in-await-expression
|
||||
self.loop = asyncio.get_event_loop()
|
||||
await self.loop.run_in_executor(self._async_executor, self._sync_update_3d)
|
||||
|
||||
# Update ui
|
||||
# https://github.com/zauberzeug/nicegui/discussions/1269
|
||||
with self.ui_3d_scene:
|
||||
# NOTE: material is defined in the glb file
|
||||
self.ui_garment_3d = self.ui_3d_scene.gltf(
|
||||
f'geo/{self.garm_3d_filename}',
|
||||
).scale(0.01).rotate(np.pi / 2, 0., 0.)
|
||||
|
||||
# Show the result! =)
|
||||
self.spin_dialog.close()
|
||||
|
||||
except KeyboardInterrupt as e:
|
||||
raise e
|
||||
except BaseException as e:
|
||||
traceback.print_exc()
|
||||
print(e)
|
||||
self.ui_3d_scene.set_visibility(True)
|
||||
self.spin_dialog.close() # If open
|
||||
ui.notify(
|
||||
'Failed to generate 3D model correctly. Try different parameter values',
|
||||
type='negative',
|
||||
close_button=True,
|
||||
position='center'
|
||||
)
|
||||
|
||||
def _sync_update_3d(self):
|
||||
"""Update 3d model"""
|
||||
|
||||
# Run simulation
|
||||
path, filename = self.pattern_state.drape_3d()
|
||||
|
||||
# NOTE: The files will be available publically at the static point
|
||||
# However, we cannot do much about it, since it won't be available for the interface otherwise
|
||||
shutil.copy2(path / filename, self.local_path_3d / self.garm_3d_filename)
|
||||
|
||||
# Design buttons updates
|
||||
async def design_sample(self):
|
||||
"""Run design sampling"""
|
||||
self.loop = asyncio.get_event_loop()
|
||||
await self.loop.run_in_executor(self._async_executor, self.pattern_state.sample_design)
|
||||
|
||||
async def random(self):
|
||||
# Sampling could be slow, so add spin always
|
||||
self.spin_dialog.open()
|
||||
|
||||
self.toggle_param_update_events(self.ui_design_refs) # Don't react to value updates
|
||||
|
||||
await self.design_sample()
|
||||
self.update_design_params_ui_state(self.ui_design_refs, self.pattern_state.design_params)
|
||||
await self.update_pattern_ui_state()
|
||||
|
||||
self.toggle_param_update_events(self.ui_design_refs) # Re-do reaction to value updates
|
||||
|
||||
self.spin_dialog.close()
|
||||
|
||||
async def default(self):
|
||||
self.toggle_param_update_events(self.ui_design_refs)
|
||||
|
||||
self.pattern_state.restore_design(False)
|
||||
self.update_design_params_ui_state(self.ui_design_refs, self.pattern_state.design_params)
|
||||
await self.update_pattern_ui_state()
|
||||
|
||||
self.toggle_param_update_events(self.ui_design_refs)
|
||||
|
||||
async def parse_design(self, text_prompt='', img_url='',api_key=None, base_url=None, model=None,text_model=None):
|
||||
"""Parse design from text or image"""
|
||||
|
||||
def _sync_parse_design():
|
||||
response = self.pattern_state.parse_chat(text_prompt, img_url,api_key, base_url, model,text_model)
|
||||
return response
|
||||
|
||||
self.spin_dialog.open()
|
||||
self.toggle_param_update_events(self.ui_design_refs)
|
||||
|
||||
self.loop = asyncio.get_event_loop()
|
||||
response = await self.loop.run_in_executor(self._async_executor, _sync_parse_design)
|
||||
|
||||
# response = self.pattern_state.parse_chat(text_prompt, img_url)
|
||||
print('GPT response: ', response)
|
||||
|
||||
self.update_design_params_ui_state(self.ui_design_refs, self.pattern_state.design_params)
|
||||
await self.update_pattern_ui_state()
|
||||
self.toggle_param_update_events(self.ui_design_refs)
|
||||
self.spin_dialog.close()
|
||||
|
||||
# !SECTION
|
||||
|
||||
def state_download(self):
|
||||
"""Download current state of a garment"""
|
||||
archive_path = self.pattern_state.save()
|
||||
ui.download(archive_path, f'Configured_design_{datetime.now().strftime("%y%m%d-%H-%M-%S")}.zip')
|
||||
68
gui/error_pages.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from nicegui import ui, app
|
||||
from nicegui import Client
|
||||
from nicegui.page import page
|
||||
import random
|
||||
|
||||
from gui.callbacks import theme_colors
|
||||
|
||||
# dresses selection =)
|
||||
error_icons = [
|
||||
'./assets/img/err_dress_20s.png',
|
||||
'./assets/img/err_dress_30s.png',
|
||||
'./assets/img/err_dress_50s.png',
|
||||
'./assets/img/err_js.png',
|
||||
'./assets/img/err_red_modern.png',
|
||||
'./assets/img/err_regency.png'
|
||||
]
|
||||
|
||||
# https://github.com/zauberzeug/nicegui/discussions/883#discussioncomment-5801636
|
||||
def error_handler(err_type, text, exception: Exception):
|
||||
"""Base error page, with customizable error messages"""
|
||||
with ui.column().classes('h-[95vh] w-[95vw] items-center justify-top space-y-8 self-center'):
|
||||
img = random.choice(error_icons)
|
||||
ui.image(img).classes('h-[45vh]').props('fit="scale-down"')
|
||||
|
||||
with ui.column().classes('h-fit w-fit py-4 px-10 items-center justify-center space-y-8 '
|
||||
f'border border-[{theme_colors.primary}] rounded-md '
|
||||
f'shadow-lg shadow-[{theme_colors.secondary}]'):
|
||||
ui.label(err_type).classes('text-3xl')
|
||||
if text:
|
||||
ui.label(text).classes('text-2xl')
|
||||
ui.label(str(exception)).classes('text-xl text-stone-500')
|
||||
|
||||
# https://www.pixelfish.com.au/blog/most-common-website-errors/
|
||||
@app.exception_handler(404)
|
||||
async def exception_handler_404(request, exception: Exception):
|
||||
with Client(page(''), request=None) as client:
|
||||
error_handler('404', 'You are looking for something that doesn\'t exist', exception)
|
||||
return client.build_response(request, 404)
|
||||
|
||||
@app.exception_handler(500)
|
||||
async def exception_handler_500(request, exception: Exception):
|
||||
with Client(page(''), request=None) as client:
|
||||
error_handler('500', 'Oops! Server error. We are fixing it ASAP =)', exception)
|
||||
return client.build_response(request, 500)
|
||||
|
||||
@app.exception_handler(400)
|
||||
async def exception_handler_400(request, exception: Exception):
|
||||
with Client(page(''), request=None) as client:
|
||||
error_handler('400', 'Oh no, bad request', exception)
|
||||
return client.build_response(request, 400)
|
||||
|
||||
@app.exception_handler(401)
|
||||
async def exception_handler_401(request, exception: Exception):
|
||||
with Client(page(''), request=None) as client:
|
||||
error_handler('401', 'You don\'t have access to this place', exception)
|
||||
return client.build_response(request, 401)
|
||||
|
||||
@app.exception_handler(403)
|
||||
async def exception_handler_403(request, exception: Exception):
|
||||
with Client(page(''), request=None) as client:
|
||||
error_handler('403', 'Sorry, you cannot come here', exception)
|
||||
return client.build_response(request, 403)
|
||||
|
||||
@app.exception_handler(503)
|
||||
async def exception_handler_503(request, exception: Exception):
|
||||
with Client(page(''), request=None) as client:
|
||||
error_handler('503', 'We are unavailable, but will be back soon!', exception)
|
||||
return client.build_response(request, 503)
|
||||
401
gui/gui_pattern.py
Normal file
@@ -0,0 +1,401 @@
|
||||
from pathlib import Path
|
||||
import time
|
||||
import yaml
|
||||
import shutil
|
||||
import string
|
||||
import random
|
||||
import trimesh
|
||||
from copy import deepcopy
|
||||
from typing import Optional
|
||||
from lmm_utils.core import MMUA
|
||||
# Custom
|
||||
from assets.garment_programs.meta_garment import MetaGarment
|
||||
from assets.bodies.body_params import BodyParameters
|
||||
import pygarment as pyg
|
||||
from pygarment.meshgen.boxmeshgen import BoxMesh
|
||||
# from pygarment.meshgen.simulation import run_sim
|
||||
import pygarment.data_config as data_config
|
||||
from pygarment.meshgen.sim_config import PathCofig
|
||||
|
||||
|
||||
|
||||
verbose = False
|
||||
|
||||
def _id_generator(size=10, chars=string.ascii_uppercase + string.digits):
|
||||
"""Generate a random string of a given size, see
|
||||
https://stackoverflow.com/questions/2257441/random-string-generation-with-upper-case-letters-and-digits
|
||||
"""
|
||||
return ''.join(random.choices(chars, k=size))
|
||||
|
||||
|
||||
class GUIPattern:
|
||||
def __init__(self) -> None:
|
||||
# Unique id to distiguish tab sessions correctly
|
||||
self.id = _id_generator(20)
|
||||
|
||||
# Paths setup
|
||||
self.save_path_root = Path.cwd() / 'tmp_gui' / 'downloads'
|
||||
self.tmp_path_root = Path.cwd() / 'tmp_gui' / 'display'
|
||||
self.save_path = self.save_path_root / self.id
|
||||
self.svg_filename = None
|
||||
self.saved_garment_archive = ''
|
||||
self.saved_garment_folder = ''
|
||||
self.tmp_path = self.tmp_path_root / self.id
|
||||
self.paths_3d = None
|
||||
|
||||
# create paths
|
||||
self.save_path.mkdir(parents=True, exist_ok=True)
|
||||
self.tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
self.body_params = None
|
||||
self.design_params = {}
|
||||
self.design_list = []
|
||||
self.design_sampler = pyg.DesignSampler()
|
||||
self.sew_pattern = None
|
||||
|
||||
self.body_id = 'mean_all'
|
||||
self.body_file = None
|
||||
self.design_file = None
|
||||
self._load_body_file(
|
||||
Path.cwd() / f'assets/bodies/{self.body_id}.yaml'
|
||||
)
|
||||
self.default_body_params = deepcopy(self.body_params)
|
||||
self._load_design_file(
|
||||
Path.cwd() / 'assets/design_params/default.yaml'
|
||||
)
|
||||
|
||||
# Status
|
||||
self.is_self_intersecting = False
|
||||
self.is_in_3D = False
|
||||
|
||||
self.reload_garment()
|
||||
# Init Agent
|
||||
self.agent=None
|
||||
|
||||
def parse_chat(self, text_prompt='', img_url='',api_key=None, base_url=None, model=None,text_model=None):
|
||||
print('Prompt: ', text_prompt)
|
||||
print('Image: ', img_url)
|
||||
self.agent.mmua=MMUA(api_key=api_key, base_url=base_url, model=model,text_model=text_model)
|
||||
# self.agent=Agent(api_key=api_key, base_url=base_url, model=model,text_model=text_model)
|
||||
text_prompt = text_prompt.strip()
|
||||
modify_mode = 'modify' in text_prompt or 'm:' in text_prompt
|
||||
stress_mode='stress' in text_prompt or 's:' in text_prompt
|
||||
|
||||
# try:
|
||||
# Modify mode
|
||||
if modify_mode:
|
||||
assert self.design_params and self.design_list, "Must provide a base design under mofify mode."
|
||||
gpt_response,gpt_design_params,gpt_design_list=self.agent.modify_design(self.design_list,text_prompt=text_prompt,design_params=self.design_params)
|
||||
# Stress mode:
|
||||
elif stress_mode:
|
||||
assert self.design_params and self.design_list, "Must provide a base design under mofify mode."
|
||||
gpt_response,gpt_design_params,gpt_design_list=self.agent.stress_design(self.design_list,img_url=img_url,design_params=self.design_params)
|
||||
# Inference from image and text
|
||||
elif text_prompt and img_url:
|
||||
gpt_response,gpt_design_params,gpt_design_list=self.agent.picture_text_design(img_url,text_prompt)
|
||||
|
||||
# Inference from text only
|
||||
elif text_prompt and not img_url:
|
||||
gpt_response,gpt_design_params,gpt_design_list=self.agent.text_design(text_prompt)
|
||||
|
||||
# Inference from image only
|
||||
elif img_url and not text_prompt:
|
||||
gpt_response,gpt_design_params,gpt_design_list=self.agent.picture_design(img_url)
|
||||
else:
|
||||
raise ValueError("At least one design description is required, text prompt or image.")
|
||||
|
||||
self.design_list = gpt_design_list
|
||||
self.set_new_design(gpt_design_params)
|
||||
self.design_params=gpt_design_params
|
||||
print('*** Design params: ', self.design_list, self.design_params)
|
||||
|
||||
return gpt_response
|
||||
|
||||
def release(self):
|
||||
"""Clean up tmp files after the session"""
|
||||
self.clear_previous_download()
|
||||
shutil.rmtree(self.save_path)
|
||||
shutil.rmtree(self.tmp_path)
|
||||
|
||||
def _load_body_file(self, path):
|
||||
self.body_file = path
|
||||
self.body_params = BodyParameters(path)
|
||||
|
||||
def _load_design_file(self, path):
|
||||
self.design_file = path
|
||||
|
||||
# Create values
|
||||
with open(path, 'r') as f:
|
||||
des = yaml.safe_load(f)['design']
|
||||
|
||||
self.design_params.update(des)
|
||||
if 'left' in self.design_params and not self.design_params['left']['enable_asym']['v']:
|
||||
self.sync_left()
|
||||
|
||||
# Update param sampler
|
||||
self.design_sampler.load(path)
|
||||
|
||||
def svg_path(self):
|
||||
return self.tmp_path / self.svg_filename
|
||||
|
||||
def set_new_design(self, design):
|
||||
self._nested_sync(design, self.design_params)
|
||||
|
||||
def set_new_body_params(self, body_params):
|
||||
self.body_params.load_from_dict(body_params)
|
||||
|
||||
def sample_design(self, reload=True):
|
||||
"""Random design parameters"""
|
||||
|
||||
new_design = self.design_sampler.randomize()
|
||||
# NOTE: re-assign the values instead up overwriting them
|
||||
self._nested_sync(new_design, self.design_params)
|
||||
|
||||
if 'left' in self.design_params and not self.design_params['left']['enable_asym']['v']:
|
||||
self.sync_left()
|
||||
|
||||
if reload:
|
||||
self.reload_garment()
|
||||
|
||||
def restore_design(self, reload=True):
|
||||
"""Restore design values to match the current loaded file"""
|
||||
new_design = self.design_sampler.default()
|
||||
# re-assign the values instead up overwriting them
|
||||
self._nested_sync(new_design, self.design_params)
|
||||
|
||||
if reload:
|
||||
self.reload_garment()
|
||||
|
||||
def reload_garment(self):
|
||||
"""Reload sewing pattern with current body and design parameters
|
||||
|
||||
NOTE: loading a pattern might be lagging, execute only when needed!
|
||||
"""
|
||||
self.sew_pattern = MetaGarment(
|
||||
'Configured_design', self.body_params, self.design_params)
|
||||
self.is_self_intersecting = self.sew_pattern.is_self_intersecting()
|
||||
self._view_serialize()
|
||||
|
||||
@staticmethod
|
||||
def _nested_sync(s_from, s_to):
|
||||
if 'v' in s_to:
|
||||
s_to['v'] = s_from['v']
|
||||
else:
|
||||
for key in s_to:
|
||||
if key in s_from:
|
||||
GUIPattern._nested_sync(s_from[key], s_to[key])
|
||||
|
||||
def sync_left(self, with_check=False):
|
||||
"""Synchronize left and right design parameters"""
|
||||
# Check if needed in the first place
|
||||
if with_check and self.design_params['left']['enable_asym']['v']:
|
||||
# Asymmetry enabled, the params should not syncronise
|
||||
return
|
||||
for k in self.design_params['left']:
|
||||
if k != 'enable_asym':
|
||||
# Use proper value assignment instead of deepcopy
|
||||
self._nested_sync(self.design_params[k], self.design_params['left'][k])
|
||||
|
||||
def _view_serialize(self):
|
||||
"""Save a sewing pattern svg representation to tmp folder be used
|
||||
for display"""
|
||||
|
||||
# Get the flat representation
|
||||
pattern = self.sew_pattern.assembly()
|
||||
|
||||
# Clear up the folder from previous version -- it's not needed any more
|
||||
self.clear_previous_svg()
|
||||
try:
|
||||
self.svg_filename = f'pattern_{time.time()}.svg'
|
||||
dwg = pattern.get_svg(self.tmp_path / self.svg_filename,
|
||||
with_text=False,
|
||||
view_ids=False,
|
||||
flat=False,
|
||||
margin=0
|
||||
)
|
||||
dwg.save()
|
||||
|
||||
self.svg_bbox_size = pattern.svg_bbox_size
|
||||
self.svg_bbox = pattern.svg_bbox
|
||||
except pyg.EmptyPatternError:
|
||||
self.svg_filename = ''
|
||||
|
||||
# Cleaning
|
||||
def clear_previous_svg(self):
|
||||
"""Clear previous svg display file"""
|
||||
if self.svg_filename:
|
||||
(self.tmp_path / self.svg_filename).unlink()
|
||||
self.svg_filename = ''
|
||||
|
||||
def clear_previous_download(self):
|
||||
"""Clear previous download package display file"""
|
||||
if self.saved_garment_folder:
|
||||
shutil.rmtree(self.saved_garment_folder)
|
||||
self.saved_garment_folder = ''
|
||||
if self.saved_garment_archive:
|
||||
self.saved_garment_archive.unlink()
|
||||
self.saved_garment_archive = ''
|
||||
|
||||
def clear_3d(self):
|
||||
if self.paths_3d is not None:
|
||||
shutil.rmtree(self.paths_3d.out_el)
|
||||
self.paths_3d = None
|
||||
|
||||
# 3D
|
||||
def drape_3d(self):
|
||||
"""Run the draping of the current frame"""
|
||||
|
||||
# Config setup
|
||||
props = data_config.Properties('./assets/Sim_props/gui_sim_props.yaml') # TODOLOW Parameter?
|
||||
props.set_section_stats('sim', fails={}, sim_time={}, spf={}, fin_frame={}, body_collisions={}, self_collisions={})
|
||||
props.set_section_stats('render', render_time={})
|
||||
|
||||
# Force the design to be fitted to mean body shape
|
||||
# TODOLOW Support body shape estimation from measurements
|
||||
|
||||
def_sew_pattern = MetaGarment(
|
||||
'Configured_design', self.default_body_params, self.design_params)
|
||||
|
||||
# Save the pattern
|
||||
pattern_folder = self.save(False, save_pattern=def_sew_pattern)
|
||||
|
||||
# Paths
|
||||
paths = PathCofig(
|
||||
in_element_path=pattern_folder,
|
||||
out_path=self.save_path,
|
||||
in_name=def_sew_pattern.name,
|
||||
out_name=self.sew_pattern.name + '_3D',
|
||||
body_name=self.body_id, # 'f_smpl_average_A40'
|
||||
smpl_body=False, # NOTE: depends on chosen body model
|
||||
add_timestamp=False
|
||||
)
|
||||
|
||||
# Generate and save garment box mesh (if not existent)
|
||||
garment_box_mesh = BoxMesh(paths.in_g_spec, props['sim']['config']['resolution_scale'])
|
||||
garment_box_mesh.load()
|
||||
garment_box_mesh.serialize(
|
||||
paths, store_panels=False, uv_config=props['render']['config']['uv_texture'])
|
||||
|
||||
# TODOLOW Don't print progress to console with so many lines
|
||||
run_sim(
|
||||
garment_box_mesh.name,
|
||||
props,
|
||||
paths,
|
||||
save_v_norms=False,
|
||||
store_usd=False, # NOTE: False for fast simulation!,
|
||||
optimize_storage=False,
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Convert to displayable element
|
||||
mesh = trimesh.load_mesh(paths.g_sim)
|
||||
|
||||
# enable double-sided material for nice viewing
|
||||
pbr_material = mesh.visual.material.to_pbr()
|
||||
pbr_material.doubleSided = True
|
||||
mesh.visual.material = pbr_material
|
||||
# export
|
||||
mesh.export(paths.g_sim_glb)
|
||||
|
||||
self.paths_3d = paths
|
||||
self.is_in_3D = True
|
||||
|
||||
return paths.out_el, paths.g_sim_glb.name
|
||||
|
||||
# Current state
|
||||
def is_design_sectioned(self):
|
||||
"""Check if design parameters are grouped by sections:
|
||||
the top level of design dictionary does not contain actual parameters
|
||||
"""
|
||||
for param in self.design_params:
|
||||
if 'v' in self.design_params[param]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_slow_design(self) -> bool:
|
||||
"""Check is parameters that result in slow pattern generation are enabled
|
||||
|
||||
E.g. curved armhole evaluation
|
||||
"""
|
||||
# Pants
|
||||
if (self.design_params['meta']['bottom']['v'] == 'Pants'):
|
||||
return True
|
||||
|
||||
# Upper garment
|
||||
is_not_upper = self.design_params['meta']['upper']['v'] is None
|
||||
if is_not_upper:
|
||||
return False
|
||||
|
||||
# Upper + fitted + strapless
|
||||
is_asymm = self.design_params['left']['enable_asym']['v']
|
||||
is_fitted = 'Fitted' in self.design_params['meta']['upper']['v']
|
||||
is_strapless = self.design_params['fitted_shirt']['strapless']['v']
|
||||
is_asymm_strapless = self.design_params['left']['fitted_shirt']['strapless']['v']
|
||||
|
||||
is_strapless = is_fitted and is_strapless
|
||||
is_asymm_strapless = is_fitted and is_asymm_strapless
|
||||
|
||||
# Has a hoody
|
||||
collar_component = self.design_params['collar']['component']['style']['v']
|
||||
has_hoody = collar_component is not None and 'Hood' in collar_component
|
||||
|
||||
# Sleeve potential setup
|
||||
sleeves = self.design_params['sleeve']
|
||||
is_sleeveless = sleeves['sleeveless']['v']
|
||||
is_curve = sleeves['armhole_shape']['v'] == 'ArmholeCurve'
|
||||
is_curve = not is_sleeveless and is_curve
|
||||
|
||||
is_asym_sleeveless = self.design_params['left']['sleeve']['sleeveless']['v']
|
||||
is_asymm_curve = self.design_params['left']['sleeve']['armhole_shape']['v'] == 'ArmholeCurve'
|
||||
is_asymm_curve = not is_asym_sleeveless and is_asymm_curve
|
||||
|
||||
if is_asymm:
|
||||
right_check = (not is_strapless) and is_curve
|
||||
left_check = (not is_asymm_strapless) and is_asymm_curve
|
||||
return right_check or left_check
|
||||
else:
|
||||
return (not is_strapless) and is_curve or has_hoody
|
||||
|
||||
def save(self, pack=True, save_pattern: Optional[MetaGarment]=None):
|
||||
"""Save current garment design to self.save_path """
|
||||
|
||||
# Save current pattern
|
||||
if save_pattern is None:
|
||||
save_pattern = self.sew_pattern
|
||||
|
||||
pattern = save_pattern.assembly()
|
||||
|
||||
# Save as json file
|
||||
self.saved_garment_folder = pattern.serialize(
|
||||
self.save_path,
|
||||
to_subfolder=True,
|
||||
with_3d=False, with_text=False, view_ids=False,
|
||||
with_printable=True,
|
||||
empty_ok=True
|
||||
)
|
||||
|
||||
self.saved_garment_folder = Path(self.saved_garment_folder)
|
||||
self.body_params.save(self.saved_garment_folder)
|
||||
|
||||
with open(self.saved_garment_folder / 'design_params.yaml', 'w') as f:
|
||||
yaml.dump(
|
||||
{'design': self.design_params},
|
||||
f,
|
||||
default_flow_style=False,
|
||||
sort_keys=False
|
||||
)
|
||||
|
||||
# pack
|
||||
if pack:
|
||||
# Only add geometry if design didn't change since last drape
|
||||
if not self.is_in_3D:
|
||||
self.clear_3d() # Clean any saved 3D if it's not synced with current design
|
||||
self.saved_garment_archive = Path(shutil.make_archive(
|
||||
self.save_path / '..' / f'{self.saved_garment_folder.name}_{self.id}', 'zip',
|
||||
root_dir=self.save_path
|
||||
))
|
||||
|
||||
print(f'Success! {self.sew_pattern.name} saved to {self.saved_garment_folder}')
|
||||
|
||||
return self.saved_garment_archive if pack else self.saved_garment_folder
|
||||
|
||||
24
gui/maya_garmentviewer.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Loads maya interface for editing & testing template files
|
||||
* Maya 2022+
|
||||
* Qualoth
|
||||
"""
|
||||
|
||||
from maya import cmds
|
||||
from importlib import reload
|
||||
|
||||
# My modules
|
||||
from pygarment import mayaqltools as mymaya
|
||||
reload(mymaya)
|
||||
|
||||
# -------------- Main -------------
|
||||
if __name__ == "__main__":
|
||||
print('Load plugins')
|
||||
mymaya.qualothwrapper.load_plugin()
|
||||
cmds.loadPlugin('mtoa.mll') # https://stackoverflow.com/questions/50422566/how-to-register-arnold-render
|
||||
cmds.loadPlugin('objExport.mll') # same as in https://forums.autodesk.com/t5/maya-programming/invalid-file-type-specified-atomimport/td-p/9121166
|
||||
|
||||
try:
|
||||
mymaya.garmentUI.start_GUI()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
1837
lmm_utils/Qwen/qwen2vl_lora_mlp/qwen2vl_modify_modeling_qwen2_vl.py
Normal file
0
lmm_utils/__init__.py
Normal file
135
lmm_utils/agent.py
Normal file
@@ -0,0 +1,135 @@
|
||||
|
||||
import copy
|
||||
from lmm_utils.core import MMUA
|
||||
from lmm_utils.projector import input_caption2random_default_cption
|
||||
from lmm_utils.predict_garmentcode_picture import Predictor
|
||||
|
||||
class Agent():
|
||||
def __init__(self,api_key=None, base_url=None, model=None,text_model=None,model_init=True):
|
||||
|
||||
self.mmua=MMUA(api_key=api_key, base_url=base_url, model=model,text_model=text_model)
|
||||
self.dsl_ga= Predictor(model_init=model_init)
|
||||
|
||||
def modify_design(self, design_list, text_prompt,design_params):
|
||||
|
||||
gpt_design_list=None
|
||||
gpt_response=None
|
||||
gpt_design_params=None
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.text_forusermodel_gpt(design_list,
|
||||
user_input=text_prompt)
|
||||
except Exception as e:
|
||||
print("modify_fail,trying again.please wait")
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.text_forusermodel_gpt(design_list,
|
||||
user_input=text_prompt)
|
||||
except Exception as e:
|
||||
print("modify_fail,pleae input prompt again")
|
||||
gpt_response = "modify_fail,pleae input prompt again"
|
||||
if gpt_design_list is not None:
|
||||
gpt_design_list = input_caption2random_default_cption(gpt_design_list)
|
||||
more_caption_list = set(gpt_design_list)-set(design_list)
|
||||
gpt_design_params = self.dsl_ga.caption2yaml(more_caption_list,modify=True,cache_input_design_data=copy.deepcopy(design_params))
|
||||
return gpt_response, gpt_design_params, gpt_design_list
|
||||
|
||||
|
||||
def stress_design(self, design_list, img_url,design_params):
|
||||
gpt_design_list=None
|
||||
gpt_response=None
|
||||
gpt_design_params=None
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.picture_caption_gpt_red(img_url, caption=design_list)
|
||||
except Exception as e:
|
||||
print("stress_fail,trying again.please wait")
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.picture_caption_gpt_red(img_url, caption=design_list)
|
||||
except Exception as e:
|
||||
print("stress_fail,please input prompt and picture again")
|
||||
gpt_response = "stress_fail,please input prompt and picture again"
|
||||
if gpt_design_list is not None:
|
||||
gpt_design_list = input_caption2random_default_cption(gpt_design_list)
|
||||
more_caption_list = set(gpt_design_list) - set(design_list)
|
||||
gpt_design_params = self.dsl_ga.caption2yaml(more_caption_list, modify=True,
|
||||
cache_input_design_data=copy.deepcopy(design_params))
|
||||
|
||||
return gpt_response, gpt_design_params, gpt_design_list
|
||||
|
||||
|
||||
def picture_text_design(self, img_url,text_prompt):
|
||||
gpt_design_list=None
|
||||
gpt_response=None
|
||||
gpt_design_params=None
|
||||
recognize_picture_bool = True
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.picture_gpt(img_url)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::Generation_FAILURE::Failed for image [I]{img_url} and [T]{text_prompt}"
|
||||
f" due to {str(e)}, trying again...")
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.picture_gpt(img_url)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::PARSE_FAILURE::Failed to parse image [I]{img_url} "
|
||||
f"and [T]{text_prompt} again due to {str(e)}, give up.")
|
||||
gpt_response = "Generation failed, please try another image or prompt."
|
||||
recognize_picture_bool = False
|
||||
|
||||
if recognize_picture_bool:
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.text_forusermodel_gpt(
|
||||
caption=gpt_design_list, user_input=text_prompt)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::AUTHORING_FAILURE::Failed to understand instruction {text_prompt} "
|
||||
f"due to {str(e)}, trying again...")
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.text_forusermodel_gpt(
|
||||
caption=gpt_design_list, user_input=text_prompt)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::AUTHORING_FAILURE::Failed to understand instruction {text_prompt}"
|
||||
f" due to {str(e)}, give up.")
|
||||
gpt_response = "Authoring failed, please try another instruction."
|
||||
if gpt_design_list is not None:
|
||||
gpt_design_list = input_caption2random_default_cption(gpt_design_list)
|
||||
gpt_design_params = self.dsl_ga.caption2yaml(gpt_design_list)
|
||||
return gpt_response, gpt_design_params, gpt_design_list
|
||||
def picture_design(self, img_url):
|
||||
gpt_design_list=None
|
||||
gpt_response=None
|
||||
gpt_design_params=None
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.picture_gpt(img_url)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::PARSE_IMAGE_FAILURE::Failed for image {img_url} due to {str(e)}, trying again...")
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.picture_gpt(img_url)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::PARSE_IMAGE_FAILURE::Failed for image {img_url} due to {str(e)}, give up.")
|
||||
gpt_response = "Generation failed, please try another image."
|
||||
if gpt_design_list is not None:
|
||||
gpt_design_list = input_caption2random_default_cption(gpt_design_list)
|
||||
gpt_design_params = self.dsl_ga.caption2yaml(gpt_design_list,image_path=img_url)
|
||||
|
||||
return gpt_response, gpt_design_params, gpt_design_list
|
||||
|
||||
|
||||
def text_design(self, text_prompt):
|
||||
gpt_design_list=None
|
||||
gpt_response=None
|
||||
gpt_design_params=None
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.text_gpt(text_prompt)
|
||||
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::PARSE_TEXT_FAILURE::Failed to parse {text_prompt} due to {str(e)}, trying again...")
|
||||
try:
|
||||
gpt_design_list, gpt_response = self.mmua.text_gpt(text_prompt)
|
||||
except Exception as e:
|
||||
print(f"GPT_UTIL::PARSE_TEXT_FAILURE::Failed to parse {text_prompt} due to {str(e)}, give up.")
|
||||
gpt_response="Generation failed, please try another prompt."
|
||||
if gpt_design_list is not None:
|
||||
gpt_design_list = input_caption2random_default_cption(gpt_design_list)
|
||||
gpt_design_params = self.dsl_ga.caption2yaml(gpt_design_list)
|
||||
|
||||
return gpt_response, gpt_design_params, gpt_design_list
|
||||
|
||||
|
||||
|
||||
2260
lmm_utils/core.py
Normal file
116
lmm_utils/fintuned_qwen2vl_model.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from transformers import AutoModel, AutoConfig
|
||||
from peft import PeftModel, PeftConfig
|
||||
import torch
|
||||
from datasets import Dataset
|
||||
from modelscope import snapshot_download, AutoTokenizer
|
||||
from qwen_vl_utils import process_vision_info
|
||||
from peft import LoraConfig, TaskType, get_peft_model, PeftModel
|
||||
from transformers import (
|
||||
TrainingArguments,
|
||||
Trainer,
|
||||
DataCollatorForSeq2Seq,
|
||||
# Qwen2VLForConditionalGeneration,
|
||||
AutoProcessor,
|
||||
PreTrainedModel
|
||||
)
|
||||
from lmm_utils.Qwen.qwen2vl_lora_mlp.qwen2vl_modify_modeling_qwen2_vl import Qwen2VLForConditionalGeneration
|
||||
import json
|
||||
# Qwen2VLForConditionalGeneration
|
||||
class LoRAWithMLP(nn.Module):
|
||||
def __init__(self, base_model_name, mlp_hidden_size=512, num_mlp_layers=2,device='cuda:0'):
|
||||
super().__init__()
|
||||
self.device=device
|
||||
self.base_model = Qwen2VLForConditionalGeneration.from_pretrained("./lmm_utils/Qwen/Qwen2-VL-2B-Instruct/", device_map=device,
|
||||
torch_dtype=torch.bfloat16, trust_remote_code=True, )
|
||||
self.base_model.enable_input_require_grads() # This method is performed when gradient checkpoints are turned on
|
||||
config = LoraConfig(
|
||||
task_type=TaskType.CAUSAL_LM,
|
||||
# target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
|
||||
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
|
||||
inference_mode=False, # Training mode
|
||||
r=64,
|
||||
lora_alpha=16,
|
||||
lora_dropout=0.05,
|
||||
bias="none",
|
||||
)
|
||||
# Get the LoRA model
|
||||
self.lora_model = get_peft_model(self.base_model, config)
|
||||
mlp_layers = []
|
||||
input_dim = self.lora_model.config.hidden_size # Inherit large model hidden_size
|
||||
for _ in range(num_mlp_layers):
|
||||
mlp_layers.append(nn.Linear(input_dim, mlp_hidden_size,dtype=torch.bfloat16))
|
||||
mlp_layers.append(nn.ReLU())
|
||||
input_dim = mlp_hidden_size
|
||||
mlp_layers.append(nn.Linear(mlp_hidden_size, 123,dtype=torch.bfloat16)) #Output size = hidden_size
|
||||
self.mlp = nn.Sequential(*mlp_layers)
|
||||
|
||||
def forward(self, input_ids=None,
|
||||
attention_mask=None,
|
||||
inputs_embeds=None,
|
||||
labels=None,
|
||||
output_attentions=None,
|
||||
output_hidden_states=None,
|
||||
return_dict=None,
|
||||
task_ids=None,
|
||||
**kwargs,):
|
||||
# Calculate the output of the large model after LoRA adaptation
|
||||
|
||||
lora_output = self.lora_model(input_ids=input_ids,attention_mask=attention_mask,inputs_embeds=inputs_embeds,labels=None,
|
||||
output_attentions=output_attentions,
|
||||
output_hidden_states=output_hidden_states,
|
||||
return_dict=return_dict,
|
||||
task_ids=task_ids,
|
||||
)
|
||||
|
||||
# Calculate the output of MLP additional processing
|
||||
mlp_output = self.mlp(lora_output.hidden_states[:,-1])
|
||||
|
||||
return mlp_output # Let MLP adjust the output of LoRA
|
||||
|
||||
|
||||
def save_checkpoint(self, path,epoch,optimizer,scheduler,best_valid_loss,avg_train_loss):
|
||||
filtered_dict = {name: param for name, param in self.state_dict().items() if
|
||||
'base_model' not in name or 'lora' in name}
|
||||
checkpoint_dict = {
|
||||
'epoch': epoch,
|
||||
'model_state_dict': filtered_dict,
|
||||
'optimizer_state_dict': optimizer.state_dict(),
|
||||
'best_valid_loss': best_valid_loss,
|
||||
'avg_train_loss': avg_train_loss,
|
||||
"scheduler_state_dict":scheduler.state_dict(),
|
||||
}
|
||||
torch.save(checkpoint_dict, path)
|
||||
def load_checkpoint(self, path, optimizer,scheduler, device):
|
||||
"""
|
||||
Load the checkpoint and restore the model and optimizer state
|
||||
:p aram model: The model that needs to be restored
|
||||
:p aram optimizer: The optimizer that needs to be restored
|
||||
:p aram path: checkpoint file path
|
||||
:p aram device: Runtime device (default GPU)
|
||||
:return: epoch of training, best validation loss, training loss
|
||||
"""
|
||||
checkpoint = torch.load(path, map_location=device,optimizer=None,) # Load checkpoint
|
||||
|
||||
self.load_state_dict(checkpoint['model_state_dict'], strict=False) # Load the model parameters
|
||||
optimizer.load_state_dict(checkpoint['optimizer_state_dict']) # Load optimizer parameters
|
||||
scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
|
||||
epoch = checkpoint.get('epoch', 0) # Get the epoch
|
||||
best_valid_loss = checkpoint.get('best_valid_loss', float('inf')) # Get the best verification loss
|
||||
avg_train_loss = checkpoint.get('avg_train_loss', float('inf')) # Get the average training loss
|
||||
|
||||
return epoch, best_valid_loss
|
||||
|
||||
def save_weights(self, path):
|
||||
""" Only LoRA + MLP weights are saved, and the original Qwen2VL model is not included """
|
||||
# for name, param in self.state_dict().items():
|
||||
# print(name, param.requires_grad)
|
||||
filtered_dict = {name: param for name, param in self.state_dict().items() if 'base_model' not in name or 'lora' in name}
|
||||
torch.save(filtered_dict, path)
|
||||
# torch.save(self.state_dict(), path)
|
||||
|
||||
def load_weights(self, path):
|
||||
""" Load LoRA + MLP weights (need to initialize the model first) """
|
||||
state_dict = torch.load(path, map_location=self.device)
|
||||
self.load_state_dict(state_dict, strict=False) # strict=False Some layers are allowed to be missing
|
||||
153
lmm_utils/helper.py
Normal file
@@ -0,0 +1,153 @@
|
||||
import os
|
||||
from lmm_utils.core import MMUA
|
||||
import shutil
|
||||
from lmm_utils.projector import input_caption2random_default_cption
|
||||
import json
|
||||
import time
|
||||
from sim_utils import modelandreturn_picture_path, garmentyaml_folder2json_folder
|
||||
from lmm_utils.predict_garmentcode_picture import Predictor
|
||||
|
||||
|
||||
def category2yaml2json(
|
||||
category,
|
||||
category_data,
|
||||
final_json_path=None,
|
||||
sim_bool=False,
|
||||
id="root",
|
||||
api_key=None,
|
||||
base_url=None,
|
||||
model=None,
|
||||
dsl_ga=None,
|
||||
):
|
||||
"""Specify the different types of data, pass in the data, and get a json file to generate the result for the final boilerplate.
|
||||
Args:
|
||||
category(string): Specify the type of input 'picture' 'text' 'list'
|
||||
category_data: List or ImagePath(String) or Text(String) Enter different specific data depending on the type
|
||||
final_json_path (string): specifies the final JSON path,
|
||||
At the same time, the json file of the template data, the template image, the converted stylexd format, and the template generation time will be generated in the json folder
|
||||
sim_bool (bool): Specifies whether simulation is required
|
||||
id(string): Create a folder under the user_data based on the ID you entered temp_user_folder_for{id}gpt contains all the data related to the template generated this time.
|
||||
api_key (string): Specifies the API to access the model, if the input is None, the default API will be called
|
||||
base_url (string): Specifies the URL to be visited, if the input is None, the default API will be called
|
||||
model(string): Specifies the model to be accessed, if the input is None, the default model will be called
|
||||
Return :
|
||||
json_list (list): the list selected by the large model
|
||||
response(string): the reply of the large model, removing the list content in the reply
|
||||
"""
|
||||
|
||||
mmua_llm = MMUA(api_key=api_key, base_url=base_url, model=model)
|
||||
if dsl_ga is None:
|
||||
dsl_ga = Predictor()
|
||||
gpt_respond = None
|
||||
start_time = time.time()
|
||||
json_list = []
|
||||
picture_path = None
|
||||
if category == "picture": # The corresponding category_data is the image path
|
||||
json_list, gpt_respond = mmua_llm.picture_gpt(category_data)
|
||||
picture_path = category_data
|
||||
if category == "text": # The text corresponding to the input
|
||||
json_list, gpt_respond = mmua_llm.text_gpt(category_data)
|
||||
if category == "list": # It's the list of captions
|
||||
json_list = category_data
|
||||
caption_json_list = json_list
|
||||
json_list = input_caption2random_default_cption(json_list)
|
||||
dsl_ga.caption_json(caption=json_list, id=id,picture_path=picture_path)
|
||||
end_time = time.time()
|
||||
|
||||
pattern_generate_time = end_time - start_time
|
||||
print(pattern_generate_time)
|
||||
|
||||
temp_json_file_path = (
|
||||
f"user_data/temp_user_folder_for{id}gpt/now_{id}/now_{id}_specification.json"
|
||||
)
|
||||
if final_json_path is not None:
|
||||
final_json_dirname = os.path.dirname(final_json_path)
|
||||
os.makedirs(final_json_dirname, exist_ok=True)
|
||||
|
||||
with open(f"{final_json_dirname}/pattern_generate_time.txt", "a") as f:
|
||||
f.write(f"pattern_generate_time:{pattern_generate_time:.4f} s\n")
|
||||
|
||||
with open(f"{final_json_dirname}/caption.json", "w") as file:
|
||||
json.dump(caption_json_list, file, indent=4)
|
||||
|
||||
with open(f"{final_json_dirname}/gpt_respond.txt", "w") as file:
|
||||
file.write(gpt_respond)
|
||||
f"user_data/temp_user_folder_for{id}gpt"
|
||||
# Define the source file path
|
||||
|
||||
print("temp_json_file_path", temp_json_file_path)
|
||||
# print("temp_json_file_path",temp_json_file_path)
|
||||
|
||||
# Define the target file path, including the new file name
|
||||
destination = final_json_path
|
||||
|
||||
# Copy the file and retain the metadata, while modifying the file name
|
||||
shutil.copy2(temp_json_file_path, destination)
|
||||
if category == "text":
|
||||
with open(f"{final_json_dirname}/user_input.txt", "w") as file:
|
||||
# Write text
|
||||
file.write(category_data)
|
||||
if category == "picture":
|
||||
image_path = category_data
|
||||
# Next, save the original image to the folder where the json path is located, and name it the same as this json file, but with a different extension
|
||||
_, picture_file_extension = os.path.splitext(image_path)
|
||||
picture_file_path, _ = os.path.splitext(final_json_path)
|
||||
# Composite the new image path, here the name of the image is changed
|
||||
picture_path = picture_file_path + picture_file_extension
|
||||
shutil.copy2(image_path, picture_path)
|
||||
|
||||
filename = os.path.splitext(os.path.basename(final_json_path))[0]
|
||||
|
||||
if category == "list": pass
|
||||
if sim_bool:
|
||||
modelandreturn_picture_path(temp_json_file_path)
|
||||
model_png = f"user_data/temp_user_folder_for{id}gpt/now_{id}/now_{id}/now_{id}_render_front.png"
|
||||
#Copy the mockup to the destination folder
|
||||
model_png_end_path = final_json_dirname + "/sim_garment_front.png"
|
||||
shutil.copy2(model_png, model_png_end_path)
|
||||
model_png = f"user_data/temp_user_folder_for{id}gpt/now_{id}/now_{id}/now_{id}_render_back.png"
|
||||
# Copy the mockup to the destination folder
|
||||
model_png_end_path = final_json_dirname + "/sim_garment_back.png"
|
||||
|
||||
shutil.copy2(model_png, model_png_end_path)
|
||||
|
||||
# Duplicate the PNG image of the plate
|
||||
|
||||
pattern_png = (
|
||||
f"user_data/temp_user_folder_for{id}gpt/now_{id}/now_{id}_pattern.png"
|
||||
)
|
||||
pattern_png_end_path = final_json_dirname + f"/{filename}_pattern.png"
|
||||
shutil.copy2(pattern_png, pattern_png_end_path)
|
||||
|
||||
# Copy the yaml file
|
||||
yaml_file = f"user_data/temp_user_folder_for{id}gpt//now_{id}/now_{id}.yaml"
|
||||
yaml_end_path = final_json_dirname + f"/{filename}.yaml"
|
||||
shutil.copy2(yaml_file, yaml_end_path)
|
||||
if final_json_path is None:
|
||||
temp_json_dirname = os.path.dirname(temp_json_file_path)
|
||||
with open(f"{temp_json_dirname}/pattern_generate_time.txt", "a") as f:
|
||||
f.write(f"pattern_generate_time:{pattern_generate_time:.4f} s\n")
|
||||
|
||||
with open(f"{temp_json_dirname}/caption.json", "w") as file:
|
||||
json.dump(caption_json_list, file, indent=4)
|
||||
|
||||
with open(f"{temp_json_dirname}/gpt_respond.txt", "w") as file:
|
||||
file.write(gpt_respond)
|
||||
|
||||
if category == "text":
|
||||
with open(f"{temp_json_dirname}/user_input.txt", "w") as file:
|
||||
# Write text
|
||||
file.write(category_data)
|
||||
if category == "picture":
|
||||
image_path = category_data
|
||||
# Next, save the original image to the folder where the json path is located, and name it the same as this json file, but with a different extension
|
||||
_, picture_file_extension = os.path.splitext(image_path)
|
||||
picture_file_path, _ = os.path.splitext(temp_json_file_path)
|
||||
# Composite the new image path, here the name of the image is changed
|
||||
picture_path = picture_file_path + picture_file_extension
|
||||
|
||||
shutil.copy2(image_path, picture_path)
|
||||
if sim_bool:
|
||||
modelandreturn_picture_path(temp_json_file_path)
|
||||
|
||||
return json_list, gpt_respond
|
||||
478
lmm_utils/predict_garmentcode_picture.py
Normal file
@@ -0,0 +1,478 @@
|
||||
from torch.utils.data import DataLoader
|
||||
import torch
|
||||
from modelscope import AutoTokenizer
|
||||
from qwen_vl_utils import process_vision_info
|
||||
from transformers import AutoProcessor
|
||||
import json
|
||||
from lmm_utils.fintuned_qwen2vl_model import LoRAWithMLP
|
||||
from lmm_utils.projector import vec_2_pattern_yaml
|
||||
from lmm_utils.projector import save_design2yaml
|
||||
import yaml
|
||||
import os
|
||||
import numpy as np
|
||||
from lmm_utils.sim_utils import garmentyaml_folder2json_folder
|
||||
from pathlib import Path
|
||||
def load_system_config():
|
||||
root_path = Path(__file__).resolve().parent.parent # Navigate to the project's home directory
|
||||
config_path = root_path / "system.json"
|
||||
with open(config_path, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
_config = load_system_config()
|
||||
|
||||
class Predictor:
|
||||
def __init__(self, model_path="./lmm_utils/Qwen/Qwen2-VL-2B-Instruct", device=None,model_init=True):
|
||||
self.model_init = model_init
|
||||
if not model_init:
|
||||
return
|
||||
|
||||
if device is None:
|
||||
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
||||
else:
|
||||
self.device = device
|
||||
self.model =LoRAWithMLP(base_model_name=model_path, mlp_hidden_size=512, num_mlp_layers=2,
|
||||
device=device)
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False, trust_remote_code=True)
|
||||
self.processor = AutoProcessor.from_pretrained(model_path)
|
||||
mask_list = torch.tensor([1, 1, 1, 1, 0, 0,
|
||||
1, # fitted
|
||||
0, 0, 0, # shirt
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, # collar b_beizer_y
|
||||
1, 1, 1, 0, 1, 0, 0, # collar
|
||||
1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, # sleeve
|
||||
1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, # lfet sleeve _cuff
|
||||
0, 0, 0, 0, 0, # skirt
|
||||
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, # flare-skirt
|
||||
1, 0, 0, 1, 0, # godet-skirt
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, # peicl-skirt
|
||||
1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 0, 0]).to(device=self.device) # 1 represents the discrete value identified by the MMUA as the final result.
|
||||
self.mask_list = 1 - mask_list
|
||||
checkpoint = torch.load(
|
||||
_config['param_model'],
|
||||
map_location='cpu')
|
||||
self.model.load_state_dict(checkpoint['model_state_dict'], strict=False) # Load the model parameters
|
||||
self.model.to(self.device)
|
||||
def predict(self,img_path,caption):
|
||||
messages_list = [[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "image",
|
||||
"image": f"{img_path}",
|
||||
"resized_height": 280,
|
||||
"resized_width": 280,
|
||||
},
|
||||
{"type": "text", "text": f"garmentcode Yes:{caption}"},
|
||||
],
|
||||
}
|
||||
]]
|
||||
|
||||
"""
|
||||
The dataset is preprocessed
|
||||
"""
|
||||
MAX_LENGTH = 8192
|
||||
input_ids, attention_mask, labels = [], [], []
|
||||
msgs = messages_list
|
||||
texts = self.processor.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
|
||||
# def process_func(self, conversation):
|
||||
image_inputs, video_inputs = process_vision_info(msgs) # Get data (preprocessed)
|
||||
inputs = self.processor(
|
||||
text=texts,
|
||||
images=image_inputs,
|
||||
videos=video_inputs,
|
||||
padding=True,
|
||||
return_tensors="pt",
|
||||
|
||||
)
|
||||
inputs['image_grid_thw'] = inputs['image_grid_thw'] # Transform from (1,h,w,c) to (h,w,c)
|
||||
input_ids = inputs['input_ids']
|
||||
attention_mask = inputs['attention_mask']
|
||||
labels = None
|
||||
batch = {"input_ids": input_ids, "attention_mask": attention_mask, "labels": labels,
|
||||
"pixel_values": inputs['pixel_values'], "image_grid_thw": inputs['image_grid_thw']}
|
||||
batch = {k: v.to(self.device) for k, v in batch.items() if isinstance(v, torch.Tensor)}
|
||||
with torch.no_grad():
|
||||
outputs = self.model(**batch)
|
||||
outputs = outputs * self.mask_list
|
||||
return outputs
|
||||
|
||||
def caption2yaml(self,caption, yaml_path='assets/design_params/default_text_value.yaml', new_yaml_path=None,
|
||||
return_template_yaml='assets/design_params/default_template.yaml',
|
||||
body_param_files="assets/bodies/mean_all_full.yaml", modify=False, image_path=None,
|
||||
cache_input_design_data=None):
|
||||
'''Map the caption to a yaml file.
|
||||
Args:
|
||||
caption(list): the list generated by the large model.
|
||||
yaml_path (string): The basic YAML file used to fill in the value represented by the caption into the YAML file,
|
||||
which is a specially marked YAML file.
|
||||
new_yaml_path (string): Save this yaml file to a new path
|
||||
modify=False and text_bool=False, which are used to control the processing of the length of the lower body.
|
||||
Return :
|
||||
design(dict): the data of the processed YAML file
|
||||
'''
|
||||
|
||||
with open(body_param_files, 'r', encoding='utf-8') as yaml_file:
|
||||
body_param = yaml.safe_load(yaml_file)
|
||||
body = body_param['body']
|
||||
with open(yaml_path, 'r', encoding='utf-8') as yaml_file:
|
||||
default_yaml = yaml.safe_load(yaml_file)
|
||||
design = default_yaml['design']
|
||||
with open(return_template_yaml, 'r', encoding='utf-8') as yaml_file:
|
||||
default_template_yaml = yaml.safe_load(yaml_file)
|
||||
design_template = default_template_yaml['design']
|
||||
if cache_input_design_data is not None:
|
||||
design_template = cache_input_design_data
|
||||
|
||||
for design_item in caption:
|
||||
design_item_list = design_item.split('__')
|
||||
temp = design
|
||||
template = design_template
|
||||
for i in range(len(design_item_list)):
|
||||
|
||||
if i == (len(design_item_list) - 1):
|
||||
final_text = design_item_list[i]
|
||||
if final_text.isdigit():
|
||||
final_text = int(final_text)
|
||||
if final_text == 'None':
|
||||
final_text = None
|
||||
if final_text == 'True':
|
||||
final_text = True
|
||||
if final_text == 'False':
|
||||
final_text = False
|
||||
if isinstance(temp['range'][0], dict):
|
||||
for item in temp['range']:
|
||||
if final_text in item:
|
||||
final_text = item[final_text]
|
||||
|
||||
temp['v'] = final_text
|
||||
template['v'] = final_text
|
||||
else:
|
||||
try:
|
||||
temp = temp[design_item_list[i]]
|
||||
template = template[design_item_list[i]]
|
||||
except Exception as e:
|
||||
print(temp)
|
||||
|
||||
design = design_template
|
||||
if 'meta__upper__None' in caption:
|
||||
design['skirt']['rise']['v'] = 0.5
|
||||
design['flare-skirt']['rise']['v'] = 0.5
|
||||
design['pencil-skirt']['rise']['v'] = 0.5
|
||||
design['levels-skirt']['rise']['v'] = 0.5
|
||||
|
||||
if "meta__bottom__SkirtManyPanels" in caption:
|
||||
if "flare-skirt__length__micro" in caption:
|
||||
design['flare-skirt']['length']['v'] = 0.15
|
||||
if "flare-skirt__length__mini" in caption:
|
||||
design['flare-skirt']['length']['v'] = 0.2
|
||||
if "flare-skirt__length__above-knee" in caption:
|
||||
design['flare-skirt']['length']['v'] = 0.3
|
||||
|
||||
if "flare-skirt__length__knee-length" in caption:
|
||||
design['flare-skirt']['length']['v'] = 0.35
|
||||
if "flare-skirt__length__midi" in caption:
|
||||
design['flare-skirt']['length']['v'] = 0.45
|
||||
if "flare-skirt__length__floor-length" in caption:
|
||||
design['flare-skirt']['length']['v'] = 0.6
|
||||
|
||||
if not modify:
|
||||
shirt_length = 0
|
||||
waist_length = 0
|
||||
if 'meta__upper__Shirt' in caption:
|
||||
front_frac = (body['bust'] - body['back_width']) / 2 / body['bust']
|
||||
fb_diff = (front_frac - (0.5 - front_frac)) * body['bust']
|
||||
sh_tan = float(np.tan(np.deg2rad(body['_shoulder_incl'])))
|
||||
shirt_length = design['shirt']['length']['v'] * body['waist_line'] - sh_tan * fb_diff
|
||||
|
||||
if 'meta__upper__FittedShirt' in caption:
|
||||
m_bust = body['bust']
|
||||
front_frac = (body['bust'] - body['back_width']) / 2 / body['bust']
|
||||
sh_tan = float(np.tan(np.deg2rad(body['_shoulder_incl'])))
|
||||
width = front_frac * m_bust
|
||||
adjustment = sh_tan * (width - body['shoulder_w'] / 2)
|
||||
fitted_shirt_length = body['waist_over_bust_line'] - adjustment
|
||||
shirt_length = fitted_shirt_length
|
||||
|
||||
if "meta__wb__None" not in caption:
|
||||
waist_length = design['waistband']['width']['v'] * body["hips_line"]
|
||||
if ("meta__bottom__None" not in caption and 'meta__bottom__Pants' not in caption
|
||||
and 'meta__upper__None' not in caption and "meta__connected__True" in caption):
|
||||
if "meta__bottom__Skirt2" in caption:
|
||||
if "skirt__length__micro" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
elif "skirt__length__mini" in caption:
|
||||
all_length = 70.38289360159473
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__above-knee" in caption:
|
||||
all_length = 83.15389360159473
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__knee-length" in caption:
|
||||
all_length = 98.05339360159472
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__midi" in caption:
|
||||
all_length = 108.69589360159473
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__floor-length" in caption:
|
||||
all_length = 121.46689360159472
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif ("meta__bottom__SkirtCircle" in caption or "meta__bottom__SkirtManyPanels" in caption
|
||||
or "meta__bottom__AsymmSkirtCircle" in caption):
|
||||
if "flare-skirt__length__micro" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['flare-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['flare-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "flare-skirt__length__mini" in caption:
|
||||
all_length = 70.38289360159473
|
||||
design['flare-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['flare-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "flare-skirt__length__above-knee" in caption:
|
||||
all_length = 83.15389360159473
|
||||
design['flare-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['flare-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "flare-skirt__length__knee-length" in caption:
|
||||
all_length = 98.05339360159472
|
||||
design['flare-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['flare-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "flare-skirt__length__midi" in caption:
|
||||
all_length = 108.69589360159473
|
||||
design['flare-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['flare-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "flare-skirt__length__floor-length" in caption:
|
||||
all_length = 121.46689360159472
|
||||
design['flare-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['flare-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
elif "meta__bottom__GodetSkirt" in caption:
|
||||
if "godet-skirt__base__Skirt2" in caption:
|
||||
if "skirt__length__micro" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__mini" in caption:
|
||||
all_length = 70.38289360159473
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__above-knee" in caption:
|
||||
all_length = 83.15389360159473
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__knee-length" in caption:
|
||||
all_length = 98.05339360159472
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__midi" in caption:
|
||||
all_length = 108.69589360159473
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "skirt__length__floor-length" in caption:
|
||||
all_length = 121.46689360159472
|
||||
design['skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['skirt']['rise']['v'] * body["hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "godet-skirt__base__PencilSkirt" in caption:
|
||||
if "pencil-skirt__length__micro" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__mini" in caption:
|
||||
all_length = 70.38289360159473
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__above-knee" in caption:
|
||||
all_length = 83.15389360159473
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__knee-length" in caption:
|
||||
all_length = 98.05339360159472
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__midi" in caption:
|
||||
all_length = 108.69589360159473
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__floor-length" in caption:
|
||||
all_length = 121.46689360159472
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "meta__bottom__PencilSkirt" in caption:
|
||||
|
||||
if "pencil-skirt__length__micro" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__mini" in caption:
|
||||
all_length = 70.38289360159473
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
|
||||
elif "pencil-skirt__length__above-knee" in caption:
|
||||
all_length = 83.15389360159473
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length -
|
||||
design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__knee-length" in caption:
|
||||
all_length = 98.05339360159472
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length -
|
||||
design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__midi" in caption:
|
||||
all_length = 108.69589360159473
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length -
|
||||
design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "pencil-skirt__length__floor-length" in caption:
|
||||
all_length = 121.46689360159472
|
||||
design['pencil-skirt']['length']['v'] = (all_length - shirt_length - waist_length -
|
||||
design['pencil-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "meta__bottom__SkirtLevels" in caption:
|
||||
if "levels-skirt__length__micro" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['levels-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['levels-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "levels-skirt__length__mini" in caption:
|
||||
all_length = 63.99739360159472
|
||||
design['levels-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['levels-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "levels-skirt__length__above-knee" in caption:
|
||||
all_length = 83.15389360159473
|
||||
design['levels-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['levels-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "levels-skirt__length__knee-length" in caption:
|
||||
all_length = 98.05339360159472
|
||||
design['levels-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['levels-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "levels-skirt__length__midi" in caption:
|
||||
all_length = 108.69589360159473
|
||||
design['levels-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['levels-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
elif "levels-skirt__length__floor-length" in caption:
|
||||
all_length = 121.46689360159472
|
||||
design['levels-skirt']['length']['v'] = (all_length - shirt_length - waist_length
|
||||
- design['levels-skirt']['rise']['v'] * body[
|
||||
"hips_line"]) / \
|
||||
body["_leg_length"]
|
||||
|
||||
if image_path and os.path.exists(image_path) and self.model_init:
|
||||
temp_cwd = os.getcwd()
|
||||
image_path = temp_cwd + '/' + image_path
|
||||
param_vec = self.predict(img_path=image_path, caption=caption)
|
||||
param_vec = param_vec[0].float().cpu().numpy()
|
||||
design = vec_2_pattern_yaml(design, param_vec, self.mask_list.tolist())
|
||||
|
||||
if new_yaml_path is not None: save_design2yaml(design, new_yaml_path)
|
||||
return design
|
||||
|
||||
def caption_json(self,caption, id="root",picture_path=None,dsl_ga=None):
|
||||
os.makedirs(f"user_data/temp_user_folder_for{id}gpt", exist_ok=True)
|
||||
self.caption2yaml(
|
||||
caption=caption,
|
||||
new_yaml_path=f"user_data/temp_user_folder_for{id}gpt/now_{id}.yaml",
|
||||
yaml_path="assets/design_params/default_text_value.yaml",
|
||||
image_path=picture_path
|
||||
)
|
||||
# In this case, a folder with the same name as the yaml file will be generated
|
||||
# under the corresponding output folder based on the file name of the yaml file,
|
||||
# and a json file with the same name as the yaml file will be the final result.
|
||||
# Here the output will be the now_picture file in the now_picture folder
|
||||
garmentyaml_folder2json_folder(
|
||||
input_folder=f"user_data/temp_user_folder_for{id}gpt",
|
||||
output_folder=f"user_data/temp_user_folder_for{id}gpt",
|
||||
)
|
||||
785
lmm_utils/projector.py
Normal file
@@ -0,0 +1,785 @@
|
||||
import os
|
||||
import uuid
|
||||
import json
|
||||
import yaml
|
||||
import random
|
||||
import numpy as np
|
||||
from collections import OrderedDict
|
||||
|
||||
# from predict_garmentcode_picture import predict
|
||||
CONNECT_TAG = '__'
|
||||
|
||||
all_text_dict={
|
||||
"meta__upper": [
|
||||
"meta__upper__FittedShirt",
|
||||
"meta__upper__Shirt",
|
||||
"meta__upper__None"
|
||||
],
|
||||
"meta__wb": [
|
||||
"meta__wb__StraightWB",
|
||||
"meta__wb__FittedWB",
|
||||
"meta__wb__None"
|
||||
],
|
||||
"meta__bottom": [
|
||||
"meta__bottom__SkirtCircle",
|
||||
"meta__bottom__AsymmSkirtCircle",
|
||||
"meta__bottom__GodetSkirt",
|
||||
"meta__bottom__Pants",
|
||||
"meta__bottom__Skirt2",
|
||||
"meta__bottom__SkirtManyPanels",
|
||||
"meta__bottom__PencilSkirt",
|
||||
"meta__bottom__SkirtLevels",
|
||||
"meta__bottom__None"
|
||||
],
|
||||
"meta__connected": [
|
||||
"meta__connected__True",
|
||||
"meta__connected__False"
|
||||
],
|
||||
"waistband__waist": [
|
||||
"waistband__waist__fitted",
|
||||
"waistband__waist__slightly-loose",
|
||||
"waistband__waist__loose"
|
||||
],
|
||||
"waistband__width": [
|
||||
"waistband__width__narrow",
|
||||
"waistband__width__medium",
|
||||
"waistband__width__wide"
|
||||
],
|
||||
"fitted_shirt__strapless": [
|
||||
"fitted_shirt__strapless__True",
|
||||
"fitted_shirt__strapless__False"
|
||||
],
|
||||
"shirt__length": [
|
||||
"shirt__length__super-cropped",
|
||||
"shirt__length__regular",
|
||||
"shirt__length__long"
|
||||
],
|
||||
"shirt__width": [
|
||||
"shirt__width__normal",
|
||||
"shirt__width__relaxed"
|
||||
],
|
||||
"shirt__flare": [
|
||||
"shirt__flare__tight",
|
||||
"shirt__flare__straight",
|
||||
"shirt__flare__flared",
|
||||
"shirt__flare__very-flared"
|
||||
],
|
||||
"collar__f_collar": [
|
||||
"collar__f_collar__CircleNeckHalf",
|
||||
"collar__f_collar__CurvyNeckHalf",
|
||||
"collar__f_collar__VNeckHalf",
|
||||
"collar__f_collar__SquareNeckHalf",
|
||||
"collar__f_collar__TrapezoidNeckHalf",
|
||||
"collar__f_collar__CircleArcNeckHalf",
|
||||
"collar__f_collar__Bezier2NeckHalf"
|
||||
],
|
||||
"collar__b_collar": [
|
||||
"collar__b_collar__CircleNeckHalf",
|
||||
"collar__b_collar__CurvyNeckHalf",
|
||||
"collar__b_collar__VNeckHalf",
|
||||
"collar__b_collar__SquareNeckHalf",
|
||||
"collar__b_collar__TrapezoidNeckHalf",
|
||||
"collar__b_collar__CircleArcNeckHalf",
|
||||
"collar__b_collar__Bezier2NeckHalf"
|
||||
],
|
||||
"collar__width": [
|
||||
"collar__width__very-narrow",
|
||||
"collar__width__medium",
|
||||
"collar__width__wide"
|
||||
],
|
||||
"collar__fc_depth": [
|
||||
"collar__fc_depth__shallow",
|
||||
"collar__fc_depth__medium",
|
||||
"collar__fc_depth__deep"
|
||||
],
|
||||
"collar__bc_depth": [
|
||||
"collar__bc_depth__shallow",
|
||||
"collar__bc_depth__medium",
|
||||
"collar__bc_depth__deep"
|
||||
],
|
||||
"collar__fc_angle": [
|
||||
"collar__fc_angle__acute",
|
||||
"collar__fc_angle__standard",
|
||||
"collar__fc_angle__obtuse"
|
||||
],
|
||||
"collar__bc_angle": [
|
||||
"collar__bc_angle__acute",
|
||||
"collar__bc_angle__standard",
|
||||
"collar__bc_angle__obtuse"
|
||||
],
|
||||
"collar__f_bezier_x": [
|
||||
"collar__f_bezier_x__left",
|
||||
"collar__f_bezier_x__center",
|
||||
"collar__f_bezier_x__right"
|
||||
],
|
||||
"collar__f_bezier_y": [
|
||||
"collar__f_bezier_y__top",
|
||||
"collar__f_bezier_y__center",
|
||||
"collar__f_bezier_y__bottom"
|
||||
],
|
||||
"collar__b_bezier_x": [
|
||||
"collar__b_bezier_x__left",
|
||||
"collar__b_bezier_x__center",
|
||||
"collar__b_bezier_x__right"
|
||||
],
|
||||
"collar__b_bezier_y": [
|
||||
"collar__b_bezier_y__top",
|
||||
"collar__b_bezier_y__center",
|
||||
"collar__b_bezier_y__bottom"
|
||||
],
|
||||
"collar__f_flip_curve": [
|
||||
"collar__f_flip_curve__True",
|
||||
"collar__f_flip_curve__False"
|
||||
],
|
||||
"collar__b_flip_curve": [
|
||||
"collar__b_flip_curve__True",
|
||||
"collar__b_flip_curve__False"
|
||||
],
|
||||
"collar__component__style": [
|
||||
"collar__component__style__Turtle",
|
||||
"collar__component__style__SimpleLapel",
|
||||
"collar__component__style__Hood2Panels",
|
||||
"collar__component__style__None"
|
||||
],
|
||||
"collar__component__depth": [
|
||||
"collar__component__depth__shallow",
|
||||
"collar__component__depth__medium",
|
||||
"collar__component__depth__deep"
|
||||
],
|
||||
"collar__component__lapel_standing": [
|
||||
"collar__component__lapel_standing__True",
|
||||
"collar__component__lapel_standing__False"
|
||||
],
|
||||
"collar__component__hood_depth": [
|
||||
"collar__component__hood_depth__shallow",
|
||||
"collar__component__hood_depth__medium",
|
||||
"collar__component__hood_depth__deep"
|
||||
],
|
||||
"collar__component__hood_length": [
|
||||
"collar__component__hood_length__short",
|
||||
"collar__component__hood_length__medium",
|
||||
"collar__component__hood_length__long"
|
||||
],
|
||||
"sleeve__sleeveless": [
|
||||
"sleeve__sleeveless__True",
|
||||
"sleeve__sleeveless__False"
|
||||
],
|
||||
"sleeve__armhole_shape": [
|
||||
"sleeve__armhole_shape__ArmholeSquare",
|
||||
"sleeve__armhole_shape__ArmholeAngle",
|
||||
"sleeve__armhole_shape__ArmholeCurve"
|
||||
],
|
||||
"sleeve__length": [
|
||||
"sleeve__length__short",
|
||||
"sleeve__length__half",
|
||||
"sleeve__length__three-quarter",
|
||||
"sleeve__length__long",
|
||||
"sleeve__length__full"
|
||||
],
|
||||
"sleeve__connecting_width": [
|
||||
"sleeve__connecting_width__narrow",
|
||||
"sleeve__connecting_width__medium",
|
||||
"sleeve__connecting_width__loose",
|
||||
"sleeve__connecting_width__very-loose"
|
||||
],
|
||||
"sleeve__end_width": [
|
||||
"sleeve__end_width__closing",
|
||||
"sleeve__end_width__straight",
|
||||
"sleeve__end_width__opening"
|
||||
],
|
||||
"sleeve__sleeve_angle": [
|
||||
"sleeve__sleeve_angle__small",
|
||||
"sleeve__sleeve_angle__medium",
|
||||
"sleeve__sleeve_angle__large"
|
||||
],
|
||||
"sleeve__opening_dir_mix": [
|
||||
"sleeve__opening_dir_mix__negative-twist",
|
||||
"sleeve__opening_dir_mix__standard",
|
||||
"sleeve__opening_dir_mix__positive-twist"
|
||||
],
|
||||
"sleeve__standing_shoulder": [
|
||||
"sleeve__standing_shoulder__True",
|
||||
"sleeve__standing_shoulder__False"
|
||||
],
|
||||
"sleeve__standing_shoulder_len": [
|
||||
"sleeve__standing_shoulder_len__short",
|
||||
"sleeve__standing_shoulder_len__medium",
|
||||
"sleeve__standing_shoulder_len__long"
|
||||
],
|
||||
"sleeve__connect_ruffle": [
|
||||
"sleeve__connect_ruffle__none",
|
||||
"sleeve__connect_ruffle__some",
|
||||
"sleeve__connect_ruffle__obvious"
|
||||
],
|
||||
"sleeve__smoothing_coeff": [
|
||||
"sleeve__smoothing_coeff__very-smooth",
|
||||
"sleeve__smoothing_coeff__moderate",
|
||||
"sleeve__smoothing_coeff__less-smooth"
|
||||
],
|
||||
"sleeve__cuff__type": [
|
||||
"sleeve__cuff__type__CuffBand",
|
||||
"sleeve__cuff__type__CuffSkirt",
|
||||
"sleeve__cuff__type__CuffBandSkirt",
|
||||
"sleeve__cuff__type__None"
|
||||
],
|
||||
"sleeve__cuff__top_ruffle": [
|
||||
"sleeve__cuff__top_ruffle__straight",
|
||||
"sleeve__cuff__top_ruffle__tapered",
|
||||
"sleeve__cuff__top_ruffle__very_tapered"
|
||||
],
|
||||
"sleeve__cuff__cuff_len": [
|
||||
"sleeve__cuff__cuff_len__short",
|
||||
"sleeve__cuff__cuff_len__medium",
|
||||
"sleeve__cuff__cuff_len__long"
|
||||
],
|
||||
"sleeve__cuff__skirt_fraction": [
|
||||
"sleeve__cuff__skirt_fraction__small",
|
||||
"sleeve__cuff__skirt_fraction__medium",
|
||||
"sleeve__cuff__skirt_fraction__large"
|
||||
],
|
||||
"sleeve__cuff__skirt_flare": [
|
||||
"sleeve__cuff__skirt_flare__slight",
|
||||
"sleeve__cuff__skirt_flare__moderate",
|
||||
"sleeve__cuff__skirt_flare__significant"
|
||||
],
|
||||
"sleeve__cuff__skirt_ruffle": [
|
||||
"sleeve__cuff__skirt_ruffle__none",
|
||||
"sleeve__cuff__skirt_ruffle__some"
|
||||
],
|
||||
"left__enable_asym": [
|
||||
"left__enable_asym__True",
|
||||
"left__enable_asym__False"
|
||||
],
|
||||
"left__fitted_shirt__strapless": [
|
||||
"left__fitted_shirt__strapless__True",
|
||||
"left__fitted_shirt__strapless__False"
|
||||
],
|
||||
"left__shirt__width": [
|
||||
"left__shirt__width__normal",
|
||||
"left__shirt__width__relaxed"
|
||||
],
|
||||
"left__shirt__flare": [
|
||||
"left__shirt__flare__tight",
|
||||
"left__shirt__flare__straight",
|
||||
"left__shirt__flare__flared",
|
||||
"left__shirt__flare__very-flared"
|
||||
],
|
||||
"left__collar__f_collar": [
|
||||
"left__collar__f_collar__CircleNeckHalf",
|
||||
"left__collar__f_collar__CurvyNeckHalf",
|
||||
"left__collar__f_collar__VNeckHalf",
|
||||
"left__collar__f_collar__SquareNeckHalf",
|
||||
"left__collar__f_collar__TrapezoidNeckHalf",
|
||||
"left__collar__f_collar__CircleArcNeckHalf",
|
||||
"left__collar__f_collar__Bezier2NeckHalf"
|
||||
],
|
||||
"left__collar__b_collar": [
|
||||
"left__collar__b_collar__CircleNeckHalf",
|
||||
"left__collar__b_collar__CurvyNeckHalf",
|
||||
"left__collar__b_collar__VNeckHalf",
|
||||
"left__collar__b_collar__SquareNeckHalf",
|
||||
"left__collar__b_collar__TrapezoidNeckHalf",
|
||||
"left__collar__b_collar__CircleArcNeckHalf",
|
||||
"left__collar__b_collar__Bezier2NeckHalf"
|
||||
],
|
||||
"left__collar__width": [
|
||||
"left__collar__width__narrow",
|
||||
"left__collar__width__medium",
|
||||
"left__collar__width__wide"
|
||||
],
|
||||
"left__collar__fc_angle": [
|
||||
"left__collar__fc_angle__acute",
|
||||
"left__collar__fc_angle__standard",
|
||||
"left__collar__fc_angle__obtuse"
|
||||
],
|
||||
"left__collar__bc_angle": [
|
||||
"left__collar__bc_angle__acute",
|
||||
"left__collar__bc_angle__standard",
|
||||
"left__collar__bc_angle__obtuse"
|
||||
],
|
||||
"left__collar__f_bezier_x": [
|
||||
"left__collar__f_bezier_x__left",
|
||||
"left__collar__f_bezier_x__center",
|
||||
"left__collar__f_bezier_x__right"
|
||||
],
|
||||
"left__collar__f_bezier_y": [
|
||||
"left__collar__f_bezier_y__top",
|
||||
"left__collar__f_bezier_y__center",
|
||||
"left__collar__f_bezier_y__bottom"
|
||||
],
|
||||
"left__collar__b_bezier_x": [
|
||||
"left__collar__b_bezier_x__left",
|
||||
"left__collar__b_bezier_x__center",
|
||||
"left__collar__b_bezier_x__right"
|
||||
],
|
||||
"left__collar__b_bezier_y": [
|
||||
"left__collar__b_bezier_y__top",
|
||||
"left__collar__b_bezier_y__center",
|
||||
"left__collar__b_bezier_y__bottom"
|
||||
],
|
||||
"left__collar__f_flip_curve": [
|
||||
"left__collar__f_flip_curve__True",
|
||||
"left__collar__f_flip_curve__False"
|
||||
],
|
||||
"left__collar__b_flip_curve": [
|
||||
"left__collar__b_flip_curve__True",
|
||||
"left__collar__b_flip_curve__False"
|
||||
],
|
||||
"left__sleeve__sleeveless": [
|
||||
"left__sleeve__sleeveless__True",
|
||||
"left__sleeve__sleeveless__False"
|
||||
],
|
||||
"left__sleeve__armhole_shape": [
|
||||
"left__sleeve__armhole_shape__ArmholeSquare",
|
||||
"left__sleeve__armhole_shape__ArmholeAngle",
|
||||
"left__sleeve__armhole_shape__ArmholeCurve"
|
||||
],
|
||||
"left__sleeve__length": [
|
||||
"left__sleeve__length__short",
|
||||
"left__sleeve__length__half",
|
||||
"left__sleeve__length__three-quarter",
|
||||
"left__sleeve__length__long",
|
||||
"left__sleeve__length__full"
|
||||
],
|
||||
"left__sleeve__connecting_width": [
|
||||
"left__sleeve__connecting_width__narrow",
|
||||
"left__sleeve__connecting_width__medium",
|
||||
"left__sleeve__connecting_width__loose",
|
||||
"left__sleeve__connecting_width__very-loose"
|
||||
],
|
||||
"left__sleeve__end_width": [
|
||||
"left__sleeve__end_width__closing",
|
||||
"left__sleeve__end_width__straight",
|
||||
"left__sleeve__end_width__opening"
|
||||
],
|
||||
"left__sleeve__sleeve_angle": [
|
||||
"left__sleeve__sleeve_angle__small",
|
||||
"left__sleeve__sleeve_angle__medium",
|
||||
"left__sleeve__sleeve_angle__large"
|
||||
],
|
||||
"left__sleeve__opening_dir_mix": [
|
||||
"left__sleeve__opening_dir_mix__negative-twist",
|
||||
"left__sleeve__opening_dir_mix__standard",
|
||||
"left__sleeve__opening_dir_mix__positive-twist"
|
||||
],
|
||||
"left__sleeve__standing_shoulder": [
|
||||
"left__sleeve__standing_shoulder__True",
|
||||
"left__sleeve__standing_shoulder__False"
|
||||
],
|
||||
"left__sleeve__standing_shoulder_len": [
|
||||
"left__sleeve__standing_shoulder_len__short",
|
||||
"left__sleeve__standing_shoulder_len__medium",
|
||||
"left__sleeve__standing_shoulder_len__long"
|
||||
],
|
||||
"left__sleeve__connect_ruffle": [
|
||||
"left__sleeve__connect_ruffle__none",
|
||||
"left__sleeve__connect_ruffle__some",
|
||||
"left__sleeve__connect_ruffle__obvious"
|
||||
],
|
||||
"left__sleeve__smoothing_coeff": [
|
||||
"left__sleeve__smoothing_coeff__very-smooth",
|
||||
"left__sleeve__smoothing_coeff__moderate",
|
||||
"left__sleeve__smoothing_coeff__less-smooth"
|
||||
],
|
||||
"left__sleeve__cuff__type": [
|
||||
"left__sleeve__cuff__type__CuffBand",
|
||||
"left__sleeve__cuff__type__CuffSkirt",
|
||||
"left__sleeve__cuff__type__CuffBandSkirt",
|
||||
"left__sleeve__cuff__type__None"
|
||||
],
|
||||
"left__sleeve__cuff__top_ruffle": [
|
||||
"left__sleeve__cuff__top_ruffle__none",
|
||||
"left__sleeve__cuff__top_ruffle__moderate",
|
||||
"left__sleeve__cuff__top_ruffle__obvious"
|
||||
],
|
||||
"left__sleeve__cuff__cuff_len": [
|
||||
"left__sleeve__cuff__cuff_len__short",
|
||||
"left__sleeve__cuff__cuff_len__medium",
|
||||
"left__sleeve__cuff__cuff_len__long"
|
||||
],
|
||||
"left__sleeve__cuff__skirt_fraction": [
|
||||
"left__sleeve__cuff__skirt_fraction__small",
|
||||
"left__sleeve__cuff__skirt_fraction__medium",
|
||||
"left__sleeve__cuff__skirt_fraction__large"
|
||||
],
|
||||
"left__sleeve__cuff__skirt_flare": [
|
||||
"left__sleeve__cuff__skirt_flare__slight",
|
||||
"left__sleeve__cuff__skirt_flare__moderate",
|
||||
"left__sleeve__cuff__skirt_flare__significant"
|
||||
],
|
||||
"left__sleeve__cuff__skirt_ruffle": [
|
||||
"left__sleeve__cuff__skirt_ruffle__none",
|
||||
"left__sleeve__cuff__skirt_ruffle__some"
|
||||
],
|
||||
"skirt__length": [
|
||||
"skirt__length__micro",
|
||||
"skirt__length__mini",
|
||||
"skirt__length__above-knee",
|
||||
"skirt__length__knee-length",
|
||||
"skirt__length__midi",
|
||||
"skirt__length__floor-length"
|
||||
],
|
||||
"skirt__rise": [
|
||||
"skirt__rise__low",
|
||||
"skirt__rise__mid",
|
||||
"skirt__rise__high"
|
||||
],
|
||||
"skirt__ruffle": [
|
||||
"skirt__ruffle__none",
|
||||
"skirt__ruffle__moderate",
|
||||
"skirt__ruffle__rich"
|
||||
],
|
||||
"skirt__bottom_cut": [
|
||||
"skirt__bottom_cut__none",
|
||||
"skirt__bottom_cut__shallow",
|
||||
"skirt__bottom_cut__deep"
|
||||
],
|
||||
"skirt__flare": [
|
||||
"skirt__flare__small",
|
||||
"skirt__flare__medium",
|
||||
"skirt__flare__large"
|
||||
],
|
||||
"flare-skirt__length": [
|
||||
"flare-skirt__length__micro",
|
||||
"flare-skirt__length__mini",
|
||||
"flare-skirt__length__above-knee",
|
||||
"flare-skirt__length__knee-length",
|
||||
"flare-skirt__length__midi",
|
||||
"flare-skirt__length__floor-length"
|
||||
],
|
||||
"flare-skirt__rise": [
|
||||
"flare-skirt__rise__low",
|
||||
"flare-skirt__rise__mid",
|
||||
"flare-skirt__rise__high"
|
||||
],
|
||||
"flare-skirt__suns": [
|
||||
"flare-skirt__suns__slight",
|
||||
"flare-skirt__suns__moderate",
|
||||
"flare-skirt__suns__significant"
|
||||
],
|
||||
"flare-skirt__skirt-many-panels__n_panels": [
|
||||
"flare-skirt__skirt-many-panels__n_panels__few",
|
||||
"flare-skirt__skirt-many-panels__n_panels__medium",
|
||||
"flare-skirt__skirt-many-panels__n_panels__many"
|
||||
],
|
||||
"flare-skirt__skirt-many-panels__panel_curve": [
|
||||
"flare-skirt__skirt-many-panels__panel_curve__inward",
|
||||
"flare-skirt__skirt-many-panels__panel_curve__straight",
|
||||
"flare-skirt__skirt-many-panels__panel_curve__outward"
|
||||
],
|
||||
"flare-skirt__asymm__front_length": [
|
||||
"flare-skirt__asymm__front_length__highly-asymmetric",
|
||||
"flare-skirt__asymm__front_length__strongly-asymmetric",
|
||||
"flare-skirt__asymm__front_length__moderately-asymmetric",
|
||||
"flare-skirt__asymm__front_length__slightly-asymmetric",
|
||||
"flare-skirt__asymm__front_length__symmetric"
|
||||
],
|
||||
"flare-skirt__cut__add": [
|
||||
"flare-skirt__cut__add__True",
|
||||
"flare-skirt__cut__add__False"
|
||||
],
|
||||
"flare-skirt__cut__depth": [
|
||||
"flare-skirt__cut__depth__shallow",
|
||||
"flare-skirt__cut__depth__medium",
|
||||
"flare-skirt__cut__depth__deep"
|
||||
],
|
||||
"flare-skirt__cut__width": [
|
||||
"flare-skirt__cut__width__narrow",
|
||||
"flare-skirt__cut__width__medium",
|
||||
"flare-skirt__cut__width__wide"
|
||||
],
|
||||
"flare-skirt__cut__place": [
|
||||
"flare-skirt__cut__place__back_left",
|
||||
"flare-skirt__cut__place__back_center",
|
||||
"flare-skirt__cut__place__back_right",
|
||||
"flare-skirt__cut__place__front_left",
|
||||
"flare-skirt__cut__place__front_center",
|
||||
"flare-skirt__cut__place__front_right",
|
||||
],
|
||||
"godet-skirt__base": [
|
||||
"godet-skirt__base__Skirt2",
|
||||
"godet-skirt__base__PencilSkirt"
|
||||
],
|
||||
"godet-skirt__insert_w": [
|
||||
"godet-skirt__insert_w__narrow",
|
||||
"godet-skirt__insert_w__medium",
|
||||
"godet-skirt__insert_w__wide"
|
||||
],
|
||||
"godet-skirt__insert_depth": [
|
||||
"godet-skirt__insert_depth__shallow",
|
||||
"godet-skirt__insert_depth__medium",
|
||||
"godet-skirt__insert_depth__deep"
|
||||
],
|
||||
"godet-skirt__num_inserts": [
|
||||
"godet-skirt__num_inserts__4",
|
||||
"godet-skirt__num_inserts__6",
|
||||
"godet-skirt__num_inserts__8",
|
||||
"godet-skirt__num_inserts__10",
|
||||
"godet-skirt__num_inserts__12"
|
||||
],
|
||||
"godet-skirt__cuts_distance": [
|
||||
"godet-skirt__cuts_distance__close",
|
||||
"godet-skirt__cuts_distance__medium",
|
||||
"godet-skirt__cuts_distance__far"
|
||||
],
|
||||
"pencil-skirt__length": [
|
||||
"pencil-skirt__length__micro",
|
||||
"pencil-skirt__length__mini",
|
||||
"pencil-skirt__length__above-knee",
|
||||
"pencil-skirt__length__knee-length",
|
||||
"pencil-skirt__length__midi",
|
||||
"pencil-skirt__length__floor-length"
|
||||
],
|
||||
"pencil-skirt__rise": [
|
||||
"pencil-skirt__rise__low",
|
||||
"pencil-skirt__rise__mid",
|
||||
"pencil-skirt__rise__high"
|
||||
],
|
||||
"pencil-skirt__flare": [
|
||||
"pencil-skirt__flare__tight",
|
||||
"pencil-skirt__flare__straight",
|
||||
"pencil-skirt__flare__slight-flare"
|
||||
],
|
||||
"pencil-skirt__low_angle": [
|
||||
"pencil-skirt__low_angle__inward",
|
||||
"pencil-skirt__low_angle__straight",
|
||||
"pencil-skirt__low_angle__outward"
|
||||
],
|
||||
"pencil-skirt__front_slit": [
|
||||
"pencil-skirt__front_slit__none",
|
||||
"pencil-skirt__front_slit__shallow",
|
||||
"pencil-skirt__front_slit__deep"
|
||||
],
|
||||
"pencil-skirt__back_slit": [
|
||||
"pencil-skirt__back_slit__none",
|
||||
"pencil-skirt__back_slit__shallow",
|
||||
"pencil-skirt__back_slit__deep"
|
||||
],
|
||||
"pencil-skirt__left_slit": [
|
||||
"pencil-skirt__left_slit__none",
|
||||
"pencil-skirt__left_slit__shallow",
|
||||
"pencil-skirt__left_slit__deep"
|
||||
],
|
||||
"pencil-skirt__right_slit": [
|
||||
"pencil-skirt__right_slit__none",
|
||||
"pencil-skirt__right_slit__shallow",
|
||||
"pencil-skirt__right_slit__deep"
|
||||
],
|
||||
"pencil-skirt__style_side_cut": [
|
||||
"pencil-skirt__style_side_cut__Sun",
|
||||
"pencil-skirt__style_side_cut__SIGGRAPH_logo",
|
||||
"pencil-skirt__style_side_cut__None"
|
||||
],
|
||||
"levels-skirt__base": [
|
||||
"levels-skirt__base__Skirt2",
|
||||
"levels-skirt__base__PencilSkirt",
|
||||
"levels-skirt__base__SkirtCircle",
|
||||
"levels-skirt__base__AsymmSkirtCircle"
|
||||
],
|
||||
"levels-skirt__level": [
|
||||
"levels-skirt__level__Skirt2",
|
||||
"levels-skirt__level__SkirtCircle",
|
||||
"levels-skirt__level__AsymmSkirtCircle"
|
||||
],
|
||||
"levels-skirt__num_levels": [
|
||||
"levels-skirt__num_levels__1",
|
||||
"levels-skirt__num_levels__2",
|
||||
"levels-skirt__num_levels__3",
|
||||
"levels-skirt__num_levels__4",
|
||||
"levels-skirt__num_levels__5"
|
||||
],
|
||||
"levels-skirt__level_ruffle": [
|
||||
"levels-skirt__level_ruffle__none",
|
||||
"levels-skirt__level_ruffle__moderate",
|
||||
"levels-skirt__level_ruffle__rich"
|
||||
],
|
||||
"levels-skirt__length": [
|
||||
"levels-skirt__length__micro",
|
||||
"levels-skirt__length__mini",
|
||||
"levels-skirt__length__above-knee",
|
||||
"levels-skirt__length__knee-length",
|
||||
"levels-skirt__length__midi",
|
||||
"levels-skirt__length__floor-length"
|
||||
],
|
||||
"levels-skirt__rise": [
|
||||
"levels-skirt__rise__low",
|
||||
"levels-skirt__rise__mid",
|
||||
"levels-skirt__rise__high"
|
||||
],
|
||||
"levels-skirt__base_length_frac": [
|
||||
"levels-skirt__base_length_frac__short",
|
||||
"levels-skirt__base_length_frac__medium",
|
||||
"levels-skirt__base_length_frac__long"
|
||||
],
|
||||
"pants__length": [
|
||||
"pants__length__micro",
|
||||
"pants__length__short",
|
||||
"pants__length__knee-length",
|
||||
"pants__length__capri",
|
||||
"pants__length__ankle-length",
|
||||
"pants__length__full-length"
|
||||
],
|
||||
"pants__width": [
|
||||
"pants__width__fitted",
|
||||
"pants__width__normal",
|
||||
"pants__width__loose"
|
||||
],
|
||||
"pants__flare": [
|
||||
"pants__flare__tapering",
|
||||
"pants__flare__straight",
|
||||
"pants__flare__slight-flare"
|
||||
],
|
||||
"pants__rise": [
|
||||
"pants__rise__low",
|
||||
"pants__rise__mid",
|
||||
"pants__rise__high"
|
||||
],
|
||||
"pants__cuff__type": [
|
||||
"pants__cuff__type__CuffBand",
|
||||
"pants__cuff__type__CuffSkirt",
|
||||
"pants__cuff__type__CuffBandSkirt",
|
||||
"pants__cuff__type__None"
|
||||
],
|
||||
"pants__cuff__top_ruffle": [
|
||||
"pants__cuff__top_ruffle__none",
|
||||
"pants__cuff__top_ruffle__moderate",
|
||||
"pants__cuff__top_ruffle__rich"
|
||||
],
|
||||
"pants__cuff__cuff_len": [
|
||||
"pants__cuff__cuff_len__short",
|
||||
"pants__cuff__cuff_len__medium",
|
||||
"pants__cuff__cuff_len__long"
|
||||
],
|
||||
"pants__cuff__skirt_fraction": [
|
||||
"pants__cuff__skirt_fraction__small",
|
||||
"pants__cuff__skirt_fraction__medium",
|
||||
"pants__cuff__skirt_fraction__large"
|
||||
],
|
||||
"pants__cuff__skirt_flare": [
|
||||
"pants__cuff__skirt_flare__slight",
|
||||
"pants__cuff__skirt_flare__moderate",
|
||||
"pants__cuff__skirt_flare__significant"
|
||||
],
|
||||
"pants__cuff__skirt_ruffle": [
|
||||
"pants__cuff__skirt_ruffle__none",
|
||||
"pants__cuff__skirt_ruffle__some"
|
||||
]
|
||||
}
|
||||
|
||||
def list_to_prefix_dict(strings):
|
||||
"""
|
||||
Converts the list of strings to a dictionary of Prefix -> Original String List (in the order in which it appears).
|
||||
A prefix refers to the part after the last '__' segment is removed.
|
||||
"""
|
||||
result = OrderedDict()
|
||||
for s in strings:
|
||||
parts = s.split(CONNECT_TAG)
|
||||
prefix = CONNECT_TAG.join(parts[:-1]) # Remove the last fragment
|
||||
if prefix not in result:
|
||||
result[prefix] = []
|
||||
result[prefix].append(s) # Put the full string in
|
||||
return result
|
||||
def input_caption2random_default_cption(test_gpt_caption=None):
|
||||
'''all_text_dict: dict is the dict of the text space, and it is ordered,
|
||||
which can guarantee the order (because the network is trained to ensure the order)
|
||||
test_gpt_caption:list is the list of gpt_caption, here there is no specific order,
|
||||
Below, the text in the test_gpt_caption will be retained, and the others will be randomly selected,
|
||||
and the order will be guaranteed, and a list will be returned'''
|
||||
test_gpt_caption_list=list_to_prefix_dict(test_gpt_caption)
|
||||
random_list = []
|
||||
for key ,value_list in all_text_dict.items():
|
||||
# if key == 'levels-skirt__base':
|
||||
# print(value_list)
|
||||
if key not in test_gpt_caption_list:
|
||||
if key == 'shirt__length':
|
||||
random_list.append("shirt__length__super-cropped")
|
||||
elif key == 'pants__cuff__type':
|
||||
random_list.append("pants__cuff__type__None")
|
||||
elif key == "sleeve__cuff__type":
|
||||
random_list.append("sleeve__cuff__type__None")
|
||||
elif key == "left__sleeve__cuff__type":
|
||||
random_list.append("left__sleeve__cuff__type__None")
|
||||
elif key == 'collar__component__style':
|
||||
random_list.append("collar__component__style__None")
|
||||
elif key == "pencil-skirt__low_angle":
|
||||
random_list.append("pencil-skirt__low_angle__straight")
|
||||
elif key == "sleeve__connecting_width":
|
||||
random_list.append("sleeve__connecting_width__medium")
|
||||
elif key == "sleeve__end_width":
|
||||
random_list.append("sleeve__end_width__straight")
|
||||
elif key == "left__sleeve__connecting_width":
|
||||
random_list.append("left__sleeve__connecting_width__medium")
|
||||
elif key == "left__sleeve__end_width":
|
||||
random_list.append("left__sleeve__end_width__straight")
|
||||
elif 'False' in value_list[0] or 'True' in value_list[0]:
|
||||
random_list.append(value_list[1])
|
||||
else:
|
||||
chice_num=random.randint(0,len(value_list)-1)
|
||||
random_list.append(value_list[chice_num])
|
||||
else:
|
||||
if test_gpt_caption_list[key][0] in value_list:
|
||||
res= test_gpt_caption_list[key][0]
|
||||
random_list.append(res)
|
||||
else:
|
||||
chice_num = random.randint(0, len(value_list) - 1)
|
||||
random_list.append(value_list[chice_num])
|
||||
|
||||
|
||||
return random_list
|
||||
def vec_2_pattern_yaml(yaml_data,param_vec,mask_list):
|
||||
yaml_file_name = None
|
||||
pattern_data_dict = {}
|
||||
pattern_vec = {}
|
||||
cont = 0
|
||||
param_vec = np.clip(param_vec, 0, 1)
|
||||
|
||||
|
||||
def extract_new_vec_v2v(data, path=''):
|
||||
"""
|
||||
Recursively traverses the data structure and extracts all the 'v' values.
|
||||
:p aram data: YAML loaded data
|
||||
:p aram path: The path of the current field, which is used to display the hierarchy
|
||||
:return: No return value, print directly
|
||||
"""
|
||||
nonlocal cont
|
||||
nonlocal param_vec
|
||||
nonlocal mask_list
|
||||
if isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
# If the key is 'v', the path and the corresponding value are printed
|
||||
if key == 'v':
|
||||
range = data['range']
|
||||
|
||||
if mask_list[cont] == 0 or data['v'] == 0:
|
||||
cont = cont + 1
|
||||
continue
|
||||
elif data['type'] == 'select_null' or data['type'] == 'select':
|
||||
pass
|
||||
elif data['type'] == 'bool':
|
||||
pass
|
||||
elif data['type'] == 'int':
|
||||
re_normal_value = param_vec[cont] * (range[1] - range[0]) + range[0]
|
||||
data['v'] = int(re_normal_value)
|
||||
elif data['type'] == 'float':
|
||||
re_normal_value = param_vec[cont] * (range[1] - range[0]) + range[0]
|
||||
data['v'] = float(re_normal_value*mask_list[cont])
|
||||
|
||||
cont = cont + 1
|
||||
else:
|
||||
# Recursively moves on to the next layer
|
||||
extract_new_vec_v2v(value, path + key + '.')
|
||||
extract_new_vec_v2v(yaml_data, path='')
|
||||
return yaml_data
|
||||
|
||||
class NoAliasDumper(yaml.Dumper):
|
||||
def ignore_aliases(self, data):
|
||||
return True
|
||||
|
||||
def save_design2yaml(design, new_yaml_path):
|
||||
garment_param = {'design': design}
|
||||
new_yaml_path_dir = os.path.dirname(new_yaml_path)
|
||||
os.makedirs(new_yaml_path_dir, exist_ok=True)
|
||||
|
||||
with open(new_yaml_path, 'w') as yaml_file:
|
||||
yaml.dump(garment_param, yaml_file, default_flow_style=False, allow_unicode=True, sort_keys=False,
|
||||
Dumper=NoAliasDumper)
|
||||
160
lmm_utils/sim_utils.py
Normal file
@@ -0,0 +1,160 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
import pygarment.data_config as data_config
|
||||
from assets.bodies.body_params import BodyParameters
|
||||
from assets.garment_programs.meta_garment import MetaGarment
|
||||
from pygarment.data_config import Properties
|
||||
from pygarment.meshgen.boxmeshgen import BoxMesh
|
||||
from pygarment.meshgen.sim_config import PathCofig
|
||||
# from pygarment.meshgen.simulation import run_sim
|
||||
|
||||
|
||||
#Convert the yaml file directly into an output plate
|
||||
def garmentyaml_folder2json_folder(input_folder='', output_folder='',
|
||||
body_to_use='neutral'):
|
||||
''' Convert the yaml files of the input folder to json files and save them in the output folder There is a folder with a yaml file name under the output folder, which contains JSON files and boilerplate images
|
||||
Args:
|
||||
input_folder(string): Enter a folder
|
||||
output_folder (string): The output folder
|
||||
body_to_use (string): Select a human body parameter
|
||||
'''
|
||||
|
||||
bodies_measurements = {
|
||||
# Our model
|
||||
'neutral': './assets/bodies/mean_all.yaml',
|
||||
'mean_female': './assets/bodies/mean_female.yaml',
|
||||
'mean_male': './assets/bodies/mean_male.yaml',
|
||||
|
||||
# SMPL
|
||||
'f_smpl': './assets/bodies/f_smpl_average_A40.yaml',
|
||||
'm_smpl': './assets/bodies/m_smpl_average_A40.yaml',
|
||||
#t pose
|
||||
'mean_all_tpose': './assets/bodies/mean_all_tpose.yaml'
|
||||
}
|
||||
body_to_use = body_to_use # CHANGE HERE to use different set of body measurements
|
||||
# body_to_use = 'mean_all_tpose'
|
||||
body = BodyParameters(bodies_measurements[body_to_use])
|
||||
|
||||
design_files = {
|
||||
't-shirt': './assets/design_params/t-shirt.yaml',
|
||||
# Add paths HERE to load other parameters
|
||||
}
|
||||
|
||||
pattern_dir = input_folder # This is the input file, write the yaml folder here
|
||||
design_files_list = [file for file in Path(pattern_dir).iterdir() if file.suffix == '.yaml']
|
||||
|
||||
design_files_stem = [item.stem for item in design_files_list]
|
||||
design_files_list = [str(item) for item in design_files_list]
|
||||
print(design_files_list)
|
||||
design_files = {
|
||||
k: v for k, v in zip(design_files_stem, design_files_list)
|
||||
|
||||
}
|
||||
|
||||
designs = {}
|
||||
for df in design_files:
|
||||
with open(design_files[df], 'r') as f:
|
||||
designs[df] = yaml.safe_load(f)['design']
|
||||
|
||||
test_garments = []
|
||||
for df in designs:
|
||||
try:
|
||||
garment = MetaGarment(df, body, designs[df])
|
||||
test_garments.append(garment)
|
||||
except Exception as e:
|
||||
print(f"An error occurred with {df}: {e}")
|
||||
continue
|
||||
outpath = Path(output_folder) # This is the folder for the output
|
||||
outpath.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for piece in test_garments:
|
||||
pattern = piece.assembly()
|
||||
|
||||
if piece.is_self_intersecting():
|
||||
print(f'{piece.name} is Self-intersecting')
|
||||
|
||||
folder = pattern.serialize(
|
||||
outpath,
|
||||
tag='',
|
||||
to_subfolder=True,
|
||||
with_3d=False, with_text=False, view_ids=False,
|
||||
with_printable=True
|
||||
)
|
||||
|
||||
body.save(folder)
|
||||
if piece.name in design_files:
|
||||
shutil.copy(design_files[piece.name], folder)
|
||||
else:
|
||||
shutil.copy(design_files['base'], folder)
|
||||
|
||||
print(f'Success! {piece.name} saved to {folder}')
|
||||
|
||||
|
||||
def json2modelfolder(input_json):
|
||||
''' If you simulate a json file, you will save a folder in the directory where the json file is located, removing the specifiction ending as the folder name, and containing all the simulated information
|
||||
Args:
|
||||
input_json (string): A json file that needs to be mocked
|
||||
|
||||
'''
|
||||
props = data_config.Properties('./assets/Sim_props/default_sim_props.yaml')
|
||||
props.set_section_stats('sim', fails={}, sim_time={}, spf={}, fin_frame={}, body_collisions={}, self_collisions={})
|
||||
props.set_section_stats('render', render_time={})
|
||||
|
||||
input_path = Path(input_json)
|
||||
garment_name, _, _ = input_path.stem.rpartition('_') # assuming ending in '_specification'
|
||||
# garment_name = os.path.splitext(os.path.basename(input_path))[0]
|
||||
sys_props = data_config.Properties('./system.json')
|
||||
paths = PathCofig(
|
||||
in_element_path=input_path.parent,
|
||||
out_path=input_path.parent,
|
||||
in_name=garment_name,
|
||||
body_name='mean_all', # 'f_smpl_average_A40'
|
||||
smpl_body=False, # NOTE: depends on chosen body model
|
||||
add_timestamp=False
|
||||
)
|
||||
|
||||
# Generate and save garment box mesh (if not existent)
|
||||
print(f"Generate box mesh of {garment_name} with resolution {props['sim']['config']['resolution_scale']}...")
|
||||
print('\nGarment load: ', paths.in_g_spec)
|
||||
|
||||
garment_box_mesh = BoxMesh(paths.in_g_spec, props['sim']['config']['resolution_scale'])
|
||||
garment_box_mesh.load()
|
||||
garment_box_mesh.serialize(
|
||||
paths, store_panels=False, uv_config=props['render']['config']['uv_texture'])
|
||||
|
||||
props.serialize(paths.element_sim_props)
|
||||
|
||||
run_sim(
|
||||
garment_box_mesh.name,
|
||||
props,
|
||||
paths,
|
||||
save_v_norms=False,
|
||||
store_usd=False, # NOTE: False for fast simulation!
|
||||
optimize_storage=False, # props['sim']['config']['optimize_storage'],
|
||||
verbose=False
|
||||
)
|
||||
|
||||
props.serialize(paths.element_sim_props)
|
||||
|
||||
|
||||
def modelandreturn_picture_path(input_json):
|
||||
|
||||
''' If you simulate a json file, you will save a folder in the directory where the json file is located, removing the specifiction ending as the folder name.
|
||||
Contains all the information for the simulation and returns the path to the simulation from the front perspective
|
||||
Args:
|
||||
input_json (string): A json file that needs to be mocked
|
||||
Responds:
|
||||
image_path2 (string): the path of the simulation map in the front view.
|
||||
|
||||
'''
|
||||
|
||||
json2modelfolder(input_json)
|
||||
input_json=Path(input_json)
|
||||
input_folder=input_json.parent
|
||||
garment_name, _, _ = input_json.stem.rpartition('_')
|
||||
image_path2=input_folder / garment_name / f"{str(garment_name)}_render_front.png"
|
||||
|
||||
return image_path2
|
||||
89
lmm_utils/test_picture_batch.py
Executable file
@@ -0,0 +1,89 @@
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
import shutil
|
||||
import argparse
|
||||
import tqdm
|
||||
from functools import partial
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from helper import category2yaml2json
|
||||
from lmm_utils.predict_garmentcode_picture import Predictor
|
||||
|
||||
|
||||
def search_picture_files(directory):
|
||||
"""Search for all image files in the directory"""
|
||||
picture_files = []
|
||||
for root, _, files in os.walk(directory):
|
||||
for file in files:
|
||||
if file.lower().endswith(('.jpg', '.png', '.jpeg', '.gif')):
|
||||
picture_files.append(os.path.join(root, file))
|
||||
return picture_files
|
||||
|
||||
|
||||
def main(input_folder_path, output_folder_path,sim_bool=False):
|
||||
dsl_ga = Predictor()
|
||||
all_picture_files = search_picture_files(input_folder_path)
|
||||
input_output_list = []
|
||||
uuid_list = []
|
||||
for input_picture_path in all_picture_files:
|
||||
output_json_path = input_picture_path.replace(input_folder_path, output_folder_path)
|
||||
output_json_dir = os.path.dirname(output_json_path)
|
||||
json_file_name = os.path.splitext(os.path.basename(input_picture_path))[0]
|
||||
output_json_path = os.path.join(output_json_dir, json_file_name, json_file_name + '.json')
|
||||
input_output_list.append((input_picture_path, output_json_path))
|
||||
threadPool = ThreadPoolExecutor(max_workers=5, thread_name_prefix="img_thread")
|
||||
futures = []
|
||||
for i, (input_picture_path, output_json_path) in tqdm.tqdm(enumerate(input_output_list)):
|
||||
if os.path.exists(output_json_path):
|
||||
continue
|
||||
item_id = str(uuid.uuid4())
|
||||
uuid_list.append(item_id)
|
||||
task = partial(
|
||||
category2yaml2json,
|
||||
category='picture',
|
||||
category_data=input_picture_path,
|
||||
final_json_path=output_json_path,
|
||||
id=item_id,
|
||||
model='Qwen/Qwen2.5-VL-72B-Instruct',
|
||||
base_url='https://api-inference.modelscope.cn/v1/',
|
||||
api_key='108a28f0-de01-4c43-b189-6cad25d32990',
|
||||
dsl_ga=dsl_ga,
|
||||
sim_bool=sim_bool
|
||||
)
|
||||
future = threadPool.submit(task)
|
||||
futures.append((future, input_picture_path))
|
||||
print(i, input_picture_path, output_json_path)
|
||||
fail_picture_list = []
|
||||
for future, input_picture_path in futures:
|
||||
try:
|
||||
result = future.result()
|
||||
print(f"Task result: {result}")
|
||||
except Exception as e:
|
||||
fail_picture_list.append(input_picture_path)
|
||||
print(f"Task failed: {e}")
|
||||
|
||||
# Clean up the temporary folder
|
||||
for _uuid in uuid_list:
|
||||
try:
|
||||
shutil.rmtree(f"user_data/temp_user_folder_for{_uuid}gpt")
|
||||
except Exception as e:
|
||||
print(f"Error when deleting temp folder: {e}")
|
||||
pass
|
||||
|
||||
threadPool.shutdown(wait=True)
|
||||
|
||||
# Save the list of failures
|
||||
fail_dir = f"user_data/fail_{os.path.basename(input_folder_path)}"
|
||||
os.makedirs(fail_dir, exist_ok=True)
|
||||
with open(os.path.join(fail_dir, "fail_picture_list.json"), "w") as file:
|
||||
json.dump(fail_picture_list, file, indent=4)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Image-to-GarmentCode generation script")
|
||||
parser.add_argument('--input', type=str, required=True, help='Input image folder path')
|
||||
parser.add_argument('--output', type=str, required=True, help='Output folder path')
|
||||
parser.add_argument('--sim', type=bool, default=False, help='Enable simulation mode (default: False)')
|
||||
args = parser.parse_args()
|
||||
main(args.input, args.output,args.sim)
|
||||
89
lmm_utils/test_text_batch.py
Executable file
@@ -0,0 +1,89 @@
|
||||
import os
|
||||
import shutil
|
||||
import traceback
|
||||
import json
|
||||
import uuid
|
||||
import argparse
|
||||
from functools import partial
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from helper import category2yaml2json
|
||||
from lmm_utils.predict_garmentcode_picture import Predictor
|
||||
import tqdm
|
||||
|
||||
|
||||
def main(input_text_json_path, output_folder_path,sim_bool=False):
|
||||
# Load the entered JSON text list
|
||||
with open(input_text_json_path, 'r') as f:
|
||||
all_text = json.load(f)
|
||||
|
||||
input_output_list = []
|
||||
json_file_name = os.path.splitext(os.path.basename(input_text_json_path))[0]
|
||||
|
||||
for index, input_text in enumerate(all_text):
|
||||
output_json_path = os.path.join(output_folder_path, json_file_name, str(index), f"{index}.json")
|
||||
input_output_list.append((input_text, output_json_path))
|
||||
|
||||
# Initialize the predictor and thread pool
|
||||
dsl_ga = Predictor()
|
||||
threadPool = ThreadPoolExecutor(max_workers=10, thread_name_prefix="test_")
|
||||
futures = []
|
||||
uuid_list = []
|
||||
|
||||
for i, (input_text, output_json_path) in tqdm.tqdm(enumerate(input_output_list)):
|
||||
if os.path.exists(output_json_path):
|
||||
continue
|
||||
item_id = str(uuid.uuid4())
|
||||
uuid_list.append(item_id)
|
||||
|
||||
task = partial(
|
||||
category2yaml2json,
|
||||
category='text',
|
||||
category_data=input_text,
|
||||
final_json_path=output_json_path,
|
||||
id=item_id,
|
||||
model='Qwen/Qwen2.5-72B-Instruct',
|
||||
base_url='https://api-inference.modelscope.cn/v1/',
|
||||
api_key='108a28f0-de01-4c43-b189-6cad25d32990',
|
||||
dsl_ga=dsl_ga,
|
||||
sim_bool=sim_bool
|
||||
)
|
||||
future = threadPool.submit(task)
|
||||
futures.append((future, input_text))
|
||||
print(i, input_text, output_json_path)
|
||||
|
||||
# Collect failure information
|
||||
fail_picture_list = []
|
||||
for future, input_text in futures:
|
||||
try:
|
||||
result = future.result()
|
||||
print(f"Task result: {result}")
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
fail_picture_list.append(input_text)
|
||||
print(f"Task failed: {e}")
|
||||
|
||||
# Clean up the temporary folder
|
||||
for _uuid in uuid_list:
|
||||
try:
|
||||
shutil.rmtree(f"user_data/temp_user_folder_for{_uuid}gpt")
|
||||
except Exception as e:
|
||||
print(f"Error when deleting temp folder: {e}")
|
||||
pass
|
||||
|
||||
threadPool.shutdown(wait=True)
|
||||
|
||||
# Save a record of failures
|
||||
fail_dir = f"user_data/fail_{os.path.basename(input_text_json_path).split('.')[0]}"
|
||||
os.makedirs(fail_dir, exist_ok=True)
|
||||
with open(os.path.join(fail_dir, "fail_picture_list.json"), "w") as file:
|
||||
json.dump(fail_picture_list, file, indent=4)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Text-to-GarmentCode generation script")
|
||||
parser.add_argument('--input', type=str, required=True, help='Path to input JSON file with text list')
|
||||
parser.add_argument('--output', type=str, required=True, help='Path to output folder for results')
|
||||
parser.add_argument('--sim', type=bool, default=False, help='Enable simulation mode (default: False)')
|
||||
args = parser.parse_args()
|
||||
main(args.input, args.output,args.sim)
|
||||
635
lmm_utils/validation.py
Normal file
@@ -0,0 +1,635 @@
|
||||
_ALL_TEXT =[
|
||||
"meta__upper__FittedShirt",
|
||||
"meta__upper__Shirt",
|
||||
"meta__upper__None",
|
||||
"meta__wb__StraightWB",
|
||||
"meta__wb__FittedWB",
|
||||
"meta__wb__None",
|
||||
"meta__bottom__SkirtCircle",
|
||||
"meta__bottom__AsymmSkirtCircle",
|
||||
"meta__bottom__GodetSkirt",
|
||||
"meta__bottom__Pants",
|
||||
"meta__bottom__Skirt2",
|
||||
"meta__bottom__SkirtManyPanels",
|
||||
"meta__bottom__PencilSkirt",
|
||||
"meta__bottom__SkirtLevels",
|
||||
"meta__bottom__None",
|
||||
"meta__connected__True",
|
||||
"meta__connected__False",
|
||||
"waistband__waist__fitted",
|
||||
"waistband__waist__slightly-loose",
|
||||
"waistband__waist__loose",
|
||||
"waistband__width__narrow",
|
||||
"waistband__width__medium",
|
||||
"waistband__width__wide",
|
||||
"fitted_shirt__strapless__True",
|
||||
"fitted_shirt__strapless__False",
|
||||
"shirt__length__super-cropped",
|
||||
"shirt__length__regular",
|
||||
"shirt__length__long",
|
||||
"shirt__width__normal",
|
||||
"shirt__width__relaxed",
|
||||
"shirt__flare__tight",
|
||||
"shirt__flare__straight",
|
||||
"shirt__flare__flared",
|
||||
"shirt__flare__very-flared",
|
||||
"collar__f_collar__CircleNeckHalf",
|
||||
"collar__f_collar__CurvyNeckHalf",
|
||||
"collar__f_collar__VNeckHalf",
|
||||
"collar__f_collar__SquareNeckHalf",
|
||||
"collar__f_collar__TrapezoidNeckHalf",
|
||||
"collar__f_collar__CircleArcNeckHalf",
|
||||
"collar__f_collar__Bezier2NeckHalf",
|
||||
"collar__b_collar__CircleNeckHalf",
|
||||
"collar__b_collar__CurvyNeckHalf",
|
||||
"collar__b_collar__VNeckHalf",
|
||||
"collar__b_collar__SquareNeckHalf",
|
||||
"collar__b_collar__TrapezoidNeckHalf",
|
||||
"collar__b_collar__CircleArcNeckHalf",
|
||||
"collar__b_collar__Bezier2NeckHalf",
|
||||
"collar__width__very-narrow",
|
||||
"collar__width__medium",
|
||||
"collar__width__wide",
|
||||
"collar__fc_depth__shallow",
|
||||
"collar__fc_depth__medium",
|
||||
"collar__fc_depth__deep",
|
||||
"collar__bc_depth__shallow",
|
||||
"collar__bc_depth__medium",
|
||||
"collar__bc_depth__deep",
|
||||
"collar__fc_angle__acute",
|
||||
"collar__fc_angle__standard",
|
||||
"collar__fc_angle__obtuse",
|
||||
"collar__bc_angle__acute",
|
||||
"collar__bc_angle__standard",
|
||||
"collar__bc_angle__obtuse",
|
||||
"collar__f_bezier_x__left",
|
||||
"collar__f_bezier_x__center",
|
||||
"collar__f_bezier_x__right",
|
||||
"collar__f_bezier_y__top",
|
||||
"collar__f_bezier_y__center",
|
||||
"collar__f_bezier_y__bottom",
|
||||
"collar__b_bezier_x__left",
|
||||
"collar__b_bezier_x__center",
|
||||
"collar__b_bezier_x__right",
|
||||
"collar__b_bezier_y__top",
|
||||
"collar__b_bezier_y__center",
|
||||
"collar__b_bezier_y__bottom",
|
||||
"collar__f_flip_curve__True",
|
||||
"collar__f_flip_curve__False",
|
||||
"collar__b_flip_curve__True",
|
||||
"collar__b_flip_curve__False",
|
||||
"collar__component__style__Turtle",
|
||||
"collar__component__style__SimpleLapel",
|
||||
"collar__component__style__Hood2Panels",
|
||||
"collar__component__style__None",
|
||||
"collar__component__depth__shallow",
|
||||
"collar__component__depth__medium",
|
||||
"collar__component__depth__deep",
|
||||
"collar__component__lapel_standing__True",
|
||||
"collar__component__lapel_standing__False",
|
||||
"collar__component__hood_depth__shallow",
|
||||
"collar__component__hood_depth__medium",
|
||||
"collar__component__hood_depth__deep",
|
||||
"collar__component__hood_length__short",
|
||||
"collar__component__hood_length__medium",
|
||||
"collar__component__hood_length__long",
|
||||
"sleeve__sleeveless__True",
|
||||
"sleeve__sleeveless__False",
|
||||
"sleeve__armhole_shape__ArmholeSquare",
|
||||
"sleeve__armhole_shape__ArmholeAngle",
|
||||
"sleeve__armhole_shape__ArmholeCurve",
|
||||
"sleeve__length__short",
|
||||
"sleeve__length__half",
|
||||
"sleeve__length__three-quarter",
|
||||
"sleeve__length__long",
|
||||
"sleeve__length__full",
|
||||
"sleeve__connecting_width__narrow",
|
||||
"sleeve__connecting_width__medium",
|
||||
"sleeve__connecting_width__loose",
|
||||
"sleeve__connecting_width__very-loose",
|
||||
"sleeve__end_width__closing",
|
||||
"sleeve__end_width__straight",
|
||||
"sleeve__end_width__opening",
|
||||
"sleeve__sleeve_angle__small",
|
||||
"sleeve__sleeve_angle__medium",
|
||||
"sleeve__sleeve_angle__large",
|
||||
"sleeve__opening_dir_mix__negative-twist",
|
||||
"sleeve__opening_dir_mix__standard",
|
||||
"sleeve__opening_dir_mix__positive-twist",
|
||||
"sleeve__standing_shoulder__True",
|
||||
"sleeve__standing_shoulder__False",
|
||||
"sleeve__standing_shoulder_len__short",
|
||||
"sleeve__standing_shoulder_len__medium",
|
||||
"sleeve__standing_shoulder_len__long",
|
||||
"sleeve__connect_ruffle__none",
|
||||
"sleeve__connect_ruffle__some",
|
||||
"sleeve__connect_ruffle__obvious",
|
||||
"sleeve__smoothing_coeff__very-smooth",
|
||||
"sleeve__smoothing_coeff__moderate",
|
||||
"sleeve__smoothing_coeff__less-smooth",
|
||||
"sleeve__cuff__type__CuffBand",
|
||||
"sleeve__cuff__type__CuffSkirt",
|
||||
"sleeve__cuff__type__CuffBandSkirt",
|
||||
"sleeve__cuff__type__None",
|
||||
"sleeve__cuff__top_ruffle__straight",
|
||||
"sleeve__cuff__top_ruffle__tapered",
|
||||
"sleeve__cuff__top_ruffle__very_tapered",
|
||||
"sleeve__cuff__cuff_len__short",
|
||||
"sleeve__cuff__cuff_len__medium",
|
||||
"sleeve__cuff__cuff_len__long",
|
||||
"sleeve__cuff__skirt_fraction__small",
|
||||
"sleeve__cuff__skirt_fraction__medium",
|
||||
"sleeve__cuff__skirt_fraction__large",
|
||||
"sleeve__cuff__skirt_flare__slight",
|
||||
"sleeve__cuff__skirt_flare__moderate",
|
||||
"sleeve__cuff__skirt_flare__significant",
|
||||
"sleeve__cuff__skirt_ruffle__none",
|
||||
"sleeve__cuff__skirt_ruffle__some",
|
||||
"left__enable_asym__True",
|
||||
"left__enable_asym__False",
|
||||
"left__fitted_shirt__strapless__True",
|
||||
"left__fitted_shirt__strapless__False",
|
||||
"left__shirt__width__normal",
|
||||
"left__shirt__width__relaxed",
|
||||
"left__shirt__flare__tight",
|
||||
"left__shirt__flare__straight",
|
||||
"left__shirt__flare__flared",
|
||||
"left__shirt__flare__very-flared",
|
||||
"left__collar__f_collar__CircleNeckHalf",
|
||||
"left__collar__f_collar__CurvyNeckHalf",
|
||||
"left__collar__f_collar__VNeckHalf",
|
||||
"left__collar__f_collar__SquareNeckHalf",
|
||||
"left__collar__f_collar__TrapezoidNeckHalf",
|
||||
"left__collar__f_collar__CircleArcNeckHalf",
|
||||
"left__collar__f_collar__Bezier2NeckHalf",
|
||||
"left__collar__b_collar__CircleNeckHalf",
|
||||
"left__collar__b_collar__CurvyNeckHalf",
|
||||
"left__collar__b_collar__VNeckHalf",
|
||||
"left__collar__b_collar__SquareNeckHalf",
|
||||
"left__collar__b_collar__TrapezoidNeckHalf",
|
||||
"left__collar__b_collar__CircleArcNeckHalf",
|
||||
"left__collar__b_collar__Bezier2NeckHalf",
|
||||
"left__collar__width__narrow",
|
||||
"left__collar__width__medium",
|
||||
"left__collar__width__wide",
|
||||
"left__collar__fc_angle__acute",
|
||||
"left__collar__fc_angle__standard",
|
||||
"left__collar__fc_angle__obtuse",
|
||||
"left__collar__bc_angle__acute",
|
||||
"left__collar__bc_angle__standard",
|
||||
"left__collar__bc_angle__obtuse",
|
||||
"left__collar__f_bezier_x__left",
|
||||
"left__collar__f_bezier_x__center",
|
||||
"left__collar__f_bezier_x__right",
|
||||
"left__collar__f_bezier_y__top",
|
||||
"left__collar__f_bezier_y__center",
|
||||
"left__collar__f_bezier_y__bottom",
|
||||
"left__collar__b_bezier_x__left",
|
||||
"left__collar__b_bezier_x__center",
|
||||
"left__collar__b_bezier_x__right",
|
||||
"left__collar__b_bezier_y__top",
|
||||
"left__collar__b_bezier_y__center",
|
||||
"left__collar__b_bezier_y__bottom",
|
||||
"left__collar__f_flip_curve__True",
|
||||
"left__collar__f_flip_curve__False",
|
||||
"left__collar__b_flip_curve__True",
|
||||
"left__collar__b_flip_curve__False",
|
||||
"left__sleeve__sleeveless__True",
|
||||
"left__sleeve__sleeveless__False",
|
||||
"left__sleeve__armhole_shape__ArmholeSquare",
|
||||
"left__sleeve__armhole_shape__ArmholeAngle",
|
||||
"left__sleeve__armhole_shape__ArmholeCurve",
|
||||
"left__sleeve__length__short",
|
||||
"left__sleeve__length__half",
|
||||
"left__sleeve__length__three-quarter",
|
||||
"left__sleeve__length__long",
|
||||
"left__sleeve__length__full",
|
||||
"left__sleeve__connecting_width__narrow",
|
||||
"left__sleeve__connecting_width__medium",
|
||||
"left__sleeve__connecting_width__loose",
|
||||
"left__sleeve__connecting_width__very-loose",
|
||||
"left__sleeve__end_width__closing",
|
||||
"left__sleeve__end_width__straight",
|
||||
"left__sleeve__end_width__opening",
|
||||
"left__sleeve__sleeve_angle__small",
|
||||
"left__sleeve__sleeve_angle__medium",
|
||||
"left__sleeve__sleeve_angle__large",
|
||||
"left__sleeve__opening_dir_mix__negative-twist",
|
||||
"left__sleeve__opening_dir_mix__standard",
|
||||
"left__sleeve__opening_dir_mix__positive-twist",
|
||||
"left__sleeve__standing_shoulder__True",
|
||||
"left__sleeve__standing_shoulder__False",
|
||||
"left__sleeve__standing_shoulder_len__short",
|
||||
"left__sleeve__standing_shoulder_len__medium",
|
||||
"left__sleeve__standing_shoulder_len__long",
|
||||
"left__sleeve__connect_ruffle__none",
|
||||
"left__sleeve__connect_ruffle__some",
|
||||
"left__sleeve__connect_ruffle__obvious",
|
||||
"left__sleeve__smoothing_coeff__very-smooth",
|
||||
"left__sleeve__smoothing_coeff__moderate",
|
||||
"left__sleeve__smoothing_coeff__less-smooth",
|
||||
"left__sleeve__cuff__type__CuffBand",
|
||||
"left__sleeve__cuff__type__CuffSkirt",
|
||||
"left__sleeve__cuff__type__CuffBandSkirt",
|
||||
"left__sleeve__cuff__type__None",
|
||||
"left__sleeve__cuff__top_ruffle__straight",
|
||||
"left__sleeve__cuff__top_ruffle__tapered",
|
||||
"left__sleeve__cuff__top_ruffle__very_tapered",
|
||||
"left__sleeve__cuff__cuff_len__short",
|
||||
"left__sleeve__cuff__cuff_len__medium",
|
||||
"left__sleeve__cuff__cuff_len__long",
|
||||
"left__sleeve__cuff__skirt_fraction__small",
|
||||
"left__sleeve__cuff__skirt_fraction__medium",
|
||||
"left__sleeve__cuff__skirt_fraction__large",
|
||||
"left__sleeve__cuff__skirt_flare__slight",
|
||||
"left__sleeve__cuff__skirt_flare__moderate",
|
||||
"left__sleeve__cuff__skirt_flare__significant",
|
||||
"left__sleeve__cuff__skirt_ruffle__none",
|
||||
"left__sleeve__cuff__skirt_ruffle__some",
|
||||
"skirt__length__micro",
|
||||
"skirt__length__mini",
|
||||
"skirt__length__above-knee",
|
||||
"skirt__length__knee-length",
|
||||
"skirt__length__midi",
|
||||
"skirt__length__floor-length",
|
||||
"skirt__rise__low",
|
||||
"skirt__rise__mid",
|
||||
"skirt__rise__high",
|
||||
"skirt__ruffle__none",
|
||||
"skirt__ruffle__moderate",
|
||||
"skirt__ruffle__rich",
|
||||
"skirt__bottom_cut__none",
|
||||
"skirt__bottom_cut__shallow",
|
||||
"skirt__bottom_cut__deep",
|
||||
"skirt__flare__none",
|
||||
"skirt__flare__small",
|
||||
"skirt__flare__medium",
|
||||
"skirt__flare__large",
|
||||
"flare-skirt__length__micro",
|
||||
"flare-skirt__length__mini",
|
||||
"flare-skirt__length__above-knee",
|
||||
"flare-skirt__length__knee-length",
|
||||
"flare-skirt__length__midi",
|
||||
"flare-skirt__length__floor-length",
|
||||
"flare-skirt__rise__low",
|
||||
"flare-skirt__rise__mid",
|
||||
"flare-skirt__rise__high",
|
||||
"flare-skirt__suns__slight",
|
||||
"flare-skirt__suns__moderate",
|
||||
"flare-skirt__suns__significant",
|
||||
"flare-skirt__skirt-many-panels__n_panels__few",
|
||||
"flare-skirt__skirt-many-panels__n_panels__medium",
|
||||
"flare-skirt__skirt-many-panels__n_panels__many",
|
||||
"flare-skirt__skirt-many-panels__panel_curve__inward",
|
||||
"flare-skirt__skirt-many-panels__panel_curve__straight",
|
||||
"flare-skirt__skirt-many-panels__panel_curve__outward",
|
||||
"flare-skirt__asymm__front_length__highly-asymmetric",
|
||||
"flare-skirt__asymm__front_length__strongly-asymmetric",
|
||||
"flare-skirt__asymm__front_length__moderately-asymmetric",
|
||||
"flare-skirt__asymm__front_length__slightly-asymmetric",
|
||||
"flare-skirt__asymm__front_length__symmetric",
|
||||
"flare-skirt__cut__add__True",
|
||||
"flare-skirt__cut__add__False",
|
||||
"flare-skirt__cut__depth__shallow",
|
||||
"flare-skirt__cut__depth__medium",
|
||||
"flare-skirt__cut__depth__deep",
|
||||
"flare-skirt__cut__width__narrow",
|
||||
"flare-skirt__cut__width__medium",
|
||||
"flare-skirt__cut__width__wide",
|
||||
"flare-skirt__cut__place__back_left",
|
||||
"flare-skirt__cut__place__back_center",
|
||||
"flare-skirt__cut__place__back_right",
|
||||
"flare-skirt__cut__place__front_left",
|
||||
"flare-skirt__cut__place__front_center",
|
||||
"flare-skirt__cut__place__front_right",
|
||||
"godet-skirt__base__Skirt2",
|
||||
"godet-skirt__base__PencilSkirt",
|
||||
"godet-skirt__insert_w__narrow",
|
||||
"godet-skirt__insert_w__medium",
|
||||
"godet-skirt__insert_w__wide",
|
||||
"godet-skirt__insert_depth__shallow",
|
||||
"godet-skirt__insert_depth__medium",
|
||||
"godet-skirt__insert_depth__deep",
|
||||
"godet-skirt__num_inserts__4",
|
||||
"godet-skirt__num_inserts__6",
|
||||
"godet-skirt__num_inserts__8",
|
||||
"godet-skirt__num_inserts__10",
|
||||
"godet-skirt__num_inserts__12",
|
||||
"godet-skirt__cuts_distance__close",
|
||||
"godet-skirt__cuts_distance__medium",
|
||||
"godet-skirt__cuts_distance__far",
|
||||
"pencil-skirt__length__micro",
|
||||
"pencil-skirt__length__mini",
|
||||
"pencil-skirt__length__above-knee",
|
||||
"pencil-skirt__length__knee-length",
|
||||
"pencil-skirt__length__midi",
|
||||
"pencil-skirt__length__floor-length",
|
||||
"pencil-skirt__rise__low",
|
||||
"pencil-skirt__rise__mid",
|
||||
"pencil-skirt__rise__high",
|
||||
"pencil-skirt__flare__tight",
|
||||
"pencil-skirt__flare__straight",
|
||||
"pencil-skirt__flare__slight-flare",
|
||||
"pencil-skirt__low_angle__inward",
|
||||
"pencil-skirt__low_angle__straight",
|
||||
"pencil-skirt__low_angle__outward",
|
||||
"pencil-skirt__front_slit__none",
|
||||
"pencil-skirt__front_slit__shallow",
|
||||
"pencil-skirt__front_slit__deep",
|
||||
"pencil-skirt__back_slit__none",
|
||||
"pencil-skirt__back_slit__shallow",
|
||||
"pencil-skirt__back_slit__deep",
|
||||
"pencil-skirt__left_slit__none",
|
||||
"pencil-skirt__left_slit__shallow",
|
||||
"pencil-skirt__left_slit__deep",
|
||||
"pencil-skirt__right_slit__none",
|
||||
"pencil-skirt__right_slit__shallow",
|
||||
"pencil-skirt__right_slit__deep",
|
||||
"pencil-skirt__style_side_cut__Sun",
|
||||
"pencil-skirt__style_side_cut__SIGGRAPH_logo",
|
||||
"pencil-skirt__style_side_cut__None",
|
||||
"levels-skirt__base__Skirt2",
|
||||
"levels-skirt__base__PencilSkirt",
|
||||
"levels-skirt__base__SkirtCircle",
|
||||
"levels-skirt__base__AsymmSkirtCircle",
|
||||
"levels-skirt__level__Skirt2",
|
||||
"levels-skirt__level__SkirtCircle",
|
||||
"levels-skirt__level__AsymmSkirtCircle",
|
||||
"levels-skirt__num_levels__1",
|
||||
"levels-skirt__num_levels__2",
|
||||
"levels-skirt__num_levels__3",
|
||||
"levels-skirt__num_levels__4",
|
||||
"levels-skirt__num_levels__5",
|
||||
"levels-skirt__level_ruffle__none",
|
||||
"levels-skirt__level_ruffle__moderate",
|
||||
"levels-skirt__level_ruffle__rich",
|
||||
"levels-skirt__length__micro",
|
||||
"levels-skirt__length__mini",
|
||||
"levels-skirt__length__above-knee",
|
||||
"levels-skirt__length__knee-length",
|
||||
"levels-skirt__length__midi",
|
||||
"levels-skirt__length__floor-length",
|
||||
"levels-skirt__rise__low",
|
||||
"levels-skirt__rise__mid",
|
||||
"levels-skirt__rise__high",
|
||||
"levels-skirt__base_length_frac__short",
|
||||
"levels-skirt__base_length_frac__medium",
|
||||
"levels-skirt__base_length_frac__long",
|
||||
"pants__length__micro",
|
||||
"pants__length__short",
|
||||
"pants__length__knee-length",
|
||||
"pants__length__capri",
|
||||
"pants__length__ankle-length",
|
||||
"pants__length__full-length",
|
||||
"pants__width__fitted",
|
||||
"pants__width__normal",
|
||||
"pants__width__loose",
|
||||
"pants__flare__tapering",
|
||||
"pants__flare__straight",
|
||||
"pants__flare__slight-flare",
|
||||
"pants__rise__low",
|
||||
"pants__rise__mid",
|
||||
"pants__rise__high",
|
||||
"pants__cuff__type__CuffBand",
|
||||
"pants__cuff__type__CuffSkirt",
|
||||
"pants__cuff__type__CuffBandSkirt",
|
||||
"pants__cuff__type__None",
|
||||
"pants__cuff__top_ruffle__straight",
|
||||
"pants__cuff__top_ruffle__tapered",
|
||||
"pants__cuff__top_ruffle__very_tapered",
|
||||
"pants__cuff__cuff_len__short",
|
||||
"pants__cuff__cuff_len__medium",
|
||||
"pants__cuff__cuff_len__long",
|
||||
"pants__cuff__skirt_fraction__small",
|
||||
"pants__cuff__skirt_fraction__medium",
|
||||
"pants__cuff__skirt_fraction__large",
|
||||
"pants__cuff__skirt_flare__slight",
|
||||
"pants__cuff__skirt_flare__moderate",
|
||||
"pants__cuff__skirt_flare__significant",
|
||||
"pants__cuff__skirt_ruffle__none",
|
||||
"pants__cuff__skirt_ruffle__some"
|
||||
]
|
||||
|
||||
fail_text_list = []
|
||||
|
||||
def list_in_text( my_list, start_string, connect_tag='__'):
|
||||
''' Search the list for all items starting with start-string and only the first two items separated by __,
|
||||
Then use the text to correspond to the startstring. The first two items are used to determine whether they are in the list of predictions
|
||||
Args:_ALL_TEXT
|
||||
my_list (list): The list to be searched
|
||||
start_string(string): A string at the beginning
|
||||
connect_tag (string): The symbol that Chinese the book is connected in the my_list
|
||||
|
||||
'''
|
||||
global fail_text_list
|
||||
temp_flag=True
|
||||
meta_item_list = [
|
||||
connect_tag.join(item.split(connect_tag)[:start_string.count(connect_tag) + 2]) # Split and keep the next field starting with the start_string
|
||||
for item in my_list
|
||||
if item.startswith(start_string) and item.count(connect_tag) >= start_string.count(connect_tag) + 1
|
||||
]
|
||||
meta_item_text = [
|
||||
connect_tag.join(item.split(connect_tag)[:start_string.count(connect_tag) + 2]) # Split and keep the next field starting with the start_string
|
||||
for item in _ALL_TEXT
|
||||
if item.startswith(start_string) and item.count(connect_tag) >= start_string.count(connect_tag) + 1
|
||||
]
|
||||
|
||||
meta_item_list_set=set(meta_item_list)
|
||||
meta_item_text_set=set(meta_item_text)
|
||||
start_string_fail_list=list(meta_item_text_set-meta_item_list_set)
|
||||
if len(start_string_fail_list)>0:
|
||||
fail_text_list.extend(start_string_fail_list)
|
||||
temp_flag=False
|
||||
print(f'mylist{start_string}Words that are missing from the opening word:{start_string_fail_list}')
|
||||
return temp_flag
|
||||
def bool2condition(my_list,connect_tag='__'):
|
||||
global fail_text_list
|
||||
fail_text_list = []
|
||||
bool_flag = True
|
||||
meta_list=[item.split(connect_tag)[-1] for item in my_list if item.startswith("meta")]
|
||||
# Determine whether meta_upper wb bottom is generated
|
||||
if list_in_text(my_list,'meta')==False:
|
||||
bool_flag = False
|
||||
left_bool="False"
|
||||
left_flag = [s for s in my_list if s.startswith("left__enable_asym")]
|
||||
if meta_list[0]!="None":
|
||||
if len(left_flag)==0:
|
||||
print("left__enable_asym")
|
||||
fail_text_list.append("left__enable_asym")
|
||||
bool_flag = False
|
||||
else:
|
||||
left_bool=left_flag[0].split(connect_tag)[-1]
|
||||
left_fittedshirt_strapless_bool=True
|
||||
left_fittedshirt_strapless_flag=[s for s in my_list if s.startswith("left__fitted_shirt__strapless")]
|
||||
if meta_list[0] =='FittedShirt' and left_bool=='True' :
|
||||
if len(left_fittedshirt_strapless_flag)==0 :
|
||||
print("left__fitted_shirt__strapless")
|
||||
fail_text_list.append("left__fitted_shirt__strapless")
|
||||
bool_flag = False
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
if len(left_fittedshirt_strapless_flag) > 0:
|
||||
if left_fittedshirt_strapless_flag[0].split(connect_tag)[-1] == 'False':
|
||||
left_fittedshirt_strapless_bool = False
|
||||
for meta_item in meta_list:
|
||||
if meta_item =='FittedShirt':
|
||||
if list_in_text(my_list,'fitted_shirt')==False:
|
||||
bool_flag = False
|
||||
if meta_item =='Shirt':
|
||||
if list_in_text(my_list,'shirt')==False:
|
||||
bool_flag=False
|
||||
if left_bool == 'True':
|
||||
if list_in_text(my_list, 'left__shirt') == False:
|
||||
bool_flag = False
|
||||
if meta_item=="StraightWB" or meta_item=="FittedWB":
|
||||
if list_in_text(my_list,'waistband')==False:
|
||||
bool_flag=False
|
||||
if meta_item =="SkirtCircle" or meta_item =="SkirtManyPanels" or meta_item=='AsymmSkirtCircle':
|
||||
if list_in_text(my_list,"flare-skirt")==False:
|
||||
bool_flag=False
|
||||
if meta_item=='SkirtCircle':
|
||||
if list_in_text(my_list,'flare-skirt__cut')==False:
|
||||
bool_flag=False
|
||||
if meta_item=='AsymmSkirtCircle':
|
||||
if list_in_text(my_list,"flare-skirt__asymm__front_length")==False:
|
||||
bool_flag=False
|
||||
if list_in_text(my_list,'flare-skirt__cut')==False:
|
||||
bool_flag=False
|
||||
if meta_item=='SkirtManyPanels':
|
||||
if list_in_text(my_list,"flare-skirt__skirt-many-panels")==False:
|
||||
bool_flag=False
|
||||
if meta_item=='GodetSkirt':
|
||||
if list_in_text(my_list,"godet-skirt")==False:
|
||||
bool_flag=False
|
||||
godet_base_item = [item.split(connect_tag)[-1] for item in my_list if item.startswith("godet-skirt__base")]
|
||||
if len(godet_base_item) == 0:
|
||||
bool_flag = False
|
||||
print("godet-skirt__base")
|
||||
fail_text_list.append("godet-skirt__base")
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
else:
|
||||
godet_base_item = godet_base_item[0]
|
||||
if godet_base_item == "Skirt2":
|
||||
if list_in_text(my_list, "skirt") == False:
|
||||
bool_flag = False
|
||||
if godet_base_item == "PencilSkirt":
|
||||
if list_in_text(my_list, "pencil-skirt") == False:
|
||||
bool_flag = False
|
||||
if meta_item=='SkirtLevels':
|
||||
if list_in_text(my_list,"levels-skirt")==False:
|
||||
bool_flag=False
|
||||
base_item = [item.split(connect_tag)[-1] for item in my_list if item.startswith("levels-skirt__base")]
|
||||
if len(base_item) == 0:
|
||||
bool_flag = False
|
||||
print("levels-skirt__base")
|
||||
fail_text_list.append("levels-skirt__base")
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
else:
|
||||
base_item = base_item[0]
|
||||
if base_item == "Skirt2":
|
||||
if list_in_text(my_list, "skirt") == False:
|
||||
bool_flag = False
|
||||
if base_item == "PencilSkirt":
|
||||
if list_in_text(my_list, "pencil-skirt") == False:
|
||||
bool_flag = False
|
||||
if base_item=="SkirtCircle" or base_item=="AsymmSkirtCircle":
|
||||
if list_in_text(my_list, "flare-skirt") == False:
|
||||
bool_flag = False
|
||||
level_item = [item.split(connect_tag)[-1] for item in my_list if item.startswith("levels-skirt__level")]
|
||||
if len(level_item) == 0:
|
||||
bool_flag = False
|
||||
print("levels-skirt__level")
|
||||
fail_text_list.append("levels-skirt__level")
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
else:
|
||||
level_item = level_item[0]
|
||||
if level_item == "Skirt2":
|
||||
if list_in_text(my_list, "skirt") == False:
|
||||
bool_flag = False
|
||||
if level_item == "SkirtCircle" or level_item == "AsymmSkirtCircle":
|
||||
if list_in_text(my_list, "flare-skirt") == False:
|
||||
bool_flag = False
|
||||
if meta_item=='Pants':
|
||||
if list_in_text(my_list,"pants")==False:
|
||||
bool_flag=False
|
||||
cuff_type = [item.split(connect_tag)[-1] for item in my_list if item.startswith("pants__cuff__type")]
|
||||
if len(cuff_type) == 0:
|
||||
bool_flag = False
|
||||
print("pants__cuff__type")
|
||||
fail_text_list.append("pants__cuff__type")
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
else:
|
||||
if cuff_type[0] != "None":
|
||||
if list_in_text(my_list, "pants__cuff") == False:
|
||||
bool_flag = False
|
||||
if meta_item=='Skirt2':
|
||||
if list_in_text(my_list,"skirt")==False:
|
||||
bool_flag=False
|
||||
if meta_item=='PencilSkirt':
|
||||
if list_in_text(my_list,"pencil-skirt")==False:
|
||||
bool_flag=False
|
||||
meta_upper = [item.split(connect_tag)[-1] for item in my_list if item.startswith("meta__upper")]
|
||||
if len(meta_upper)==0:
|
||||
print("meta__upper")
|
||||
fail_text_list.append("meta__upper")
|
||||
bool_flag = False
|
||||
return bool_flag,list(set(fail_text_list))
|
||||
meta_upper=meta_upper[0]
|
||||
strapless=None
|
||||
if meta_upper =='FittedShirt':
|
||||
strapless=[item.split(connect_tag)[-1] for item in my_list if item.startswith("fitted_shirt__strapless")]
|
||||
if len(strapless)==0:
|
||||
bool_flag=False
|
||||
print("fitted_shirt__strapless")
|
||||
fail_text_list.append("fitted_shirt__strapless")
|
||||
return bool_flag,list(set(fail_text_list))
|
||||
strapless=strapless[0]
|
||||
if (strapless==str(False) and meta_upper =='FittedShirt')or meta_upper=="Shirt":
|
||||
if list_in_text(my_list,"collar")==False:
|
||||
bool_flag=False
|
||||
if left_bool == 'True' and (left_fittedshirt_strapless_bool ==False or meta_upper=="Shirt") :
|
||||
if list_in_text(my_list, "left__collar") == False:
|
||||
bool_flag = False
|
||||
sleeveless=[item.split(connect_tag)[-1] for item in my_list if item.startswith("sleeve__sleeveless")]
|
||||
if len(sleeveless)==0:
|
||||
bool_flag=False
|
||||
print("sleeve__sleeveless")
|
||||
fail_text_list.append("sleeve__sleeveless")
|
||||
return bool_flag,list(set(fail_text_list))
|
||||
sleeveless=sleeveless[0]
|
||||
if sleeveless==str(False):
|
||||
if list_in_text(my_list,"sleeve")==False :
|
||||
bool_flag=False
|
||||
cuff_type = [item.split(connect_tag)[-1] for item in my_list if item.startswith("sleeve__cuff__type")]
|
||||
if len(cuff_type)==0:
|
||||
bool_flag = False
|
||||
print("sleeve__cuff__type")
|
||||
fail_text_list.append("sleeve__cuff__type")
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
else:
|
||||
if cuff_type[0]!="None":
|
||||
if list_in_text(my_list,"sleeve__cuff")==False:
|
||||
bool_flag = False
|
||||
if left_bool == 'True' and (left_fittedshirt_strapless_bool == False or meta_upper == "Shirt"):
|
||||
if list_in_text(my_list,"left__sleeve")==False:
|
||||
bool_flag=False
|
||||
#For the processing of the neckline, find the type of neckline and see if it is empty
|
||||
cuff_type = [item.split(connect_tag)[-1] for item in my_list if item.startswith("left__sleeve__cuff__type")]
|
||||
if len(cuff_type) == 0:
|
||||
bool_flag = False
|
||||
print("left__sleeve__cuff__type")
|
||||
fail_text_list.append("left__sleeve__cuff__type")
|
||||
return bool_flag, sorted(list(set(fail_text_list)))
|
||||
else:
|
||||
if cuff_type[0] != "None":
|
||||
if list_in_text(my_list, "left__sleeve__cuff") == False:
|
||||
bool_flag = False
|
||||
return bool_flag,sorted(list(set(fail_text_list)))
|
||||
|
||||
def bool2text_alltext(text_list):
|
||||
flag = True
|
||||
no_in_textspace = list(set(text_list)-set(_ALL_TEXT))
|
||||
if len(no_in_textspace) > 0:
|
||||
flag = False
|
||||
return flag,no_in_textspace
|
||||
|
||||
|
||||
26
pygarment/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
A Python library for building parametric sewing pattern programs
|
||||
"""
|
||||
|
||||
# Building blocks
|
||||
from pygarment.garmentcode.component import Component
|
||||
from pygarment.garmentcode.panel import Panel
|
||||
from pygarment.garmentcode.edge import Edge, CircleEdge, CurveEdge, EdgeSequence
|
||||
from pygarment.garmentcode.connector import Stitches
|
||||
from pygarment.garmentcode.interface import Interface
|
||||
from pygarment.garmentcode.edge_factory import EdgeSeqFactory
|
||||
from pygarment.garmentcode.edge_factory import CircleEdgeFactory
|
||||
from pygarment.garmentcode.edge_factory import EdgeFactory
|
||||
from pygarment.garmentcode.edge_factory import CurveEdgeFactory
|
||||
|
||||
|
||||
# Operations
|
||||
import pygarment.garmentcode.operators as ops
|
||||
import pygarment.garmentcode.utils as utils
|
||||
|
||||
# Parameter support
|
||||
from pygarment.garmentcode.params import BodyParametrizationBase, DesignSampler
|
||||
|
||||
# Errors
|
||||
from pygarment.pattern.core import EmptyPatternError
|
||||
|
||||
400
pygarment/data_config.py
Normal file
@@ -0,0 +1,400 @@
|
||||
"""
|
||||
The module contain Porperties class to manage paramters & stats in various parts of the system
|
||||
"""
|
||||
|
||||
from datetime import timedelta
|
||||
import json
|
||||
import yaml
|
||||
from numbers import Number
|
||||
import traceback
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
# for system info
|
||||
import platform
|
||||
import psutil
|
||||
|
||||
# --- Nice dumping of floats ---
|
||||
def float_representer(dumper, data):
|
||||
|
||||
if data != data or (data == 0.0 and data == 1.0):
|
||||
value = '.nan'
|
||||
elif data == dumper.inf_value:
|
||||
value = '.inf'
|
||||
elif data == -dumper.inf_value:
|
||||
value = '-.inf'
|
||||
else:
|
||||
# Custom representation:
|
||||
# https://stackoverflow.com/a/33944926
|
||||
value = f'{data:.3g}'
|
||||
if '.' not in value or 'e' in value: # e only appears for large int numbers with this precision
|
||||
# An integer hidden as a float
|
||||
value = f'{int(data):d}.0'
|
||||
|
||||
return dumper.represent_scalar('tag:yaml.org,2002:float', value)
|
||||
yaml.add_representer(float, float_representer)
|
||||
|
||||
|
||||
# --- Main class ----
|
||||
class Properties():
|
||||
"""Keeps, loads, and saves cofiguration & statistic information
|
||||
Supports gets&sets as a dictionary
|
||||
Provides shortcuts for batch-init configurations
|
||||
|
||||
One of the usages -- store system-dependent basic cofiguration
|
||||
"""
|
||||
def __init__(self, filename="", clean_stats=False):
|
||||
self.properties = {}
|
||||
self.properties_on_load = {}
|
||||
|
||||
if filename:
|
||||
self.properties = self._from_file(filename)
|
||||
self.properties_on_load = self._from_file(filename)
|
||||
if clean_stats: # only makes sense when initialized from file =)
|
||||
self.clean_stats(self.properties)
|
||||
|
||||
# ---- Base utils ----
|
||||
def has(self, key):
|
||||
"""Used to query if a top-level property/section is already defined"""
|
||||
return key in self.properties
|
||||
|
||||
def serialize(self, filename, backup=None):
|
||||
"""Log current props to file. If logging failed, at least restore
|
||||
provided backup or originally loaded props
|
||||
* backup is expected to be a Properties object
|
||||
"""
|
||||
try:
|
||||
extention = Path(filename).suffix.lower()
|
||||
if extention == '.json':
|
||||
with open(filename, 'w') as f_json:
|
||||
json.dump(self.properties, f_json, indent=2, sort_keys=True)
|
||||
elif extention == '.yaml':
|
||||
with open(filename, 'w') as f:
|
||||
yaml.dump(
|
||||
self.properties,
|
||||
f,
|
||||
default_flow_style=False,
|
||||
sort_keys=False
|
||||
)
|
||||
else:
|
||||
raise ValueError(f'{self.__class__.__name__}::ERROR::Unsupported file type on serialization: {extention}')
|
||||
|
||||
except Exception as e:
|
||||
print('Exception occured while saving properties:')
|
||||
traceback.print_exception(*sys.exc_info())
|
||||
# save backup, s.t. the data is not lost due to interruption of
|
||||
# the file override
|
||||
|
||||
if backup is not None:
|
||||
backup.serialize(filename)
|
||||
else:
|
||||
with open(filename, 'w') as f_json:
|
||||
json.dump(self.properties_on_load, f_json,
|
||||
indent=2, sort_keys=True)
|
||||
raise RuntimeError('Error occured while saving properties. Backup version is saved instead')
|
||||
|
||||
def merge(self, filename="", clean_stats=False, re_write=True,
|
||||
adding_tag='added'):
|
||||
"""Merge current set of properties with the one from file
|
||||
* re_write=True sets the default merging of Python dicts, values
|
||||
from new props overrite
|
||||
the one from old one if keys are the same
|
||||
* re_write=False will keep both properties if their values are
|
||||
different (imported one marked with adding_tag)
|
||||
"""
|
||||
new_props = self._from_file(filename)
|
||||
if clean_stats:
|
||||
self.clean_stats(new_props)
|
||||
# merge
|
||||
self._recursive_dict_update(self.properties, new_props, re_write, adding_tag)
|
||||
|
||||
# --- Specialised utils (require domain knowledge) --
|
||||
|
||||
def is_fail(self, dataname):
|
||||
"""
|
||||
Check if a particular object is listed as fail in any of the sections
|
||||
Fails may be listed in the stats subsection of any of the section
|
||||
"""
|
||||
_, fails_list = self.count_fails()
|
||||
|
||||
return dataname in fails_list
|
||||
|
||||
def is_fail_section(self, dataname):
|
||||
"""
|
||||
Check if a particular object is listed as fail in any of the sections
|
||||
Fails may be listed in the stats subsection of any of the section
|
||||
return the section name
|
||||
"""
|
||||
|
||||
for section_key in self.properties:
|
||||
section = self.properties[section_key]
|
||||
if isinstance(section, dict) and 'stats' in section and ('fails' in section['stats']):
|
||||
if isinstance(section['stats']['fails'], dict):
|
||||
for key in section['stats']['fails']:
|
||||
if not isinstance(section['stats']['fails'][key], list):
|
||||
raise NotImplementedError(
|
||||
'Properties::ERROR:: Fails subsections of the type {} is not supported'.format(
|
||||
type(section['stats']['fails'][key])))
|
||||
|
||||
if dataname in section['stats']['fails'][key]: # expects a list as value
|
||||
return True, key
|
||||
|
||||
elif isinstance(section['stats']['fails'], list):
|
||||
if dataname in section['stats']['fails'][key]: # expects a list as value
|
||||
return True, 'fails'
|
||||
else:
|
||||
raise NotImplementedError('Properties::ERROR:: Fails subsections of the type {} is not supported'.format(type(section['stats']['fails'])))
|
||||
|
||||
return False, None
|
||||
|
||||
def count_fails(self, log=False):
|
||||
"""
|
||||
Number of (unique) datapoints marked as fail
|
||||
"""
|
||||
fails = []
|
||||
for section_key in self.properties:
|
||||
section = self.properties[section_key]
|
||||
section_fails = []
|
||||
if isinstance(section, dict) and 'stats' in section and ('fails' in section['stats']):
|
||||
if isinstance(section['stats']['fails'], dict):
|
||||
for key in section['stats']['fails']:
|
||||
if not isinstance(section['stats']['fails'][key], list):
|
||||
raise NotImplementedError(
|
||||
'Properties::ERROR:: Fails subsections of the type {} is not supported'.format(
|
||||
type(section['stats']['fails'][key])))
|
||||
|
||||
section_fails += section['stats']['fails'][key] # expects a list as value
|
||||
|
||||
elif isinstance(section['stats']['fails'], list):
|
||||
section_fails += section['stats']['fails']
|
||||
else:
|
||||
raise NotImplementedError('Properties::Error:: Fails subsections of the type {} is not supported'.format(type(section['stats']['fails'])))
|
||||
|
||||
if log:
|
||||
section['stats']['fails_count'] = len(list(set(section_fails)))
|
||||
|
||||
fails += section_fails
|
||||
|
||||
fails = list(set(fails))
|
||||
|
||||
return len(fails), fails
|
||||
|
||||
def add_fail(self, section_name, fail_type, info):
|
||||
"""Write a failure case to a requested section's stats"""
|
||||
|
||||
section = self.properties[section_name]
|
||||
if 'fails' not in section['stats']:
|
||||
section['stats']['fails'] = {}
|
||||
try:
|
||||
section['stats']['fails'][fail_type].append(info)
|
||||
except KeyError:
|
||||
section['stats']['fails'][fail_type] = [info]
|
||||
|
||||
|
||||
# ---------- Properties updates ---------------
|
||||
def set_basic(self, **kwconfig):
|
||||
"""Adds/updates info on the top level of properties
|
||||
Only to be used for basic information!
|
||||
"""
|
||||
# section exists
|
||||
for key, value in kwconfig.items():
|
||||
self.properties[key] = value
|
||||
|
||||
def set_section_config(self, section, **kwconfig):
|
||||
"""adds or modifies a (top level) section and updates its configuration info
|
||||
"""
|
||||
# create new section
|
||||
if section not in self.properties:
|
||||
self.properties[section] = {
|
||||
'config': kwconfig,
|
||||
'stats': {}
|
||||
}
|
||||
return
|
||||
# section exists
|
||||
for key, value in kwconfig.items():
|
||||
self.properties[section]['config'][key] = value
|
||||
|
||||
def set_section_stats(self, section, **kwstats):
|
||||
"""adds or modifies a (top level) section and updates its statistical info
|
||||
"""
|
||||
# create new section
|
||||
if section not in self.properties:
|
||||
self.properties[section] = {
|
||||
'config': {},
|
||||
'stats': kwstats
|
||||
}
|
||||
return
|
||||
# section exists
|
||||
for key, value in kwstats.items():
|
||||
self.properties[section]['stats'][key] = value
|
||||
|
||||
def clean_stats(self, properties):
|
||||
""" Remove info from all Stats sub sections """
|
||||
for _, value in properties.items():
|
||||
# detect section
|
||||
if isinstance(value, dict) and 'stats' in value:
|
||||
value['stats'] = {}
|
||||
|
||||
def summarize_stats(self,
|
||||
key,
|
||||
log_sum=False, log_avg=False,
|
||||
log_median=False, log_80=False, log_95=False,
|
||||
log_min=False, log_max=False,
|
||||
as_time=False):
|
||||
"""Make a summary of requested key with requested statistics in current props"""
|
||||
updated = False
|
||||
for section in self.properties.values():
|
||||
# check all stats sections
|
||||
if isinstance(section, dict) and 'stats' in section:
|
||||
if key in section['stats']:
|
||||
stats_values = section['stats'][key]
|
||||
if isinstance(stats_values, dict):
|
||||
stats_values = list(stats_values.values())
|
||||
|
||||
# summarize all foundable statistics
|
||||
if isinstance(stats_values, list) and len(stats_values) > 0 and isinstance(stats_values[0], Number):
|
||||
if log_sum:
|
||||
section['stats'][key + "_sum"] = str(timedelta(seconds=sum(stats_values))) if as_time else sum(stats_values)
|
||||
updated = True
|
||||
if log_avg:
|
||||
section['stats'][key + "_avg"] = sum(stats_values) / len(stats_values)
|
||||
if as_time:
|
||||
section['stats'][key + "_avg"] = str(timedelta(seconds=section['stats'][key + "_avg"]))
|
||||
updated = True
|
||||
if log_median:
|
||||
section['stats'][key + "_med"] = str(timedelta(seconds=np.percentile(stats_values, 50))) if as_time else float(np.percentile(stats_values, 50))
|
||||
updated = True
|
||||
if log_80:
|
||||
section['stats'][key + "_p80"] = str(timedelta(seconds=np.percentile(stats_values, 80))) if as_time else float(np.percentile(stats_values, 80))
|
||||
updated = True
|
||||
if log_95:
|
||||
section['stats'][key + "_p95"] = str(timedelta(seconds=np.percentile(stats_values, 95))) if as_time else float(np.percentile(stats_values, 95))
|
||||
updated = True
|
||||
if log_min:
|
||||
section['stats'][key + "_min"] = str(timedelta(seconds=min(stats_values))) if as_time else min(stats_values)
|
||||
updated = True
|
||||
if log_max:
|
||||
section['stats'][key + "_max"] = str(timedelta(seconds=max(stats_values))) if as_time else max(stats_values)
|
||||
updated = True
|
||||
return updated
|
||||
|
||||
# -- Specialised updates (require domain knowledge) --
|
||||
def add_sys_info(self):
|
||||
"""Add or update system information on the top level of config"""
|
||||
|
||||
if sys.version_info.major < 3:
|
||||
raise NotImplementedError('{}::Requesting system info is not supported for Python 2'.format(self.__class__.__name__))
|
||||
|
||||
# https://stackoverflow.com/questions/3103178/how-to-get-the-system-info-with-python
|
||||
|
||||
self.properties['system_info'] = {}
|
||||
|
||||
self.properties['system_info']['platform'] = platform.system()
|
||||
self.properties['system_info']['platform-release'] = platform.release()
|
||||
self.properties['system_info']['platform-version'] = platform.version()
|
||||
self.properties['system_info']['architecture'] = platform.machine()
|
||||
self.properties['system_info']['processor'] = platform.processor()
|
||||
self.properties['system_info']['ram'] = str(round(psutil.virtual_memory().total / (1024.0 ** 3))) + " GB"
|
||||
|
||||
try:
|
||||
import warp # Optional section
|
||||
if warp.context.runtime is None:
|
||||
# runtime = warp.context.Runtime()
|
||||
warp.init()
|
||||
else:
|
||||
print(f'{self.__class__.__name__}::INFO::Saving GPU info -- warp already initialized')
|
||||
curr_device = warp.get_device()
|
||||
self.properties['system_info']['GPU'] = curr_device.name if curr_device.is_cuda else 'Not used'
|
||||
except ImportError:
|
||||
pass # Don't do anything if warp not available
|
||||
|
||||
def stats_summary(self):
|
||||
"""
|
||||
Compute data simulation processing statistics
|
||||
"""
|
||||
updated_render = self.summarize_stats('render_time', log_sum=True, log_avg=True, as_time=True)
|
||||
updated_frames = self.summarize_stats('fin_frame', log_avg=True)
|
||||
updated_sim_time = self.summarize_stats('sim_time', log_sum=True, log_avg=True, as_time=True)
|
||||
updated_spf = self.summarize_stats('spf', log_avg=True, as_time=True)
|
||||
updated_scan = self.summarize_stats('processing_time', log_sum=True, log_avg=True, as_time=True)
|
||||
updated_scan_faces = self.summarize_stats('faces_removed', log_avg=True)
|
||||
|
||||
updated_self_collisions = self.summarize_stats(
|
||||
'self_collisions', log_avg=True, log_median=True, log_80=True, log_95=True)
|
||||
updated_body_collisions = self.summarize_stats(
|
||||
'body_collisions', log_avg=True, log_median=True, log_80=True, log_95=True)
|
||||
|
||||
updated_face_count = self.summarize_stats(
|
||||
'face_count', log_avg=True, log_median=True, log_min=True, log_max=True)
|
||||
updated_panel_count = self.summarize_stats(
|
||||
'panel_count', log_avg=True, log_median=True, log_min=True, log_max=True)
|
||||
|
||||
# fails
|
||||
self.count_fails(log=True)
|
||||
|
||||
if not (updated_frames and updated_render and updated_sim_time and updated_spf and updated_self_collisions and updated_body_collisions):
|
||||
print(f'{self.__class__.__name__}::WARNING::Sim stats summary '
|
||||
'requested, but not all sections were updated')
|
||||
|
||||
# ---- Private utils ----
|
||||
def _from_file(self, filename):
|
||||
""" Load properties from previously created file """
|
||||
extention = Path(filename).suffix.lower()
|
||||
if extention == '.json':
|
||||
with open(filename, 'r') as f_json:
|
||||
return json.load(f_json)
|
||||
elif extention == '.yaml':
|
||||
with open(filename, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
else:
|
||||
raise ValueError(f'{self.__class__.__name__}::ERROR::Unsupported file type on load: {extention}')
|
||||
|
||||
|
||||
def _recursive_dict_update(self, in_dict, new_dict, re_write=True, adding_tag='added', in_stats=False):
|
||||
"""
|
||||
updates input dictionary with the update_dict properly updating all the inner dictionaries
|
||||
re_write = True replaces the values with the ones from new dictionary if they happen to be different,
|
||||
re_write = False extends dictionary to include both values if different
|
||||
|
||||
"in_stats" shows if we are currently in any of the stats subsections.
|
||||
In this case, lists are merged instead of being re-written
|
||||
"""
|
||||
if not isinstance(new_dict, dict):
|
||||
in_dict = new_dict # just update with all values
|
||||
return
|
||||
|
||||
for new_key in new_dict:
|
||||
if new_key in in_dict and isinstance(in_dict[new_key], dict):
|
||||
# update inner dict properly
|
||||
self._recursive_dict_update(
|
||||
in_dict[new_key], new_dict[new_key],
|
||||
re_write, adding_tag,
|
||||
(in_stats or new_key == 'stats'))
|
||||
elif not re_write and new_key in in_dict and in_dict[new_key] != new_dict[new_key]:
|
||||
if in_stats and isinstance(in_dict[new_key], list):
|
||||
# merge lists inside stats sections
|
||||
in_dict[new_key] = in_dict[new_key] + new_dict[new_key]
|
||||
else:
|
||||
# Keep both versions (e.g. in configs)
|
||||
adding_name = new_key + '_' + adding_tag
|
||||
while adding_name in in_dict: # in case even the added version is already there
|
||||
adding_name = adding_name + '_added'
|
||||
|
||||
in_dict[adding_name] = new_dict[new_key]
|
||||
in_dict[new_key + '_' + self['name']] = in_dict[new_key]
|
||||
else: # at sertain depth there will be no more dicts -- recusrion stops
|
||||
in_dict[new_key] = new_dict[new_key]
|
||||
# if new_dict is empty -- no update happens
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.properties[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.properties[key] = value
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.properties
|
||||
|
||||
def __str__(self):
|
||||
return str(self.properties)
|
||||