├── .editorconfig ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── build.yml │ ├── docs.yml │ ├── pr-checks.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── compas_occ.png ├── docs ├── _images │ ├── PLACEHOLDER │ ├── compas_occ.png │ ├── example_brep_explorer.png │ ├── example_brep_fillet.png │ ├── example_brep_from_booleans.png │ ├── example_brep_from_mesh.png │ ├── example_brep_overlap.png │ ├── example_brep_slice.png │ ├── example_brep_split.png │ ├── example_brep_trimmed.png │ ├── example_brep_with_hole.png │ ├── example_brep_with_holes.png │ ├── example_curve_closest_parameters_curve.png │ ├── example_curve_closest_point.png │ ├── example_curve_comparison1.png │ ├── example_curve_comparison2.png │ ├── example_curve_divide.png │ ├── example_curve_from_circle.png │ ├── example_curve_from_interpolation.png │ ├── example_curve_from_line.png │ ├── example_curve_from_parameters.png │ ├── example_curve_from_poles.png │ ├── example_curve_joining_joined.png │ ├── example_curve_joining_separate.png │ ├── example_segmentation.png │ ├── example_surface_aabb.png │ ├── example_surface_frames.png │ ├── example_surface_from_extrusion-1.png │ ├── example_surface_from_extrusion-2.png │ ├── example_surface_from_fill.png │ ├── example_surface_from_meshgrid.png │ ├── example_surface_from_parameters.png │ ├── example_surface_from_points.png │ ├── example_surface_intersections_with_line.png │ ├── example_surface_isocurves.png │ ├── example_surface_jsondata.png │ ├── example_surface_of_revolution.png │ ├── example_surface_random.png │ ├── example_surface_spacepoints.png │ └── example_surface_viewobject.png ├── _static │ └── PLACEHOLDER ├── acknowledgements.rst ├── api.rst ├── api │ ├── compas_occ.brep.rst │ ├── compas_occ.conversions.rst │ ├── compas_occ.geometry.curves.rst │ ├── compas_occ.geometry.rst │ └── compas_occ.geometry.surfaces.rst ├── conf.py ├── examples.rst ├── examples │ ├── breps │ │ ├── brep_exchange.py │ │ ├── brep_explorer.py │ │ ├── brep_explorer.rst │ │ ├── brep_fillet.py │ │ ├── brep_fillet.rst │ │ ├── brep_from_booleans.py │ │ ├── brep_from_booleans.rst │ │ ├── brep_from_loft.py │ │ ├── brep_from_mesh.py │ │ ├── brep_from_mesh.rst │ │ ├── brep_from_shape.py │ │ ├── brep_intersections.py │ │ ├── brep_overlap.py │ │ ├── brep_overlap.rst │ │ ├── brep_serialisation.py │ │ ├── brep_slice.py │ │ ├── brep_slice.rst │ │ ├── brep_split.py │ │ ├── brep_split.rst │ │ ├── brep_trim.py │ │ ├── brep_trim.rst │ │ ├── brep_with_hole.py │ │ ├── brep_with_hole.rst │ │ ├── brep_with_holes.py │ │ ├── brep_with_holes.rst │ │ └── index.rst │ ├── curves │ │ ├── curve_closest_parameters_curve.py │ │ ├── curve_closest_parameters_curve.rst │ │ ├── curve_closest_point.py │ │ ├── curve_closest_point.rst │ │ ├── curve_comparison1.py │ │ ├── curve_comparison1.rst │ │ ├── curve_comparison2.py │ │ ├── curve_comparison2.rst │ │ ├── curve_divide.py │ │ ├── curve_divide.rst │ │ ├── curve_from_circle.py │ │ ├── curve_from_circle.rst │ │ ├── curve_from_ellipse.py │ │ ├── curve_from_ellipse.rst │ │ ├── curve_from_interpolation.py │ │ ├── curve_from_interpolation.rst │ │ ├── curve_from_line.py │ │ ├── curve_from_line.rst │ │ ├── curve_from_parameters.py │ │ ├── curve_from_parameters.rst │ │ ├── curve_from_points.py │ │ ├── curve_from_points.rst │ │ ├── curve_joining.py │ │ ├── curve_joining.rst │ │ ├── curve_segmentation.py │ │ ├── curve_segmentation.rst │ │ └── index.rst │ └── surfaces │ │ ├── __temp │ │ ├── surface_from_extrusion-1.py │ │ ├── surface_from_extrusion-1.rst │ │ ├── surface_from_extrusion-2.py │ │ ├── surface_from_extrusion-2.rst │ │ └── surface_of_revolution.py │ │ ├── index.rst │ │ ├── surface_aabb.py │ │ ├── surface_aabb.rst │ │ ├── surface_frames.py │ │ ├── surface_frames.rst │ │ ├── surface_from_fill.py │ │ ├── surface_from_fill.rst │ │ ├── surface_from_meshgrid.py │ │ ├── surface_from_meshgrid.rst │ │ ├── surface_from_parameters.py │ │ ├── surface_from_parameters.rst │ │ ├── surface_from_points.py │ │ ├── surface_from_points.rst │ │ ├── surface_intersections_with_line.py │ │ ├── surface_intersections_with_line.rst │ │ ├── surface_isocurves.py │ │ ├── surface_isocurves.rst │ │ ├── surface_jsondata.py │ │ ├── surface_jsondata.rst │ │ ├── surface_obb.py │ │ ├── surface_random.py │ │ ├── surface_random.rst │ │ ├── surface_spacepoints.py │ │ └── surface_spacepoints.rst ├── index.rst ├── installation.rst ├── license.rst ├── tutorial.rst └── write_rst.py ├── environment.yml ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── src └── compas_occ │ ├── __init__.py │ ├── brep │ ├── __init__.py │ ├── brep.py │ ├── brepedge.py │ ├── brepface.py │ ├── breploop.py │ ├── brepvertex.py │ ├── builder.py │ └── errors.py │ ├── conversions │ ├── __init__.py │ ├── arrays.py │ ├── geometry.py │ ├── meshes.py │ └── transformations.py │ ├── geometry │ ├── __init__.py │ ├── curves │ │ ├── __init__.py │ │ ├── bezier.py │ │ ├── curve.py │ │ ├── curve2d.py │ │ └── nurbs.py │ └── surfaces │ │ ├── __init__.py │ │ ├── __temp │ │ ├── extrusion.py │ │ └── revolution.py │ │ ├── nurbs.py │ │ └── surface.py │ └── occ.py ├── tasks.py └── tests ├── curve_from_rhino.json ├── surface_from_rhino.json ├── test_breps.py ├── test_conversions.py ├── test_data_nurbscurve.py ├── test_data_nurbssurface.py └── test_trivial.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | charset = utf-8 12 | max_line_length = 180 13 | 14 | [*.{bat,cmd,ps1}] 15 | end_of_line = crlf 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | 20 | [*.yml] 21 | indent_size = 2 22 | 23 | [Makefile] 24 | indent_style = tab 25 | indent_size = 4 26 | 27 | [*.yml] 28 | indent_style = space 29 | indent_size = 2 30 | 31 | [LICENSE] 32 | insert_final_newline = false 33 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at brg@arch.ethz.ch. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | 13 | Steps to reproduce the behavior: 14 | 15 | 1. Context [e.g. ST3, Rhino, Blender, ...] 16 | 2. Sample script 17 | 3. Sample data 18 | 4. See error 19 | 20 | **Expected behavior** 21 | 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Desktop (please complete the following information):** 29 | 30 | - OS: [e.g. iOS] 31 | - Python version [e.g. 2.7] 32 | - Python package manager [e.g. macports, pip, conda] 33 | 34 | **Additional context** 35 | 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | # Feature Request 7 | 8 | As a [role], I want [something] so that [benefit]. 9 | 10 | ## Details 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | 14 | A clear and concise description of what the problem is. 15 | 16 | **Describe the solution you'd like** 17 | 18 | A clear and concise description of what you want to happen. 19 | 20 | **Describe alternatives you've considered** 21 | 22 | A clear and concise description of any alternative solutions or features you've considered. 23 | 24 | **Additional context** 25 | 26 | Add any other context or screenshots about the feature request here. 27 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ### What type of change is this? 6 | 7 | - [ ] Bug fix in a **backwards-compatible** manner. 8 | - [ ] New feature in a **backwards-compatible** manner. 9 | - [ ] Breaking change: bug fix or new feature that involve incompatible API changes. 10 | - [ ] Other (e.g. doc update, configuration, etc) 11 | 12 | ### Checklist 13 | 14 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 15 | 16 | - [ ] I added a line to the `CHANGELOG.md` file in the `Unreleased` section under the most fitting heading (e.g. `Added`, `Changed`, `Removed`). 17 | - [ ] I ran all tests on my computer and it's all green (i.e. `invoke test`). 18 | - [ ] I ran lint on my computer and there are no errors (i.e. `invoke lint`). 19 | - [ ] I added new functions/classes and made them available on a second-level import, e.g. `compas.datastructures.Mesh`. 20 | - [ ] I have added tests that prove my fix is effective or that my feature works. 21 | - [ ] I have added necessary documentation (if appropriate) 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | Build: 13 | if: "!contains(github.event.pull_request.labels.*.name, 'docs-only')" 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [macos-latest, windows-latest, ubuntu-latest] 18 | python: ["3.10", "3.11", "3.12"] 19 | 20 | steps: 21 | - uses: compas-dev/compas-actions.build@v4 22 | with: 23 | invoke_lint: true 24 | invoke_test: true 25 | use_conda: true 26 | python: ${{ matrix.python }} 27 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "v*" 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | docs: 15 | runs-on: windows-latest 16 | steps: 17 | - uses: compas-dev/compas-actions.docs@v4 18 | with: 19 | github_token: ${{ secrets.GITHUB_TOKEN }} 20 | use_conda: true 21 | doc_url: https://compas.dev/compas_occ/ 22 | -------------------------------------------------------------------------------- /.github/workflows/pr-checks.yml: -------------------------------------------------------------------------------- 1 | name: verify-pr-checklist 2 | on: 3 | pull_request: 4 | types: [assigned, opened, synchronize, reopened, labeled, unlabeled] 5 | branches: 6 | - main 7 | - master 8 | 9 | jobs: 10 | build: 11 | name: Check Actions 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Changelog check 16 | uses: Zomzog/changelog-checker@v1.2.0 17 | with: 18 | fileName: CHANGELOG.md 19 | checkNotification: Simple 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | Release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Create Release 14 | id: create_release 15 | uses: actions/create-release@v1 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | with: 19 | tag_name: ${{ github.ref }} 20 | release_name: Release ${{ github.ref }} 21 | draft: false 22 | prerelease: false 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | # ============================================================================== 104 | # COMPAS OCC 105 | # ============================================================================== 106 | 107 | *.3dmbak 108 | *.rhl 109 | *.rui_bak 110 | 111 | temp/** 112 | !temp/PLACEHOLDER 113 | 114 | .DS_Store 115 | 116 | .vscode 117 | 118 | docs/api/generated/ 119 | 120 | conda.recipe/ 121 | 122 | __*.stp 123 | 124 | scripts/__floor 125 | 126 | docs/examples/breps/booleans.iges 127 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) ETH Zurich - Block Research Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # COMPAS OCC 2 | 3 | ![build](https://github.com/compas-dev/compas_occ/workflows/build/badge.svg) 4 | [![GitHub - License](https://img.shields.io/github/license/compas-dev/compas_occ.svg)](https://github.com/compas-dev/compas_occ) 5 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/compas_occ.svg)](https://pypi.python.org/project/compas_occ) 6 | [![PyPI - Latest Release](https://img.shields.io/pypi/v/compas_occ.svg)](https://pypi.python.org/project/compas_occ) 7 | [![Conda - Latest Release](https://anaconda.org/conda-forge/compas_occ/badges/version.svg)](https://anaconda.org/conda-forge/compas_occ) 8 | [![Conda - Platform](https://img.shields.io/conda/pn/conda-forge/compas_occ)](https://anaconda.org/conda-forge/compas_occ) 9 | 10 | COMPAS wrapper for the Python bindings (`pythonocc-core`) of the 3D geometry kernel of Open Cascade. 11 | 12 | More info coming soon. 13 | In the meantime, see the docs at . 14 | -------------------------------------------------------------------------------- /compas_occ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/compas_occ.png -------------------------------------------------------------------------------- /docs/_images/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | # container for images to be included in the docs 2 | -------------------------------------------------------------------------------- /docs/_images/compas_occ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/compas_occ.png -------------------------------------------------------------------------------- /docs/_images/example_brep_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_explorer.png -------------------------------------------------------------------------------- /docs/_images/example_brep_fillet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_fillet.png -------------------------------------------------------------------------------- /docs/_images/example_brep_from_booleans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_from_booleans.png -------------------------------------------------------------------------------- /docs/_images/example_brep_from_mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_from_mesh.png -------------------------------------------------------------------------------- /docs/_images/example_brep_overlap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_overlap.png -------------------------------------------------------------------------------- /docs/_images/example_brep_slice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_slice.png -------------------------------------------------------------------------------- /docs/_images/example_brep_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_split.png -------------------------------------------------------------------------------- /docs/_images/example_brep_trimmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_trimmed.png -------------------------------------------------------------------------------- /docs/_images/example_brep_with_hole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_with_hole.png -------------------------------------------------------------------------------- /docs/_images/example_brep_with_holes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_brep_with_holes.png -------------------------------------------------------------------------------- /docs/_images/example_curve_closest_parameters_curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_closest_parameters_curve.png -------------------------------------------------------------------------------- /docs/_images/example_curve_closest_point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_closest_point.png -------------------------------------------------------------------------------- /docs/_images/example_curve_comparison1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_comparison1.png -------------------------------------------------------------------------------- /docs/_images/example_curve_comparison2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_comparison2.png -------------------------------------------------------------------------------- /docs/_images/example_curve_divide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_divide.png -------------------------------------------------------------------------------- /docs/_images/example_curve_from_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_from_circle.png -------------------------------------------------------------------------------- /docs/_images/example_curve_from_interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_from_interpolation.png -------------------------------------------------------------------------------- /docs/_images/example_curve_from_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_from_line.png -------------------------------------------------------------------------------- /docs/_images/example_curve_from_parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_from_parameters.png -------------------------------------------------------------------------------- /docs/_images/example_curve_from_poles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_from_poles.png -------------------------------------------------------------------------------- /docs/_images/example_curve_joining_joined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_joining_joined.png -------------------------------------------------------------------------------- /docs/_images/example_curve_joining_separate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_curve_joining_separate.png -------------------------------------------------------------------------------- /docs/_images/example_segmentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_segmentation.png -------------------------------------------------------------------------------- /docs/_images/example_surface_aabb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_aabb.png -------------------------------------------------------------------------------- /docs/_images/example_surface_frames.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_frames.png -------------------------------------------------------------------------------- /docs/_images/example_surface_from_extrusion-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_from_extrusion-1.png -------------------------------------------------------------------------------- /docs/_images/example_surface_from_extrusion-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_from_extrusion-2.png -------------------------------------------------------------------------------- /docs/_images/example_surface_from_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_from_fill.png -------------------------------------------------------------------------------- /docs/_images/example_surface_from_meshgrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_from_meshgrid.png -------------------------------------------------------------------------------- /docs/_images/example_surface_from_parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_from_parameters.png -------------------------------------------------------------------------------- /docs/_images/example_surface_from_points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_from_points.png -------------------------------------------------------------------------------- /docs/_images/example_surface_intersections_with_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_intersections_with_line.png -------------------------------------------------------------------------------- /docs/_images/example_surface_isocurves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_isocurves.png -------------------------------------------------------------------------------- /docs/_images/example_surface_jsondata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_jsondata.png -------------------------------------------------------------------------------- /docs/_images/example_surface_of_revolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_of_revolution.png -------------------------------------------------------------------------------- /docs/_images/example_surface_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_random.png -------------------------------------------------------------------------------- /docs/_images/example_surface_spacepoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_spacepoints.png -------------------------------------------------------------------------------- /docs/_images/example_surface_viewobject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/docs/_images/example_surface_viewobject.png -------------------------------------------------------------------------------- /docs/_static/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | # container for static files, e.g. logo, banner images, javascript, stylesheets, ... 2 | -------------------------------------------------------------------------------- /docs/acknowledgements.rst: -------------------------------------------------------------------------------- 1 | **************** 2 | Acknowledgements 3 | **************** 4 | 5 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | API Reference 3 | ******************************************************************************** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | api/compas_occ.brep 9 | api/compas_occ.conversions 10 | api/compas_occ.geometry 11 | -------------------------------------------------------------------------------- /docs/api/compas_occ.brep.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | compas_occ.brep 3 | ******************************************************************************** 4 | 5 | .. currentmodule:: compas_occ.brep 6 | 7 | Classes 8 | ======= 9 | 10 | .. autosummary:: 11 | :toctree: generated/ 12 | :nosignatures: 13 | 14 | OCCBrep 15 | OCCBrepVertex 16 | OCCBrepEdge 17 | OCCBrepLoop 18 | OCCBrepFace 19 | -------------------------------------------------------------------------------- /docs/api/compas_occ.conversions.rst: -------------------------------------------------------------------------------- 1 | 2 | ******************************************************************************** 3 | compas_occ.conversions 4 | ******************************************************************************** 5 | 6 | .. currentmodule:: compas_occ.conversions 7 | 8 | .. rst-class:: lead 9 | 10 | None 11 | 12 | Functions 13 | ========= 14 | 15 | .. autosummary:: 16 | :toctree: generated/ 17 | :nosignatures: 18 | 19 | array1_from_floats1 20 | array1_from_integers1 21 | array1_from_points1 22 | array2_from_floats2 23 | array2_from_points2 24 | ax2_to_compas 25 | ax3_to_compas 26 | axis_to_compas 27 | axis_to_compas_vector 28 | axis_to_occ 29 | bezier_to_compas 30 | bspline_to_compas 31 | circle_to_compas 32 | circle_to_occ 33 | compas_mesh_to_occ_shell 34 | compas_quadmesh_to_occ_shell 35 | compas_transformation_to_trsf 36 | compas_trimesh_to_occ_shell 37 | cone_to_occ 38 | cylinder_to_compas 39 | cylinder_to_occ 40 | direction_to_compas 41 | direction_to_occ 42 | ellipse_to_compas 43 | ellipse_to_occ 44 | floats2_from_array2 45 | frame_to_occ_ax2 46 | frame_to_occ_ax3 47 | harray1_from_points1 48 | hyperbola_to_compas 49 | line_to_compas 50 | line_to_occ 51 | location_to_compas 52 | ngon_to_face 53 | parabola_to_compas 54 | plane_to_compas 55 | plane_to_occ 56 | plane_to_occ_ax2 57 | plane_to_occ_ax3 58 | point2d_to_compas 59 | point_to_compas 60 | point_to_occ 61 | points1_from_array1 62 | points2_from_array2 63 | quad_to_face 64 | sphere_to_compas 65 | sphere_to_occ 66 | torus_to_occ 67 | triangle_to_face 68 | vector2d_to_compas 69 | vector_to_compas 70 | vector_to_occ 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /docs/api/compas_occ.geometry.curves.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curves 3 | ******************************************************************************** 4 | 5 | .. currentmodule:: compas_occ.geometry 6 | 7 | Classes 8 | ======= 9 | 10 | .. autosummary:: 11 | :toctree: generated/ 12 | :nosignatures: 13 | 14 | OCCCurve 15 | OCCCurve2d 16 | OCCNurbsCurve 17 | -------------------------------------------------------------------------------- /docs/api/compas_occ.geometry.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | compas_occ.geometry 3 | ******************************************************************************** 4 | 5 | .. currentmodule:: compas_occ.geometry 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | compas_occ.geometry.curves 11 | compas_occ.geometry.surfaces 12 | -------------------------------------------------------------------------------- /docs/api/compas_occ.geometry.surfaces.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surfaces 3 | ******************************************************************************** 4 | 5 | .. currentmodule:: compas_occ.geometry 6 | 7 | Classes 8 | ======= 9 | 10 | .. autosummary:: 11 | :toctree: generated/ 12 | :nosignatures: 13 | 14 | OCCSurface 15 | OCCExtrusionSurface 16 | OCCRevolutionSurface 17 | OCCNurbsSurface 18 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # -*- coding: utf-8 -*- 3 | 4 | from sphinx.writers import html, html5 5 | import sphinx_compas2_theme 6 | 7 | # -- General configuration ------------------------------------------------ 8 | 9 | project = "COMPAS OCC" 10 | copyright = "COMPAS Association" 11 | author = "Tom Van Mele" 12 | package = "compas_occ" 13 | organization = "compas-dev" 14 | 15 | master_doc = "index" 16 | source_suffix = {".rst": "restructuredtext", ".md": "markdown"} 17 | templates_path = sphinx_compas2_theme.get_autosummary_templates_path() 18 | exclude_patterns = sphinx_compas2_theme.default_exclude_patterns 19 | add_module_names = True 20 | language = "en" 21 | 22 | latest_version = sphinx_compas2_theme.get_latest_version() 23 | 24 | if latest_version == "Unreleased": 25 | release = "Unreleased" 26 | version = "latest" 27 | else: 28 | release = latest_version 29 | version = ".".join(release.split(".")[0:2]) # type: ignore 30 | 31 | # -- Extension configuration ------------------------------------------------ 32 | 33 | extensions = sphinx_compas2_theme.default_extensions 34 | extensions.remove("sphinx.ext.linkcode") 35 | 36 | # numpydoc options 37 | 38 | numpydoc_show_class_members = False 39 | numpydoc_class_members_toctree = False 40 | numpydoc_attributes_as_param_list = True 41 | 42 | # bibtex options 43 | 44 | # autodoc options 45 | 46 | autodoc_type_aliases = {} 47 | 48 | autodoc_typehints = "description" 49 | autodoc_typehints_format = "short" 50 | autodoc_typehints_description_target = "documented" 51 | 52 | autodoc_mock_imports = sphinx_compas2_theme.default_mock_imports 53 | 54 | autodoc_default_options = { 55 | "undoc-members": True, 56 | "show-inheritance": True, 57 | } 58 | 59 | autodoc_member_order = "groupwise" 60 | 61 | autoclass_content = "class" 62 | 63 | 64 | def setup(app): 65 | app.connect("autodoc-skip-member", sphinx_compas2_theme.skip) 66 | 67 | 68 | # autosummary options 69 | 70 | autosummary_generate = True 71 | autosummary_mock_imports = sphinx_compas2_theme.default_mock_imports 72 | 73 | # graph options 74 | 75 | # plot options 76 | 77 | # intersphinx options 78 | 79 | intersphinx_mapping = { 80 | "python": ("https://docs.python.org/", None), 81 | "compas": ("https://compas.dev/compas/latest/", None), 82 | } 83 | 84 | # linkcode 85 | 86 | # linkcode_resolve = sphinx_compas2_theme.get_linkcode_resolve(organization, package) 87 | 88 | # extlinks 89 | 90 | extlinks = {} 91 | 92 | # from pytorch 93 | 94 | sphinx_compas2_theme.replace(html.HTMLTranslator) 95 | sphinx_compas2_theme.replace(html5.HTML5Translator) 96 | 97 | # -- Options for HTML output ---------------------------------------------- 98 | 99 | html_theme = "sidebaronly" 100 | html_title = project 101 | 102 | favicons = [ 103 | { 104 | "rel": "icon", 105 | "href": "compas.ico", 106 | } 107 | ] 108 | 109 | html_theme_options = { 110 | "icon_links": [ 111 | { 112 | "name": "GitHub", 113 | "url": f"https://github.com/{organization}/{package}", 114 | "icon": "fa-brands fa-github", 115 | "type": "fontawesome", 116 | }, 117 | { 118 | "name": "Discourse", 119 | "url": "http://forum.compas-framework.org/", 120 | "icon": "fa-brands fa-discourse", 121 | "type": "fontawesome", 122 | }, 123 | { 124 | "name": "PyPI", 125 | "url": f"https://pypi.org/project/{package}/", 126 | "icon": "fa-brands fa-python", 127 | "type": "fontawesome", 128 | }, 129 | ], 130 | "switcher": { 131 | "json_url": f"https://raw.githubusercontent.com/{organization}/{package}/gh-pages/versions.json", 132 | "version_match": version, 133 | }, 134 | "check_switcher": False, 135 | "logo": { 136 | "image_light": "_static/compas_icon_white.png", 137 | "image_dark": "_static/compas_icon_white.png", 138 | "text": project, 139 | }, 140 | "navigation_depth": 3, 141 | } 142 | 143 | 144 | html_context = { 145 | "github_url": "https://github.com", 146 | "github_user": organization, 147 | "github_repo": package, 148 | "github_version": "main", 149 | "doc_path": "docs", 150 | } 151 | 152 | html_static_path = sphinx_compas2_theme.get_html_static_path() + ["_static"] 153 | html_css_files = [] 154 | html_extra_path = [] 155 | html_last_updated_fmt = "" 156 | html_copy_source = False 157 | html_show_sourcelink = True 158 | html_permalinks = False 159 | html_permalinks_icon = "" 160 | html_compact_lists = True 161 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Examples 3 | ******************************************************************************** 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | :titlesonly: 8 | 9 | examples/curves/index 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :titlesonly: 14 | 15 | examples/surfaces/index 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | :titlesonly: 20 | 21 | examples/breps/index 22 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_exchange.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.geometry import Box 6 | from compas.geometry import Cylinder 7 | from compas.geometry import Frame 8 | from compas_occ.brep import OCCBrep 9 | 10 | IGES = Path(__file__).parent / "booleans.iges" 11 | STEP = Path(__file__).parent / "booleans.step" 12 | 13 | # ============================================================================= 14 | # Construct boolean Brep 15 | # ============================================================================= 16 | 17 | R = 1.4 18 | YZ = Frame.worldYZ() 19 | ZX = Frame.worldZX() 20 | XY = Frame.worldXY() 21 | 22 | box = OCCBrep.from_box(Box(2 * R)) 23 | cx = OCCBrep.from_cylinder(Cylinder(0.7 * R, 4 * R, frame=YZ)) 24 | cy = OCCBrep.from_cylinder(Cylinder(0.7 * R, 4 * R, frame=ZX)) 25 | cz = OCCBrep.from_cylinder(Cylinder(0.7 * R, 4 * R, frame=XY)) 26 | 27 | brep = box.boolean_union(cx, cy, cz) 28 | 29 | # ============================================================================= 30 | # Write/Read to IGES 31 | # ============================================================================= 32 | 33 | brep.to_iges(IGES) 34 | brep = OCCBrep.from_iges(IGES) 35 | 36 | # ============================================================================= 37 | # Write/Read to STEP 38 | # ============================================================================= 39 | 40 | brep.to_step(STEP) 41 | brep = OCCBrep.from_step(STEP) 42 | 43 | # ============================================================================= 44 | # Visualisation 45 | # ============================================================================= 46 | 47 | viewer = Viewer() 48 | 49 | viewer.renderer.camera.target = [0, 0, 0] 50 | viewer.renderer.camera.position = [4, -6, 2] 51 | 52 | viewer.scene.add(brep, linewidth=2, show_points=False) 53 | 54 | viewer.show() 55 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_explorer.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Box 5 | from compas_occ.brep import OCCBrep 6 | 7 | box = OCCBrep.from_box(Box(1)) 8 | 9 | vertex = box.vertices[0] 10 | vertices = box.vertex_neighbors(vertex) 11 | edges = box.vertex_edges(vertex) 12 | faces = box.vertex_faces(vertex) 13 | 14 | # ============================================================================= 15 | # Visualization 16 | # ============================================================================= 17 | 18 | viewer = Viewer() 19 | 20 | viewer.renderer.camera.target = [0, 0, 0] 21 | viewer.renderer.camera.position = [2, -4, 1] 22 | 23 | viewer.scene.add(vertex.point, pointcolor=Color.red(), pointsize=20) 24 | 25 | for vertex in vertices: 26 | viewer.scene.add(vertex.point, pointsize=20) 27 | 28 | for edge in edges: 29 | viewer.scene.add(edge.to_line(), linewidth=5, linecolor=Color(0.2, 0.2, 0.2)) 30 | 31 | for face in faces: 32 | brep = OCCBrep.from_brepfaces([face]) 33 | viewer.scene.add(brep, opacity=0.5) 34 | 35 | viewer.scene.add(box, linewidth=2, linecolor=Color(0.2, 0.2, 0.2), show_faces=False, show_points=False) 36 | 37 | viewer.show() 38 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_explorer.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Explore Brep Topology 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_explorer.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_explorer.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_fillet.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas_occ.brep import OCCBrep 6 | 7 | # Load the brep from a STEP file 8 | # and extract the individual letters. 9 | 10 | filepath = Path(__file__).parent / "FCA.stp" 11 | brep = OCCBrep.from_step(filepath) 12 | letters = list(brep.solids) 13 | 14 | # Make sure the letters are valid solids. 15 | 16 | for letter in letters: 17 | letter.heal() 18 | letter.make_solid() 19 | 20 | # For each letter, exclude the edges that are too short and the edges connected to it, 21 | # and fillet the rest. 22 | 23 | for letter in letters: 24 | exclude = [] 25 | for loop in letter.loops: 26 | do_fillet = True 27 | for edge in loop.edges: 28 | for vertex in edge.vertices: 29 | if any(e.length < 0.01 for e in letter.vertex_edges(vertex)): 30 | do_fillet = False 31 | break 32 | if not do_fillet: 33 | break 34 | if not do_fillet: 35 | for vertex in loop.vertices: 36 | for edge in letter.vertex_edges(vertex): 37 | exclude.append(edge) 38 | 39 | letter.fillet(0.1, exclude=exclude) 40 | 41 | # ============================================================================= 42 | # Visualization 43 | # ============================================================================= 44 | 45 | viewer = Viewer() 46 | 47 | viewer.renderer.camera.target = [5, 2, 0] 48 | viewer.renderer.camera.position = [5, -1, 10] 49 | 50 | for letter in letters: 51 | viewer.scene.add(letter, linewidth=2, opacity=0.7, show_points=False) 52 | 53 | viewer.show() 54 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_fillet.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Add a Fillet to the Edges of a Brep 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_fillet.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_fillet.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_from_booleans.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.geometry import Box 4 | from compas.geometry import Cylinder 5 | from compas.geometry import Frame 6 | from compas.tolerance import TOL 7 | 8 | # from compas_occ.brep import OCCBrep 9 | 10 | TOL.lineardeflection = 0.1 11 | 12 | R = 1.4 13 | YZ = Frame.worldYZ() 14 | ZX = Frame.worldZX() 15 | XY = Frame.worldXY() 16 | 17 | box = Box(2 * R).to_brep() 18 | cx = Cylinder(0.7 * R, 4 * R, frame=YZ).to_brep() 19 | cy = Cylinder(0.7 * R, 4 * R, frame=ZX).to_brep() 20 | cz = Cylinder(0.7 * R, 4 * R, frame=XY).to_brep() 21 | 22 | # result = OCCBrep.from_boolean_difference(box, [cx, cy, cz]) 23 | result = box - (cx + cy + cz) 24 | 25 | result.to_step("/Users/vanmelet/Desktop/booleans.step") 26 | 27 | # ============================================================================== 28 | # Visualisation 29 | # ============================================================================== 30 | 31 | viewer = Viewer() 32 | 33 | viewer.renderer.camera.target = [0, 0, 0] 34 | viewer.renderer.camera.position = [4, -6, 2] 35 | 36 | viewer.scene.add(result, linewidth=2, show_points=False) 37 | 38 | viewer.show() 39 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_from_booleans.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Brep Shape From Booleans 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_from_booleans.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_from_booleans.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_from_loft.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.geometry import Circle 4 | from compas.geometry import Frame 5 | from compas.tolerance import TOL 6 | from compas_occ.brep import OCCBrep 7 | from compas_occ.geometry import OCCNurbsCurve 8 | 9 | frame = Frame.worldYZ() 10 | c1 = Circle(1.0, frame=frame) 11 | 12 | frame = Frame.worldYZ() 13 | frame.point = [3, 0, 0] 14 | c2 = Circle(3.0, frame=frame) 15 | 16 | frame = Frame.worldYZ() 17 | frame.point = [6, 0, 0] 18 | c3 = Circle(0.5, frame=frame) 19 | 20 | frame = Frame.worldYZ() 21 | frame.point = [9, 0, 0] 22 | c4 = Circle(3.0, frame=frame) 23 | 24 | curves = [ 25 | OCCNurbsCurve.from_circle(c1), 26 | OCCNurbsCurve.from_circle(c2), 27 | OCCNurbsCurve.from_circle(c3), 28 | OCCNurbsCurve.from_circle(c4), 29 | ] 30 | 31 | brep = OCCBrep.from_loft(curves) # type: ignore 32 | 33 | TOL.lineardeflection = 1 34 | 35 | viewer = Viewer() 36 | viewer.scene.add(brep, linewidth=2) 37 | viewer.show() 38 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_from_mesh.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | import compas 4 | from compas.datastructures import Mesh 5 | from compas.geometry import Brep 6 | 7 | # Construct a mesh from an OBJ file 8 | # and convert to a brep 9 | 10 | mesh: Mesh = Mesh.from_obj(compas.get("tubemesh.obj")) 11 | brep = Brep.from_mesh(mesh) 12 | 13 | # ============================================================================= 14 | # Visualization 15 | # ============================================================================= 16 | 17 | viewer = Viewer() 18 | 19 | viewer.renderer.camera.target = [1, 1, 1] 20 | viewer.renderer.camera.position = [1, -6, 2] 21 | 22 | viewer.scene.add(brep, linewidth=2, show_points=False) 23 | 24 | viewer.show() 25 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_from_mesh.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Brep From Mesh 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_from_mesh.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_from_mesh.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_from_shape.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Cylinder 5 | from compas.geometry import Torus 6 | 7 | cylinder = Cylinder(radius=1.0, height=2.0).to_brep() 8 | torus = Torus(radius_axis=1.5, radius_pipe=0.3).to_brep() 9 | 10 | # ============================================================================= 11 | # Visualization 12 | # ============================================================================= 13 | 14 | viewer = Viewer() 15 | 16 | viewer.renderer.camera.target = [0, 0, 0] 17 | viewer.renderer.camera.position = [2, -4, 1] 18 | 19 | viewer.scene.add(cylinder, opacity=0.9, show_points=False, linewidth=2) 20 | viewer.scene.add(torus, opacity=0.9, show_points=False, linewidth=2, facecolor=Color.green()) 21 | 22 | viewer.show() 23 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_intersections.py: -------------------------------------------------------------------------------- 1 | # type: ignore 2 | from compas_viewer import Viewer 3 | 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Sphere 7 | from compas_occ.brep import OCCBrep 8 | 9 | points = [ 10 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 11 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 12 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 13 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 14 | ] 15 | 16 | surface = OCCBrep.from_surface(NurbsSurface.from_points(points=points)) 17 | sphere = OCCBrep.from_sphere(Sphere(radius=1)) 18 | 19 | x = surface.intersect(sphere) 20 | assert x, "..." 21 | 22 | curves = [] 23 | for edge in x.edges: 24 | curves.append(edge.curve) 25 | 26 | # ============================================================================= 27 | # Visualization 28 | # ============================================================================= 29 | 30 | viewer = Viewer() 31 | viewer.scene.add(surface, linewidth=2, show_points=False, opacity=0.5) 32 | viewer.scene.add(sphere, linewidth=2, show_points=False, opacity=0.5) 33 | viewer.scene.add(curves, linewidth=2) 34 | viewer.show() 35 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_overlap.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Box 5 | from compas.geometry import Brep 6 | 7 | A = Box(1).to_brep() 8 | 9 | box = Box(1) 10 | box.translate([1, 0.3, 0.5]) 11 | B = Brep.from_box(box) 12 | 13 | FA, FB = A.overlap(B) # type: ignore 14 | 15 | # ============================================================================= 16 | # Visualization 17 | # ============================================================================= 18 | 19 | viewer = Viewer() 20 | 21 | viewer.renderer.camera.target = [-1, 2, 0] 22 | viewer.renderer.camera.position = [3, -3, 1] 23 | 24 | viewer.scene.add(A, opacity=0.5, linewidth=3) 25 | viewer.scene.add(B, opacity=0.5, linewidth=3) 26 | 27 | for face in FA[:1]: 28 | brep = Brep.from_brepfaces([face]) 29 | viewer.scene.add( 30 | brep, 31 | surfacecolor=Color.red().lightened(50), 32 | linewidth=3, 33 | linecolor=Color.red(), 34 | ) 35 | 36 | for face in FB[:1]: 37 | brep = Brep.from_brepfaces([face]) 38 | viewer.scene.add( 39 | brep, 40 | surfacecolor=Color.blue().lightened(50), 41 | linewidth=3, 42 | linecolor=Color.blue(), 43 | ) 44 | 45 | viewer.show() 46 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_overlap.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Find the Overlap Between Two Breps 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_overlap.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_overlap.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_serialisation.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | import compas 4 | from compas.geometry import Box 5 | from compas.geometry import Circle 6 | from compas.geometry import Cylinder 7 | from compas.geometry import Frame 8 | from compas.geometry import NurbsCurve 9 | from compas.geometry import NurbsSurface 10 | from compas.geometry import Plane 11 | from compas.geometry import Point 12 | from compas.geometry import Sphere 13 | from compas.geometry import Vector 14 | from compas_occ.brep import OCCBrep 15 | from compas_occ.brep import OCCBrepEdge 16 | from compas_occ.brep import OCCBrepFace 17 | from compas_occ.brep import OCCBrepLoop 18 | 19 | 20 | def brep_with_hole(): 21 | points = [ 22 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 23 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 24 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 25 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 26 | ] 27 | surface = NurbsSurface.from_points(points=points) 28 | circle = Circle( 29 | 0.5, 30 | frame=Frame( 31 | Point(1.5, 1.5, 1.5), 32 | Vector(1, 0, 0), 33 | Vector(0, 1, 0), 34 | ), 35 | ) 36 | curve = NurbsCurve.from_circle(circle) 37 | edge = OCCBrepEdge.from_curve_and_surface(curve=curve, surface=surface) 38 | loop = OCCBrepLoop.from_edges([edge]) 39 | face = OCCBrepFace.from_surface(surface) 40 | face.add_loop(loop) 41 | brep = OCCBrep.from_brepfaces([face]) 42 | return brep 43 | 44 | 45 | def brep_with_holes(): 46 | circle1 = Circle(1.0, frame=Frame([2, 2, 0])) 47 | circle2 = Circle(2.0, frame=Frame([-2, -2, 0])) 48 | circle3 = Circle(0.5, frame=Frame([2, -2, 0])) 49 | 50 | loop1 = OCCBrepLoop.from_edges([OCCBrepEdge.from_circle(circle1)]) 51 | loop2 = OCCBrepLoop.from_edges([OCCBrepEdge.from_circle(circle2)]) 52 | loop3 = OCCBrepLoop.from_edges([OCCBrepEdge.from_circle(circle3)]) 53 | 54 | face = OCCBrepFace.from_plane(Plane.worldXY(), domain_u=(-5, 5), domain_v=(-5, 5)) 55 | face.add_loops([loop1, loop2, loop3], reverse=True) 56 | 57 | brep = OCCBrep.from_brepfaces([face]) 58 | return brep 59 | 60 | 61 | def brep_from_booleans(): 62 | R = 1.4 63 | 64 | box = Box(2 * R).to_brep() 65 | sphere = Sphere(radius=1.25 * R).to_brep() 66 | 67 | cylx = Cylinder(radius=0.7 * R, height=3 * R, frame=Frame.worldYZ()).to_brep() 68 | cyly = Cylinder(radius=0.7 * R, height=3 * R, frame=Frame.worldZX()).to_brep() 69 | cylz = Cylinder(radius=0.7 * R, height=3 * R, frame=Frame.worldXY()).to_brep() 70 | 71 | brep = OCCBrep.from_boolean_intersection(box, sphere) 72 | brep = OCCBrep.from_boolean_difference(brep, cylz) 73 | brep = OCCBrep.from_boolean_difference(brep, cylx) 74 | brep = OCCBrep.from_boolean_difference(brep, cyly) 75 | return brep 76 | 77 | 78 | # ============================================================================= 79 | # Dump/Load 80 | # ============================================================================= 81 | 82 | # brep = OCCBrep.from_box(Box(1)) 83 | # brep = OCCBrep.from_sphere(Sphere(1.0)) 84 | # brep = OCCBrep.from_cylinder(Cylinder(1.0, 2.0)) 85 | brep = brep_from_booleans() 86 | # brep = brep_with_hole() 87 | # brep = brep_with_holes() 88 | 89 | brep: OCCBrep = compas.json_loads(compas.json_dumps(brep)) # type: ignore 90 | 91 | # ============================================================================= 92 | # Viz 93 | # ============================================================================= 94 | 95 | viewer = Viewer() 96 | viewer.scene.add(brep) 97 | viewer.show() 98 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_slice.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.geometry import Box 6 | from compas.geometry import Plane 7 | from compas.geometry import Rotation 8 | 9 | box = Box(1).to_brep() 10 | 11 | plane = Plane.worldXY() 12 | R = Rotation.from_axis_and_angle([0, 1, 0], radians(30)) 13 | plane.transform(R) 14 | 15 | slice = box.slice(plane) 16 | 17 | # ============================================================================= 18 | # Visualization 19 | # ============================================================================= 20 | 21 | viewer = Viewer() 22 | 23 | viewer.renderer.camera.target = [0, 0, 0] 24 | viewer.renderer.camera.position = [2, -4, 1] 25 | 26 | viewer.scene.add(box, opacity=0.5, show_points=False) 27 | viewer.scene.add(slice, linewidth=2) 28 | 29 | viewer.show() 30 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_slice.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Slice a Brep With a Plane 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_slice.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_slice.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_split.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.colors import Color 6 | from compas.geometry import Box 7 | from compas.geometry import Brep 8 | from compas.geometry import Plane 9 | from compas.geometry import Rotation 10 | from compas.geometry import is_point_infrontof_plane 11 | 12 | box = Box(1).to_brep() 13 | 14 | R = Rotation.from_axis_and_angle([0, 1, 0], radians(30)) 15 | plane = Plane.worldXY() 16 | plane.transform(R) 17 | splitter = Brep.from_plane(plane, domain_u=(-2, +2), domain_v=(-2, +2)) 18 | 19 | result = box.split(splitter) 20 | 21 | # ============================================================================= 22 | # Visualization 23 | # ============================================================================= 24 | 25 | viewer = Viewer() 26 | 27 | viewer.renderer.camera.target = [0, 0, 0] 28 | viewer.renderer.camera.position = [2, -4, 1] 29 | 30 | viewer.scene.add(splitter, linewidth=2, opacity=0.3) 31 | 32 | for brep in result: # type: ignore 33 | if is_point_infrontof_plane(brep.centroid, plane): 34 | viewer.scene.add( 35 | brep, 36 | surfacecolor=Color.red().lightened(50), 37 | linecolor=Color.red(), 38 | linewidth=2, 39 | show_points=False, 40 | ) 41 | else: 42 | viewer.scene.add( 43 | brep, 44 | surfacecolor=Color.blue().lightened(50), 45 | linecolor=Color.blue(), 46 | linewidth=2, 47 | show_points=False, 48 | ) 49 | 50 | viewer.show() 51 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_split.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Split a Brep With a Plane 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_split.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_split.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_trim.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.geometry import Box 6 | from compas.geometry import Plane 7 | from compas.geometry import Rotation 8 | 9 | box = Box(1).to_brep() 10 | 11 | R = Rotation.from_axis_and_angle([0, 1, 0], radians(30)) 12 | plane = Plane.worldXY() 13 | plane.transform(R) 14 | 15 | trimmed = box.trimmed(plane) 16 | 17 | # ============================================================================= 18 | # Visualization 19 | # ============================================================================= 20 | 21 | viewer = Viewer() 22 | 23 | viewer.renderer.camera.target = [0, 0, 0] 24 | viewer.renderer.camera.position = [2, -4, 1] 25 | 26 | viewer.scene.add(plane, opacity=0.5) 27 | viewer.scene.add(trimmed, linewidth=2, show_points=False) 28 | viewer.scene.add(box, linewidth=1, show_points=False, show_faces=False) 29 | 30 | viewer.show() 31 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_trim.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Trim a Brep With a Plane 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_trimmed.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_trim.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_with_hole.py: -------------------------------------------------------------------------------- 1 | # type: ignore 2 | # this should be included in the compas API 3 | from compas_viewer import Viewer 4 | 5 | from compas.geometry import Brep 6 | from compas.geometry import Circle 7 | from compas.geometry import Frame 8 | from compas.geometry import NurbsCurve 9 | from compas.geometry import NurbsSurface 10 | from compas.geometry import Point 11 | from compas.geometry import Vector 12 | from compas_occ.brep import OCCBrepEdge 13 | from compas_occ.brep import OCCBrepFace 14 | from compas_occ.brep import OCCBrepLoop 15 | 16 | points = [ 17 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 18 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 19 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 20 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 21 | ] 22 | 23 | surface = NurbsSurface.from_points(points=points) 24 | 25 | circle = Circle( 26 | 0.5, 27 | frame=Frame( 28 | Point(1.5, 1.5, 1.5), 29 | Vector(1, 0, 0), 30 | Vector(0, 1, 0), 31 | ), 32 | ) 33 | 34 | # projected is still 3D 35 | # embedded is 2D 36 | # and the 2D curve should keep track of the embedding surface 37 | curve = NurbsCurve.from_circle(circle) 38 | 39 | edge = OCCBrepEdge.from_curve(curve=curve, surface=surface) 40 | loop = OCCBrepLoop.from_edges([edge]) 41 | 42 | # perhaps this should be: 43 | # face = OCCBrepFace() 44 | # face.set_surface(surface) 45 | # face.add_boundary(loop) => if the loop edges are not embedded in the surface, they should be 46 | # face.add_hole(loop) => if the loop edges ... 47 | face = OCCBrepFace.from_surface(surface) 48 | face.add_loop(loop) 49 | 50 | brep = Brep.from_brepfaces([face]) 51 | 52 | # ============================================================================= 53 | # Visualization 54 | # ============================================================================= 55 | 56 | viewer = Viewer() 57 | viewer.scene.add(brep, linewidth=2, show_points=False) 58 | viewer.show() 59 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_with_hole.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Brep with Hole 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_with_hole.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_with_hole.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_with_holes.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.geometry import Brep 4 | from compas.geometry import Circle 5 | from compas.geometry import Frame 6 | from compas.geometry import Plane 7 | from compas_occ.brep import OCCBrepEdge 8 | from compas_occ.brep import OCCBrepFace 9 | from compas_occ.brep import OCCBrepLoop 10 | 11 | circle1 = Circle(1.0, frame=Frame([2, 2, 0])) 12 | circle2 = Circle(2.0, frame=Frame([-2, -2, 0])) 13 | circle3 = Circle(0.5, frame=Frame([2, -2, 0])) 14 | 15 | loop1 = OCCBrepLoop.from_edges([OCCBrepEdge.from_circle(circle1)]) 16 | loop2 = OCCBrepLoop.from_edges([OCCBrepEdge.from_circle(circle2)]) 17 | loop3 = OCCBrepLoop.from_edges([OCCBrepEdge.from_circle(circle3)]) 18 | 19 | face = OCCBrepFace.from_plane(Plane.worldXY(), domain_u=(-5, 5), domain_v=(-5, 5)) 20 | face.add_loops([loop1, loop2, loop3], reverse=True) 21 | 22 | brep = Brep.from_brepfaces([face]) 23 | 24 | # ============================================================================= 25 | # Visualization 26 | # ============================================================================= 27 | 28 | viewer = Viewer() 29 | viewer.scene.add(brep, linewidth=2, show_point=False) 30 | viewer.show() 31 | -------------------------------------------------------------------------------- /docs/examples/breps/brep_with_holes.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Brep with Holes 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_brep_with_holes.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: brep_with_holes.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/breps/index.rst: -------------------------------------------------------------------------------- 1 | Breps 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | :titlesonly: 7 | :glob: 8 | 9 | * -------------------------------------------------------------------------------- /docs/examples/curves/curve_closest_parameters_curve.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | 7 | curve0: NurbsCurve 8 | curve1: NurbsCurve 9 | 10 | points0 = [Point(0, 0, 0), Point(3, 6, 0), Point(6, -3, 3), Point(10, 0, 0)] 11 | curve0 = NurbsCurve.from_points(points0) 12 | 13 | points1 = [Point(6, -3, 0), Point(3, 1, 0), Point(6, 6, 3), Point(3, 12, 0)] 14 | curve1 = NurbsCurve.from_points(points1) 15 | 16 | parameters, distance = curve0.closest_parameters_curve(curve1, return_distance=True) # type: ignore 17 | points = curve0.closest_points_curve(curve1, return_distance=False) 18 | 19 | # ============================================================================= 20 | # Visualization 21 | # ============================================================================= 22 | 23 | viewer = Viewer() 24 | 25 | viewer.scene.add(curve0, linewidth=3) 26 | viewer.scene.add(curve1, linewidth=3) 27 | 28 | viewer.scene.add(list(points), pointcolor=Color(1, 0, 0), pointsize=20) 29 | 30 | viewer.show() 31 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_closest_parameters_curve.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Closest Parameters Curve 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_closest_parameters_curve.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: curve_closest_parameters_curve.py 10 | :language: python 11 | 12 | .. .. code-block:: text 13 | 14 | .. (0.47576829374770674, 0.336691471561402) 15 | .. 0.43417581808963873 16 | .. (Point(4.390, 1.286, 1.068), Point(4.552, 1.380, 0.677)) 17 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_closest_point.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | 7 | points = [Point(0, 0, 0), Point(3, 0, 2), Point(6, 0, -3), Point(8, 0, 0)] 8 | curve = NurbsCurve.from_interpolation(points) 9 | 10 | point = Point(2, -1, 0) 11 | closest, t = curve.closest_point(point, return_parameter=True) # type: ignore 12 | 13 | # ============================================================================= 14 | # Visualization 15 | # ============================================================================= 16 | 17 | viewer = Viewer() 18 | 19 | viewer.scene.add(curve, linewidth=3) 20 | viewer.scene.add(point, pointcolor=Color(0, 0, 1), pointsize=20, name="Point") 21 | viewer.scene.add(closest, pointcolor=Color(1, 0, 0), pointsize=20, name="Closest") 22 | 23 | viewer.show() 24 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_closest_point.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Closest Point 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_closest_point.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: curve_closest_point.py 10 | :language: python 11 | 12 | .. .. code-block:: text 13 | 14 | .. True 15 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_comparison1.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Bezier 5 | from compas.geometry import NurbsCurve 6 | from compas.geometry import Point 7 | from compas.geometry import Polyline 8 | 9 | points = [Point(0, 0, 0), Point(1, 3, 0), Point(2, 0, 0)] 10 | bezier = Bezier(points) 11 | 12 | points = [Point(3, 0, 0), Point(4, 3, 0), Point(5, 0, 0)] 13 | 14 | curve1 = NurbsCurve.from_parameters( 15 | points=points, 16 | weights=[1.0, 1.0, 1.0], 17 | knots=[0.0, 1.0], 18 | multiplicities=[3, 3], 19 | degree=2, 20 | ) 21 | 22 | curve2 = NurbsCurve.from_parameters( 23 | points=points, 24 | weights=[1.0, 2.0, 1.0], 25 | knots=[0.0, 1.0], 26 | multiplicities=[3, 3], 27 | degree=2, 28 | ) 29 | 30 | curve3 = NurbsCurve.from_parameters( 31 | points=points, 32 | weights=[1.0, 1.0, 1.0], 33 | knots=[0.0, 1.0, 2.0, 3.0, 4.0, 5.0], 34 | multiplicities=[1, 1, 1, 1, 1, 1], 35 | degree=2, 36 | ) 37 | 38 | # ============================================================================== 39 | # Visualization 40 | # ============================================================================== 41 | 42 | viewer = Viewer() 43 | viewer.renderer.view = "top" 44 | 45 | viewer.scene.add( 46 | Polyline(bezier.points), 47 | show_points=True, 48 | pointsize=20, 49 | pointcolor=Color.red(), 50 | linewidth=1, 51 | linecolor=Color(0.3, 0.3, 0.3), 52 | ) 53 | viewer.scene.add(bezier.to_polyline(), linewidth=5, linecolor=Color(0, 0, 0), show_points=False) 54 | 55 | viewer.scene.add( 56 | Polyline(curve1.points), 57 | show_points=True, 58 | pointsize=20, 59 | pointcolor=Color.red(), 60 | linewidth=1, 61 | linecolor=Color(0.3, 0.3, 0.3), 62 | ) 63 | viewer.scene.add(curve1, linewidth=5, linecolor=Color(0.0, 0.0, 0.0)) 64 | viewer.scene.add(curve2, linewidth=5, linecolor=Color(0.0, 1.0, 1.0)) 65 | viewer.scene.add(curve3, linewidth=5, linecolor=Color(0.0, 0.0, 1.0)) 66 | 67 | viewer.show() 68 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_comparison1.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Comparison 1 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_comparison1.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: curve_comparison1.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_comparison2.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Bezier 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | from compas_occ.geometry import OCCNurbsCurve 8 | 9 | points = [Point(0, 0, 0), Point(1, 2, 0), Point(2, -2, 0), Point(3, 0, 0)] 10 | bezier = Bezier(points) 11 | 12 | points = [Point(4, 0, 0), Point(5, 2, 0), Point(6, -2, 0), Point(7, 0, 0)] 13 | 14 | curve1 = OCCNurbsCurve.from_parameters( 15 | points=points, 16 | weights=[1.0, 1.0, 1.0, 1.0], 17 | knots=[0.0, 1.0], 18 | multiplicities=[4, 4], 19 | degree=3, 20 | ) 21 | 22 | curve2 = OCCNurbsCurve.from_parameters( 23 | points=points, 24 | weights=[1.0, 2.0, 2.0, 1.0], 25 | knots=[0.0, 1.0], 26 | multiplicities=[4, 4], 27 | degree=3, 28 | ) 29 | 30 | curve3 = OCCNurbsCurve.from_parameters( 31 | points=points, 32 | weights=[1.0, 1.0, 1.0, 1.0], 33 | knots=[0.0, 1 / 3, 2 / 3, 1.0], 34 | multiplicities=[3, 1, 1, 3], 35 | degree=3, 36 | ) 37 | 38 | curve4 = OCCNurbsCurve.from_parameters( 39 | points=points, 40 | weights=[1.0, 1.0, 1.0, 1.0], 41 | knots=[0.0, 1 / 5, 2 / 5, 3 / 5, 4 / 5, 1.0], 42 | multiplicities=[2, 1, 1, 1, 1, 2], 43 | degree=3, 44 | ) 45 | 46 | curve5 = OCCNurbsCurve.from_parameters( 47 | points=points, 48 | weights=[1.0, 1.0, 1.0, 1.0], 49 | knots=[0.0, 1 / 7, 2 / 7, 3 / 7, 4 / 7, 5 / 7, 6 / 7, 1.0], 50 | multiplicities=[1, 1, 1, 1, 1, 1, 1, 1], 51 | degree=3, 52 | ) 53 | 54 | curve6 = OCCNurbsCurve.from_parameters( 55 | points=points, 56 | weights=[1.0, 1.0, 1.0, 1.0], 57 | knots=[0.0, 0.5, 1.0], 58 | multiplicities=[3, 1, 3], 59 | degree=2, 60 | ) 61 | 62 | # ============================================================================== 63 | # Visualization 64 | # ============================================================================== 65 | 66 | viewer = Viewer() 67 | viewer.renderer.view = "top" 68 | 69 | viewer.scene.add( 70 | Polyline(bezier.points), 71 | show_points=True, 72 | pointsize=20, 73 | pointcolor=Color.red(), 74 | linewidth=1, 75 | linecolor=Color(0.3, 0.3, 0.3), 76 | ) 77 | viewer.scene.add(bezier.to_polyline(), linewidth=5, linecolor=Color(0, 0, 0)) 78 | 79 | viewer.scene.add( 80 | Polyline(curve1.points), 81 | show_points=True, 82 | pointsize=20, 83 | pointcolor=Color.red(), 84 | linewidth=1, 85 | linecolor=Color(0.3, 0.3, 0.3), 86 | ) 87 | viewer.scene.add(curve1, linewidth=5, linecolor=Color(0, 0, 0)) 88 | viewer.scene.add(curve2, linewidth=3, linecolor=Color.blue()) 89 | viewer.scene.add(curve3, linewidth=3, linecolor=Color(0.2, 0.2, 1.0)) 90 | viewer.scene.add(curve4, linewidth=3, linecolor=Color(0.4, 0.4, 1.0)) 91 | viewer.scene.add(curve5, linewidth=3, linecolor=Color(0.6, 0.6, 1.0)) 92 | viewer.scene.add(curve6, linewidth=3, linecolor=Color(0.8, 0.8, 1.0)) 93 | 94 | viewer.show() 95 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_comparison2.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Comparison 2 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_comparison2.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: curve_comparison2.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_divide.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.geometry import NurbsCurve 4 | from compas.geometry import Point 5 | from compas.itertools import pairwise 6 | 7 | points = [Point(0, 0, 0), Point(3, -6, 0), Point(6, 2, 0), Point(9, -2, 0)] 8 | curve = NurbsCurve.from_points(points) 9 | 10 | N = 10 11 | params, points = curve.divide(N, return_points=True) # type: ignore 12 | 13 | for u, v in pairwise(params): 14 | segment = curve.segmented(u, v) 15 | print(segment.length()) 16 | 17 | # ============================================================================= 18 | # Visualization 19 | # ============================================================================= 20 | 21 | viewer = Viewer() 22 | viewer.renderer.view = "top" 23 | 24 | viewer.scene.add(curve, linewidth=2) 25 | viewer.scene.add(points, pointsize=20) 26 | viewer.show() 27 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_divide.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Divide 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_divide.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: curve_divide.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_circle.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Circle 5 | from compas.geometry import NurbsCurve 6 | from compas.geometry import Polyline 7 | 8 | circle = Circle(1.0) 9 | curve = NurbsCurve.from_circle(circle) 10 | 11 | # ============================================================================== 12 | # Visualisation 13 | # ============================================================================== 14 | 15 | viewer = Viewer() 16 | viewer.renderer.view = "top" 17 | 18 | viewer.scene.add(curve, linewidth=3) 19 | viewer.scene.add( 20 | Polyline(curve.points), 21 | show_points=True, 22 | pointsize=20, 23 | pointcolor=Color.red(), 24 | linewidth=1, 25 | linecolor=Color(0.3, 0.3, 0.3), 26 | ) 27 | 28 | viewer.show() 29 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_circle.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve From Circle 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_from_circle.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: curve_from_circle.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_ellipse.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Ellipse 5 | from compas.geometry import NurbsCurve 6 | from compas.geometry import Polyline 7 | 8 | ellipse = Ellipse(2.0, 1.0) 9 | curve = NurbsCurve.from_ellipse(ellipse) 10 | 11 | # ============================================================================== 12 | # Visualisation 13 | # ============================================================================== 14 | 15 | viewer = Viewer() 16 | viewer.renderer.view = "top" 17 | 18 | viewer.scene.add(curve, linewidth=3) 19 | viewer.scene.add( 20 | Polyline(curve.points), 21 | show_points=True, 22 | pointsize=20, 23 | pointcolor=Color.red(), 24 | linewidth=1, 25 | linecolor=Color(0.3, 0.3, 0.3), 26 | ) 27 | 28 | viewer.show() 29 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_ellipse.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve From Ellipse 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_from_ellipse.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: curve_from_ellipse.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_interpolation.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.geometry import Bezier 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | 7 | points = [Point(0, 0, 0), Point(3, 6, 0), Point(6, -3, 3), Point(10, 0, 0)] 8 | bezier = Bezier(points) 9 | points = bezier.to_points(10) 10 | 11 | curve = NurbsCurve.from_interpolation(points) 12 | 13 | # ============================================================================== 14 | # Visualisation 15 | # ============================================================================== 16 | 17 | viewer = Viewer() 18 | 19 | viewer.scene.add(curve, linewidth=2) 20 | viewer.scene.add(points, pointsize=20) 21 | 22 | viewer.show() 23 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_interpolation.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve From Interpolation 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_from_interpolation.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: curve_from_interpolation.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_line.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Line 5 | from compas.geometry import NurbsCurve 6 | from compas.geometry import Point 7 | 8 | line = Line(Point(0, 0, 0), Point(3, 3, 0)) 9 | curve = NurbsCurve.from_line(line) 10 | 11 | # ============================================================================== 12 | # Visualisation 13 | # ============================================================================== 14 | 15 | viewer = Viewer() 16 | viewer.renderer.view = "top" 17 | 18 | viewer.scene.add(curve, linewidth=3) 19 | viewer.scene.add(curve.points, pointsize=20, pointcolor=Color(1, 0, 0)) 20 | 21 | viewer.show() 22 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_line.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve From Line 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_from_line.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | 10 | .. literalinclude:: curve_from_line.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_parameters.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | 8 | points = [Point(0, 0, 0), Point(3, 6, 0), Point(6, -3, 3), Point(10, 0, 0)] 9 | 10 | curve = NurbsCurve.from_parameters( 11 | points=points, 12 | weights=[1.0, 1.0, 1.0, 1.0], 13 | knots=[0.0, 1.0], 14 | multiplicities=[4, 4], 15 | degree=3, 16 | ) 17 | 18 | # ============================================================================== 19 | # Visualisation 20 | # ============================================================================== 21 | 22 | viewer = Viewer() 23 | 24 | viewer.scene.add(curve, linewidth=3) 25 | 26 | viewer.scene.add( 27 | Polyline(curve.points), 28 | show_points=True, 29 | pointsize=20, 30 | pointcolor=Color.red(), 31 | linewidth=1, 32 | linecolor=Color(0.3, 0.3, 0.3), 33 | ) 34 | 35 | viewer.show() 36 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_parameters.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve From Parameters 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_from_parameters.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: curve_from_parameters.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_points.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | 8 | points = [Point(0, 0, 0), Point(3, 6, 0), Point(6, -3, 3), Point(10, 0, 0)] 9 | 10 | curve = NurbsCurve.from_points(points) 11 | 12 | # ============================================================================== 13 | # Visualisation 14 | # ============================================================================== 15 | 16 | viewer = Viewer() 17 | 18 | viewer.scene.add(curve, linewidth=3) 19 | 20 | viewer.scene.add( 21 | Polyline(curve.points), 22 | show_points=True, 23 | pointsize=20, 24 | pointcolor=Color.red(), 25 | linewidth=1, 26 | linecolor=Color(0.3, 0.3, 0.3), 27 | ) 28 | 29 | viewer.show() 30 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_from_points.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve From Control Points 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_from_poles.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: curve_from_points.py 10 | :language: python 11 | 12 | .. .. code-block:: python 13 | 14 | .. print(curve) 15 | 16 | .. .. code-block:: text 17 | 18 | .. BSplineCurve 19 | .. ------------ 20 | .. Poles: [Point(0.000, 0.000, 0.000), Point(3.000, 6.000, 0.000), Point(6.000, -3.000, 3.000), Point(10.000, 0.000, 0.000)] 21 | .. Knots: [0.0, 1.0] 22 | .. Mults: [4, 4] 23 | .. Degree: 3 24 | .. Order: 4 25 | .. Domain: (0.0, 1.0) 26 | .. Closed: False 27 | .. Periodic: False 28 | .. Rational: False 29 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_joining.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | 7 | points1 = [Point(0, 0, 0), Point(1, 1, 0), Point(3, 0, 0)] 8 | points2 = [Point(3, 0, 0), Point(4, -2, 0), Point(5, 0, 0)] 9 | 10 | curve1 = NurbsCurve.from_interpolation(points1) 11 | curve2 = NurbsCurve.from_interpolation(points2) 12 | 13 | joined = curve1.joined(curve2) 14 | 15 | # ============================================================================== 16 | # Visualisation 17 | # ============================================================================== 18 | 19 | viewer = Viewer() 20 | 21 | viewer.scene.add(curve1, linewidth=3, linecolor=Color.red(), name="Red") 22 | viewer.scene.add(curve2, linewidth=3, linecolor=Color.green(), name="Green") 23 | viewer.scene.add(joined, linewidth=3, linecolor=Color.blue(), name="Blue") 24 | 25 | viewer.show() 26 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_joining.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Joining 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_curve_joining_separate.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. figure:: /_images/example_curve_joining_joined.png 10 | :figclass: figure 11 | :class: figure-img img-fluid 12 | 13 | .. literalinclude:: curve_joining.py 14 | :language: python 15 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_segmentation.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | 7 | pointsA = [Point(0, 0, 0), Point(3, 6, 0), Point(6, -3, 3), Point(10, 0, 0)] 8 | curveA = NurbsCurve.from_points(pointsA) 9 | 10 | curveA.segment(u=0.2, v=0.5) 11 | 12 | print(curveA.domain) 13 | 14 | pointsB = [Point(0, -1, 0), Point(3, 5, 0), Point(6, -4, 3), Point(10, -1, 0)] 15 | curveB = NurbsCurve.from_points(pointsB) 16 | 17 | segment = curveB.segmented(u=0.2, v=0.5) 18 | 19 | print(curveB.domain) 20 | print(segment.domain) 21 | 22 | # ============================================================================== 23 | # Visualisation 24 | # ============================================================================== 25 | 26 | viewer = Viewer() 27 | 28 | viewer.scene.add(curveA, linewidth=4, linecolor=Color.red()) 29 | viewer.scene.add(curveB, linewidth=1, linecolor=Color(0, 0, 0)) 30 | viewer.scene.add(segment, linewidth=4, linecolor=Color.green()) 31 | 32 | viewer.show() 33 | -------------------------------------------------------------------------------- /docs/examples/curves/curve_segmentation.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Curve Segmentation 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_segmentation.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: curve_segmentation.py 10 | :language: python 11 | 12 | .. .. code-block:: text 13 | 14 | .. (0.2, 0.5) 15 | 16 | .. (0.0, 1.0) 17 | .. (0.2, 0.5) 18 | -------------------------------------------------------------------------------- /docs/examples/curves/index.rst: -------------------------------------------------------------------------------- 1 | Curves 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | :titlesonly: 7 | :glob: 8 | 9 | * -------------------------------------------------------------------------------- /docs/examples/surfaces/__temp/surface_from_extrusion-1.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import Circle 5 | from compas.geometry import NurbsCurve 6 | from compas.geometry import NurbsSurface 7 | from compas.geometry import Vector 8 | 9 | curve = NurbsCurve.from_circle(Circle(2.0)) 10 | 11 | surface = NurbsSurface.from_extrusion(curve, Vector(0, 0, 5)) 12 | 13 | # ============================================================================= 14 | # Visualisation 15 | # ============================================================================= 16 | 17 | viewer = Viewer() 18 | 19 | viewer.renderer.camera.target = [0, 0, 2] 20 | viewer.renderer.camera.position = [-5, -10, 4] 21 | 22 | viewer.scene.add(curve, linewidth=5, linecolor=Color.red()) 23 | viewer.scene.add(surface) 24 | viewer.show() 25 | -------------------------------------------------------------------------------- /docs/examples/surfaces/__temp/surface_from_extrusion-1.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface From Extrusion 1 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_from_extrusion-1.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_from_extrusion-1.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/__temp/surface_from_extrusion-2.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.geometry import NurbsCurve 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Vector 7 | 8 | points = [Point(0, 0, 0), Point(0, -6, 3), Point(0, 2, 6), Point(0, -2, 9)] 9 | curve = NurbsCurve.from_points(points) 10 | vector = Vector(5, 0, 0) 11 | 12 | surface = NurbsSurface.from_extrusion(curve, vector) 13 | 14 | # ============================================================================= 15 | # Visualisation 16 | # ============================================================================= 17 | 18 | viewer = Viewer() 19 | 20 | viewer.renderer.camera.target = [2, 0, 5] 21 | viewer.renderer.camera.position = [-7, -10, 6] 22 | 23 | viewer.scene.add(curve.to_polyline(), linewidth=5, linecolor=(1, 0, 0)) 24 | viewer.scene.add(surface) 25 | viewer.show() 26 | -------------------------------------------------------------------------------- /docs/examples/surfaces/__temp/surface_from_extrusion-2.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface From Extrusion 2 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_from_extrusion-2.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_from_extrusion-2.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/__temp/surface_of_revolution.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import Point 6 | from compas.geometry import Vector 7 | from compas_occ.geometry import OCCRevolutionSurface 8 | 9 | points = [Point(0, 0, 0), Point(0, -6, 3), Point(0, 2, 6), Point(0, -2, 9)] 10 | curve = NurbsCurve.from_points(points) 11 | point = Point(0, 0, 0) 12 | vector = Vector(0, 0, 1) 13 | 14 | # TODO: TypeError: __init__() missing 1 required positional argument: 'occ_surface' 15 | surface = OCCRevolutionSurface(curve, point=point, vector=vector) 16 | 17 | # ============================================================================= 18 | # Visualisation 19 | # ============================================================================= 20 | 21 | viewer = Viewer() 22 | # viewer.renderer.camera.position = [-5, -10, 7] 23 | # viewer.renderer.camera.target = [0, 0, 5] 24 | 25 | viewer.scene.add(curve.to_polyline(), linewidth=5, linecolor=Color.red()) 26 | viewer.scene.add(surface.to_mesh()) 27 | viewer.show() 28 | -------------------------------------------------------------------------------- /docs/examples/surfaces/index.rst: -------------------------------------------------------------------------------- 1 | Surfaces 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | :titlesonly: 7 | :glob: 8 | 9 | * -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_aabb.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.colors import Color 6 | from compas.geometry import NurbsSurface 7 | from compas.geometry import Point 8 | from compas.geometry import Polyline 9 | from compas.geometry import Rotation 10 | from compas.geometry import Translation 11 | 12 | points = [ 13 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0), Point(4, 0, 0)], 14 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0), Point(4, 1, 0)], 15 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0), Point(4, 2, 0)], 16 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0), Point(4, 3, 0)], 17 | ] 18 | 19 | surface = NurbsSurface.from_points(points=points) 20 | 21 | T = Translation.from_vector([0, -1.5, 0]) 22 | R = Rotation.from_axis_and_angle([0, 0, 1], radians(45)) 23 | 24 | surface.transform(R * T) 25 | 26 | # ============================================================================== 27 | # AABB 28 | # ============================================================================== 29 | 30 | box = surface.aabb() 31 | 32 | # ============================================================================== 33 | # Visualisation 34 | # ============================================================================== 35 | 36 | viewer = Viewer() 37 | 38 | points = list(surface.points) 39 | u_direction = [Polyline(row) for row in points] 40 | v_direction = [Polyline(col) for col in zip(*points)] 41 | 42 | viewer.scene.add( 43 | u_direction, 44 | linewidth=2, 45 | linecolor=Color.red(), 46 | ) 47 | viewer.scene.add( 48 | v_direction, 49 | linewidth=2, 50 | linecolor=Color.green(), 51 | ) 52 | viewer.scene.add(points, pointsize=20) 53 | 54 | 55 | viewer.scene.add(surface, show_lines=False) 56 | viewer.scene.add(box, show_faces=False) 57 | 58 | viewer.show() 59 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_aabb.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface Axis-Aligned Bounding Box 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_aabb.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_aabb.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_frames.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | from compas_viewer.scene import Collection 3 | 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.itertools import flatten 7 | from compas.itertools import linspace 8 | from compas.itertools import meshgrid 9 | 10 | points = [ 11 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 12 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 13 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 14 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 15 | ] 16 | 17 | surface = NurbsSurface.from_points(points=points) 18 | 19 | # ============================================================================== 20 | # Frames 21 | # ============================================================================== 22 | 23 | U, V = meshgrid(linspace(*surface.domain_u), linspace(*surface.domain_v), "ij") 24 | frames = [surface.frame_at(u, v) for u, v in zip(flatten(U), flatten(V))] 25 | 26 | # ============================================================================== 27 | # Visualisation 28 | # ============================================================================== 29 | 30 | viewer = Viewer() 31 | 32 | viewer.scene.add(surface, show_lines=False) 33 | viewer.scene.add(Collection(frames), size=0.1, pointsize=0.25) # type: ignore 34 | 35 | viewer.show() 36 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_frames.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface Frames 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_frames.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_frames.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_fill.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsCurve 5 | from compas.geometry import NurbsSurface 6 | from compas.geometry import Point 7 | 8 | points1 = [Point(0, -10, 0), Point(1, -8, 0), Point(-1, -6, 0), Point(0, -4, 0)] 9 | points2 = [Point(5, -10, 0), Point(4, -8, 0), Point(6, -6, 0), Point(5, -4, 0)] 10 | 11 | nurbscurve1 = NurbsCurve.from_interpolation(points1) 12 | nurbscurve2 = NurbsCurve.from_interpolation(points2) 13 | 14 | nurbssurface_2curves = NurbsSurface.from_fill(nurbscurve1, nurbscurve2) 15 | 16 | points3 = [Point(0, 0, 0), Point(1, 2, 0), Point(-1, 4, 0), Point(0, 6, 0)] 17 | points4 = [Point(0, 6, 0), Point(3, 6, -1), Point(5, 6, 0)] 18 | points5 = [Point(5, 6, 0), Point(4, 2, 0), Point(5, 0, 0)] 19 | points6 = [Point(5, 0, 0), Point(2, -1, 1), Point(0, 0, 0)] 20 | 21 | nurbscurve3 = NurbsCurve.from_interpolation(points3) 22 | nurbscurve4 = NurbsCurve.from_interpolation(points4) 23 | nurbscurve5 = NurbsCurve.from_interpolation(points5) 24 | nurbscurve6 = NurbsCurve.from_interpolation(points6) 25 | 26 | nurbssurface_4curves = NurbsSurface.from_fill( 27 | nurbscurve3, 28 | nurbscurve4, 29 | nurbscurve5, 30 | nurbscurve6, 31 | style="curved", 32 | ) 33 | 34 | # ============================================================================== 35 | # Visualisation 36 | # ============================================================================== 37 | 38 | viewer = Viewer() 39 | 40 | viewer.scene.add(nurbscurve1, linewidth=3, linecolor=Color(1, 0, 0)) 41 | viewer.scene.add(nurbscurve2, linewidth=3, linecolor=Color(0, 1, 0)) 42 | 43 | viewer.scene.add(nurbscurve3, linewidth=3, linecolor=Color(1, 0, 0)) 44 | viewer.scene.add(nurbscurve4, linewidth=3, linecolor=Color(0, 1, 0)) 45 | viewer.scene.add(nurbscurve5, linewidth=3, linecolor=Color(1, 0, 1)) 46 | viewer.scene.add(nurbscurve6, linewidth=3, linecolor=Color(0, 0, 1)) 47 | 48 | viewer.scene.add(nurbssurface_2curves, show_lines=False) 49 | viewer.scene.add(nurbssurface_4curves, show_lines=False) 50 | 51 | viewer.show() 52 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_fill.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface From Fill 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_from_fill.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_from_fill.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_meshgrid.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | from compas.itertools import linspace 8 | from compas.itertools import meshgrid 9 | 10 | UU, VV = meshgrid(linspace(0, 8, 9), linspace(0, 5, 6)) 11 | 12 | Z = 0.5 13 | 14 | points = [] 15 | for i, (U, V) in enumerate(zip(UU, VV)): 16 | row = [] 17 | for j, (u, v) in enumerate(zip(U, V)): 18 | if i == 0 or i == 5 or j == 0 or j == 8: 19 | z = 0.0 20 | elif i < 2 or i > 3: 21 | z = -1.0 22 | else: 23 | if j < 2 or j > 6: 24 | z = -1.0 25 | else: 26 | z = Z 27 | row.append(Point(u, v, z)) 28 | points.append(row) 29 | 30 | surface = NurbsSurface.from_points(points=points) 31 | 32 | # ============================================================================== 33 | # Visualisation 34 | # ============================================================================== 35 | 36 | viewer = Viewer() 37 | 38 | viewer.scene.add(surface) 39 | 40 | # control polygon 41 | 42 | points = list(surface.points) 43 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 44 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 45 | viewer.scene.add(points, pointsize=10) 46 | 47 | # isocurves 48 | 49 | u_curves = [] 50 | for u in surface.space_u(17): # type: ignore 51 | u_curves.append(surface.isocurve_u(u).to_polyline()) 52 | 53 | v_curves = [] 54 | for v in surface.space_v(17): # type: ignore 55 | v_curves.append(surface.isocurve_v(v).to_polyline()) 56 | 57 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 58 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 59 | 60 | viewer.show() 61 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_meshgrid.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface From Meshgrid 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_from_meshgrid.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_from_meshgrid.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_parameters.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | 8 | points = [ 9 | [ 10 | Point(0, 0, 0), 11 | Point(1, 0, +0), 12 | Point(2, 0, +0), 13 | Point(3, 0, +0), 14 | Point(4, 0, +0), 15 | Point(5, 0, 0), 16 | ], 17 | [ 18 | Point(0, 1, 0), 19 | Point(1, 1, -1), 20 | Point(2, 1, -1), 21 | Point(3, 1, -1), 22 | Point(4, 1, -1), 23 | Point(5, 1, 0), 24 | ], 25 | [ 26 | Point(0, 2, 0), 27 | Point(1, 2, -1), 28 | Point(2, 2, +2), 29 | Point(3, 2, +2), 30 | Point(4, 2, -1), 31 | Point(5, 2, 0), 32 | ], 33 | [ 34 | Point(0, 3, 0), 35 | Point(1, 3, -1), 36 | Point(2, 3, +2), 37 | Point(3, 3, +2), 38 | Point(4, 3, -1), 39 | Point(5, 3, 0), 40 | ], 41 | [ 42 | Point(0, 4, 0), 43 | Point(1, 4, -1), 44 | Point(2, 4, -1), 45 | Point(3, 4, -1), 46 | Point(4, 4, -1), 47 | Point(5, 4, 0), 48 | ], 49 | [ 50 | Point(0, 5, 0), 51 | Point(1, 5, +0), 52 | Point(2, 5, +0), 53 | Point(3, 5, +0), 54 | Point(4, 5, +0), 55 | Point(5, 5, 0), 56 | ], 57 | ] 58 | 59 | weights = [ 60 | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], 61 | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], 62 | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], 63 | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], 64 | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], 65 | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], 66 | ] 67 | 68 | surface = NurbsSurface.from_parameters( 69 | points=points, 70 | weights=weights, 71 | knots_u=[ 72 | 1.0, 73 | 1 + 1 / 9, 74 | 1 + 2 / 9, 75 | 1 + 3 / 9, 76 | 1 + 4 / 9, 77 | 1 + 5 / 9, 78 | 1 + 6 / 9, 79 | 1 + 7 / 9, 80 | 1 + 8 / 9, 81 | 2.0, 82 | ], 83 | knots_v=[0.0, 1 / 9, 2 / 9, 3 / 9, 4 / 9, 5 / 9, 6 / 9, 7 / 9, 8 / 9, 1.0], 84 | mults_u=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 85 | mults_v=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 86 | degree_u=3, 87 | degree_v=3, 88 | ) 89 | 90 | # ============================================================================== 91 | # Visualisation 92 | # ============================================================================== 93 | 94 | viewer = Viewer() 95 | 96 | viewer.scene.add(surface) 97 | 98 | # control polygon 99 | 100 | points = list(surface.points) 101 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 102 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 103 | viewer.scene.add(points, pointsize=10) 104 | 105 | # isocurves 106 | 107 | u_curves = [] 108 | for u in surface.space_u(7): # type: ignore 109 | u_curves.append(surface.isocurve_u(u).to_polyline()) 110 | 111 | v_curves = [] 112 | for v in surface.space_v(7): # type: ignore 113 | v_curves.append(surface.isocurve_v(v).to_polyline()) 114 | 115 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 116 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 117 | 118 | viewer.show() 119 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_parameters.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface From Parameters 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_from_parameters.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_from_parameters.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_points.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | 8 | points = [ 9 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 10 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 11 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 12 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 13 | ] 14 | 15 | surface = NurbsSurface.from_points(points=points) 16 | 17 | # ============================================================================== 18 | # Visualisation 19 | # ============================================================================== 20 | 21 | viewer = Viewer() 22 | 23 | viewer.scene.add(surface) 24 | 25 | # control polygon 26 | 27 | points = list(surface.points) 28 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 29 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 30 | viewer.scene.add(points, pointsize=10) 31 | 32 | # isocurves 33 | 34 | u_curves = [] 35 | for u in surface.space_u(7): # type: ignore 36 | u_curves.append(surface.isocurve_u(u).to_polyline()) 37 | 38 | v_curves = [] 39 | for v in surface.space_v(7): # type: ignore 40 | v_curves.append(surface.isocurve_v(v).to_polyline()) 41 | 42 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 43 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 44 | 45 | viewer.show() 46 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_from_points.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface From Points 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_from_points.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_from_points.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_intersections_with_line.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.colors import Color 6 | from compas.geometry import Line 7 | from compas.geometry import NurbsSurface 8 | from compas.geometry import Point 9 | from compas.geometry import Polyline 10 | from compas.geometry import Rotation 11 | from compas.geometry import Vector 12 | from compas.geometry import centroid_points_xy 13 | from compas.itertools import flatten 14 | 15 | points = [ 16 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 17 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 18 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 19 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 20 | ] 21 | 22 | surface = NurbsSurface.from_points(points=points) 23 | 24 | # ============================================================================== 25 | # Intersections 26 | # ============================================================================== 27 | 28 | base = Point(*centroid_points_xy(list(flatten(points)))) 29 | line = Line(base, base + Vector(0, 0, 1)) 30 | 31 | Ry = Rotation.from_axis_and_angle(Vector.Yaxis(), radians(30), point=base) 32 | line.transform(Ry) 33 | 34 | lines = [] 35 | for i in range(30): 36 | Rz = Rotation.from_axis_and_angle(Vector.Zaxis(), radians(i * 360 / 30), point=base) 37 | lines.append(line.transformed(Rz)) 38 | 39 | intersections = [] 40 | for line in lines: 41 | x = surface.intersections_with_line(line) 42 | if x: 43 | intersections.append(x[0]) 44 | 45 | # ============================================================================== 46 | # Visualisation 47 | # ============================================================================== 48 | 49 | viewer = Viewer(rendermode="ghosted") 50 | 51 | viewer.scene.add(surface) 52 | viewer.scene.add(intersections, pointsize=10, pointcolor=Color.blue()) 53 | for x in intersections: 54 | viewer.scene.add(Line(base, base + (x - base).scaled(1.2)), linewidth=1, linecolor=Color.blue()) 55 | 56 | # control polygon 57 | 58 | points = list(surface.points) 59 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 60 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 61 | viewer.scene.add(points, pointsize=10) 62 | 63 | # isocurves 64 | 65 | u_curves = [] 66 | for u in surface.space_u(7): # type: ignore 67 | u_curves.append(surface.isocurve_u(u).to_polyline()) 68 | 69 | v_curves = [] 70 | for v in surface.space_v(7): # type: ignore 71 | v_curves.append(surface.isocurve_v(v).to_polyline()) 72 | 73 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 74 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 75 | 76 | viewer.show() 77 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_intersections_with_line.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface Intersections With Line 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_intersections_with_line.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_intersections_with_line.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_isocurves.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | 7 | points = [ 8 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0), Point(4, 0, 0)], 9 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0), Point(4, 1, 0)], 10 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0), Point(4, 2, 0)], 11 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0), Point(4, 3, 0)], 12 | ] 13 | 14 | surface = NurbsSurface.from_points(points=points) 15 | 16 | # ============================================================================== 17 | # Visualisation 18 | # ============================================================================== 19 | 20 | viewer = Viewer() 21 | 22 | u_curves = [] 23 | for u in surface.space_u(17): # type: ignore 24 | u_curves.append(surface.isocurve_u(u).to_polyline()) 25 | 26 | v_curves = [] 27 | for v in surface.space_v(17): # type: ignore 28 | v_curves.append(surface.isocurve_v(v).to_polyline()) 29 | 30 | viewer.scene.add(u_curves, linecolor=Color.red(), linewidth=3) 31 | viewer.scene.add(v_curves, linecolor=Color.green(), linewidth=3) 32 | 33 | viewer.show() 34 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_isocurves.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface Isocurves 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_isocurves.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_isocurves.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_jsondata.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | 8 | points = [ 9 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0)], 10 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0)], 11 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0)], 12 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0)], 13 | ] 14 | 15 | surface = NurbsSurface.from_points(points=points) 16 | 17 | # ============================================================================== 18 | # JSON Data 19 | # ============================================================================== 20 | 21 | string = surface.to_jsonstring(pretty=True) 22 | 23 | print(string) 24 | 25 | other = NurbsSurface.from_jsonstring(string) 26 | 27 | print(surface == other) 28 | 29 | # ============================================================================== 30 | # Visualisation 31 | # ============================================================================== 32 | 33 | viewer = Viewer() 34 | 35 | viewer.scene.add(other) # type: ignore 36 | 37 | # control polygon of original 38 | 39 | points = list(surface.points) 40 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 41 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 42 | viewer.scene.add(points, pointsize=10) 43 | 44 | # isocurves of original 45 | 46 | u_curves = [] 47 | for u in surface.space_u(7): # type: ignore 48 | u_curves.append(surface.isocurve_u(u).to_polyline()) 49 | 50 | v_curves = [] 51 | for v in surface.space_v(7): # type: ignore 52 | v_curves.append(surface.isocurve_v(v).to_polyline()) 53 | 54 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 55 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 56 | 57 | viewer.show() 58 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_jsondata.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface JSON Data 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_jsondata.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_jsondata.py 10 | :language: python 11 | 12 | .. code-block:: json 13 | 14 | { 15 | "is_u_periodic": false, 16 | "is_v_periodic": false, 17 | "points": [ 18 | [ 19 | [ 20 | 0.0, 21 | 0.0, 22 | 0.0 23 | ], 24 | [ 25 | 1.0, 26 | 0.0, 27 | 0.0 28 | ], 29 | [ 30 | 2.0, 31 | 0.0, 32 | 0.0 33 | ], 34 | [ 35 | 3.0, 36 | 0.0, 37 | 0.0 38 | ] 39 | ], 40 | [ 41 | [ 42 | 0.0, 43 | 1.0, 44 | 0.0 45 | ], 46 | [ 47 | 1.0, 48 | 1.0, 49 | 2.0 50 | ], 51 | [ 52 | 2.0, 53 | 1.0, 54 | 2.0 55 | ], 56 | [ 57 | 3.0, 58 | 1.0, 59 | 0.0 60 | ] 61 | ], 62 | [ 63 | [ 64 | 0.0, 65 | 2.0, 66 | 0.0 67 | ], 68 | [ 69 | 1.0, 70 | 2.0, 71 | 2.0 72 | ], 73 | [ 74 | 2.0, 75 | 2.0, 76 | 2.0 77 | ], 78 | [ 79 | 3.0, 80 | 2.0, 81 | 0.0 82 | ] 83 | ], 84 | [ 85 | [ 86 | 0.0, 87 | 3.0, 88 | 0.0 89 | ], 90 | [ 91 | 1.0, 92 | 3.0, 93 | 0.0 94 | ], 95 | [ 96 | 2.0, 97 | 3.0, 98 | 0.0 99 | ], 100 | [ 101 | 3.0, 102 | 3.0, 103 | 0.0 104 | ] 105 | ] 106 | ], 107 | "u_degree": 3, 108 | "u_knots": [ 109 | 0.0, 110 | 1.0 111 | ], 112 | "u_mults": [ 113 | 4, 114 | 4 115 | ], 116 | "v_degree": 3, 117 | "v_knots": [ 118 | 0.0, 119 | 1.0 120 | ], 121 | "v_mults": [ 122 | 4, 123 | 4 124 | ], 125 | "weights": [ 126 | [ 127 | 1.0, 128 | 1.0, 129 | 1.0, 130 | 1.0 131 | ], 132 | [ 133 | 1.0, 134 | 1.0, 135 | 1.0, 136 | 1.0 137 | ], 138 | [ 139 | 1.0, 140 | 1.0, 141 | 1.0, 142 | 1.0 143 | ], 144 | [ 145 | 1.0, 146 | 1.0, 147 | 1.0, 148 | 1.0 149 | ] 150 | ] 151 | } 152 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_obb.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.colors import Color 6 | from compas.geometry import NurbsSurface 7 | from compas.geometry import Point 8 | from compas.geometry import Polyline 9 | from compas.geometry import Rotation 10 | from compas.geometry import Translation 11 | 12 | points = [ 13 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0), Point(4, 0, 0)], 14 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0), Point(4, 1, 0)], 15 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0), Point(4, 2, 0)], 16 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0), Point(4, 3, 0)], 17 | ] 18 | 19 | surface = NurbsSurface.from_points(points=points) 20 | 21 | T = Translation.from_vector([0, -1.5, 0]) 22 | R = Rotation.from_axis_and_angle([0, 0, 1], radians(45)) 23 | 24 | surface.transform(R * T) 25 | 26 | # ============================================================================== 27 | # OBB 28 | # ============================================================================== 29 | 30 | box = surface.obb() 31 | 32 | # ============================================================================== 33 | # Visualisation 34 | # ============================================================================== 35 | 36 | viewer = Viewer() 37 | 38 | viewer.scene.add(surface) 39 | viewer.scene.add(box, show_faces=False) 40 | 41 | # control polygon 42 | 43 | points = list(surface.points) 44 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 45 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 46 | viewer.scene.add(points, pointsize=10) 47 | 48 | # isocurves 49 | 50 | u_curves = [] 51 | for u in surface.space_u(7): # type: ignore 52 | u_curves.append(surface.isocurve_u(u).to_polyline()) 53 | 54 | v_curves = [] 55 | for v in surface.space_v(7): # type: ignore 56 | v_curves.append(surface.isocurve_v(v).to_polyline()) 57 | 58 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 59 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 60 | 61 | viewer.show() 62 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_random.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from compas_viewer import Viewer 4 | 5 | from compas.colors import Color 6 | from compas.geometry import NurbsSurface 7 | from compas.geometry import Polyline 8 | 9 | U = 10 10 | V = 20 11 | 12 | surface = NurbsSurface.from_meshgrid(nu=U, nv=V) 13 | 14 | # ============================================================================== 15 | # Update 16 | # ============================================================================== 17 | 18 | for u in range(1, U): 19 | for v in range(1, V): 20 | point = surface.points[u, v] 21 | point.z = random.choice([+1, -1]) * random.random() 22 | surface.points[u, v] = point 23 | 24 | # ============================================================================== 25 | # Visualisation 26 | # ============================================================================== 27 | 28 | viewer = Viewer() 29 | 30 | viewer.scene.add(surface) 31 | 32 | # control polygon 33 | 34 | points = list(surface.points) 35 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 36 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 37 | viewer.scene.add(points, pointsize=10) 38 | 39 | # isocurves 40 | 41 | u_curves = [] 42 | for u in surface.space_u(53): # type: ignore 43 | u_curves.append(surface.isocurve_u(u).to_polyline()) 44 | 45 | v_curves = [] 46 | for v in surface.space_v(53): # type: ignore 47 | v_curves.append(surface.isocurve_v(v).to_polyline()) 48 | 49 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 50 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 51 | 52 | viewer.show() 53 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_random.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface With Random Control Points 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_random.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_random.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_spacepoints.py: -------------------------------------------------------------------------------- 1 | from compas_viewer import Viewer 2 | 3 | from compas.colors import Color 4 | from compas.geometry import NurbsSurface 5 | from compas.geometry import Point 6 | from compas.geometry import Polyline 7 | from compas.itertools import flatten 8 | from compas.itertools import linspace 9 | from compas.itertools import meshgrid 10 | 11 | points = [ 12 | [Point(0, 0, 0), Point(1, 0, 0), Point(2, 0, 0), Point(3, 0, 0), Point(4, 0, 0)], 13 | [Point(0, 1, 0), Point(1, 1, 2), Point(2, 1, 2), Point(3, 1, 0), Point(4, 1, 0)], 14 | [Point(0, 2, 0), Point(1, 2, 2), Point(2, 2, 2), Point(3, 2, 0), Point(4, 2, 0)], 15 | [Point(0, 3, 0), Point(1, 3, 0), Point(2, 3, 0), Point(3, 3, 0), Point(4, 3, 0)], 16 | ] 17 | 18 | surface = NurbsSurface.from_points(points=points) 19 | 20 | # ============================================================================== 21 | # Points over UV space 22 | # ============================================================================== 23 | 24 | U, V = meshgrid(linspace(*surface.domain_u), linspace(*surface.domain_v), "ij") 25 | spacepoints = [surface.point_at(u, v) for u, v in zip(flatten(U), flatten(V))] 26 | 27 | # ============================================================================== 28 | # Visualisation 29 | # ============================================================================== 30 | 31 | viewer = Viewer() 32 | 33 | viewer.scene.add(surface) 34 | viewer.scene.add(spacepoints) 35 | 36 | # control polygon 37 | 38 | points = list(surface.points) 39 | viewer.scene.add([Polyline(row) for row in points], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 40 | viewer.scene.add([Polyline(col) for col in zip(*points)], linewidth=1, linecolor=Color(0.3, 0.3, 0.3)) 41 | viewer.scene.add(points, pointsize=10) 42 | 43 | # isocurves 44 | 45 | u_curves = [] 46 | for u in surface.space_u(7): # type: ignore 47 | u_curves.append(surface.isocurve_u(u).to_polyline()) 48 | 49 | v_curves = [] 50 | for v in surface.space_v(7): # type: ignore 51 | v_curves.append(surface.isocurve_v(v).to_polyline()) 52 | 53 | viewer.scene.add(u_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 54 | viewer.scene.add(v_curves, linecolor=Color(0.8, 0.8, 0.8), linewidth=3) 55 | 56 | viewer.show() 57 | -------------------------------------------------------------------------------- /docs/examples/surfaces/surface_spacepoints.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Surface Points Over UV Space 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/example_surface_spacepoints.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. literalinclude:: surface_spacepoints.py 10 | :language: python 11 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | COMPAS OCC 3 | ******************************************************************************** 4 | 5 | .. figure:: /_images/compas_occ.png 6 | :figclass: figure 7 | :class: figure-img img-fluid 8 | 9 | .. rst-class:: lead 10 | 11 | COMPAS OCC provides an easy-to-use interface to the Python bindings 12 | of the `3D modelling kernel of Open CasCade `_. 13 | :mod:`compas_occ.geometry` defines :class:`~compas_occ.geometry.Curve`, :class:`~compas_occ.geometry.NurbsCurve`, 14 | :class:`~compas_occ.geometry.Surface` and :class:`~compas_occ.geometry.NurbsSurface`, which are wrappers around 15 | ``Geom_Curve`` [1]_, ``Geom_BSplineCurve`` [2]_, ``Geom_Surface`` [3]_ and ``Geom_BSplineSurface`` [4]_ of OCC, repsectively. 16 | The :mod:`compas_occ` wrappers provide an API for working with NURBS curves and surfaces similar to the API of RhinoCommon. 17 | :mod:`compas_occ.brep` is a package for working with Boundary Representation (Brep) objects 18 | with the NURBS curves and surfaces of :mod:`compas_occ.geometry` as underlying geometry. 19 | 20 | 21 | Table of Contents 22 | ================= 23 | 24 | .. toctree:: 25 | :maxdepth: 3 26 | :titlesonly: 27 | 28 | Introduction 29 | installation 30 | examples 31 | api 32 | license 33 | acknowledgements 34 | 35 | 36 | Indices and tables 37 | ================== 38 | 39 | * :ref:`genindex` 40 | * :ref:`modindex` 41 | 42 | 43 | References 44 | ========== 45 | 46 | .. [1] https://dev.opencascade.org/doc/refman/html/class_geom___curve.html 47 | 48 | .. [2] https://dev.opencascade.org/doc/refman/html/class_geom___b_spline_curve.html 49 | 50 | .. [3] https://dev.opencascade.org/doc/refman/html/class_geom___surface.html 51 | 52 | .. [4] https://dev.opencascade.org/doc/refman/html/class_geom___b_spline_surface.html 53 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | Installation 3 | ******************************************************************************** 4 | 5 | Stable 6 | ====== 7 | 8 | Stable releases of :mod:`compas_occ` can be installed via ``conda-forge``. 9 | 10 | .. code-block:: bash 11 | 12 | conda create -n occ -c conda-forge compas_occ 13 | 14 | Several examples use the COMPAS Viewer for visualisation. 15 | To install :mod:`compas_viewer` in the same environment 16 | 17 | .. code-block:: bash 18 | 19 | conda activate occ 20 | pip install compas_viewer 21 | 22 | 23 | Development 24 | =========== 25 | 26 | To get the latest development version, you can install from local source, or directly from the github repo. 27 | 28 | .. code-block:: bash 29 | 30 | conda create -n occ -c conda-forge compas pythonocc-core 31 | conda activate occ 32 | git clone https://github.com/compas-dev/compas_occ.git 33 | cd compas_occ 34 | pip install -e ".[dev]" 35 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | License 3 | ******************************************************************************** 4 | 5 | .. literalinclude:: ../LICENSE 6 | -------------------------------------------------------------------------------- /docs/tutorial.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Tutorial 3 | ******** 4 | 5 | This tutorial gives a brief overview of the functionality of :mod:`compas_occ` and recommended best practices. 6 | 7 | 8 | Working with Curves 9 | =================== 10 | 11 | 12 | Working with Surfaces 13 | ===================== 14 | 15 | 16 | Working with Breps 17 | ================== 18 | 19 | 20 | Visualisation 21 | ============= 22 | 23 | 24 | Using the plugin system 25 | ======================= 26 | 27 | :mod:`compas_occ` provides a NURBS and Brep (Boundary Representation) backend for COMPAS based on OpenCasCade. 28 | Although it ca be used as a standalone package, the recommended way to use it is through the plugin system. 29 | The following snippets accomplish the same thing, but the first uses :mod:`compas_occ` directly, and the second uses it as a plugin. 30 | 31 | .. code-block:: python 32 | 33 | from compas.geometry import Point 34 | from compas_occ.geometry import OCCNurbsCurve 35 | 36 | points = [ 37 | Point(0, 0, 0), 38 | Point(3, 6, 0), 39 | Point(6, -3, 3), 40 | Point(10, 0, 0) 41 | ] 42 | 43 | curve = OCCNurbsCurve.from_points(points) 44 | 45 | .. code-block:: python 46 | 47 | from compas.geometry import Point 48 | from compas.geometry import NurbsCurve 49 | 50 | points = [ 51 | Point(0, 0, 0), 52 | Point(3, 6, 0), 53 | Point(6, -3, 3), 54 | Point(10, 0, 0) 55 | ] 56 | 57 | curve = NurbsCurve.from_points(points) 58 | 59 | The advatage of using the plugin system is that it allows COMPAS to automatically switch to different backends depending on the current environment without chaging the script. 60 | For example, when working in Rhino, the first script will throw an error, whereas the second script will work as expected by switching to RhinoCommon as a backend. 61 | -------------------------------------------------------------------------------- /docs/write_rst.py: -------------------------------------------------------------------------------- 1 | # from pathlib import Path 2 | from compas_occ import conversions as module 3 | 4 | TPL = """ 5 | ******************************************************************************** 6 | {currentmodule} 7 | ******************************************************************************** 8 | 9 | .. currentmodule:: {currentmodule} 10 | 11 | .. rst-class:: lead 12 | 13 | {lead} 14 | {sections} 15 | 16 | """ 17 | 18 | SECTION = """ 19 | {title} 20 | {line} 21 | 22 | {summary} 23 | 24 | .. autosummary:: 25 | :toctree: generated/ 26 | :nosignatures: 27 | 28 | {items} 29 | """ 30 | 31 | __newall__ = { 32 | "functions": [], 33 | "classes": [], 34 | "errors": [], 35 | "numpy": [], 36 | "pluggables": [], 37 | "plugins": [], 38 | } 39 | 40 | for name in module.__all__: 41 | obj = getattr(module, name) 42 | 43 | if name.endswith("_numpy"): 44 | __newall__["numpy"].append(name) 45 | continue 46 | 47 | if issubclass(type(obj), Exception): 48 | __newall__["errors"].append(name) 49 | continue 50 | 51 | if hasattr(obj, "__pluggable__"): 52 | __newall__["pluggables"].append(name) 53 | continue 54 | 55 | if hasattr(obj, "__plugin__"): 56 | __newall__["plugins"].append(name) 57 | continue 58 | 59 | if isinstance(obj, type): 60 | __newall__["classes"].append(name) 61 | else: 62 | __newall__["functions"].append(name) 63 | 64 | 65 | currentmodule = module.__name__ 66 | 67 | lead = module.__doc__ 68 | 69 | classes = "" 70 | for name in sorted(__newall__["classes"]): 71 | classes += " {name}\n".format(name=name) 72 | 73 | if classes: 74 | classes = SECTION.format( 75 | title="Classes", 76 | line="=" * len("Classes"), 77 | summary="", 78 | items=classes, 79 | ) 80 | 81 | errors = "" 82 | for name in sorted(__newall__["errors"]): 83 | errors += " {name}\n".format(name=name) 84 | 85 | if errors: 86 | errors = SECTION.format( 87 | title="Exceptions", 88 | line="=" * len("Exceptions"), 89 | summary="", 90 | items=errors, 91 | ) 92 | 93 | functions = "" 94 | for name in sorted(__newall__["functions"]): 95 | functions += " {name}\n".format(name=name) 96 | 97 | if functions: 98 | functions = SECTION.format( 99 | title="Functions", 100 | line="=" * len("Functions"), 101 | summary="", 102 | items=functions, 103 | ) 104 | 105 | numpy = "" 106 | for name in sorted(__newall__["numpy"]): 107 | numpy += " {name}\n".format(name=name) 108 | 109 | if numpy: 110 | numpy = SECTION.format( 111 | title="Functions using Numpy", 112 | line="=" * len("Functions using Numpy"), 113 | summary="In environments where numpy is not available, these functions can still be accessed through RPC.", 114 | items=numpy, 115 | ) 116 | 117 | pluggables = "" 118 | for name in sorted(__newall__["pluggables"]): 119 | pluggables += " {name}\n".format(name=name) 120 | 121 | if pluggables: 122 | pluggables = SECTION.format( 123 | title="Pluggables", 124 | line="=" * len("Pluggables"), 125 | summary="Pluggables are functions that don't have an actual implementation, but receive an implementation from a plugin.", 126 | items=pluggables, 127 | ) 128 | 129 | plugins = "" 130 | for name in sorted(__newall__["plugins"]): 131 | plugins += " {name}\n".format(name=name) 132 | 133 | if plugins: 134 | plugins = SECTION.format( 135 | title="Plugins", 136 | line="=" * len("Plugins"), 137 | summary="Plugins provide implementations for pluggables. You can use the plugin directly, or through the pluggable.", 138 | items=plugins, 139 | ) 140 | 141 | sections = "".join([classes, errors, functions, numpy, pluggables, plugins]) 142 | 143 | # docs = Path(__file__).parent 144 | 145 | with open( 146 | "/Users/vanmelet/Code/compas/docs/api/{name}.rst".format(name=module.__name__), "w" 147 | ) as f: 148 | f.write( 149 | TPL.format( 150 | currentmodule=currentmodule, 151 | lead=lead, 152 | sections=sections, 153 | ) 154 | ) 155 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: occ 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - pythonocc-core < 7.8 6 | - pip: 7 | - -r requirements.txt 8 | - -r requirements-dev.txt 9 | - -e . 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=66.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | # ============================================================================ 6 | # project info 7 | # ============================================================================ 8 | 9 | [project] 10 | name = "compas_occ" 11 | description = "COMPAS wrapper for the Python bindings of OCC." 12 | keywords = ["compas", "nurbs", "brep", "occ"] 13 | authors = [ 14 | { name = "tom van mele", email = "tom.v.mele@gmail.com" }, 15 | { name = "Lotte Aldinger", email = "aldinger@arch.ethz.ch" }, 16 | { name = "Chen Kasirer", email = "kasirer@arch.ethz.ch" }, 17 | ] 18 | license = { file = "LICENSE" } 19 | readme = "README.md" 20 | requires-python = ">=3.9" 21 | dynamic = ["dependencies", "optional-dependencies", "version"] 22 | classifiers = [ 23 | "Development Status :: 4 - Beta", 24 | "Topic :: Scientific/Engineering", 25 | "Programming Language :: Python", 26 | "Programming Language :: Python :: 3", 27 | "Programming Language :: Python :: 3.9", 28 | "Programming Language :: Python :: 3.10", 29 | "Programming Language :: Python :: 3.11", 30 | ] 31 | 32 | [project.urls] 33 | Homepage = "https://compas-dev.github.io/compas_occ" 34 | Repository = "https://github.com/compas-dev/compas_occ" 35 | 36 | # ============================================================================ 37 | # setuptools config 38 | # ============================================================================ 39 | 40 | [tool.setuptools] 41 | package-dir = { "" = "src" } 42 | include-package-data = true 43 | zip-safe = false 44 | 45 | [tool.setuptools.dynamic] 46 | version = { attr = "compas_occ.__version__" } 47 | dependencies = { file = "requirements.txt" } 48 | optional-dependencies = { dev = { file = "requirements-dev.txt" } } 49 | 50 | [tool.setuptools.packages.find] 51 | where = ["src"] 52 | 53 | [tool.setuptools.package-data] 54 | 55 | # ============================================================================ 56 | # replace pytest.ini 57 | # ============================================================================ 58 | 59 | [tool.pytest.ini_options] 60 | minversion = "6.0" 61 | testpaths = ["tests", "src/compas_occ"] 62 | python_files = ["test_*.py", "*_test.py", "test.py"] 63 | addopts = ["-ra", "--strict-markers", "--doctest-glob=*.rst", "--tb=short"] 64 | doctest_optionflags = [ 65 | "NORMALIZE_WHITESPACE", 66 | "IGNORE_EXCEPTION_DETAIL", 67 | "ALLOW_UNICODE", 68 | "ALLOW_BYTES", 69 | "NUMBER", 70 | ] 71 | 72 | # ============================================================================ 73 | # replace bumpversion.cfg 74 | # ============================================================================ 75 | 76 | [tool.bumpversion] 77 | current_version = "1.3.0" 78 | message = "Bump version to {new_version}" 79 | commit = true 80 | tag = true 81 | 82 | [[tool.bumpversion.files]] 83 | filename = "src/compas_occ/__init__.py" 84 | search = "{current_version}" 85 | replace = "{new_version}" 86 | 87 | [[tool.bumpversion.files]] 88 | filename = "CHANGELOG.md" 89 | search = "Unreleased" 90 | replace = "[{new_version}] {now:%Y-%m-%d}" 91 | 92 | # ============================================================================ 93 | # replace setup.cfg 94 | # ============================================================================ 95 | 96 | [tool.black] 97 | line-length = 179 98 | 99 | [tool.ruff] 100 | line-length = 179 101 | indent-width = 4 102 | target-version = "py39" 103 | 104 | [tool.ruff.lint] 105 | select = ["E", "F", "I"] 106 | 107 | [tool.ruff.lint.per-file-ignores] 108 | "__init__.py" = ["I001"] 109 | "tests/*" = ["I001"] 110 | "tasks.py" = ["I001"] 111 | 112 | [tool.ruff.lint.isort] 113 | force-single-line = true 114 | known-first-party = [ 115 | "compas", 116 | "compas_occ", 117 | ] 118 | 119 | [tool.ruff.lint.pydocstyle] 120 | convention = "numpy" 121 | 122 | [tool.ruff.lint.pycodestyle] 123 | max-doc-length = 179 124 | 125 | [tool.ruff.format] 126 | docstring-code-format = true 127 | docstring-code-line-length = "dynamic" 128 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | attrs >=17.4 2 | black >=22.12.0 3 | bump-my-version 4 | compas_invocations2 5 | compas_viewer 6 | invoke >=0.14 7 | ruff 8 | sphinx_compas2_theme 9 | twine 10 | wheel 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | compas >=2.3 2 | -------------------------------------------------------------------------------- /src/compas_occ/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | __author__ = ["tom van mele"] 4 | __copyright__ = "Block Research Group - ETH Zurich" 5 | __license__ = "MIT License" 6 | __email__ = "van.mele@arch.ethz.ch" 7 | __version__ = "1.3.0" 8 | 9 | 10 | HERE = os.path.dirname(__file__) 11 | 12 | HOME = os.path.abspath(os.path.join(HERE, "../../")) 13 | DATA = os.path.abspath(os.path.join(HOME, "data")) 14 | DOCS = os.path.abspath(os.path.join(HOME, "docs")) 15 | TEMP = os.path.abspath(os.path.join(HOME, "temp")) 16 | 17 | 18 | __all__ = ["HOME", "DATA", "DOCS", "TEMP"] 19 | 20 | __all_plugins__ = [ 21 | "compas_occ.geometry.curves", 22 | "compas_occ.geometry.surfaces", 23 | "compas_occ.brep", 24 | ] 25 | -------------------------------------------------------------------------------- /src/compas_occ/brep/__init__.py: -------------------------------------------------------------------------------- 1 | from compas.plugins import plugin 2 | from compas.geometry import Brep # noqa: F401 3 | 4 | from .brepvertex import OCCBrepVertex # noqa: F401 5 | from .brepedge import OCCBrepEdge # noqa: F401 6 | from .breploop import OCCBrepLoop # noqa: F401 7 | from .brepface import OCCBrepFace # noqa: F401 8 | from .brep import OCCBrep # noqa: F401 9 | 10 | 11 | @plugin(category="factories", requires=["compas_occ"]) 12 | def from_boolean_difference(*args, **kwargs): 13 | return OCCBrep.from_boolean_difference(*args, **kwargs) 14 | 15 | 16 | @plugin(category="factories", requires=["compas_occ"]) 17 | def from_boolean_intersection(*args, **kwargs): 18 | return OCCBrep.from_boolean_intersection(*args, **kwargs) 19 | 20 | 21 | @plugin(category="factories", requires=["compas_occ"]) 22 | def from_boolean_union(*args, **kwargs): 23 | return OCCBrep.from_boolean_union(*args, **kwargs) 24 | 25 | 26 | @plugin(category="factories", requires=["compas_occ"]) 27 | def from_box(*args, **kwargs): 28 | return OCCBrep.from_box(*args, **kwargs) 29 | 30 | 31 | @plugin(category="factories", requires=["compas_occ"]) 32 | def from_brepfaces(*args, **kwargs): 33 | return OCCBrep.from_brepfaces(*args, **kwargs) 34 | 35 | 36 | @plugin(category="factories", requires=["compas_occ"]) 37 | def from_cone(*args, **kwargs): 38 | return OCCBrep.from_cone(*args, **kwargs) 39 | 40 | 41 | @plugin(category="factories", requires=["compas_occ"]) 42 | def from_curves(*args, **kwargs): 43 | return OCCBrep.from_curves(*args, **kwargs) 44 | 45 | 46 | @plugin(category="factories", requires=["compas_occ"]) 47 | def from_cylinder(*args, **kwargs): 48 | return OCCBrep.from_cylinder(*args, **kwargs) 49 | 50 | 51 | @plugin(category="factories", requires=["compas_occ"]) 52 | def from_extrusion(*args, **kwargs): 53 | return OCCBrep.from_extrusion(*args, **kwargs) 54 | 55 | 56 | @plugin(category="factories", requires=["compas_occ"]) 57 | def from_iges(*args, **kwargs): 58 | return OCCBrep.from_iges(*args, **kwargs) 59 | 60 | 61 | @plugin(category="factories", requires=["compas_occ"]) 62 | def from_mesh(*args, **kwargs): 63 | return OCCBrep.from_mesh(*args, **kwargs) 64 | 65 | 66 | @plugin(category="factories", requires=["compas_occ"]) 67 | def from_native(*args, **kwargs): 68 | return OCCBrep.from_native(*args, **kwargs) 69 | 70 | 71 | @plugin(category="factories", requires=["compas_occ"]) 72 | def from_plane(*args, **kwargs): 73 | return OCCBrep.from_plane(*args, **kwargs) 74 | 75 | 76 | @plugin(category="factories", requires=["compas_occ"]) 77 | def from_planes(*args, **kwargs): 78 | return OCCBrep.from_planes(*args, **kwargs) 79 | 80 | 81 | @plugin(category="factories", requires=["compas_occ"]) 82 | def from_polygons(*args, **kwargs): 83 | return OCCBrep.from_polygons(*args, **kwargs) 84 | 85 | 86 | @plugin(category="factories", requires=["compas_occ"]) 87 | def from_sphere(*args, **kwargs): 88 | return OCCBrep.from_sphere(*args, **kwargs) 89 | 90 | 91 | @plugin(category="factories", requires=["compas_occ"]) 92 | def from_step(*args, **kwargs): 93 | return OCCBrep.from_step(*args, **kwargs) 94 | 95 | 96 | @plugin(category="factories", requires=["compas_occ"]) 97 | def from_surface(*args, **kwargs): 98 | return OCCBrep.from_surface(*args, **kwargs) 99 | 100 | 101 | @plugin(category="factories", requires=["compas_occ"]) 102 | def from_sweep(*args, **kwargs): 103 | return OCCBrep.from_sweep(*args, **kwargs) 104 | 105 | 106 | @plugin(category="factories", requires=["compas_occ"]) 107 | def from_torus(*args, **kwargs): 108 | return OCCBrep.from_torus(*args, **kwargs) 109 | 110 | 111 | @plugin(category="factories", requires=["compas_occ"]) 112 | def new_brep(cls, *args, **kwargs): 113 | return super(Brep, cls).__new__(OCCBrep) 114 | -------------------------------------------------------------------------------- /src/compas_occ/brep/breploop.py: -------------------------------------------------------------------------------- 1 | from OCC.Core import BRepAlgo 2 | from OCC.Core import BRepBuilderAPI 3 | from OCC.Core import BRepTools 4 | from OCC.Core import ShapeFix 5 | from OCC.Core import TopoDS 6 | 7 | from compas.geometry import BrepLoop 8 | from compas.geometry import Polygon 9 | from compas.geometry import Polyline 10 | from compas.itertools import pairwise 11 | from compas_occ.brep import OCCBrepEdge 12 | from compas_occ.brep import OCCBrepVertex 13 | 14 | 15 | def wire_from_edges(edges: list[OCCBrepEdge]) -> TopoDS.TopoDS_Wire: 16 | """Construct a wire from a list of edges. 17 | 18 | Parameters 19 | ---------- 20 | edges : list[:class:`compas_occ.brep.OCCBrepEdge`] 21 | The edges. 22 | 23 | Returns 24 | ------- 25 | ``TopoDS.TopoDS_Wire`` 26 | 27 | """ 28 | builder = BRepBuilderAPI.BRepBuilderAPI_MakeWire() 29 | for edge in edges: 30 | builder.Add(edge.occ_edge) 31 | return builder.Wire() 32 | 33 | 34 | class OCCBrepLoop(BrepLoop): 35 | """Class representing an edge loop in the BRep of a geometric shape. 36 | 37 | Parameters 38 | ---------- 39 | occ_wire : ``TopoDS.TopoDS_Wire`` 40 | An OCC BRep wire. 41 | 42 | Attributes 43 | ---------- 44 | vertices : list[:class:`~compas_occ.brep.BrepVertex`], read-only 45 | List of BRep vertices. 46 | edges : list[:class:`~compas_occ.brep.BrepEdge`], read-only 47 | List of BRep edges. 48 | 49 | """ 50 | 51 | _occ_wire: TopoDS.TopoDS_Wire 52 | 53 | @property 54 | def __data__(self) -> dict: 55 | raise NotImplementedError 56 | 57 | @classmethod 58 | def __from_data__(cls, data: dict) -> "OCCBrepLoop": 59 | raise NotImplementedError 60 | 61 | def __init__(self, occ_wire: TopoDS.TopoDS_Wire): 62 | super().__init__() 63 | self._occ_wire = occ_wire 64 | 65 | def __eq__(self, other: "OCCBrepLoop") -> bool: 66 | return self.is_equal(other) 67 | 68 | def is_same(self, other: "OCCBrepLoop") -> bool: 69 | """Check if this loop is the same as another loop. 70 | 71 | Two loops are the same if they have the same location. 72 | 73 | Parameters 74 | ---------- 75 | other : :class:`OCCBrepLoop` 76 | The other loop. 77 | 78 | Returns 79 | ------- 80 | bool 81 | ``True`` if the loops are the same, ``False`` otherwise. 82 | 83 | """ 84 | if not isinstance(other, OCCBrepLoop): 85 | return False 86 | return self.occ_wire.IsSame(other.occ_wire) 87 | 88 | def is_equal(self, other: "OCCBrepLoop") -> bool: 89 | """Check if this loop is equal to another loop. 90 | 91 | Two loops are equal if they have the same location and orientation. 92 | 93 | Parameters 94 | ---------- 95 | other : :class:`OCCBrepLoop` 96 | The other loop. 97 | 98 | Returns 99 | ------- 100 | bool 101 | ``True`` if the loops are equal, ``False`` otherwise. 102 | 103 | """ 104 | if not isinstance(other, OCCBrepLoop): 105 | return False 106 | return self.occ_wire.IsEqual(other.occ_wire) 107 | 108 | # ============================================================================== 109 | # OCC Properties 110 | # ============================================================================== 111 | 112 | @property 113 | def occ_shape(self) -> TopoDS.TopoDS_Wire: 114 | return self._occ_wire 115 | 116 | @property 117 | def occ_wire(self) -> TopoDS.TopoDS_Wire: 118 | return self._occ_wire 119 | 120 | @occ_wire.setter 121 | def occ_wire(self, loop: TopoDS.TopoDS_Wire) -> None: 122 | self._occ_wire = loop 123 | 124 | # ============================================================================== 125 | # Properties 126 | # ============================================================================== 127 | 128 | @property 129 | def is_valid(self) -> bool: 130 | return BRepAlgo.brepalgo.IsValid(self.occ_wire) 131 | 132 | @property 133 | def vertices(self) -> list[OCCBrepVertex]: 134 | vertices = [] 135 | explorer = BRepTools.BRepTools_WireExplorer(self.occ_wire) 136 | while explorer.More(): 137 | vertex = explorer.CurrentVertex() 138 | vertices.append(OCCBrepVertex(vertex)) 139 | explorer.Next() 140 | return vertices 141 | 142 | @property 143 | def edges(self) -> list[OCCBrepEdge]: 144 | edges = [] 145 | explorer = BRepTools.BRepTools_WireExplorer(self.occ_wire) 146 | while explorer.More(): 147 | edge = explorer.Current() 148 | edges.append(OCCBrepEdge(edge)) 149 | explorer.Next() 150 | return edges 151 | 152 | @edges.setter 153 | def edges(self, edges: list[OCCBrepEdge]) -> None: 154 | self.occ_wire = wire_from_edges(edges) 155 | 156 | # ============================================================================== 157 | # Constructors 158 | # ============================================================================== 159 | 160 | @classmethod 161 | def from_edges(cls, edges: list[OCCBrepEdge]) -> "OCCBrepLoop": 162 | """Construct a loop from a collection of edges. 163 | 164 | Parameters 165 | ---------- 166 | edges : list[:class:`compas_occ.brep.BrepEdge`] 167 | The edges. 168 | 169 | Returns 170 | ------- 171 | :class:`OCCBrepLoop` 172 | 173 | """ 174 | return cls(wire_from_edges(edges)) 175 | 176 | @classmethod 177 | def from_polyline(cls, polyline: Polyline) -> "OCCBrepLoop": 178 | """Construct a loop from a polyline. 179 | 180 | Parameters 181 | ---------- 182 | polyline : :class:`compas.geometry.Polyline` 183 | The polyline. 184 | 185 | Returns 186 | ------- 187 | :class:`OCCBrepLoop` 188 | 189 | """ 190 | edges = [] 191 | for a, b in pairwise(polyline.points): 192 | edge = OCCBrepEdge.from_point_point(a, b) 193 | edges.append(edge) 194 | return cls(wire_from_edges(edges)) 195 | 196 | @classmethod 197 | def from_polygon(cls, polygon: Polygon) -> "OCCBrepLoop": 198 | """Construct a loop from a polygon. 199 | 200 | Parameters 201 | ---------- 202 | polygon : :class:`compas.geometry.Polygon` 203 | The polygon. 204 | 205 | Returns 206 | ------- 207 | :class:`OCCBrepLoop` 208 | 209 | """ 210 | edges = [] 211 | for a, b in pairwise(polygon.points): 212 | edge = OCCBrepEdge.from_point_point(a, b) 213 | edges.append(edge) 214 | return cls(wire_from_edges(edges)) 215 | 216 | # ============================================================================== 217 | # Methods 218 | # ============================================================================== 219 | 220 | def fix(self) -> None: 221 | """Try to fix the loop. 222 | 223 | Returns 224 | ------- 225 | None 226 | 227 | """ 228 | fixer = ShapeFix.ShapeFix_Wire(self.occ_wire) # type: ignore 229 | fixer.Perform() 230 | self.occ_wire = fixer.Wire() 231 | -------------------------------------------------------------------------------- /src/compas_occ/brep/brepvertex.py: -------------------------------------------------------------------------------- 1 | from OCC.Core import BRep 2 | from OCC.Core import BRepBuilderAPI 3 | from OCC.Core import TopoDS 4 | 5 | from compas.geometry import BrepVertex 6 | from compas.geometry import Point 7 | from compas_occ.conversions.geometry import point_to_occ 8 | 9 | 10 | class OCCBrepVertex(BrepVertex): 11 | """Class representing a vertex in the BRep of a geometric shape. 12 | 13 | Parameters 14 | ---------- 15 | occ_vertex : ``TopoDS.TopoDS_Vertex`` 16 | An OCC topological vertex data structure. 17 | 18 | Attributes 19 | ---------- 20 | point : :class:`~compas.geometry.Point`, read-only 21 | The geometric point underlying the topological vertex. 22 | 23 | """ 24 | 25 | _occ_vertex: TopoDS.TopoDS_Vertex 26 | 27 | @property 28 | def __data__(self) -> dict: 29 | raise NotImplementedError 30 | 31 | @classmethod 32 | def __from_data__(cls, data: dict) -> "OCCBrepVertex": 33 | raise NotImplementedError 34 | 35 | def __init__(self, occ_vertex: TopoDS.TopoDS_Vertex): 36 | super().__init__() 37 | self._occ_vertex = occ_vertex 38 | 39 | def __eq__(self, other: "OCCBrepVertex") -> bool: 40 | return self.is_equal(other) 41 | 42 | def is_same(self, other: "OCCBrepVertex") -> bool: 43 | """Check if this vertex is the same as another vertex. 44 | 45 | Two vertices are the same if they have the same location. 46 | 47 | Parameters 48 | ---------- 49 | other : :class:`OCCBrepVertex` 50 | The other vertex. 51 | 52 | Returns 53 | ------- 54 | bool 55 | ``True`` if the vertices are the same, ``False`` otherwise. 56 | 57 | """ 58 | if not isinstance(other, OCCBrepVertex): 59 | return False 60 | return self.occ_vertex.IsSame(other.occ_vertex) 61 | 62 | def is_equal(self, other: "OCCBrepVertex") -> bool: 63 | """Check if this vertex is equal to another vertex. 64 | 65 | Two vertices are equal if they have the same location and orientation. 66 | 67 | Parameters 68 | ---------- 69 | other : :class:`OCCBrepVertex` 70 | The other vertex. 71 | 72 | Returns 73 | ------- 74 | bool 75 | ``True`` if the vertices are equal, ``False`` otherwise. 76 | 77 | """ 78 | if not isinstance(other, OCCBrepVertex): 79 | return False 80 | return self.occ_vertex.IsEqual(other.occ_vertex) 81 | 82 | # ============================================================================== 83 | # OCC Properties 84 | # ============================================================================== 85 | 86 | @property 87 | def occ_vertex(self) -> TopoDS.TopoDS_Vertex: 88 | return self._occ_vertex 89 | 90 | @occ_vertex.setter 91 | def occ_vertex(self, occ_vertex: TopoDS.TopoDS_Vertex) -> None: 92 | self._occ_vertex = occ_vertex 93 | 94 | # ============================================================================== 95 | # Properties 96 | # ============================================================================== 97 | 98 | @property 99 | def point(self) -> Point: 100 | p = BRep.BRep_Tool.Pnt(self.occ_vertex) 101 | return Point(p.X(), p.Y(), p.Z()) 102 | 103 | @point.setter 104 | def point(self, point: Point) -> None: 105 | builder = BRepBuilderAPI.BRepBuilderAPI_MakeVertex(point_to_occ(point)) 106 | self._occ_vertex = builder.Vertex() 107 | 108 | # ============================================================================== 109 | # Constructors 110 | # ============================================================================== 111 | 112 | @classmethod 113 | def from_point(cls, point: Point) -> "BrepVertex": 114 | """Construct a vertex from a point. 115 | 116 | Parameters 117 | ---------- 118 | point : :class:`compas.geometry.Point` 119 | The point. 120 | 121 | Returns 122 | ------- 123 | :class:`BrepVertex` 124 | 125 | """ 126 | builder = BRepBuilderAPI.BRepBuilderAPI_MakeVertex(point_to_occ(point)) 127 | return cls(builder.Vertex()) 128 | -------------------------------------------------------------------------------- /src/compas_occ/brep/builder.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from OCC.Core import BRep 4 | from OCC.Core import BRepAlgo 5 | from OCC.Core import BRepBuilderAPI 6 | from OCC.Core import Geom 7 | from OCC.Core import Geom2d 8 | from OCC.Core import ShapeFix 9 | from OCC.Core import TopAbs 10 | from OCC.Core import TopoDS 11 | 12 | from compas.geometry import CurveType 13 | from compas.geometry import SurfaceType 14 | from compas_occ import conversions 15 | 16 | from .brep import OCCBrep 17 | 18 | 19 | class OCCBrepBuilder: 20 | """Class for building OCC Breps from serialisation data. 21 | 22 | Parameters 23 | ---------- 24 | make_solid : bool, optional 25 | """ 26 | 27 | def __init__(self, make_solid: Optional[bool] = True): 28 | self.shell = TopoDS.TopoDS_Shell() 29 | self.builder = BRep.BRep_Builder() 30 | self.builder.MakeShell(self.shell) 31 | self.make_solid = make_solid 32 | 33 | def build(self, faces: list[dict]) -> OCCBrep: 34 | """Build a COMPAS OCC brep from list of faces represented by their serialisation data. 35 | 36 | Parameters 37 | ---------- 38 | faces : list[dict] 39 | A list of faces represented by their serialisation data. 40 | 41 | Returns 42 | ------- 43 | :class:`OCCBrep` 44 | 45 | """ 46 | for facedata in faces: 47 | face = self.build_face(facedata) 48 | self.builder.Add(self.shell, face) 49 | 50 | brep = OCCBrep.from_native(self.shell) 51 | brep.heal() 52 | if self.make_solid: 53 | brep.make_solid() 54 | return brep 55 | 56 | def build_edge(self, edgedata: dict) -> TopoDS.TopoDS_Edge: 57 | """Build an OCC edge from edge serialisation data with 3D curve geometry. 58 | 59 | Parameters 60 | ---------- 61 | edgedata : dict 62 | The serialisation data representing an edge. 63 | 64 | Returns 65 | ------- 66 | `TopoDS.TopoDS_Edge` 67 | 68 | """ 69 | start = conversions.point_to_occ(edgedata["start"]) 70 | end = conversions.point_to_occ(edgedata["end"]) 71 | u, v = edgedata["domain"] 72 | 73 | if edgedata["type"] == CurveType.LINE: 74 | curve = conversions.line_to_occ(edgedata["curve"]) 75 | params = [start, end] 76 | 77 | elif edgedata["type"] == CurveType.CIRCLE: 78 | curve = Geom.Geom_Circle(conversions.circle_to_occ(edgedata["curve"])) 79 | params = [start, end, u, v] 80 | 81 | elif edgedata["type"] == CurveType.ELLIPSE: 82 | curve = Geom.Geom_Ellipse(conversions.ellipse_to_occ(edgedata["curve"])) 83 | params = [start, end, u, v] 84 | 85 | elif edgedata["type"] == CurveType.BSPLINE: 86 | curve = edgedata["curve"].native_curve 87 | params = [start, end, u, v] 88 | 89 | else: 90 | raise NotImplementedError 91 | 92 | return BRepBuilderAPI.BRepBuilderAPI_MakeEdge(curve, *params).Edge() 93 | 94 | def build_edge2d(self, edgedata, surface: Geom.Geom_Surface) -> TopoDS.TopoDS_Edge: 95 | """Build an OCC edge from edge serialisation data with 2D curve geometry embedded on a surface. 96 | 97 | Parameters 98 | ---------- 99 | edgedata : dict 100 | The serialisation data representing an edge. 101 | 102 | Returns 103 | ------- 104 | `TopoDS.TopoDS_Edge` 105 | 106 | """ 107 | start = conversions.point_to_occ(edgedata["start"]) 108 | end = conversions.point_to_occ(edgedata["end"]) 109 | 110 | u, v = edgedata["domain"] 111 | 112 | if edgedata["type"] == CurveType.LINE: 113 | curve = Geom2d.Geom2d_Line(conversions.line_to_occ2d(edgedata["curve"])) 114 | params = [start, end] 115 | 116 | elif edgedata["type"] == CurveType.CIRCLE: 117 | curve = Geom2d.Geom2d_Circle(conversions.circle_to_occ2d(edgedata["curve"])) 118 | params = [start, end, u, v] 119 | 120 | elif edgedata["type"] == CurveType.ELLIPSE: 121 | curve = Geom2d.Geom2d_Ellipse(conversions.ellipse_to_occ2d(edgedata["curve"])) 122 | params = [start, end, u, v] 123 | 124 | elif edgedata["type"] == CurveType.BSPLINE: 125 | curve = edgedata["curve"].native_curve 126 | params = [start, end, u, v] 127 | 128 | else: 129 | raise NotImplementedError 130 | 131 | return BRepBuilderAPI.BRepBuilderAPI_MakeEdge(curve, surface, *params).Edge() 132 | 133 | def build_wire(self, edges: list[dict], surface: Geom.Geom_Surface) -> TopoDS.TopoDS_Wire: 134 | """Build an OCC wire from a list of edges represented by their serialisation data. 135 | 136 | Parameters 137 | ---------- 138 | edges : list[dict] 139 | The serialisation data of the edges. 140 | surface : `Geom.Geom_Surface` 141 | The surface geometry of the face of the wire. 142 | 143 | Returns 144 | ------- 145 | `TopoDS.TopoDS_Wire` 146 | 147 | """ 148 | builder = BRepBuilderAPI.BRepBuilderAPI_MakeWire() 149 | 150 | for edgedata in edges: 151 | if edgedata["dimension"] == 2: 152 | edge = self.build_edge2d(edgedata, surface) 153 | else: 154 | edge = self.build_edge(edgedata) 155 | 156 | if edgedata["orientation"] != edge.Orientation(): 157 | edge.Orientation(TopAbs.TopAbs_Orientation(edgedata["orientation"])) 158 | 159 | builder.Add(edge) 160 | 161 | return builder.Wire() 162 | 163 | def build_face(self, data) -> TopoDS.TopoDS_Face: 164 | """Build an OCC face from face serialisation data. 165 | 166 | Parameters 167 | ---------- 168 | data : dict 169 | The serialisation data of the face. 170 | 171 | Returns 172 | ------- 173 | `TopoDS.TopoDS_Face` 174 | 175 | """ 176 | if data["type"] == SurfaceType.PLANE: 177 | plane = conversions.plane_to_occ(data["surface"]) 178 | surface = Geom.Geom_Plane(plane) 179 | 180 | elif data["type"] == SurfaceType.CYLINDER: 181 | cylinder = conversions.cylinder_to_occ(data["surface"]) 182 | surface = Geom.Geom_CylindricalSurface(cylinder) 183 | 184 | elif data["type"] == SurfaceType.SPHERE: 185 | sphere = conversions.sphere_to_occ(data["surface"]) 186 | surface = Geom.Geom_SphericalSurface(sphere) 187 | 188 | elif data["type"] == SurfaceType.BSPLINE_SURFACE: 189 | surface = data["surface"].native_surface 190 | 191 | else: 192 | raise NotImplementedError 193 | 194 | loops = data["loops"] 195 | boundary = self.build_wire(loops[0], surface) 196 | builder = BRepBuilderAPI.BRepBuilderAPI_MakeFace(surface, boundary) 197 | 198 | if len(loops) > 1: 199 | for edges in loops[1:]: 200 | hole = self.build_wire(edges, surface) 201 | builder.Add(hole) 202 | 203 | face = builder.Face() 204 | 205 | if data["orientation"] != face.Orientation(): 206 | face.Orientation(TopAbs.TopAbs_Orientation(data["orientation"])) 207 | 208 | if not BRepAlgo.brepalgo.IsValid(face): 209 | fixer = ShapeFix.ShapeFix_Face(face) 210 | fixer.Perform() 211 | face = fixer.Face() 212 | 213 | return face 214 | -------------------------------------------------------------------------------- /src/compas_occ/brep/errors.py: -------------------------------------------------------------------------------- 1 | class BrepError(Exception): 2 | """Base class for exceptions in this module.""" 3 | 4 | pass 5 | 6 | 7 | class BrepFilletError(BrepError): 8 | """Exception raised for errors in filleting.""" 9 | 10 | pass 11 | 12 | 13 | class BrepBooleanError(BrepError): 14 | """Exception raised for errors in (re-)building breps.""" 15 | 16 | pass 17 | -------------------------------------------------------------------------------- /src/compas_occ/conversions/__init__.py: -------------------------------------------------------------------------------- 1 | from .arrays import array1_from_integers1 2 | from .arrays import array1_from_floats1 3 | from .arrays import array2_from_floats2 4 | from .arrays import array1_from_points1 5 | from .arrays import harray1_from_points1 6 | from .arrays import array2_from_points2 7 | from .arrays import points1_from_array1 8 | from .arrays import points2_from_array2 9 | from .arrays import floats2_from_array2 10 | 11 | from .geometry import axis_to_occ 12 | from .geometry import circle_to_occ 13 | from .geometry import circle_to_occ2d 14 | from .geometry import cone_to_occ 15 | from .geometry import cylinder_to_occ 16 | from .geometry import ellipse_to_occ 17 | from .geometry import ellipse_to_occ2d 18 | from .geometry import frame_to_occ_ax2 19 | from .geometry import frame_to_occ_ax22d 20 | from .geometry import frame_to_occ_ax3 21 | from .geometry import line_to_occ 22 | from .geometry import line_to_occ2d 23 | from .geometry import plane_to_occ 24 | from .geometry import plane_to_occ_ax2 25 | from .geometry import plane_to_occ_ax3 26 | from .geometry import point_to_occ 27 | from .geometry import point_to_occ2d 28 | from .geometry import sphere_to_occ 29 | from .geometry import torus_to_occ 30 | from .geometry import vector_to_occ 31 | from .geometry import vector_to_occ2d 32 | from .geometry import direction_to_occ 33 | from .geometry import direction_to_occ2d 34 | 35 | from .geometry import ax2_to_compas 36 | from .geometry import ax22d_to_compas 37 | from .geometry import ax3_to_compas 38 | from .geometry import axis_to_compas 39 | from .geometry import axis_to_compas_vector 40 | from .geometry import axis2d_to_compas 41 | from .geometry import axis2d_to_compas_vector 42 | from .geometry import bezier_to_compas 43 | from .geometry import bspline_to_compas 44 | from .geometry import circle_to_compas 45 | from .geometry import circle2d_to_compas 46 | from .geometry import cylinder_to_compas 47 | from .geometry import direction_to_compas 48 | from .geometry import direction2d_to_compas 49 | from .geometry import ellipse_to_compas 50 | from .geometry import ellipse2d_to_compas 51 | from .geometry import hyperbola_to_compas 52 | from .geometry import hyperbola2d_to_compas 53 | from .geometry import line_to_compas 54 | from .geometry import line2d_to_compas 55 | from .geometry import location_to_compas 56 | from .geometry import parabola_to_compas 57 | from .geometry import parabola2d_to_compas 58 | from .geometry import plane_to_compas 59 | from .geometry import point_to_compas 60 | from .geometry import point2d_to_compas 61 | from .geometry import vector_to_compas 62 | from .geometry import vector2d_to_compas 63 | from .geometry import sphere_to_compas 64 | 65 | from .geometry import aabb_to_compas 66 | from .geometry import obb_to_compas 67 | 68 | from .transformations import compas_transformation_to_trsf 69 | 70 | from .meshes import compas_mesh_to_occ_shell 71 | from .meshes import compas_quadmesh_to_occ_shell 72 | from .meshes import compas_trimesh_to_occ_shell 73 | from .meshes import ngon_to_face 74 | from .meshes import quad_to_face 75 | from .meshes import triangle_to_face 76 | 77 | __all__ = [ 78 | "array1_from_floats1", 79 | "array1_from_integers1", 80 | "array1_from_points1", 81 | "array2_from_floats2", 82 | "array2_from_points2", 83 | "aabb_to_compas", 84 | "axis_to_compas", 85 | "axis2d_to_compas", 86 | "axis_to_compas_vector", 87 | "axis2d_to_compas_vector", 88 | "ax2_to_compas", 89 | "ax22d_to_compas", 90 | "ax3_to_compas", 91 | "bezier_to_compas", 92 | "bspline_to_compas", 93 | "circle_to_compas", 94 | "circle2d_to_compas", 95 | "cylinder_to_compas", 96 | "direction_to_compas", 97 | "direction2d_to_compas", 98 | "ellipse_to_compas", 99 | "ellipse2d_to_compas", 100 | "hyperbola_to_compas", 101 | "hyperbola2d_to_compas", 102 | "line_to_compas", 103 | "line2d_to_compas", 104 | "location_to_compas", 105 | "obb_to_compas", 106 | "parabola_to_compas", 107 | "parabola2d_to_compas", 108 | "plane_to_compas", 109 | "point_to_compas", 110 | "point2d_to_compas", 111 | "sphere_to_compas", 112 | "vector_to_compas", 113 | "vector2d_to_compas", 114 | "axis_to_occ", 115 | "circle_to_occ", 116 | "circle_to_occ2d", 117 | "cone_to_occ", 118 | "cylinder_to_occ", 119 | "direction_to_occ", 120 | "direction_to_occ2d", 121 | "ellipse_to_occ", 122 | "ellipse_to_occ2d", 123 | "frame_to_occ_ax2", 124 | "frame_to_occ_ax22d", 125 | "frame_to_occ_ax3", 126 | "line_to_occ", 127 | "line_to_occ2d", 128 | "plane_to_occ_ax2", 129 | "plane_to_occ_ax3", 130 | "plane_to_occ", 131 | "point_to_occ", 132 | "point_to_occ2d", 133 | "sphere_to_occ", 134 | "torus_to_occ", 135 | "vector_to_occ", 136 | "vector_to_occ2d", 137 | "compas_transformation_to_trsf", 138 | "compas_mesh_to_occ_shell", 139 | "compas_quadmesh_to_occ_shell", 140 | "compas_trimesh_to_occ_shell", 141 | "floats2_from_array2", 142 | "harray1_from_points1", 143 | "ngon_to_face", 144 | "points1_from_array1", 145 | "points2_from_array2", 146 | "quad_to_face", 147 | "triangle_to_face", 148 | ] 149 | -------------------------------------------------------------------------------- /src/compas_occ/conversions/meshes.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | from typing import Union 3 | 4 | from OCC.Core.BRep import BRep_Builder 5 | from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace 6 | from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakePolygon 7 | from OCC.Core.BRepFill import BRepFill_Filling 8 | from OCC.Core.GeomAbs import GeomAbs_C0 9 | from OCC.Core.GeomAPI import GeomAPI_PointsToBSpline 10 | from OCC.Core.GeomFill import geomfill 11 | from OCC.Core.gp import gp_Pnt 12 | from OCC.Core.TopoDS import TopoDS_Face 13 | from OCC.Core.TopoDS import TopoDS_Shell 14 | from OCC.Extend.TopologyUtils import TopologyExplorer 15 | 16 | import compas.geometry 17 | from compas.datastructures import Mesh 18 | from compas.geometry import Point 19 | from compas.geometry import Polygon 20 | 21 | from .arrays import array1_from_points1 22 | 23 | Triangle = Union[Polygon, Annotated[list[Union[Annotated[list[float], 3], compas.geometry.Point]], 3]] 24 | Quad = Union[Polygon, Annotated[list[Union[Annotated[list[float], 3], compas.geometry.Point]], 4]] 25 | NGon = Union[Polygon, list[Union[Annotated[list[float], 3], compas.geometry.Point]]] 26 | 27 | 28 | def triangle_to_face(triangle: Triangle) -> TopoDS_Face: 29 | """Convert a triangle to a BRep face. 30 | 31 | Parameters 32 | ---------- 33 | triangle : [point, point, point] 34 | Three points defining a triangle. 35 | 36 | Returns 37 | ------- 38 | TopoDS_Face 39 | 40 | Raises 41 | ------ 42 | ValueError 43 | If the number of points is not 3. 44 | 45 | See Also 46 | -------- 47 | :func:`quad_to_face` 48 | :func:`ngon_to_face` 49 | 50 | Examples 51 | -------- 52 | >>> triangle = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] 53 | >>> triangle_to_face(triangle) 54 | 55 | 56 | """ 57 | if len(triangle) != 3: 58 | raise ValueError("The number of input points should be three.") 59 | 60 | polygon = BRepBuilderAPI_MakePolygon() 61 | for point in triangle: 62 | polygon.Add(gp_Pnt(*point)) 63 | polygon.Close() 64 | wire = polygon.Wire() 65 | return BRepBuilderAPI_MakeFace(wire).Face() 66 | 67 | 68 | def quad_to_face(quad: Quad) -> TopoDS_Face: 69 | """Convert a quad to a BRep face with an underlying ruled surface. 70 | 71 | Parameters 72 | ---------- 73 | quad : [point, point, point, point] 74 | Four points defining a quad. 75 | 76 | Returns 77 | ------- 78 | TopoDS_Face 79 | 80 | Raises 81 | ------ 82 | ValueError 83 | If the number of points is not 4. 84 | 85 | See Also 86 | -------- 87 | :func:`triangle_to_face` 88 | :func:`ngon_to_face` 89 | 90 | Examples 91 | -------- 92 | >>> quad = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]] 93 | >>> quad_to_face(quad) 94 | 95 | 96 | """ 97 | if len(quad) != 4: 98 | raise ValueError("The number of input points should be four.") 99 | 100 | points = [Point(*point) for point in quad] 101 | curve1 = GeomAPI_PointsToBSpline(array1_from_points1([points[0], points[1]])).Curve() 102 | curve2 = GeomAPI_PointsToBSpline(array1_from_points1([points[3], points[2]])).Curve() 103 | srf = geomfill.Surface(curve1, curve2) 104 | return BRepBuilderAPI_MakeFace(srf, 1e-6).Face() 105 | 106 | 107 | def ngon_to_face(ngon: NGon) -> TopoDS_Face: 108 | """Convert a Ngon to a BRep face with an underlying best-fit surface. 109 | 110 | Parameters 111 | ---------- 112 | ngon : sequence[point] 113 | Points defining a polygon. 114 | 115 | Returns 116 | ------- 117 | TopoDS_Face 118 | 119 | See Also 120 | -------- 121 | :func:`triangle_to_face` 122 | :func:`quad_to_face` 123 | 124 | """ 125 | points = [gp_Pnt(*point) for point in ngon] 126 | poly = BRepBuilderAPI_MakePolygon() 127 | for point in points: 128 | poly.Add(point) 129 | poly.Build() 130 | poly.Close() 131 | edges = TopologyExplorer(poly.Wire()).edges() 132 | nsided = BRepFill_Filling() 133 | for edge in edges: 134 | nsided.Add(edge, GeomAbs_C0) # type: ignore 135 | nsided.Build() 136 | return nsided.Face() 137 | 138 | 139 | def compas_trimesh_to_occ_shell(mesh: Mesh) -> TopoDS_Shell: 140 | """Convert a COMPAS triangle mesh to an OCC shell. 141 | 142 | Parameters 143 | ---------- 144 | mesh : :class:`~compas.datastructures.Mesh` 145 | A COMPAS mesh data structure with triangle faces. 146 | 147 | Returns 148 | ------- 149 | TopoDS_Shell 150 | 151 | Raises 152 | ------ 153 | ValueError 154 | If the mesh is not a triangle mesh. 155 | 156 | See Also 157 | -------- 158 | :func:`compas_quadmesh_to_occ_shell` 159 | :func:`compas_mesh_to_occ_shell` 160 | 161 | """ 162 | if not mesh.is_trimesh(): 163 | raise ValueError("The input mesh is not a triangle mesh.") 164 | 165 | shell = TopoDS_Shell() 166 | builder = BRep_Builder() 167 | builder.MakeShell(shell) 168 | 169 | for face in mesh.faces(): 170 | points = mesh.face_coordinates(face) 171 | builder.Add(shell, triangle_to_face(points)) # type: ignore 172 | 173 | return shell 174 | 175 | 176 | def compas_quadmesh_to_occ_shell(mesh: Mesh) -> TopoDS_Shell: 177 | """Convert a COMPAS quad mesh to an OCC shell. 178 | 179 | Parameters 180 | ---------- 181 | mesh : :class:`~compas.datastructures.Mesh` 182 | A COMPAS mesh data structure with quad faces. 183 | 184 | Returns 185 | ------- 186 | TopoDS_Shell 187 | 188 | Raises 189 | ------ 190 | ValueError 191 | If the input mesh is not a quad mesh. 192 | 193 | See Also 194 | -------- 195 | :func:`compas_trimesh_to_occ_shell` 196 | :func:`compas_mesh_to_occ_shell` 197 | 198 | """ 199 | if not mesh.is_quadmesh(): 200 | raise ValueError("The input mesh is not a quad mesh.") 201 | 202 | shell = TopoDS_Shell() 203 | builder = BRep_Builder() 204 | builder.MakeShell(shell) 205 | 206 | for face in mesh.faces(): 207 | points = mesh.face_coordinates(face) 208 | builder.Add(shell, quad_to_face(points)) # type: ignore 209 | 210 | return shell 211 | 212 | 213 | def compas_mesh_to_occ_shell(mesh: Mesh) -> TopoDS_Shell: 214 | """Convert a general COMPAS mesh to an OCC shell. 215 | 216 | Parameters 217 | ---------- 218 | mesh : :class:`~compas.datastructures.Mesh` 219 | A COMPAS mesh data structure. 220 | 221 | Returns 222 | ------- 223 | TopoDS_Shell 224 | 225 | See Also 226 | -------- 227 | :func:`compas_trimesh_to_occ_shell` 228 | :func:`compas_quadmesh_to_occ_shell` 229 | 230 | """ 231 | shell = TopoDS_Shell() 232 | builder = BRep_Builder() 233 | builder.MakeShell(shell) 234 | 235 | for face in mesh.faces(): 236 | points = mesh.face_coordinates(face) 237 | 238 | if len(points) == 3: 239 | builder.Add(shell, triangle_to_face(points)) # type: ignore 240 | elif len(points) == 4: 241 | builder.Add(shell, quad_to_face(points)) # type: ignore 242 | else: 243 | builder.Add(shell, ngon_to_face(points)) # type: ignore 244 | 245 | return shell 246 | -------------------------------------------------------------------------------- /src/compas_occ/conversions/transformations.py: -------------------------------------------------------------------------------- 1 | from OCC.Core.gp import gp_Trsf 2 | 3 | import compas.geometry 4 | 5 | 6 | def compas_transformation_to_trsf(matrix: compas.geometry.Transformation): 7 | """Convert a COMPAS transformation to a OCC transformation. 8 | 9 | Parameters 10 | ---------- 11 | matrix : :class:`~compas.geometry.Transformation` 12 | A COMPAS transformation. 13 | 14 | Returns 15 | ------- 16 | gp_Trsf 17 | An OCC transformation. 18 | 19 | Examples 20 | -------- 21 | >>> from compas.geometry import Translation 22 | >>> from compas_occ.conversions import compas_transformation_to_trsf 23 | >>> transformation = Translation.from_vector([1, 2, 3]) 24 | >>> compas_transformation_to_trsf(transformation) 25 | 26 | 27 | """ 28 | trsf = gp_Trsf() 29 | trsf.SetValues(*matrix.list[:12]) 30 | return trsf 31 | -------------------------------------------------------------------------------- /src/compas_occ/geometry/__init__.py: -------------------------------------------------------------------------------- 1 | from .curves import OCCCurve2d # noqa: F401 2 | from .curves import OCCCurve # noqa: F401 3 | from .curves import OCCNurbsCurve # noqa: F401 4 | 5 | from .surfaces import OCCSurface # noqa: F401 6 | from .surfaces import OCCNurbsSurface # noqa: F401 7 | -------------------------------------------------------------------------------- /src/compas_occ/geometry/curves/__init__.py: -------------------------------------------------------------------------------- 1 | from .curve2d import OCCCurve2d # noqa : F401 2 | from .curve import OCCCurve 3 | from .nurbs import OCCNurbsCurve 4 | 5 | from compas.plugins import plugin 6 | 7 | 8 | @plugin(category="factories", requires=["compas_occ"]) 9 | def curve_from_native(cls, *args, **kwargs): 10 | return OCCCurve.from_native(*args, **kwargs) 11 | 12 | 13 | @plugin(category="factories", requires=["compas_occ"]) 14 | def nurbscurve_from_native(cls, *args, **kwargs): 15 | return OCCNurbsCurve.from_native(*args, **kwargs) 16 | 17 | 18 | @plugin(category="factories", requires=["compas_occ"]) 19 | def nurbscurve_from_interpolation(cls, *args, **kwargs): 20 | return OCCNurbsCurve.from_interpolation(*args, **kwargs) 21 | 22 | 23 | @plugin(category="factories", requires=["compas_occ"]) 24 | def nurbscurve_from_parameters(cls, *args, **kwargs): 25 | return OCCNurbsCurve.from_parameters(*args, **kwargs) 26 | 27 | 28 | @plugin(category="factories", requires=["compas_occ"]) 29 | def nurbscurve_from_points(cls, *args, **kwargs): 30 | return OCCNurbsCurve.from_points(*args, **kwargs) 31 | 32 | 33 | @plugin(category="factories", requires=["compas_occ"]) 34 | def nurbscurve_from_step(cls, *args, **kwargs): 35 | return OCCNurbsCurve.from_step(*args, **kwargs) 36 | -------------------------------------------------------------------------------- /src/compas_occ/geometry/curves/bezier.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compas-dev/compas_occ/b494f60d0ede843dc637ba10b1bb580cfd0ac02c/src/compas_occ/geometry/curves/bezier.py -------------------------------------------------------------------------------- /src/compas_occ/geometry/curves/curve2d.py: -------------------------------------------------------------------------------- 1 | from OCC.Core import BRepBuilderAPI 2 | from OCC.Core import Geom2d 3 | from OCC.Core import IFSelect 4 | from OCC.Core import Interface 5 | from OCC.Core import STEPControl 6 | from OCC.Core import TopoDS 7 | from OCC.Core import gp 8 | 9 | from compas.geometry import Curve 10 | from compas.geometry import Frame 11 | from compas.geometry import Point 12 | from compas.geometry import Polyline 13 | from compas.geometry import Vector 14 | from compas_occ.conversions import point2d_to_compas 15 | from compas_occ.conversions import vector2d_to_compas 16 | 17 | 18 | class OCCCurve2d(Curve): 19 | """Class representing a general 2D curve object ussually generated through an embedding in a surface. 20 | 21 | Parameters 22 | ---------- 23 | name : str, optional 24 | The name of the curve. 25 | 26 | Attributes 27 | ---------- 28 | dimension : int, read-only 29 | The dimension of the curve is always 2. 30 | domain : tuple[float, float], read-only 31 | The domain of the parameter space of the curve. 32 | end : :class:`~compas.geometry.Point`, read-only 33 | The end point of the curve. 34 | is_closed : bool, read-only 35 | Flag indicating that the curve is closed. 36 | is_periodic : bool, read-only 37 | Flag indicating that the curve is periodic. 38 | start : :class:`~compas.geometry.Point`, read-only 39 | The start point of the curve. 40 | 41 | """ 42 | 43 | _native_curve: Geom2d.Geom2d_Curve 44 | native_curve: Geom2d.Geom2d_Curve 45 | 46 | def __init__(self, native_curve: Geom2d.Geom2d_Curve, name=None): 47 | super().__init__(name=name) 48 | self._dimension = 2 49 | self._native_curve = native_curve 50 | 51 | def __eq__(self, other: "OCCCurve2d") -> bool: 52 | raise NotImplementedError 53 | 54 | # ============================================================================== 55 | # Data 56 | # ============================================================================== 57 | 58 | # ============================================================================== 59 | # OCC Properties 60 | # ============================================================================== 61 | 62 | @property 63 | def occ_curve(self) -> Geom2d.Geom2d_Curve: 64 | return self._native_curve 65 | 66 | @property 67 | def occ_shape(self) -> TopoDS.TopoDS_Shape: 68 | return BRepBuilderAPI.BRepBuilderAPI_MakeEdge2d(self.native_curve).Shape() 69 | 70 | @property 71 | def occ_edge(self) -> TopoDS.TopoDS_Edge: 72 | return TopoDS.topods.Edge(self.occ_shape) 73 | 74 | # ============================================================================== 75 | # Properties 76 | # ============================================================================== 77 | 78 | @property 79 | def dimension(self) -> int: 80 | return self._dimension 81 | 82 | @property 83 | def domain(self) -> tuple[float, float]: 84 | return self.native_curve.FirstParameter(), self.native_curve.LastParameter() 85 | 86 | @property 87 | def start(self) -> Point: 88 | return self.point_at(self.domain[0]) 89 | 90 | @property 91 | def end(self) -> Point: 92 | return self.point_at(self.domain[1]) 93 | 94 | @property 95 | def is_closed(self) -> bool: 96 | return self.native_curve.IsClosed() 97 | 98 | @property 99 | def is_periodic(self) -> bool: 100 | return self.native_curve.IsPeriodic() 101 | 102 | # ============================================================================== 103 | # Constructors 104 | # ============================================================================== 105 | 106 | @classmethod 107 | def from_native(cls, native_curve: Geom2d.Geom2d_Curve) -> "OCCCurve2d": 108 | """Construct a NURBS curve from an existing OCC BSplineCurve. 109 | 110 | Parameters 111 | ---------- 112 | native_curve : Geom2d_Curve 113 | 114 | Returns 115 | ------- 116 | :class:`OCCCurve2d` 117 | 118 | """ 119 | return cls(native_curve) 120 | 121 | @classmethod 122 | def from_occ(cls, native_curve: Geom2d.Geom2d_Curve) -> "OCCCurve2d": 123 | """Construct a NURBS curve from an existing OCC BSplineCurve. 124 | 125 | Parameters 126 | ---------- 127 | native_curve : Geom2d_Curve 128 | 129 | Returns 130 | ------- 131 | :class:`OCCCurve2d` 132 | 133 | Warnings 134 | -------- 135 | .. deprecated:: 1.3 136 | Use `from_native` instead. 137 | 138 | """ 139 | return cls(native_curve) 140 | 141 | # ============================================================================== 142 | # Conversions 143 | # ============================================================================== 144 | 145 | def to_step(self, filepath: str, schema: str = "AP203") -> None: 146 | """Write the curve geometry to a STP file. 147 | 148 | Parameters 149 | ---------- 150 | filepath : str 151 | schema : str, optional 152 | 153 | Returns 154 | ------- 155 | None 156 | 157 | """ 158 | step_writer = STEPControl.STEPControl_Writer() 159 | Interface.Interface_Static.SetCVal("write.step.schema", schema) 160 | step_writer.Transfer(self.occ_edge, STEPControl.STEPControl_AsIs) # type: ignore 161 | status = step_writer.Write(filepath) 162 | if status != IFSelect.IFSelect_RetDone: 163 | raise AssertionError("Operation failed.") 164 | 165 | def to_polyline(self, n: int = 100) -> Polyline: 166 | """Convert the curve to a polyline. 167 | 168 | Parameters 169 | ---------- 170 | n : int, optional 171 | The number of polyline points. 172 | 173 | Returns 174 | ------- 175 | :class:`compas.geometry.Polyline` 176 | 177 | """ 178 | return Polyline(self.to_points(n=n)) 179 | 180 | # ============================================================================== 181 | # Methods 182 | # ============================================================================== 183 | 184 | def copy(self) -> "OCCCurve2d": 185 | """Make an independent copy of the current curve. 186 | 187 | Returns 188 | ------- 189 | :class:`OCCCurve2d` 190 | 191 | """ 192 | cls = type(self) 193 | native_curve = self.native_curve.Copy() 194 | return cls(native_curve) # type: ignore (Copy returns Geom2d_Geometry) 195 | 196 | def point_at(self, t: float) -> Point: 197 | """Compute the point at a curve parameter. 198 | 199 | Parameters 200 | ---------- 201 | t : float 202 | The curve parameter. 203 | 204 | Returns 205 | ------- 206 | :class:`~compas.geometry.Point` 207 | 208 | Raises 209 | ------ 210 | ValueError 211 | If the parameter is not in the curve domain. 212 | 213 | """ 214 | start, end = self.domain 215 | if t < start or t > end: 216 | raise ValueError("The parameter is not in the domain of the curve. t = {}, domain: {}".format(t, self.domain)) 217 | 218 | point = self.native_curve.Value(t) 219 | return point2d_to_compas(point) 220 | 221 | def tangent_at(self, t: float) -> Vector: 222 | """Compute the tangent vector at a curve parameter. 223 | 224 | Parameters 225 | ---------- 226 | t : float 227 | The curve parameter. 228 | 229 | Returns 230 | ------- 231 | :class:`~compas.geometry.Vector` 232 | 233 | Raises 234 | ------ 235 | ValueError 236 | If the parameter is not in the curve domain. 237 | 238 | """ 239 | start, end = self.domain 240 | if t < start or t > end: 241 | raise ValueError("The parameter is not in the domain of the curve.") 242 | 243 | point = gp.gp_Pnt2d() 244 | uvec = gp.gp_Vec2d() 245 | self.native_curve.D1(t, point, uvec) 246 | return vector2d_to_compas(uvec) 247 | 248 | def curvature_at(self, t: float) -> Vector: 249 | """Compute the curvature vector at a curve parameter. 250 | 251 | Parameters 252 | ---------- 253 | t : float 254 | The curve parameter. 255 | 256 | Returns 257 | ------- 258 | :class:`~compas.geometry.Vector` 259 | 260 | Raises 261 | ------ 262 | ValueError 263 | If the parameter is not in the curve domain. 264 | 265 | """ 266 | start, end = self.domain 267 | if t < start or t > end: 268 | raise ValueError("The parameter is not in the domain of the curve.") 269 | 270 | point = gp.gp_Pnt2d() 271 | uvec = gp.gp_Vec2d() 272 | vvec = gp.gp_Vec2d() 273 | self.native_curve.D2(t, point, uvec, vvec) 274 | return vector2d_to_compas(vvec) 275 | 276 | def frame_at(self, t: float) -> Frame: 277 | """Compute the local frame at a curve parameter. 278 | 279 | Parameters 280 | ---------- 281 | t : float 282 | The curve parameter. 283 | 284 | Returns 285 | ------- 286 | :class:`~compas.geometry.Frame` 287 | 288 | Raises 289 | ------ 290 | ValueError 291 | If the parameter is not in the curve domain. 292 | 293 | """ 294 | start, end = self.domain 295 | if t < start or t > end: 296 | raise ValueError("The parameter is not in the domain of the curve.") 297 | 298 | point = gp.gp_Pnt2d() 299 | uvec = gp.gp_Vec2d() 300 | vvec = gp.gp_Vec2d() 301 | self.native_curve.D2(t, point, uvec, vvec) 302 | 303 | return Frame( 304 | point2d_to_compas(point), 305 | vector2d_to_compas(uvec), 306 | vector2d_to_compas(vvec), 307 | ) 308 | -------------------------------------------------------------------------------- /src/compas_occ/geometry/surfaces/__init__.py: -------------------------------------------------------------------------------- 1 | from .surface import OCCSurface 2 | from .nurbs import OCCNurbsSurface 3 | 4 | from compas.plugins import plugin 5 | 6 | 7 | @plugin(category="factories", requires=["compas_occ"]) 8 | def surface_from_native(cls, *args, **kwargs): 9 | return OCCSurface.from_native(*args, **kwargs) 10 | 11 | 12 | @plugin(category="factories", requires=["compas_occ"]) 13 | def nurbssurface_from_extrusion(cls, *args, **kwargs): 14 | return OCCNurbsSurface.from_extrusion(*args, **kwargs) 15 | 16 | 17 | @plugin(category="factories", requires=["compas_occ"]) 18 | def nurbssurface_from_fill(cls, *args, **kwargs): 19 | return OCCNurbsSurface.from_fill(*args, **kwargs) 20 | 21 | 22 | @plugin(category="factories", requires=["compas_occ"]) 23 | def nurbssurface_from_interpolation(cls, *args, **kwargs): 24 | return OCCNurbsSurface.from_interpolation(*args, **kwargs) 25 | 26 | 27 | @plugin(category="factories", requires=["compas_occ"]) 28 | def nurbssurface_from_native(cls, *args, **kwargs): 29 | return OCCNurbsSurface.from_native(*args, **kwargs) 30 | 31 | 32 | @plugin(category="factories", requires=["compas_occ"]) 33 | def nurbssurface_from_parameters(cls, *args, **kwargs): 34 | return OCCNurbsSurface.from_parameters(*args, **kwargs) 35 | 36 | 37 | @plugin(category="factories", requires=["compas_occ"]) 38 | def nurbssurface_from_plane(cls, *args, **kwargs): 39 | return OCCNurbsSurface.from_plane(*args, **kwargs) 40 | 41 | 42 | @plugin(category="factories", requires=["compas_occ"]) 43 | def nurbssurface_from_points(cls, *args, **kwargs): 44 | return OCCNurbsSurface.from_points(*args, **kwargs) 45 | 46 | 47 | @plugin(category="factories", requires=["compas_occ"]) 48 | def nurbssurface_from_step(cls, *args, **kwargs): 49 | return OCCNurbsSurface.from_step(*args, **kwargs) 50 | -------------------------------------------------------------------------------- /src/compas_occ/geometry/surfaces/__temp/extrusion.py: -------------------------------------------------------------------------------- 1 | from OCC.Core.Geom import Geom_Curve 2 | from OCC.Core.Geom import Geom_SurfaceOfLinearExtrusion 3 | 4 | from compas.geometry import Vector 5 | from compas_occ.conversions.geometry import direction_to_occ 6 | from compas_occ.geometry.curves.curve import OCCCurve 7 | from compas_occ.geometry.surfaces.surface import OCCSurface 8 | 9 | 10 | class OCCExtrusionSurface(OCCSurface): 11 | """Class representing an extrusion surface based on the corresponding surface of the OCC kernel. 12 | 13 | Note that extrusion surfaces have an infinite parameter space in the "v" direction. 14 | 15 | Parameters 16 | ---------- 17 | curve : :class:`compas_occ.geometry.OCCCurve` 18 | The base curve for the extrusion. 19 | The curve should be planar. 20 | vector : :class:`compas.geometry.Vector` 21 | The direction of the extrusion. 22 | 23 | Attributes 24 | ---------- 25 | curve : :class:`compas_occ.geometry.OCCCurve` 26 | the base curve for the extrusion. 27 | vector : :class:`compas.vector.Vector` 28 | The direction of the extrusion. 29 | 30 | Examples 31 | -------- 32 | >>> 33 | 34 | """ 35 | 36 | def __init__(self, curve, vector=None, **kwargs): 37 | super().__init__(**kwargs) 38 | self._curve = None 39 | self._vector = None 40 | self.curve = curve 41 | self.vector = vector 42 | self.compute() 43 | 44 | @property 45 | def curve(self): 46 | return self._curve 47 | 48 | @curve.setter 49 | def curve(self, value): 50 | if isinstance(value, Geom_Curve): 51 | value = OCCCurve.from_occ(value) 52 | self._curve = value 53 | 54 | @property 55 | def vector(self): 56 | if self._vector is None: 57 | self._vector = Vector(0, 0, 1) 58 | return self._vector 59 | 60 | @vector.setter 61 | def vector(self, value): 62 | if value: 63 | value = Vector(*value) 64 | self._vector = value 65 | 66 | def compute(self): 67 | """Compute the extrusion surface using the current curve and direction. 68 | 69 | Returns 70 | ------- 71 | None 72 | 73 | """ 74 | direction = direction_to_occ(self.vector) 75 | surface = Geom_SurfaceOfLinearExtrusion(self.curve.occ_curve, direction) # type: ignore 76 | self.occ_surface = surface 77 | -------------------------------------------------------------------------------- /src/compas_occ/geometry/surfaces/__temp/revolution.py: -------------------------------------------------------------------------------- 1 | from OCC.Core.Geom import Geom_Curve 2 | from OCC.Core.Geom import Geom_SurfaceOfRevolution 3 | 4 | from compas.geometry import Point 5 | from compas.geometry import Vector 6 | from compas_occ.conversions.geometry import axis_to_occ 7 | from compas_occ.geometry.curves.curve import OCCCurve 8 | from compas_occ.geometry.surfaces.surface import OCCSurface 9 | 10 | 11 | class OCCRevolutionSurface(OCCSurface): 12 | """Class representing a surface of revolution based on the corresponding surface of the OCC kernel. 13 | 14 | Parameters 15 | ---------- 16 | curve : :class:`compas_occ.geometry.OCCCurve` 17 | The base curve for the revolution. 18 | The curve should be planar. 19 | point : :class:`compas.geometry.Point` 20 | The location of the axis of revolution. 21 | vector : :class:`compas.geometry.Vector` 22 | The direction of the axis of revolution. 23 | 24 | Attributes 25 | ---------- 26 | curve : :class:`compas_occ.geometry.OCCCurve` 27 | The base curve for the revolution. 28 | point : :class:`compas.geometry.Point` 29 | The location of the axis of revolution. 30 | vector : :class:`compas.geometry.Vector` 31 | The direction of the axis of revolution. 32 | 33 | Examples 34 | -------- 35 | >>> 36 | 37 | """ 38 | 39 | def __init__(self, curve, point=None, vector=None, **kwargs): 40 | super().__init__(**kwargs) 41 | self._curve = None 42 | self._point = None 43 | self._vector = None 44 | self.curve = curve 45 | self.point = point 46 | self.vector = vector 47 | self.compute() 48 | 49 | @property 50 | def curve(self): 51 | return self._curve 52 | 53 | @curve.setter 54 | def curve(self, value): 55 | if isinstance(value, Geom_Curve): 56 | value = OCCCurve.from_occ(value) 57 | self._curve = value 58 | 59 | @property 60 | def point(self): 61 | if self._point is None: 62 | self._point = Point(0, 0, 0) 63 | return self._point 64 | 65 | @point.setter 66 | def point(self, value): 67 | if value: 68 | value = Point(*value) 69 | self._point = value 70 | 71 | @property 72 | def vector(self): 73 | if self._vector is None: 74 | self._vector = Vector(0, 0, 1) 75 | return self._vector 76 | 77 | @vector.setter 78 | def vector(self, value): 79 | if value: 80 | value = Vector(*value) 81 | self._vector = value 82 | 83 | def compute(self): 84 | """Compute the surface of revolution using the current curve and axis. 85 | 86 | Returns 87 | ------- 88 | None 89 | 90 | """ 91 | axis = axis_to_occ((self.point, self.vector)) 92 | surface = Geom_SurfaceOfRevolution(self.curve.occ_curve, axis) # type: ignore 93 | self.occ_surface = surface 94 | -------------------------------------------------------------------------------- /src/compas_occ/occ.py: -------------------------------------------------------------------------------- 1 | from OCC.Core import TopAbs 2 | from OCC.Core import TopExp 3 | from OCC.Core import TopoDS 4 | from OCC.Core.BOPAlgo import BOPAlgo_Splitter 5 | from OCC.Core.BRepGProp import brepgprop 6 | from OCC.Core.GProp import GProp_GProps 7 | from OCC.Core.TopoDS import TopoDS_Compound 8 | from OCC.Core.TopoDS import TopoDS_Iterator 9 | 10 | from compas.geometry import Point 11 | 12 | from .conversions import point_to_compas 13 | 14 | 15 | def split_shapes(arguments: list[TopoDS.TopoDS_Shape], tools: list[TopoDS.TopoDS_Shape]) -> list[TopoDS.TopoDS_Shape]: 16 | """Split a group of breps by another group of breps. 17 | 18 | Parameters 19 | ---------- 20 | arguments : list[TopoDS.TopoDS_Shape] 21 | The arguments passed to the command. 22 | tools : list[TopoDS.TopoDS_Shape] 23 | The tools passed to the command. 24 | 25 | Returns 26 | ------- 27 | list[TopoDS.TopoDS_Shape] 28 | The resulting breps. 29 | 30 | """ 31 | splitter = BOPAlgo_Splitter() 32 | 33 | for occ_shape in arguments: 34 | splitter.AddArgument(occ_shape) 35 | for occ_shape in tools: 36 | splitter.AddTool(occ_shape) 37 | 38 | splitter.Perform() 39 | shape = splitter.Shape() 40 | 41 | results = [] 42 | 43 | if isinstance(shape, TopoDS_Compound): 44 | it = TopoDS_Iterator(shape) 45 | while it.More(): 46 | results.append(it.Value()) 47 | it.Next() 48 | else: 49 | results.append(shape) 50 | 51 | return results 52 | 53 | 54 | def compute_shape_centreofmass(occ_shape: TopoDS.TopoDS_Shape) -> Point: 55 | """Return a COMPAS Point at the centre of mass of a Brep. 56 | 57 | Parameters 58 | ---------- 59 | occ_shape : TopoDS.TopoDS_Shape 60 | The brep. 61 | 62 | Returns 63 | ------- 64 | :class:`Point` 65 | The centroid. 66 | 67 | """ 68 | props = GProp_GProps() 69 | brepgprop.VolumeProperties(occ_shape, props) 70 | pnt = props.CentreOfMass() 71 | return point_to_compas(pnt) 72 | 73 | 74 | def occ_shape_find_vertices(occ_shape: TopoDS.TopoDS_Shape) -> list[TopoDS.TopoDS_Vertex]: 75 | """Find all the vertices in an OCC shape. 76 | 77 | Parameters 78 | ---------- 79 | occ_shape : TopoDS.TopoDS_Shape 80 | The shape to explore. 81 | 82 | Returns 83 | ------- 84 | list[TopoDS.TopoDS_Vertex] 85 | 86 | """ 87 | vertices: list[TopoDS.TopoDS_Vertex] = [] 88 | explorer = TopExp.TopExp_Explorer(occ_shape, TopAbs.TopAbs_VERTEX) # type: ignore 89 | while explorer.More(): 90 | vertex = explorer.Current() 91 | vertices.append(vertex) # type: ignore 92 | explorer.Next() 93 | return vertices 94 | 95 | 96 | def occ_shape_find_edges(occ_shape: TopoDS.TopoDS_Shape) -> list[TopoDS.TopoDS_Edge]: 97 | """Find all the edges in an OCC shape. 98 | 99 | Parameters 100 | ---------- 101 | occ_shape : TopoDS.TopoDS_Shape 102 | The shape to explore. 103 | 104 | Returns 105 | ------- 106 | list[TopoDS.TopoDS_Edge] 107 | 108 | """ 109 | edges: list[TopoDS.TopoDS_Edge] = [] 110 | explorer = TopExp.TopExp_Explorer(occ_shape, TopAbs.TopAbs_EDGE) # type: ignore 111 | while explorer.More(): 112 | edge = explorer.Current() 113 | edges.append(edge) # type: ignore 114 | explorer.Next() 115 | return edges 116 | 117 | 118 | def occ_shape_find_loops(occ_shape: TopoDS.TopoDS_Shape) -> list[TopoDS.TopoDS_Wire]: 119 | """Find all the loops or wires in an OCC shape. 120 | 121 | Parameters 122 | ---------- 123 | occ_shape : TopoDS.TopoDS_Shape 124 | The shape to explore. 125 | 126 | Returns 127 | ------- 128 | list[TopoDS.TopoDS_Wire] 129 | 130 | """ 131 | wires: list[TopoDS.TopoDS_Wire] = [] 132 | explorer = TopExp.TopExp_Explorer(occ_shape, TopAbs.TopAbs_WIRE) # type: ignore 133 | while explorer.More(): 134 | wire = explorer.Current() 135 | wires.append(wire) # type: ignore 136 | explorer.Next() 137 | return wires 138 | 139 | 140 | def occ_shape_find_faces(occ_shape: TopoDS.TopoDS_Shape) -> list[TopoDS.TopoDS_Face]: 141 | """Find all the faces in an OCC shape. 142 | 143 | Parameters 144 | ---------- 145 | occ_shape : TopoDS.TopoDS_Shape 146 | The shape to explore. 147 | 148 | Returns 149 | ------- 150 | list[TopoDS.TopoDS_Face] 151 | 152 | """ 153 | faces: list[TopoDS.TopoDS_Face] = [] 154 | explorer = TopExp.TopExp_Explorer(occ_shape, TopAbs.TopAbs_FACE) # type: ignore 155 | while explorer.More(): 156 | face = explorer.Current() 157 | faces.append(face) # type: ignore 158 | explorer.Next() 159 | return faces 160 | 161 | 162 | def occ_shape_find_shells(occ_shape: TopoDS.TopoDS_Shape) -> list[TopoDS.TopoDS_Shell]: 163 | """Find all the shells in an OCC shape. 164 | 165 | Parameters 166 | ---------- 167 | occ_shape : TopoDS.TopoDS_Shape 168 | The shape to explore. 169 | 170 | Returns 171 | ------- 172 | list[TopoDS.TopoDS_Shell] 173 | 174 | """ 175 | shells: list[TopoDS.TopoDS_Shell] = [] 176 | explorer = TopExp.TopExp_Explorer(occ_shape, TopAbs.TopAbs_SHELL) # type: ignore 177 | while explorer.More(): 178 | shell = explorer.Current() 179 | shells.append(shell) # type: ignore 180 | explorer.Next() 181 | return shells 182 | 183 | 184 | def occ_shape_find_solids(occ_shape: TopoDS.TopoDS_Shape) -> list[TopoDS.TopoDS_Solid]: 185 | """Find all the solids in an OCC shape. 186 | 187 | Parameters 188 | ---------- 189 | occ_shape : TopoDS.TopoDS_Shape 190 | The shape to explore. 191 | 192 | Returns 193 | ------- 194 | list[TopoDS.TopoDS_Solid] 195 | 196 | """ 197 | solids: list[TopoDS.TopoDS_Solid] = [] 198 | explorer = TopExp.TopExp_Explorer(occ_shape, TopAbs.TopAbs_SOLID) # type: ignore 199 | while explorer.More(): 200 | solid = explorer.Current() 201 | solids.append(solid) # type: ignore 202 | explorer.Next() 203 | return solids 204 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import os 4 | 5 | from compas_invocations2 import build 6 | from compas_invocations2 import docs 7 | from compas_invocations2 import style 8 | from compas_invocations2 import tests 9 | from invoke.collection import Collection 10 | 11 | ns = Collection( 12 | docs.help, 13 | style.check, 14 | style.lint, 15 | style.format, 16 | docs.docs, 17 | docs.linkcheck, 18 | tests.test, 19 | tests.testdocs, 20 | build.build_ghuser_components, 21 | build.prepare_changelog, 22 | build.clean, 23 | build.release, 24 | ) 25 | ns.configure( 26 | { 27 | "base_folder": os.path.dirname(__file__), 28 | } 29 | ) 30 | -------------------------------------------------------------------------------- /tests/curve_from_rhino.json: -------------------------------------------------------------------------------- 1 | {"dtype": "compas.geometry/NurbsCurve", "data": {"points": [[0.0, 0.0, 0.0], [2.0, 10.0, 0.0], [4.0, -3.0, 7.0], [6.0, 10.0, 0.0], [8.0, -3.0, 0.0], [10.0, 0.0, 0.0]], "weights": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], "knots": [0.0, 12.919267131797966, 25.838534263595932, 38.7578013953939], "multiplicities": [4, 1, 1, 4], "degree": 3, "is_periodic": false}, "guid": "7d9c3cfe-03ed-4907-bb5a-1ec6045a5096"} -------------------------------------------------------------------------------- /tests/surface_from_rhino.json: -------------------------------------------------------------------------------- 1 | {"dtype": "compas.geometry/NurbsSurface", "data": {"points": [[[0.0, 0.0, 0.0], [0.0, 3.333333333333335, 0.0], [0.0, 6.666666666666666, 0.0], [0.0, 10.0, 0.0]], [[6.0, 0.0, 0.0], [6.0, 3.3333333333333353, 10.0], [6.0, 6.666666666666669, 10.0], [6.0, 10.000000000000004, 0.0]], [[12.0, 0.0, 0.0], [12.000000000000005, 3.3333333333333335, -3.0], [11.999999999999995, 6.66666666666666, -3.0], [12.0, 9.999999999999995, 0.0]], [[18.0, 0.0, 0.0], [18.000000000000004, 3.333333333333335, 0.0], [17.999999999999993, 6.666666666666666, 0.0], [18.0, 10.0, 0.0]]], "weights": [[1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0]], "knots_u": [0.0, 10.0], "knots_v": [0.0, 10.0], "mults_u": [4, 4], "mults_v": [4, 4], "degree_u": 3, "degree_v": 3, "is_periodic_u": false, "is_periodic_v": false}, "guid": "328f554a-ab0d-45c4-b4ef-ced882875e84"} -------------------------------------------------------------------------------- /tests/test_breps.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import random 3 | 4 | from compas.geometry import Box 5 | from compas.geometry import Cylinder 6 | from compas.geometry import Frame 7 | from compas.geometry import Sphere 8 | from compas.tolerance import TOL 9 | from compas_occ.brep import OCCBrep 10 | 11 | 12 | @pytest.mark.parametrize( 13 | "box", 14 | [ 15 | Box(1), 16 | Box(1, 2, 3), 17 | Box(random.random()), 18 | Box(random.random(), random.random(), random.random()), 19 | Box(1, frame=Frame(point=[random.random(), random.random(), random.random()])), 20 | Box(1, 2, 3, frame=Frame(point=[random.random(), random.random(), random.random()])), 21 | Box(random.random(), frame=Frame(point=[random.random(), random.random(), random.random()])), 22 | Box(random.random(), random.random(), random.random(), frame=Frame(point=[random.random(), random.random(), random.random()])), 23 | ], 24 | ) 25 | def test_brep_from_box(box: Box): 26 | brep = OCCBrep.from_box(box) 27 | brep.heal() 28 | 29 | assert TOL.is_close(box.volume, brep.volume) 30 | assert len(box.points) == len(brep.points) 31 | assert box.frame.point == brep.centroid 32 | # assert all(a == b for a, b in zip(box.points, box.points)) 33 | 34 | 35 | def test_brep_from_cylinder(): 36 | cylinder = Cylinder(radius=1, height=1) 37 | brep = OCCBrep.from_cylinder(cylinder) 38 | 39 | assert TOL.is_close(cylinder.volume, brep.volume) 40 | assert cylinder.frame.point == brep.centroid 41 | 42 | 43 | def test_brep_from_sphere(): 44 | sphere = Sphere(1) 45 | brep = OCCBrep.from_sphere(sphere) 46 | 47 | assert TOL.is_close(sphere.volume, brep.volume) 48 | assert sphere.frame.point == brep.centroid 49 | -------------------------------------------------------------------------------- /tests/test_conversions.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from compas.geometry import Pointcloud 4 | from compas.tolerance import TOL 5 | from compas_occ.conversions import array1_from_points1 6 | from compas_occ.conversions import harray1_from_points1 7 | from compas_occ.conversions import points1_from_array1 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "points1", 12 | [ 13 | Pointcloud.from_bounds(10, 10, 10, 1), 14 | Pointcloud.from_bounds(10, 10, 10, 17), 15 | Pointcloud.from_bounds(10, 10, 10, 53), 16 | ], 17 | ) 18 | def test_array1_from_points1(points1): 19 | array1 = array1_from_points1(points1) 20 | assert array1.Length() == len(points1) 21 | 22 | for item, point in zip(array1, points1): 23 | assert TOL.is_close(point.x, item.X()) 24 | assert TOL.is_close(point.y, item.Y()) 25 | assert TOL.is_close(point.z, item.Z()) 26 | 27 | 28 | @pytest.mark.parametrize( 29 | "points1", 30 | [ 31 | Pointcloud.from_bounds(10, 10, 10, 1), 32 | Pointcloud.from_bounds(10, 10, 10, 17), 33 | Pointcloud.from_bounds(10, 10, 10, 53), 34 | ], 35 | ) 36 | def test_harray1_from_points1(points1): 37 | array1 = harray1_from_points1(points1) 38 | assert array1.Length() == len(points1) 39 | 40 | for item, point in zip(array1, points1): 41 | assert TOL.is_close(point.x, item.X()) 42 | assert TOL.is_close(point.y, item.Y()) 43 | assert TOL.is_close(point.z, item.Z()) 44 | 45 | 46 | @pytest.mark.parametrize( 47 | "points1", 48 | [ 49 | Pointcloud.from_bounds(10, 10, 10, 1), 50 | Pointcloud.from_bounds(10, 10, 10, 17), 51 | Pointcloud.from_bounds(10, 10, 10, 53), 52 | ], 53 | ) 54 | def test_points1_from_array1(points1): 55 | array1 = array1_from_points1(points1) 56 | points1 = points1_from_array1(array1) 57 | assert array1.Length() == len(points1) 58 | 59 | for item, point in zip(array1, points1): 60 | assert TOL.is_close(point.x, item.X()) 61 | assert TOL.is_close(point.y, item.Y()) 62 | assert TOL.is_close(point.z, item.Z()) 63 | -------------------------------------------------------------------------------- /tests/test_data_nurbscurve.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import compas 3 | from compas.geometry import NurbsCurve 4 | from compas_occ.geometry import OCCNurbsCurve 5 | 6 | 7 | here = Path(__file__).parent 8 | filepath = here / "curve_from_rhino.json" 9 | 10 | 11 | def test_curve_from_rhino(): 12 | curve: OCCNurbsCurve = compas.json_load(filepath) # type: ignore 13 | 14 | assert isinstance(curve, OCCNurbsCurve) 15 | assert isinstance(curve, NurbsCurve) 16 | 17 | assert curve.points[0] == [0.0, 0.0, 0.0] 18 | assert curve.points[1] == [2.0, 10.0, 0.0] 19 | assert curve.points[2] == [4.0, -3.0, 7.0] 20 | assert curve.points[3] == [6.0, 10.0, 0.0] 21 | assert curve.points[4] == [8.0, -3.0, 0.0] 22 | assert curve.points[-1] == [10.0, 0.0, 0.0] 23 | 24 | after: NurbsCurve = NurbsCurve.from_jsonstring(curve.to_jsonstring()) # type: ignore 25 | 26 | assert isinstance(after, OCCNurbsCurve) 27 | assert isinstance(after, NurbsCurve) 28 | 29 | for i in range(len(curve.points)): 30 | assert curve.points[i] == after.points[i] 31 | 32 | assert curve.degree == after.degree 33 | assert curve.knots == after.knots 34 | assert curve.weights == after.weights 35 | -------------------------------------------------------------------------------- /tests/test_data_nurbssurface.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import compas 3 | from compas.tolerance import TOL 4 | from compas.geometry import NurbsSurface 5 | from compas_occ.geometry import OCCNurbsSurface 6 | 7 | here = Path(__file__).parent 8 | filepath = here / "surface_from_rhino.json" 9 | 10 | 11 | def test_surface_from_rhino(): 12 | surface: OCCNurbsSurface = compas.json_load(filepath) # type: ignore 13 | 14 | assert isinstance(surface, OCCNurbsSurface) 15 | assert isinstance(surface, NurbsSurface) 16 | 17 | for u, col in enumerate(surface.points): # type: ignore 18 | for v, point in enumerate(col): 19 | assert point == surface.points[u][v] 20 | assert TOL.is_close(point[0], u * 18 / 3) 21 | assert TOL.is_close(point[1], v * 10 / 3) 22 | 23 | after: NurbsSurface = NurbsSurface.from_jsonstring(surface.to_jsonstring()) # type: ignore 24 | 25 | assert isinstance(after, OCCNurbsSurface) 26 | assert isinstance(after, NurbsSurface) 27 | 28 | for u, col in enumerate(surface.points): # type: ignore 29 | for v, point in enumerate(col): 30 | assert point == after.points[u][v] 31 | 32 | assert surface.degree_u == after.degree_u 33 | assert surface.degree_v == after.degree_v 34 | assert surface.knots_u == after.knots_u 35 | assert surface.knots_v == after.knots_v 36 | assert surface.weights == after.weights 37 | -------------------------------------------------------------------------------- /tests/test_trivial.py: -------------------------------------------------------------------------------- 1 | import compas_occ 2 | 3 | 4 | def test_trivial(): 5 | print(compas_occ.__version__) 6 | assert True 7 | --------------------------------------------------------------------------------