├── .gitattributes ├── .github └── FUNDING.yml ├── BOSL2 ├── .github │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── check_for_tabs.json │ ├── openscad_docsgen.json │ └── workflows │ │ ├── gen_docs.yml │ │ ├── gen_tutorials.yml │ │ └── main.yml ├── .gitignore ├── .openscad_docsgen_rc ├── .openscad_mdimggen_rc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── WRITING_DOCS.md ├── affine.scad ├── attachments.scad ├── ball_bearings.scad ├── beziers.scad ├── bosl1compat.scad ├── bottlecaps.scad ├── builtins.scad ├── color.scad ├── comparisons.scad ├── constants.scad ├── coords.scad ├── cubetruss.scad ├── distributors.scad ├── drawing.scad ├── examples │ ├── BOSL2logo.scad │ ├── attachments.scad │ ├── boolean_geometry.scad │ ├── fractal_tree.scad │ ├── lsystems.scad │ ├── orientations.scad │ ├── spherical_patch.scad │ └── spring_handle.scad ├── fnliterals.scad ├── gears.scad ├── geometry.scad ├── hinges.scad ├── images │ └── BOSL2logo.png ├── joiners.scad ├── linalg.scad ├── linear_bearings.scad ├── lists.scad ├── masks2d.scad ├── masks3d.scad ├── math.scad ├── metric_screws.scad ├── modular_hose.scad ├── mutators.scad ├── nema_steppers.scad ├── partitions.scad ├── paths.scad ├── polyhedra.scad ├── regions.scad ├── resources │ ├── docs_custom.css │ └── links-filter-html.lua ├── rounding.scad ├── screw_drive.scad ├── screws.scad ├── scripts │ ├── check_for_tabs.sh │ ├── find_modular_asserts.sh │ ├── func_coverage.py │ ├── img2scad.py │ ├── increment_version.sh │ ├── linecount.sh │ ├── mkdocspdf.sh │ ├── purge_wiki_history.sh │ └── run_tests.sh ├── shapes2d.scad ├── shapes3d.scad ├── skin.scad ├── sliders.scad ├── std.scad ├── strings.scad ├── structs.scad ├── tests │ ├── README.txt │ ├── polyhedra.scad │ ├── test_affine.scad │ ├── test_attachments.scad │ ├── test_comparisons.scad │ ├── test_coords.scad │ ├── test_cubetruss.scad │ ├── test_distributors.scad │ ├── test_drawing.scad │ ├── test_edges.scad │ ├── test_fnliterals.scad │ ├── test_geometry.scad │ ├── test_linalg.scad │ ├── test_linear_bearings.scad │ ├── test_lists.scad │ ├── test_masks2d.scad │ ├── test_math.scad │ ├── test_mutators.scad │ ├── test_paths.scad │ ├── test_regions.scad │ ├── test_rounding.scad │ ├── test_screw_drive.scad │ ├── test_shapes2d.scad │ ├── test_shapes3d.scad │ ├── test_skin.scad │ ├── test_strings.scad │ ├── test_structs.scad │ ├── test_transforms.scad │ ├── test_trigonometry.scad │ ├── test_utility.scad │ ├── test_vectors.scad │ ├── test_version.scad │ └── test_vnf.scad ├── threading.scad ├── transforms.scad ├── trigonometry.scad ├── tripod_mounts.scad ├── turtle3d.scad ├── tutorials │ ├── Attachments.md │ ├── Distributors.md │ ├── FractalTree.md │ ├── Mutators.md │ ├── Paths.md │ ├── Rounding_the_Cube.md │ ├── Shapes2d.md │ ├── Shapes3d.md │ ├── Transforms.md │ └── VNF.md ├── utility.scad ├── vectors.scad ├── version.scad ├── vnf.scad ├── walls.scad └── wiring.scad ├── CHANGELOG.md ├── LICENSE ├── PolyDiceGenerator.scad ├── README.md └── images ├── bumpers.png ├── dice_render.png ├── render_v0.26.0.png ├── render_v0.26.2.png ├── render_v0.26.4.png ├── render_v0.26.8.png ├── render_v0.26.9.png ├── render_v0.27.4.png └── render_v0.27.5.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: charmaur 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /BOSL2/.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: revarbat 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Code To Reproduce Bug** 14 | ``` 15 | // Code goes here. 16 | ``` 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | - OpenSCAD Version: 27 | - Other libraries used: 28 | -------------------------------------------------------------------------------- /BOSL2/.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: revarbat 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Example Code** 20 | ``` 21 | // Code goes here. 22 | ``` 23 | 24 | **Additional context** 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /BOSL2/.github/check_for_tabs.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "check_for_tabs", 5 | "pattern": [ 6 | { 7 | "regexp": "^([^:]+):(\\d+):(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "message": 3 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /BOSL2/.github/openscad_docsgen.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "docsgen", 5 | "pattern": [ 6 | { 7 | "regexp": "^!! (WARNING|ERROR) at ([^:]+:\\d+: .*[^:])$", 8 | "severity": 1, 9 | "message": 2 10 | } 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /BOSL2/.github/workflows/gen_docs.yml: -------------------------------------------------------------------------------- 1 | name: Regenerate Docs 2 | on: [workflow_dispatch] 3 | 4 | jobs: 5 | RegenerateDocs: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v3 10 | 11 | - name: Clone Wiki 12 | uses: actions/checkout@v3 13 | with: 14 | repository: BelfrySCAD/BOSL2.wiki 15 | path: BOSL2.wiki 16 | 17 | - name: Apt Update 18 | run: sudo apt update 19 | 20 | - name: Install Required Libraries 21 | run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil gifsicle libfuse2 22 | 23 | - name: Install OpenSCAD-DocsGen package. 24 | run: sudo pip3 install openscad-docsgen 25 | 26 | - name: Install OpenSCAD 27 | run: | 28 | cd $GITHUB_WORKSPACE 29 | wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage 30 | sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad 31 | sudo chmod +x /usr/local/bin/openscad 32 | 33 | - name: Generate Docs 34 | uses: GabrielBB/xvfb-action@v1.6 35 | env: 36 | OPENSCADPATH: ${{ github.workspace }}/.. 37 | with: 38 | run: openscad-docsgen -f 39 | 40 | - name: Upload Docs to Wiki 41 | uses: SwiftDocOrg/github-wiki-publish-action@v1 42 | with: 43 | path: "BOSL2.wiki" 44 | env: 45 | GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PAT }} 46 | 47 | -------------------------------------------------------------------------------- /BOSL2/.github/workflows/gen_tutorials.yml: -------------------------------------------------------------------------------- 1 | name: Regenerate Tutorials 2 | on: [workflow_dispatch] 3 | 4 | jobs: 5 | RegenerateTutorials: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v3 10 | 11 | - name: Clone Wiki 12 | uses: actions/checkout@v3 13 | with: 14 | repository: BelfrySCAD/BOSL2.wiki 15 | path: BOSL2.wiki 16 | 17 | - name: Apt Update 18 | run: sudo apt update 19 | 20 | - name: Install Required Libraries 21 | run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil gifsicle libfuse2 22 | 23 | - name: Install OpenSCAD-DocsGen package. 24 | run: sudo pip3 install openscad-docsgen 25 | 26 | - name: Install OpenSCAD 27 | run: | 28 | cd $GITHUB_WORKSPACE 29 | wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage 30 | sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad 31 | sudo chmod +x /usr/local/bin/openscad 32 | 33 | - name: Tabs Check 34 | run: | 35 | cd $GITHUB_WORKSPACE 36 | ./scripts/check_for_tabs.sh 37 | 38 | - name: FooTest 39 | env: 40 | OPENSCADPATH: ${{ github.workspace }}/.. 41 | run: echo $OPENSCADPATH 42 | 43 | - name: Generate Tutorials 44 | uses: GabrielBB/xvfb-action@v1.6 45 | env: 46 | OPENSCADPATH: ${{ github.workspace }}/.. 47 | with: 48 | run: openscad-mdimggen -f 49 | 50 | - name: Upload Tutorials to Wiki 51 | uses: SwiftDocOrg/github-wiki-publish-action@v1 52 | with: 53 | path: "BOSL2.wiki" 54 | env: 55 | GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PAT }} 56 | 57 | -------------------------------------------------------------------------------- /BOSL2/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | on: [pull_request] 3 | 4 | jobs: 5 | Regressions: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v3 10 | 11 | - name: Install Required Libraries 12 | run: sudo apt-get install libfuse2 13 | 14 | - name: Install OpenSCAD 15 | run: | 16 | cd $GITHUB_WORKSPACE 17 | wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage 18 | sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad 19 | sudo chmod +x /usr/local/bin/openscad 20 | 21 | - name: Run Regression Tests 22 | run: | 23 | cd $GITHUB_WORKSPACE 24 | export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) 25 | ./scripts/run_tests.sh 26 | 27 | CheckTutorials: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v3 32 | 33 | - name: Clone Wiki 34 | uses: actions/checkout@v3 35 | with: 36 | repository: BelfrySCAD/BOSL2.wiki 37 | path: BOSL2.wiki 38 | 39 | - name: Apt Update 40 | run: sudo apt update 41 | 42 | - name: Install Required Libraries 43 | run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil libfuse2 44 | 45 | - name: Install OpenSCAD-DocsGen package. 46 | run: sudo pip3 install openscad-docsgen 47 | 48 | - name: Install OpenSCAD 49 | run: | 50 | cd $GITHUB_WORKSPACE 51 | wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage 52 | sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad 53 | sudo chmod +x /usr/local/bin/openscad 54 | 55 | - name: Tabs Check 56 | run: | 57 | cd $GITHUB_WORKSPACE 58 | echo "::add-matcher::.github/check_for_tabs.json" 59 | ./scripts/check_for_tabs.sh 60 | 61 | - name: Checking Tutorials 62 | run: | 63 | cd $GITHUB_WORKSPACE 64 | echo "::add-matcher::.github/openscad_docsgen.json" 65 | export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) 66 | openscad-mdimggen -T 67 | 68 | CheckDocs: 69 | runs-on: ubuntu-latest 70 | steps: 71 | - name: Checkout 72 | uses: actions/checkout@v3 73 | 74 | - name: Clone Wiki 75 | uses: actions/checkout@v3 76 | with: 77 | repository: BelfrySCAD/BOSL2.wiki 78 | path: BOSL2.wiki 79 | 80 | - name: Apt Update 81 | run: sudo apt update 82 | 83 | - name: Install Required Libraries 84 | run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil libfuse2 85 | 86 | - name: Install OpenSCAD-DocsGen package. 87 | run: sudo pip3 install openscad-docsgen 88 | 89 | - name: Install OpenSCAD 90 | run: | 91 | cd $GITHUB_WORKSPACE 92 | wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage 93 | sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad 94 | sudo chmod +x /usr/local/bin/openscad 95 | 96 | - name: Checking Docs 97 | run: | 98 | cd $GITHUB_WORKSPACE 99 | echo "::add-matcher::.github/openscad_docsgen.json" 100 | export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) 101 | openscad-docsgen -Tmf 102 | 103 | -------------------------------------------------------------------------------- /BOSL2/.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 | # Mac stuff 104 | .DS_Store 105 | 106 | # Vim temp files 107 | .*.swp 108 | 109 | foo.scad 110 | BOSL2.wiki 111 | /ref/ 112 | docsgen_report.json 113 | BOSL2_Docs.html 114 | BOSL2_Docs.epub 115 | BOSL2_Docs.pdf 116 | 117 | -------------------------------------------------------------------------------- /BOSL2/.openscad_docsgen_rc: -------------------------------------------------------------------------------- 1 | DocsDirectory: BOSL2.wiki/ 2 | TargetProfile: githubwiki 3 | ProjectName: The Belfry OpenScad Library, v2. (BOSL2) 4 | GenerateDocs: Files, TOC, Index, Topics, CheatSheet, Sidebar 5 | UsePNGAnimations: Yes 6 | IgnoreFiles: 7 | affine.scad 8 | metric_screws.scad 9 | std.scad 10 | bosl1compat.scad 11 | builtins.scad 12 | foo.scad 13 | tmp_*.scad 14 | PrioritizeFiles: 15 | constants.scad 16 | transforms.scad 17 | attachments.scad 18 | shapes2d.scad 19 | shapes3d.scad 20 | drawing.scad 21 | masks2d.scad 22 | masks3d.scad 23 | distributors.scad 24 | color.scad 25 | partitions.scad 26 | mutators.scad 27 | paths.scad 28 | regions.scad 29 | skin.scad 30 | vnf.scad 31 | beziers.scad 32 | rounding.scad 33 | turtle3d.scad 34 | math.scad 35 | linalg.scad 36 | vectors.scad 37 | coords.scad 38 | geometry.scad 39 | trigonometry.scad 40 | version.scad 41 | comparisons.scad 42 | lists.scad 43 | utility.scad 44 | strings.scad 45 | structs.scad 46 | fnliterals.scad 47 | threading.scad 48 | screws.scad 49 | metric_screws.scad 50 | screw_drive.scad 51 | bottlecaps.scad 52 | ball_bearings.scad 53 | cubetruss.scad 54 | gears.scad 55 | hinges.scad 56 | joiners.scad 57 | linear_bearings.scad 58 | modular_hose.scad 59 | nema_steppers.scad 60 | polyhedra.scad 61 | sliders.scad 62 | tripod_mounts.scad 63 | walls.scad 64 | wiring.scad 65 | DefineHeader(BulletList): Side Effects 66 | DefineHeader(Table;Headers=Anchor Name|Position): Extra Anchors 67 | DefineHeader(Table;Headers=Anchor Type|What it is): Anchor Types 68 | DefineHeader(Table;Headers=Name|Definition): Terminology 69 | DefineHeader(BulletList): Requirements 70 | DefineSynTags: 71 | Ext = Extends the OpenScad keyword of the same name. 72 | Geom = Can return geometry. 73 | Mat = Can return a transformation matrix. 74 | MatList = Can return a list of transformation matrices. 75 | Path = Can return a Path. 76 | PathList = Can return a list of Paths. 77 | Region = Can return a Region. 78 | RegList = Can return a list of Regions. 79 | Trans = Can transform children. 80 | VNF = Can return a VNF. 81 | 82 | -------------------------------------------------------------------------------- /BOSL2/.openscad_mdimggen_rc: -------------------------------------------------------------------------------- 1 | docs_dir: "BOSL2.wiki" 2 | image_root: "images/tutorials" 3 | file_prefix: "Tutorial-" 4 | source_files: "tutorials/*.md" 5 | png_animations: true 6 | 7 | -------------------------------------------------------------------------------- /BOSL2/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | If you wish to contribute bugfixes or code to the BOSL2 project, the standard way is thus: 2 | 3 | 1. Install command-line git on your system and configure authentication. 4 | - https://docs.github.com/en/github/getting-started-with-github/set-up-git 5 | 1. Alternatively, you can install GitHub Desktop. 6 | - https://desktop.github.com 7 | - https://docs.github.com/en/desktop 8 | 1. Go to the main BOSL2 GitHub repo page: https://github.com/BelfrySCAD/BOSL2/ 9 | 1. Fork the BOSL2 repository by clicking on the Fork button. 10 | 11 | - https://docs.github.com/en/github/getting-started-with-github/fork-a-repo 12 | 1. Clone your fork of the BOSL2 repository to your local computer: 13 | - If using the command-line: 14 | ``` 15 | git clone git@github.com:YOURLOGIN/BOSL2.git 16 | cd BOSL2 17 | git remote add upstream https://github.com/BelfrySCAD/BOSL2.git 18 | ``` 19 | 20 | - If using GitHub Desktop: 21 | 22 | 1. File -> Clone Repository... 23 | 2. Select your BOSL2 repository. 24 | 3. Click the Clone button. 25 | 4. When it asks "How are you planning to use this fork?", click on the button "To contribute to the parent project." 26 | 27 | 1. Before you edit files, always synchronize with the upstream repository: 28 | - If using the command-line: 29 | ``` 30 | git pull upstream 31 | ``` 32 | - If using GitHub Desktop, click on the Fetch Origin button. 33 | 1. Make changes in the source code that you want to make. 34 | 1. Commit the changes files to your repo: 35 | - If using the command-line: 36 | ``` 37 | git add --all 38 | git commit -m "COMMIT DESCRIPTION" 39 | git pull upstream 40 | git push 41 | ``` 42 | 43 | - If using GitHub Desktop: 44 | 45 | 1. Select all changed files you want to commit. 46 | 2. Enter the summary for the commit. 47 | 3. Click on the Commit button. 48 | 4. Click on the Push Origin button. 49 | 50 | 1. Go to your GitHub BOSL2 repo page. 51 | 1. Click on the `Pull Request` button, enter the description, then create the PR. 52 | 1. If a change you made fails to pass the regressions or docs validations, this will be noted at the bottom of your Pull Request page, and you will get an email about it. 53 | 54 | 55 | -------------------------------------------------------------------------------- /BOSL2/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017-2019, Revar Desmera 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /BOSL2/README.md: -------------------------------------------------------------------------------- 1 | # BOSL2 2 | ![BOSL2 Logo](images/BOSL2logo.png) 3 | 4 | **The Belfry OpenScad Library, v2** 5 | 6 | A library for OpenSCAD, filled with useful tools, shapes, masks, math and manipulators, designed to make OpenSCAD easier to use. 7 | 8 | Requires OpenSCAD 2021.01 or later. 9 | 10 | - **NOTE:** BOSL2 IS BETA CODE. THE CODE IS STILL BEING REORGANIZED. 11 | - **NOTE2:** CODE WRITTEN FOR BOSLv1 PROBABLY WON'T WORK WITH BOSL2! 12 | 13 | [![Join the chat at https://gitter.im/revarbat/BOSL2](https://badges.gitter.im/revarbat/BOSL2.svg)](https://gitter.im/revarbat/BOSL2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 14 | 15 | 16 | ## Documentation 17 | 18 | You can find the full BOSL2 library documentation at: https://github.com/BelfrySCAD/BOSL2/wiki 19 | 20 | 21 | ## Installation 22 | 23 | 1. Download the .zip or .tar.gz release file for this library. Currently you should be able to find this at https://github.com/BelfrySCAD/BOSL2/archive/refs/heads/master.zip 24 | 2. Unpack it. Make sure that you unpack the whole file structure. Some zipfile unpackers call this option "Use folder names". It should create either a `BOSL-v2.0` or `BOSL2-master` directory with the library files within it. You should see "examples", "scripts", "tests", and other subdirectories. 25 | 3. Rename the unpacked main directory to `BOSL2`. 26 | 4. Move the `BOSL2` directory into the apropriate OpenSCAD library directory. The library directory may be on the list below, but for SNAP or other prepackaged installations, it is probably somewhere else. To find it, run OpenSCAD and select Help→Library Info, and look for the entry that says "User Library Path". This is your default library directory. You may choose to change it to something more convenient by setting the environment variable OPENSCADPATH. Using this variable also means that all versions of OpenSCAD you install will look for libraries in the same location. 27 | - Windows: `My Documents\OpenSCAD\libraries\` 28 | - Linux: `$HOME/.local/share/OpenSCAD/libraries/` 29 | - Mac OS X: `$HOME/Documents/OpenSCAD/libraries/` 30 | 5. Restart OpenSCAD. 31 | 32 | 33 | ## Examples 34 | A lot of the features of this library are to allow shorter, easier-to-read, intent-based coding. For example: 35 | 36 | [`BOSL2/transforms.scad`](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad) Examples | Raw OpenSCAD Equivalent 37 | ------------------------------- | ------------------------------- 38 | `up(5)` | `translate([0,0,5])` 39 | `xrot(30,cp=[0,10,20])` | `translate([0,10,20]) rotate([30,0,0]) translate([0,-10,-20])` 40 | `xcopies(20,n=3)` | `for (dx=[-20,0,20]) translate([dx,0,0])` 41 | `zrot_copies(n=6,r=20)` | `for (zr=[0:5]) rotate([0,0,zr*60]) translate([20,0,0])` 42 | `skew(sxz=0.5,syz=0.333)` | `multmatrix([[1,0,0.5,0],[0,1,0.333,0],[0,0,1,0],[0,0,0,1]])` 43 | 44 | [`BOSL2/shapes.scad`](https://github.com/BelfrySCAD/BOSL2/wiki/shapes.scad) Examples | Raw OpenSCAD Equivalent 45 | ---------------------------------- | ------------------------------- 46 | `cube([10,20,30], anchor=BOTTOM);` | `translate([0,0,15]) cube([10,20,30], center=true);` 47 | `cuboid([20,20,30], rounding=5);` | `minkowski() {cube([10,10,20], center=true); sphere(r=5, $fn=32);}` 48 | `prismoid([30,40],[20,30],h=10);` | `hull() {translate([0,0,0.005]) cube([30,40,0.01], center=true); translate([0,0,9.995]) cube([20,30,0.01],center=true);}` 49 | `xcyl(l=20,d=4);` | `rotate([0,90,0]) cylinder(h=20, d=4, center=true);` 50 | `cyl(l=100, d=40, rounding=5);` | `minkowski() {cylinder(h=90, d=30, center=true); sphere(r=5);}` 51 | `tube(od=40,wall=5,h=30);` | `difference() {cylinder(d=40,h=30,center=true); cylinder(d=30,h=31,center=true);}` 52 | `torus(d_maj=100, d_min=30);` | `rotate_extrude() translate([50,0,0]) circle(d=30);` 53 | 54 | 55 | -------------------------------------------------------------------------------- /BOSL2/bosl1compat.scad: -------------------------------------------------------------------------------- 1 | module translate_copies(a=[[0,0,0]]) move_copies(a) children(); 2 | 3 | module xspread(spacing, n, l, sp) xcopies(spacing=spacing, n=n, l=l, sp=sp) children(); 4 | module yspread(spacing, n, l, sp) ycopies(spacing=spacing, n=n, l=l, sp=sp) children(); 5 | module zspread(spacing, n, l, sp) zcopies(spacing=spacing, n=n, l=l, sp=sp) children(); 6 | 7 | module spread(p1=[0,0,0], p2=[10,0,0], spacing, l, n=2) line_copies(p1=p1, p2=p2, spacing=spacing, l=l, n=n) children(); 8 | module grid_of(xa=[0],ya=[0],za=[0],count,spacing) grid3d(xa=xa, ya=ya, za=za, n=count, spacing=spacing) children(); 9 | 10 | module xring(n=2,r=0,sa=0,cp=[0,0,0],rot=true) xrot_copies(n=n,r=r,sa=sa,cp=cp,subrot=rot) children(); 11 | module yring(n=2,r=0,sa=0,cp=[0,0,0],rot=true) yrot_copies(n=n,r=r,sa=sa,cp=cp,subrot=rot) children(); 12 | module zring(n=2,r=0,sa=0,cp=[0,0,0],rot=true) zrot_copies(n=n,r=r,sa=sa,cp=cp,subrot=rot) children(); 13 | 14 | module leftcube(size) cube(size, anchor=RIGHT); 15 | module rightcube(size) cube(size, anchor=LEFT); 16 | module fwdcube(size) cube(size, anchor=BACK); 17 | module backcube(size) cube(size, anchor=FWD); 18 | module downcube(size) cube(size, anchor=TOP); 19 | module upcube(size) cube(size, anchor=BOT); 20 | 21 | module cube2pt(p1,p2) cuboid(p1=p1,p2=p2); 22 | module offsetcube(size=[1,1,1],v=[0,0,0]) cuboid(size,anchor=-v); 23 | module rrect(size=[1,1,1], r=0.25, center=false) cuboid(size,rounding=r,edges="Z",anchor=center?CENTER:BOT); 24 | module rcube(size=[1,1,1], r=0.25, center=false) cuboid(size,rounding=r,anchor=center?CENTER:BOT); 25 | module chamfcube(size=[1,1,1],chamfer=0.25,chamfaxes=[1,1,1],chamfcorners=false) { 26 | cuboid( 27 | size=size, chamfer=chamfer, 28 | trimcorners=chamfcorners, 29 | edges=[ 30 | if (chamfaxes.x) "X", 31 | if (chamfaxes.y) "Y", 32 | if (chamfaxes.z) "Z", 33 | ] 34 | ); 35 | } 36 | 37 | module trapezoid(size1=[1,1], size2=[1,1], h=1, shift=[0,0], align=CTR, orient=0, center) 38 | prismoid(size1=size1, size2=size2, h=h, shift=shift, spin=orient, anchor=center==undef? -align : center?CENTER:BOT); 39 | 40 | module pyramid(n=4, h=1, l=1, r, d, circum=false) { 41 | radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n)); 42 | cyl(r1=radius, r2=0, l=h, circum=circum, $fn=n, realign=true, anchor=BOT); 43 | } 44 | 45 | module prism(n=3, h=1, l=1, r, d, circum=false, center=false) { 46 | radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n)); 47 | cyl(r=radius, l=h, circum=circum, $fn=n, realign=true, anchor=center?CENTER:BOT); 48 | } 49 | 50 | module chamferred_cylinder(h,r,d,chamfer=0.25,chamfedge,angle=45,top=true,bottom=true,center=false) { 51 | chamf = chamfedge!=undef? chamfedge*sin(angle) : chamfer; 52 | cyl(h=h, r=r, d=d, chamfer1=(bottom?chamf:0), chamfer2=(top?chamf:0), chamfang=angle, anchor=center?CENTER:BOT); 53 | } 54 | 55 | module chamf_cyl(h=1, r, d, chamfer=0.25, chamfedge, angle=45, center=false, top=true, bottom=true) { 56 | chamf = chamfedge!=undef? chamfedge*sin(angle) : chamfer; 57 | cyl(h=h, r=r, d=d, chamfer1=(bottom?chamf:0), chamfer2=(top?chamf:0), chamfang=angle, anchor=center?CENTER:BOT); 58 | } 59 | 60 | module filleted_cylinder(h=1, r, d, r1, r2, d1, d2, fillet=0.25, center=false) 61 | cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, rounding=fillet, anchor=center?CENTER:BOT); 62 | 63 | module rcylinder(h=1, r=1, r1, r2, d, d1, d2, fillet=0.25, center=false) 64 | cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, rounding=fillet, anchor=center?CENTER:BOT); 65 | 66 | module thinning_brace(h=50, l=100, thick=5, ang=30, strut=5, wall=3, center=true) 67 | thinning_triangle(h=h, l=l, thick=thick, ang=ang, strut=strut, wall=wall, diagonly=true, center=center); 68 | 69 | 70 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 71 | 72 | -------------------------------------------------------------------------------- /BOSL2/builtins.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | /// Undocumented LibFile: builtins.scad 3 | /// This file has indirect calls to OpenSCAD's builtin functions and modules. 4 | /// Includes: 5 | /// use 6 | ////////////////////////////////////////////////////////////////////// 7 | 8 | /// Section: Builtin Functions 9 | 10 | /// Section: Builtin Modules 11 | module _square(size,center=false) square(size,center=center); 12 | 13 | module _circle(r,d) circle(r=r,d=d); 14 | 15 | module _text(text,size,font,halign,valign,spacing,direction,language,script) 16 | text(text, size=size, font=font, 17 | halign=halign, valign=valign, 18 | spacing=spacing, direction=direction, 19 | language=language, script=script 20 | ); 21 | 22 | module _color(color) if (color==undef || color=="default") children(); else color(color) children(); 23 | 24 | module _cube(size,center) cube(size,center=center); 25 | 26 | module _cylinder(h,r1,r2,center,r,d,d1,d2) cylinder(h,r=r,d=d,r1=r1,r2=r2,d1=d1,d2=d2,center=center); 27 | 28 | module _sphere(r,d) sphere(r=r,d=d); 29 | 30 | module _multmatrix(m) multmatrix(m) children(); 31 | module _translate(v) translate(v) children(); 32 | module _rotate(a,v) rotate(a=a,v=v) children(); 33 | module _scale(v) scale(v) children(); 34 | 35 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 36 | -------------------------------------------------------------------------------- /BOSL2/examples/BOSL2logo.scad: -------------------------------------------------------------------------------- 1 | include 2 | include 3 | include 4 | include 5 | include 6 | 7 | $fa=1; 8 | $fs=1; 9 | 10 | xdistribute(50) { 11 | recolor("#f77") 12 | diff("hole") 13 | cuboid([45,45,10], chamfer=10, edges=[RIGHT+BACK,RIGHT+FRONT], anchor=FRONT) { 14 | tag("hole")cuboid([30,30,11], chamfer=5, edges=[RIGHT+BACK,RIGHT+FRONT]); 15 | attach(FRONT,BACK, overlap=5) { 16 | diff("hole2") 17 | cuboid([45,45,10], rounding=15, edges=[RIGHT+BACK,RIGHT+FRONT]) { 18 | tag("hole2")cuboid([30,30,11], rounding=10, edges=[RIGHT+BACK,RIGHT+FRONT]); 19 | } 20 | } 21 | } 22 | 23 | recolor("#7f7") 24 | bevel_gear(pitch=8, teeth=20, face_width=12, shaft_diam=25, pitch_angle=45, slices=12, spiral_angle=30); 25 | 26 | x = 18; 27 | y = 20; 28 | s1 = 25; 29 | s2 = 20; 30 | sbez = [ 31 | [-x,-y], [-x,-y-s1], 32 | [ x,-y-s1], [ x,-y], [ x,-y+s2], 33 | [-x, y-s2], [-x, y], [-x, y+s1], 34 | [ x, y+s1], [ x, y] 35 | ]; 36 | recolor("#99f") 37 | path_sweep(regular_ngon(n=3,d=10,spin=90), bezpath_curve(sbez)); 38 | 39 | recolor("#0bf") 40 | translate([-15,-35,0]) 41 | cubetruss_corner(size=10, strut=1, h=1, bracing=false, extents=[3,8,0,0,0], clipthick=0); 42 | 43 | recolor("#777") 44 | xdistribute(24) { 45 | screw("M12,70", head="hex", anchor="origin", orient=BACK) 46 | attach(BOT,CENTER) 47 | nut("M12", thickness=10); 48 | screw("M12,70", head="hex", anchor="origin", orient=BACK) 49 | attach(BOT,CENTER) 50 | nut("M12", thickness=10); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BOSL2/examples/attachments.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | 4 | $fn=32; 5 | 6 | cuboid([60,40,40], rounding=5, edges="Z", anchor=BOTTOM) { 7 | attach(TOP, BOTTOM) prismoid([60,40],[20,20], h=50, rounding1=5, rounding2=10) { 8 | attach(TOP) cylinder(d=20, h=30, center=false) { 9 | attach(TOP) cylinder(d1=50, d2=30, h=12, center=false); 10 | } 11 | attach([FRONT, BACK, LEFT, RIGHT]) cylinder(d1=14, d2=5, h=20) { 12 | attach(TOP, LEFT, overlap=5) prismoid([30,20], [20,20], h=10, shift=[-7,0]); 13 | } 14 | } 15 | } 16 | 17 | 18 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 19 | -------------------------------------------------------------------------------- /BOSL2/examples/boolean_geometry.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | $fn = 36; 4 | 5 | rgn1 = [ 6 | square(100), 7 | move([50,50], p=circle(d=60)), 8 | [[35,35],[35,65],[65,65]] 9 | ]; 10 | 11 | rgn2 = [ 12 | [[0,0], [100,100], [100,0]], 13 | [[27,10], [90,73], [90,10]], 14 | move([70,30], p=circle(d=25)) 15 | ]; 16 | 17 | 18 | polycolor=[0.5,1,0.5]; 19 | outlinecolor="black"; 20 | 21 | 22 | module showit(label, rgn, poly=polycolor, outline=outlinecolor, width=0.5) { 23 | move([-50,-50]) { 24 | if(outline) color(outline) linear_extrude(height=max(0.1,1-width)) for(path=rgn) stroke(path, width=width, closed=true); 25 | if(poly) color(poly) linear_extrude(height=0.1) region(rgn); 26 | color("black") right(50) fwd(7) linear_extrude(height=0.1) text(text=label, size=8, halign="center", valign="center"); 27 | } 28 | } 29 | 30 | 31 | ydistribute(-125) { 32 | xdistribute(120) { 33 | showit("Region A", rgn1, poly=[1,0,0,0.5]); 34 | showit("Region B", rgn2, poly=[0,0,1,0.5]); 35 | union() { 36 | showit("A and B Overlaid", rgn1, poly=[1,0,0,0.5]); 37 | showit("", rgn2, poly=[0,0,1,0.5]); 38 | } 39 | } 40 | xdistribute(120) { 41 | showit("Union A+B", union(rgn1, rgn2)); 42 | showit("Difference A-B", difference(rgn1, rgn2)); 43 | showit("Intersection A&B", intersection(rgn1, rgn2)); 44 | showit("Exclusive OR A^B", exclusive_or(rgn1, rgn2)); 45 | } 46 | } 47 | 48 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 49 | -------------------------------------------------------------------------------- /BOSL2/examples/fractal_tree.scad: -------------------------------------------------------------------------------- 1 | include 2 | module tree(l=1500, sc=0.7, depth=10) 3 | recolor("lightgray") 4 | cylinder(l=l, d1=l/5, d2=l/5*sc) 5 | attach(TOP) 6 | if (depth>0) 7 | zrot(90) 8 | zrot_copies(n=2) 9 | yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); 10 | else 11 | recolor("springgreen") 12 | yscale(0.67) 13 | teardrop(d=l*3, l=1, anchor=BOT, spin=90); 14 | tree(); 15 | 16 | 17 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 18 | -------------------------------------------------------------------------------- /BOSL2/examples/lsystems.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | function _lsystem_recurse(s, rules, lev) = 4 | lev<=0? s : _lsystem_recurse([ 5 | for ( 6 | i = 0, 7 | slen = len(s), 8 | sout = ""; 9 | 10 | i <= slen; 11 | 12 | ch = s[i], 13 | found = search([ch], rules)[0], 14 | sout = str(sout, i==slen? "" : found==[]? ch : rules[found][1]), 15 | i = i + 1 16 | ) if (i==slen) sout 17 | ][0], rules, lev-1); 18 | 19 | 20 | function _lsystem_to_turtle(s, step=1, angle=90, startang=0) = 21 | concat( 22 | startang? ["left", startang] : [], 23 | ["angle", angle, "length", step], 24 | [ 25 | for ( 26 | i = 0, 27 | slen = len(s); 28 | 29 | i <= slen; 30 | 31 | ch = s[i], 32 | cmd = (ch=="A" || ch=="B" || ch=="F")? ["move"] : 33 | (ch=="+")? ["left"] : 34 | (ch=="-")? ["right"] : 35 | [], 36 | i=i+1 37 | ) if(i>0 && cmd!=[]) each cmd 38 | ] 39 | ); 40 | 41 | 42 | function lsystem_turtle(basis, rules, levels=5, step=1, angle=90, startang=0) = 43 | turtle(_lsystem_to_turtle(_lsystem_recurse(basis, rules, levels), step=step, angle=angle, startang=startang)); 44 | 45 | 46 | function dragon_curve (levels=9, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "FX", [["X", "X+YF+"], ["Y", "-FX-Y"]]); 47 | function terdragon_curve (levels=7, step=1) = lsystem_turtle(levels=levels, step=step, angle=120, "F", [["F", "F+F-F"]]); 48 | function twindragon_curve (levels=11, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "FX+FX+", [["X", "X+YF"], ["Y","FX-Y"]]); 49 | function moore_curve (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "LFL+F+LFL", [["L", "-RF+LFL+FR-"], ["R", "+LF-RFR-FL+"]]); 50 | function hilbert_curve (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "X", [["X","-YF+XFX+FY-"], ["Y","+XF-YFY-FX+"]]); 51 | function gosper_curve (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=60, "A", [["A", "A-B--B+A++AA+B-"], ["B", "+A-BB--B-A++A+B"]]); 52 | function quadratic_gosper (levels=2, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "-YF", [["X", "XFX-YF-YF+FX+FX-YF-YFFX+YF+FXFXYF-FX+YF+FXFX+YF-FXYF-YF-FX+FX+YFYF-"], ["Y", "+FXFX-YF-YF+FX+FXYF+FX-YFYF-FX-YF+FXYFYF-FX-YFFX+FX+YF-YF-FX+FX+YFY"]]); 53 | function peano_curve (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "X", [["X","XFYFX+F+YFXFY-F-XFYFX"], ["Y","YFXFY-F-XFYFX+F+YFXFY"]]); 54 | function koch_snowflake (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=60, "F++F++F", [["F","F-F++F-F"]]); 55 | function sierpinski_arrowhead(levels=6, step=1) = lsystem_turtle(levels=levels, step=step, angle=60, "A", [["A", "B-A-B"], ["B","A+B+A"]]); 56 | function sierpinski_triangle (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=120, "A-B-B", [["A","A-B+A+B-A"], ["B","BB"]]); 57 | function square_sierpinski (levels=5, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "F+XF+F+XF", [["X","XF-F+F-XF+F+XF-F+F-X"]]); 58 | function cesaro_curve (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=85, "F", [["F","F+F--F+F"]]); 59 | function paul_bourke1 (levels=3, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "F+F+F+F+", [["F","F+F-F-FF+F+F-F"]]); 60 | function paul_bourke_triangle(levels=6, step=1) = lsystem_turtle(levels=levels, step=step, angle=120, "F+F+F", [["F","F-F+F"]]); 61 | function paul_bourke_crystal (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "F+F+F+F", [["F","FF+F++F+F"]]); 62 | function space_filling_tree (levels=4, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "X", [["X","FX++F-FX++F-FX++F-FX++F-"],["F", "FF"]], startang=45); 63 | function krishna_anklets (levels=6, step=1) = lsystem_turtle(levels=levels, step=step, angle=45, "-X--X", [["X","XFX--XFX"]]); 64 | 65 | 66 | points = hilbert_curve(levels=5, step=100/pow(2,5)); 67 | stroke(points, width=1); 68 | 69 | 70 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 71 | -------------------------------------------------------------------------------- /BOSL2/examples/orientations.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | // Shows all the orientations on cubes in their correct rotations. 4 | 5 | orientations = [ 6 | RIGHT, BACK, UP, 7 | LEFT, FWD, DOWN, 8 | ]; 9 | axiscolors = ["red", "forestgreen", "dodgerblue"]; 10 | axisdiam = 0.5; 11 | axislen = 12; 12 | axislbllen = 15; 13 | 14 | module orient_cube(ang) { 15 | color("lightgray") cube(20, center=true); 16 | color(axiscolors.x) up ((20-1)/2+0.01) back ((20-1)/2+0.01) cube([18,1,1], center=true); 17 | color(axiscolors.y) up ((20-1)/2+0.01) right((20-1)/2+0.01) cube([1,18,1], center=true); 18 | color(axiscolors.z) back((20-1)/2+0.01) right((20-1)/2+0.01) cube([1,1,18], center=true); 19 | for (axis=[0:2], neg=[0:1]) { 20 | idx = axis + 3*neg; 21 | labels = [ 22 | "RIGHT", "BACK", "UP", 23 | "LEFT", "FWD", "DOWN" 24 | ]; 25 | rot(ang, from=UP, to=orientations[idx]) { 26 | up(10) { 27 | back(4) color("black") text3d(text=str("spin=",ang), size=2.5); 28 | fwd(2) color(axiscolors[axis]) text3d(text="orient=", size=2.5); 29 | fwd(6) color(axiscolors[axis]) text3d(text=labels[idx], size=2.5); 30 | } 31 | } 32 | } 33 | } 34 | 35 | 36 | module text3d(text, h=0.01, size=3) { 37 | linear_extrude(height=h, convexity=10) { 38 | text(text=text, size=size, valign="center", halign="center"); 39 | } 40 | } 41 | 42 | module dottedline(l, d) for(y = [0:d*3:l]) up(y) sphere(d=d); 43 | 44 | module orient_cubes() { 45 | // X axis 46 | color(axiscolors[0]) { 47 | yrot( 90) cylinder(h=axislen, d=axisdiam, center=false); 48 | right(axislbllen) rot([90,0,0]) text3d(text="X+"); 49 | yrot(-90) dottedline(l=axislen, d=axisdiam); 50 | left(axislbllen) rot([90,0,180]) text3d(text="X-"); 51 | } 52 | // Y axis 53 | color(axiscolors[1]) { 54 | xrot(-90) cylinder(h=axislen, d=axisdiam, center=false); 55 | back(axislbllen) rot([90,0,90]) text3d(text="Y+"); 56 | xrot( 90) dottedline(l=axislen, d=axisdiam); 57 | fwd(axislbllen) rot([90,0,-90]) text3d(text="Y-"); 58 | } 59 | // Z axis 60 | color(axiscolors[2]) { 61 | cylinder(h=axislen, d=axisdiam, center=false); 62 | up(axislbllen) rot([0,-90,90+$vpr[2]]) text3d(text="Z+"); 63 | xrot(180) dottedline(l=axislen, d=axisdiam); 64 | down(axislbllen) rot([0,90,-90+$vpr[2]]) text3d(text="Z-"); 65 | } 66 | 67 | for (ang = [0:90:270]) { 68 | off = rot(p=40*BACK,ang); 69 | translate(off) { 70 | orient_cube(ang); 71 | } 72 | } 73 | } 74 | 75 | 76 | orient_cubes(); 77 | 78 | 79 | 80 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 81 | -------------------------------------------------------------------------------- /BOSL2/examples/spherical_patch.scad: -------------------------------------------------------------------------------- 1 | include 2 | include 3 | 4 | // Makes a pseudo-sphere from a rectangular patch and its mirror. 5 | s = 50/sqrt(2); 6 | d = [[1,1,0],[-1,1,0],[-1,-1,0],[1,-1,0]]; 7 | p = s * d; 8 | q = s * 0.55 * d; 9 | u = s * 2.5 * UP; 10 | patch1 = [ 11 | [p[2], p[2]+q[3], p[3]+q[2], p[3] ], 12 | [p[2]+q[1], p[2]+q[2]+u, p[3]+q[3]+u, p[3]+q[0]], 13 | [p[1]+q[2], p[1]+q[1]+u, p[0]+q[0]+u, p[0]+q[3]], 14 | [p[1], p[1]+q[0], p[0]+q[1], p[0] ], 15 | ]; 16 | patch2 = bezier_patch_reverse(zflip(p=patch1)); 17 | //vnf_polyhedron([bezier_vnf(patch1),bezier_vnf(patch2)]); 18 | debug_bezier_patches([patch1, patch2], splinesteps=16, style="quincunx"); 19 | 20 | 21 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 22 | -------------------------------------------------------------------------------- /BOSL2/examples/spring_handle.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | $fn = 45; 4 | wire_d = 2; 5 | spring_l = 100; 6 | spring_d = 20; 7 | rod_d = 10; 8 | loops = 17; 9 | tight_loops=3; 10 | 11 | tpart = tight_loops/loops; 12 | lpart = wire_d * tight_loops / 100; 13 | r_table = [ 14 | [0.00, 0], 15 | [0+tpart, 0], 16 | [0.5, 1], 17 | [1-tpart, 0], 18 | [1.00, 0], 19 | ]; 20 | l_table = [ 21 | [0.00, -0.50], 22 | [0+tpart, -0.5+lpart], 23 | [1-tpart, +0.5-lpart], 24 | [1.00, +0.50], 25 | ]; 26 | lsteps = 45; 27 | tsteps = loops * lsteps; 28 | path = [ 29 | for (i = [0:1:tsteps]) 30 | let( 31 | u = i / tsteps, 32 | a = u * 360 * loops, 33 | r = lookup(u, r_table) * spring_d/2 + wire_d/2 + rod_d/2, 34 | z = lookup(u, l_table) * spring_l, 35 | pt = [r*cos(a), r*sin(a), z] 36 | ) pt 37 | ]; 38 | yrot(90) { 39 | color("lightblue") 40 | path_sweep(circle(d=wire_d), path); 41 | cylinder(d=rod_d, h=spring_l+10, center=true); 42 | } 43 | 44 | 45 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 46 | -------------------------------------------------------------------------------- /BOSL2/images/BOSL2logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/BOSL2/images/BOSL2logo.png -------------------------------------------------------------------------------- /BOSL2/linear_bearings.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // LibFile: linear_bearings.scad 3 | // Mounts and models for LMxUU style linear bearings. 4 | // Includes: 5 | // include 6 | // include 7 | // FileGroup: Parts 8 | // FileSummary: Mounts for LMxUU style linear bearings. 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | include 12 | 13 | 14 | // Section: Generic Linear Bearings 15 | 16 | // Module: linear_bearing_housing() 17 | // Synopsis: Creates a generic linear bearing mount clamp. 18 | // SynTags: Geom 19 | // Topics: Parts, Bearings 20 | // See Also: linear_bearing(), lmXuu_info(), ball_bearing() 21 | // Usage: 22 | // linear_bearing_housing(d, l, tab, gap, wall, tabwall, screwsize) [ATTACHMENTS]; 23 | // Description: 24 | // Creates a model of a clamp to hold a generic linear bearing cartridge. 25 | // Arguments: 26 | // d = Diameter of linear bearing. (Default: 15) 27 | // l = Length of linear bearing. (Default: 24) 28 | // tab = Clamp tab height. (Default: 8) 29 | // tabwall = Clamp Tab thickness. (Default: 5) 30 | // wall = Wall thickness of clamp housing. (Default: 3) 31 | // gap = Gap in clamp. (Default: 5) 32 | // screwsize = Size of screw to use to tighten clamp. (Default: 3) 33 | // --- 34 | // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` 35 | // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` 36 | // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` 37 | // Example: 38 | // linear_bearing_housing(d=19, l=29, wall=2, tab=8, screwsize=2.5); 39 | module linear_bearing_housing(d=15, l=24, tab=8, gap=5, wall=3, tabwall=5, screwsize=3, anchor=BOTTOM, spin=0, orient=UP) 40 | { 41 | od = d+2*wall; 42 | ogap = gap+2*tabwall; 43 | tabh = tab/2+od/2*sqrt(2)-ogap/2-1; 44 | h = od+tab/2; 45 | anchors = [ 46 | named_anchor("axis", [0,0,-tab/2/2]), 47 | named_anchor("screw", [0,2-ogap/2,tabh-tab/2/2],FWD), 48 | named_anchor("nut", [0,ogap/2-2,tabh-tab/2/2],FWD) 49 | ]; 50 | attachable(anchor,spin,orient, size=[l, od, h], anchors=anchors) { 51 | down(tab/2/2) 52 | difference() { 53 | union() { 54 | // Housing 55 | zrot(90) teardrop(r=od/2,h=l); 56 | 57 | // Base 58 | cube([l,od,od/2], anchor=TOP); 59 | 60 | // Tabs 61 | cube([l,ogap,od/2+tab/2], anchor=BOTTOM); 62 | } 63 | 64 | // Clear bearing space 65 | zrot(90) teardrop(r=d/2,h=l+0.05); 66 | 67 | // Clear gap 68 | cube([l+0.05,gap,od], anchor=BOTTOM); 69 | 70 | up(tabh) { 71 | screwsize = is_string(screwsize)? screwsize : str("M",screwsize); 72 | 73 | // Screwhole 74 | fwd(ogap/2-2+0.01) 75 | screw_hole(str(screwsize,",",ogap), head="socket", counterbore=3, anchor="head_bot", orient=FWD, $fn=12); 76 | 77 | // Nut holder 78 | back(ogap/2-2+0.01) 79 | nut_trap_inline(tabwall, screwsize, orient=BACK); 80 | } 81 | } 82 | children(); 83 | } 84 | } 85 | 86 | 87 | // Module: linear_bearing() 88 | // Synopsis: Creates a generic linear bearing cartridge. 89 | // SynTags: Geom 90 | // Topics: Parts, Bearings 91 | // See Also: linear_bearing_housing(), lmXuu_info(), ball_bearing() 92 | // Usage: 93 | // linear_bearing(l, od, id, length) [ATTACHMENTS]; 94 | // Description: 95 | // Creates a rough model of a generic linear ball bearing cartridge. 96 | // Arguments: 97 | // l/length = The length of the linear bearing cartridge. 98 | // od = The outer diameter of the linear bearing cartridge. 99 | // id = The inner diameter of the linear bearing cartridge. 100 | // --- 101 | // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` 102 | // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` 103 | // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` 104 | // Example: 105 | // linear_bearing(l=24, od=15, id=8); 106 | module linear_bearing(l, od=15, id=8, length, anchor=CTR, spin=0, orient=UP) { 107 | l = first_defined([l, length, 24]); 108 | attachable(anchor,spin,orient, d=od, l=l) { 109 | color("silver") { 110 | tube(id=id, od=od, l=l-1); 111 | tube(id=od-1, od=od, l=l); 112 | tube(id=id, od=id+1, l=l); 113 | tube(id=id+2, od=od-2, l=l); 114 | } 115 | children(); 116 | } 117 | } 118 | 119 | 120 | // Section: lmXuu Linear Bearings 121 | 122 | // Module: lmXuu_housing() 123 | // Synopsis: Creates a standardized LM*UU linear bearing mount clamp. 124 | // SynTags: Geom 125 | // Topics: Parts, Bearings 126 | // See Also: linear_bearing(), linear_bearing_housing(), lmXuu_info(), lmXuu_bearing(), lmXuu_housing(), ball_bearing() 127 | // Usage: 128 | // lmXuu_housing(size, tab, gap, wall, tabwall, screwsize) [ATTACHMENTS]; 129 | // Description: 130 | // Creates a model of a clamp to hold a standard sized lmXuu linear bearing cartridge. 131 | // Arguments: 132 | // size = Standard lmXuu inner size. 133 | // tab = Clamp tab height. Default: 7 134 | // tabwall = Clamp Tab thickness. Default: 5 135 | // wall = Wall thickness of clamp housing. Default: 3 136 | // gap = Gap in clamp. Default: 5 137 | // screwsize = Size of screw to use to tighten clamp. Default: 3 138 | // --- 139 | // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` 140 | // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` 141 | // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` 142 | // Example: 143 | // lmXuu_housing(size=10, wall=2, tab=6, screwsize=2.5); 144 | module lmXuu_housing(size=8, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, anchor=BOTTOM, spin=0, orient=UP) 145 | { 146 | info = lmXuu_info(size); 147 | d = info[0]; 148 | l = info[1]; 149 | linear_bearing_housing(d=d, l=l, tab=tab, gap=gap, wall=wall, tabwall=tabwall, screwsize=screwsize, orient=orient, spin=spin, anchor=anchor) children(); 150 | } 151 | 152 | 153 | // Module: lmXuu_bearing() 154 | // Synopsis: Creates a standardized LM*UU linear bearing cartridge. 155 | // SynTags: Geom 156 | // Topics: Parts, Bearings 157 | // See Also: linear_bearing(), linear_bearing_housing(), lmXuu_info(), lmXuu_bearing(), lmXuu_housing(), ball_bearing() 158 | // Usage: 159 | // lmXuu_bearing(size) [ATTACHMENTS]; 160 | // Description: 161 | // Creates a model of an lmXuu linear ball bearing cartridge. 162 | // Arguments: 163 | // size = Standard lmXuu inner size. 164 | // --- 165 | // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` 166 | // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` 167 | // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` 168 | // Example: 169 | // lmXuu_bearing(size=10); 170 | module lmXuu_bearing(size=8, anchor=CTR, spin=0, orient=UP) { 171 | info = lmXuu_info(size); 172 | linear_bearing(l=info[1], id=size, od=info[0], anchor=anchor, spin=spin, orient=orient) children(); 173 | } 174 | 175 | 176 | // Section: lmXuu Linear Bearing Info 177 | 178 | 179 | // Function: lmXuu_info() 180 | // Synopsis: Returns the sizes of a standard LM*UU linear bearing cartridge. 181 | // Topics: Parts, Bearings 182 | // See Also: linear_bearing(), linear_bearing_housing(), lmXuu_info(), lmXuu_bearing(), lmXuu_housing(), ball_bearing() 183 | // Usage: 184 | // diam_len = lmXuu_info(size); 185 | // Description: 186 | // Get dimensional info for a standard metric lmXuu linear bearing cartridge. 187 | // Returns `[DIAM, LENGTH]` for the cylindrical cartridge. 188 | // Arguments: 189 | // size = Inner diameter of lmXuu bearing, in mm. 190 | function lmXuu_info(size) = 191 | let( 192 | data = [ 193 | // size, diam, length 194 | [ 4, 8, 12], 195 | [ 5, 10, 15], 196 | [ 6, 12, 19], 197 | [ 8, 15, 24], 198 | [ 10, 19, 29], 199 | [ 12, 21, 30], 200 | [ 13, 23, 32], 201 | [ 16, 28, 37], 202 | [ 20, 32, 42], 203 | [ 25, 40, 59], 204 | [ 30, 45, 64], 205 | [ 35, 52, 70], 206 | [ 40, 60, 80], 207 | [ 50, 80, 100], 208 | [ 60, 90, 110], 209 | [ 80, 120, 140], 210 | [100, 150, 175], 211 | ], 212 | found = search([size], data, 1)[0] 213 | ) 214 | assert(found!=[], str("Unsupported lmXuu linear bearing size: ", size)) 215 | select(data[found], 1, -1); 216 | 217 | 218 | 219 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 220 | -------------------------------------------------------------------------------- /BOSL2/modular_hose.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // LibFile: modular_hose.scad 3 | // Modular hose segment and attachment ends. 4 | // Includes: 5 | // include 6 | // include 7 | // FileGroup: Parts 8 | // FileSummary: Modular flexible hose segments. 9 | ////////////////////////////////////////////////////////////////////////// 10 | 11 | // Section: Modular Hose Parts 12 | 13 | _modhose_small_end = [ 14 | turtle([ 15 | "left", 90-38.5, // 1/4" hose 16 | "arcsteps", 12, 17 | "arcleft", 6.38493, 62.15, 18 | "arcsteps", 4, 19 | "arcleft", .5, 90+38.5-62.15, 20 | "move", .76, 21 | "left", 67.5, 22 | "move", .47, 23 | "left", 90-67.5, 24 | "move", 4.165, 25 | "right", 30, 26 | "move", 2.1 27 | ], 28 | state=[4.864,0]), 29 | turtle([ // 1/2" hose 30 | "left", 90-41, 31 | "arcsteps", 16, 32 | "arcleft", 10.7407, 64.27, 33 | "arcsteps", 4, 34 | "arcleft", .5, 90+41-64.27, 35 | "move", .95-.4, 36 | "left", 45, 37 | "move", .4*sqrt(2), 38 | "left",45, 39 | "move", 7.643-.4, 40 | "right", 30, 41 | "move", 4.06 42 | ], 43 | state=[8.1, 0]), 44 | turtle([ // 3/4" hose 45 | "left", 90-30.4, 46 | "arcsteps", 16, 47 | "arcleft", 13.99219,53, 48 | "arcsteps", 4, 49 | "arcleft", .47,90-53+30.4, 50 | "move", .597, 51 | "left", 52 | "move", 9.908-1.905/tan(25) +3.81*cos(30), // Change to 25 deg angle 53 | "right", 25, // to remove narrow point in wall 54 | "move",1.905 /sin(25), 55 | ], 56 | state=[11.989,0]) 57 | ]; 58 | 59 | 60 | _modhose_big_end = [ 61 | turtle([ // 1/4" hose 62 | "left", 90-22, 63 | "move", 6.5, 64 | "left",.75, 65 | "arcsteps", 8, 66 | "arcleft", 6.5, 37.3, 67 | "setdir",90, 68 | "move", .21, 69 | "right", 70 | "move", 1.24, 71 | "right", 45, 72 | "move", .7835, 73 | "right", 19, 74 | "move", 1.05, 75 | "setdir", -90, 76 | "move", 1, 77 | "right", 22, 78 | "move", 8.76 79 | ], 80 | state = [3.268,0]), 81 | turtle([ // 1/2" hose 82 | "left", 83 | "right", 22, 84 | "move", 9, 85 | "arcsteps", 8, 86 | "arcleft", 11, 36.5, 87 | "setdir",90, 88 | "move",2-1.366, 89 | "right", 90 | "move",.91, 91 | "arcsteps", 4, 92 | "arcright", 1.25, 90, 93 | "move", 2.2, 94 | "arcsteps", 8, 95 | "arcright", 13, 22.4, 96 | "move", 8.73 97 | ], 98 | state=[6.42154, 0]), 99 | turtle([ // 3/4" hose 100 | "left", 90-22, 101 | "move", 7.633, 102 | "arcsteps", 16, 103 | "arcleft", 13.77, 35.27, 104 | "setdir", 90, 105 | "move", 1.09, 106 | "right", 107 | "move",1.0177, 108 | "right", 45, 109 | "move", 1.009, 110 | "right", 77.8-45, 111 | "move", .3, 112 | "arcright", 15.5, 34.2, 113 | "move", 6.47 114 | ], 115 | state=[9.90237,0]) 116 | ]; 117 | 118 | 119 | _modhose_waist = [1.7698, 1.8251, 3.95998]; 120 | 121 | 122 | // Module: modular_hose() 123 | // Synopsis: Creates modular hose parts. 124 | // Topics: Modular Hose, Parts 125 | // See Also: modular_hose_radius(), tube() 126 | // Usage: 127 | // modular_hose(size, type, [clearance], [waist_len], [anchor], [spin], [orient]) [ATTACHMENTS]; 128 | // Description: 129 | // Construct moduler hose segments or modular hose ends for connection to standard 130 | // modular hose systems. The 1/4", 1/2" and 3/4" sizes are supported and you can 131 | // produce just one end to make a mount or end attachment to a modular hose, 132 | // or you can make modular hose segments. To make assembly possible with printed 133 | // parts you can add clearances that make the ball end smaller and the socket end 134 | // larger. These work by simply increasing the radius of the whole end by the specified 135 | // amount. On a Prusa printer with PETG, a clearance of 0.05 allows the 3/4" hose parts to mate 136 | // with standard modular hose or itself. A clearance of 0.05 to 0.1 allows the 1/2" parts to mate with 137 | // standard hose, and with clearance 0 the 1/4" parts will mate with standard hose. Note that clearance values 138 | // are different for the different sizes. You will have to experiment with your machine and materials. Small 139 | // adjustments will change the stiffness of the connection. 140 | // Arguments: 141 | // size = size of modular hose part, must be 1/4, 1/2 or 3/4. 142 | // type = type of part to make, either "segment", "socket" (or "big"), or "ball" (or "small") 143 | // clearance = clearance to make assembly possible. Either a scalar to apply the same to both ends or a vector [small,large] to apply different clearances to the two ends. Default: 0 144 | // waist_len = size of central "waist" of the part. Default: standard length. 145 | // Example: 146 | // modular_hose(1/4,"segment"); 147 | // right(25)modular_hose(1/2,"segment"); 148 | // right(60)modular_hose(3/4,"segment"); 149 | // Example: A mount point for modular hose 150 | // cylinder(h=10, r=20) 151 | // attach(TOP) modular_hose(1/2, "ball", waist_len=15); 152 | // Example: Mounting plate for something at the end of the hose 153 | // cuboid([50,50,5]) 154 | // attach(TOP) modular_hose(3/4, "socket", waist_len=0); 155 | function modular_hose(size, type, clearance=0, waist_len, anchor=BOTTOM, spin=0,orient=UP) = no_function("modular_hose"); 156 | module modular_hose(size, type, clearance=0, waist_len, anchor=BOTTOM, spin=0,orient=UP) 157 | { 158 | clearance = force_list(clearance,2); 159 | ind = search([size],[1/4, 1/2, 3/4])[0]; 160 | sbound = 161 | assert(ind!=[], "Must specify size as 1/4, 1/2 or 3/4") 162 | pointlist_bounds(_modhose_small_end[ind]); 163 | bbound = pointlist_bounds(_modhose_big_end[ind]); 164 | smallend = 165 | assert(is_vector(clearance,2), "Clearance must be a scalar or length 2 vector") 166 | move([-clearance[0],-sbound[0].y],p=_modhose_small_end[ind]); 167 | bigend = move([clearance[1], -bbound[0].y], p=_modhose_big_end[ind]); 168 | 169 | midlength = first_defined([waist_len, _modhose_waist[ind]]); 170 | dummy = assert(midlength>=0,"midlength must be nonnegative"); 171 | 172 | goodtypes = ["small","big","segment","socket","ball"]; 173 | shape = 174 | assert(in_list(type,goodtypes), str("type must be one of ",goodtypes)) 175 | type=="segment"? concat(back(midlength,p=smallend),yflip(p=bigend)) 176 | : type=="small" || type=="ball" ? 177 | concat(back(midlength,p=smallend), 178 | [[last(smallend).x,0],[ smallend[0].x,0]]) 179 | : concat( back(midlength,p=bigend), 180 | [[last(bigend).x,0],[ bigend[0].x,0]]); 181 | bounds = pointlist_bounds(shape); 182 | center = mean(bounds); 183 | attachable(anchor,spin,orient,l=bounds[1].y-bounds[0].y, r=bounds[1].x) 184 | { 185 | rotate_extrude(convexity=4) 186 | polygon(fwd(center.y,p=shape)); 187 | children(); 188 | } 189 | } 190 | 191 | 192 | // Function: modular_hose_radius() 193 | // Synopsis: Returns the waist radius of the given modular hose size. 194 | // Topics: Modular Hose, Parts 195 | // See Also: modular_hose(), tube() 196 | // Usage: 197 | // r = modular_hose_radius(size, [outer]); 198 | // Description: 199 | // Returns the inner (or outer) diameter of the waist section 200 | // of the modular hose to enable hollowing out connecting channels. 201 | // Note: diameter is accurate to about 1e-4. 202 | // Arguments: 203 | // size = size of hose part, must be 1/4, 1/2 or 3/4 204 | // outer = set to true to get the outer diameter. 205 | // Example(3D): 206 | // $fn=64; 207 | // back_half() 208 | // diff("remove") 209 | // cuboid(50){ 210 | // attach(TOP) modular_hose(1/2, "ball"); 211 | // up(0.01)position(TOP+RIGHT)tag("remove") 212 | // rot(180) 213 | // xrot(-90) 214 | // rotate_extrude(angle=135) 215 | // right(25) 216 | // circle(r=modular_hose_radius(1/2)); 217 | // } 218 | function modular_hose_radius(size, outer=false) = 219 | let( 220 | ind = search([size],[1/4, 1/2, 3/4])[0] 221 | ) 222 | assert(ind!=[], "Must specify size as 1/4, 1/2 or 3/4") 223 | let( 224 | b = select(_modhose_big_end[ind], [0,-1]), 225 | s = select(_modhose_small_end[ind], [0,-1]) 226 | ) 227 | outer ? b[1][0] : b[0][0]; 228 | 229 | 230 | 231 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 232 | -------------------------------------------------------------------------------- /BOSL2/resources/docs_custom.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: Arial; 3 | color: #1a1a1a; 4 | background-color: #fdfdfd; 5 | } 6 | body { 7 | margin: 0 auto; 8 | max-width: 50em; 9 | padding-left: 50px; 10 | padding-right: 50px; 11 | padding-top: 50px; 12 | padding-bottom: 50px; 13 | hyphens: auto; 14 | overflow-wrap: break-word; 15 | text-rendering: optimizeLegibility; 16 | font-kerning: normal; 17 | font-size: 0.75em; 18 | } 19 | @media (max-width: 600px) { 20 | body { 21 | font-size: 90%; 22 | padding: 12px; 23 | } 24 | h1 { 25 | font-size: 1.8em; 26 | } 27 | } 28 | @media print { 29 | html { 30 | background-color: white; 31 | } 32 | body { 33 | background-color: transparent; 34 | color: black; 35 | font-size: 12pt; 36 | } 37 | p, h2, h3 { 38 | orphans: 3; 39 | widows: 3; 40 | } 41 | h2, h3, h4 { 42 | page-break-after: avoid; 43 | } 44 | } 45 | p { 46 | margin: 1em 0; 47 | } 48 | a { 49 | color: #1a1a1a; 50 | } 51 | a:visited { 52 | color: #1a1a1a; 53 | } 54 | img { 55 | max-width: 100%; 56 | } 57 | h1 { 58 | margin-top: 2.5em; 59 | } 60 | h2, h3, h4, h5, h6 { 61 | margin-top: 1.4em; 62 | } 63 | h5, h6 { 64 | font-size: 1em; 65 | font-style: italic; 66 | } 67 | h6 { 68 | font-weight: normal; 69 | } 70 | h1, h2 { 71 | border-bottom: 1px solid #1a1a1a; 72 | } 73 | ol, ul { 74 | padding-left: 1.7em; 75 | margin-top: 1em; 76 | } 77 | li > ol, li > ul { 78 | margin-top: 0; 79 | } 80 | blockquote { 81 | margin: 1em 0 1em 1.7em; 82 | padding-left: 1em; 83 | border-left: 2px solid #e6e6e6; 84 | color: #606060; 85 | } 86 | code { 87 | font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace; 88 | font-size: 85%; 89 | margin: 0; 90 | hyphens: manual; 91 | } 92 | pre { 93 | margin: 1em 0; 94 | overflow: auto; 95 | } 96 | pre code { 97 | padding: 0; 98 | overflow: visible; 99 | overflow-wrap: normal; 100 | } 101 | .sourceCode { 102 | background-color: transparent; 103 | overflow: visible; 104 | } 105 | hr { 106 | background-color: #1a1a1a; 107 | border: none; 108 | height: 1px; 109 | margin: 1em 0; 110 | } 111 | table { 112 | margin: 1em 0; 113 | border-collapse: collapse; 114 | width: 100%; 115 | overflow-x: auto; 116 | display: block; 117 | font-variant-numeric: lining-nums tabular-nums; 118 | } 119 | table caption { 120 | margin-bottom: 0.75em; 121 | } 122 | thead { 123 | background-color: #ccf 124 | } 125 | tbody { 126 | margin-top: 0.5em; 127 | } 128 | tr { 129 | } 130 | th { 131 | padding: 0.25em 0.5em 0.25em 0.5em; 132 | vertical-align: top; 133 | text-align: left; 134 | border: 1px solid #1a1a1a; 135 | } 136 | td { 137 | padding: 0.125em 0.5em 0.25em 0.5em; 138 | vertical-align: top; 139 | border: 1px solid #1a1a1a; 140 | } 141 | header { 142 | margin-bottom: 4em; 143 | text-align: center; 144 | } 145 | #TOC li { 146 | list-style: none; 147 | } 148 | #TOC ul { 149 | padding-left: 1.3em; 150 | } 151 | #TOC > ul { 152 | padding-left: 0; 153 | } 154 | #TOC a:not(:hover) { 155 | text-decoration: none; 156 | } 157 | code{white-space: pre-wrap;} 158 | span.smallcaps{font-variant: small-caps;} 159 | div.columns{display: flex; gap: min(4vw, 1.5em);} 160 | div.column{flex: auto; overflow-x: auto;} 161 | div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} 162 | ul.task-list{list-style: none;} 163 | ul.task-list li input[type="checkbox"] { 164 | width: 0.8em; 165 | margin: 0 0.8em 0.2em -1.6em; 166 | vertical-align: middle; 167 | } 168 | 169 | -------------------------------------------------------------------------------- /BOSL2/resources/links-filter-html.lua: -------------------------------------------------------------------------------- 1 | -- links-filter.lua 2 | function Link(el) 3 | found, _, a, b = string.find(el.target, "^(%w+)%.scad#(.*)$") 4 | if found then 5 | el.target = string.format("#%sscadmd__%s", string.lower(a), string.lower(b)) 6 | return el 7 | end 8 | 9 | found, _, a = string.find(el.target, "^(%w+)%.scad$") 10 | if found then 11 | el.target = string.format("#%sscadmd", string.lower(a)) 12 | return el 13 | end 14 | 15 | found, _, a, b = string.find(el.target, "^Tutorial-(%w+)%#(.*)$") 16 | if found then 17 | el.target = string.format("#tutorial-%smd__%s", string.lower(a), string.lower(b)) 18 | return el 19 | end 20 | 21 | found, _, a = string.find(el.target, "^Tutorial-(%w+)$") 22 | if found then 23 | el.target = string.format("#tutorial-%smd", string.lower(a)) 24 | return el 25 | end 26 | 27 | found, _, a, b = string.find(el.target, "^(%w+)%.md#(.*)$") 28 | if found then 29 | el.target = string.format("#%smd__%s", string.lower(a), string.lower(b)) 30 | return el 31 | end 32 | 33 | found, _, a = string.find(el.target, "^(%w+)%.md$") 34 | if found then 35 | el.target = string.format("#%smd", string.lower(a)) 36 | return el 37 | end 38 | 39 | found, _, a, b = string.find(el.target, "^(%w+)#(.*)$") 40 | if found then 41 | el.target = string.format("#%smd__%s", string.lower(a), string.lower(b)) 42 | return el 43 | end 44 | 45 | found, _, a = string.find(el.target, "^(%w+)$") 46 | if found then 47 | el.target = string.format("#%smd", string.lower(a)) 48 | return el 49 | end 50 | 51 | return el 52 | end 53 | 54 | -------------------------------------------------------------------------------- /BOSL2/scripts/check_for_tabs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if grep -H -n -P '\t' *.scad ; then 4 | echo "Tabs found in source code." 2>&1 5 | exit 1 6 | fi 7 | exit 0 8 | 9 | 10 | -------------------------------------------------------------------------------- /BOSL2/scripts/find_modular_asserts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | awk ' 4 | /^module/{ 5 | m=1 6 | split($2,narr,"(") 7 | module=narr[1]"()" 8 | } 9 | /^function/{ 10 | m=0 11 | module="" 12 | } 13 | /[^=] *assert\(/{ 14 | if(m) { 15 | if(fname!=FILENAME) { 16 | fname=FILENAME 17 | print "File",fname 18 | } 19 | if(prevmodule!=module) { 20 | prevmodule=module 21 | print " Module",module 22 | } 23 | assertline=$0 24 | sub(/^ */, "", assertline) 25 | print " ",FNR,":",assertline 26 | } 27 | } 28 | ' *.scad 29 | 30 | -------------------------------------------------------------------------------- /BOSL2/scripts/func_coverage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import operator 5 | 6 | funcs = {} 7 | for filename in os.listdir("."): 8 | if filename.endswith(".scad"): 9 | filepath = os.path.join(".",filename) 10 | with open(filepath, "r") as f: 11 | for linenum,line in enumerate(f.readlines()): 12 | if line.startswith("function "): 13 | funcname = line[9:].strip().split("(")[0].strip() 14 | if funcname.startswith("_"): 15 | continue 16 | if funcname in funcs: 17 | print("WARNING!!! Function {} re-defined at {}:{}".format(funcname, filename, linenum+1)); 18 | print(" Previously defined at {}:{}".format(*funcs[funcname])); 19 | else: 20 | funcs[funcname] = (filename, linenum+1) 21 | 22 | covered = {} 23 | uncovered = funcs.copy() 24 | for filename in os.listdir("tests"): 25 | if filename.startswith("test_") and filename.endswith(".scad"): 26 | filepath = os.path.join("tests",filename) 27 | with open(filepath, "r") as f: 28 | for linenum,line in enumerate(f.readlines()): 29 | if line.startswith("module "): 30 | testmodule = line[7:].strip().split("(")[0].strip() 31 | if testmodule.startswith("test_"): 32 | funcname = testmodule.split("_",1)[1] 33 | if funcname in uncovered: 34 | if filename != "test_" + uncovered[funcname][0]: 35 | print("WARNING!!! Function {} defined at {}:{}".format(funcname, *uncovered[funcname])); 36 | print(" but tested at {}:{}".format(filename, linenum+1)); 37 | covered[funcname] = (filename,linenum+1) 38 | del uncovered[funcname] 39 | 40 | uncovered_by_file = {} 41 | for funcname in sorted(list(uncovered.keys())): 42 | filename = uncovered[funcname][0] 43 | if filename not in uncovered_by_file: 44 | uncovered_by_file[filename] = [] 45 | uncovered_by_file[filename].append(funcname) 46 | 47 | mostest = [] 48 | for filename in uncovered_by_file.keys(): 49 | mostest.append( (len(uncovered_by_file[filename]), filename) ) 50 | 51 | # for funcname in sorted(covered): 52 | # print("COVERED: function {}".format(funcname)) 53 | 54 | print("NOT COVERED:") 55 | for cnt, filename in sorted(mostest, key=operator.itemgetter(0)): 56 | filefuncs = uncovered_by_file[filename] 57 | print(" {}: {:d} uncovered functions".format(filename, cnt)) 58 | for funcname in filefuncs: 59 | print(" {}".format(funcname)) 60 | 61 | totfuncs = len(funcs.keys()) 62 | covfuncs = len(covered) 63 | 64 | print( 65 | "Total coverage: {} of {} functions ({:.2f}%)".format( 66 | covfuncs, totfuncs, 100.0*covfuncs/totfuncs 67 | ) 68 | ) 69 | 70 | # vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 71 | -------------------------------------------------------------------------------- /BOSL2/scripts/img2scad.py: -------------------------------------------------------------------------------- 1 | #!env python3 2 | 3 | import re 4 | import os 5 | import sys 6 | import os.path 7 | import argparse 8 | 9 | from PIL import Image 10 | 11 | 12 | def img2scad(filename, varname, resize, outf): 13 | indent = " " * 4 14 | im = Image.open(filename).convert('L') 15 | if resize: 16 | print("Resizing to {}x{}".format(resize[0],resize[1])) 17 | im = im.resize(resize) 18 | pix = im.load() 19 | width, height = im.size 20 | print("// Image {} ({}x{})".format(filename, width, height), file=outf) 21 | print("{} = [".format(varname), file=outf) 22 | line = indent 23 | for x in range(width): 24 | line += "[ " 25 | for y in range(height): 26 | line += "{:d}, ".format(pix[x,y]) 27 | if len(line) > 60: 28 | print(line, file=outf) 29 | line = indent * 2 30 | line += " ]," 31 | if line != indent: 32 | print(line, file=outf) 33 | line = indent 34 | print("];", file=outf) 35 | print("", file=outf) 36 | 37 | 38 | def main(): 39 | parser = argparse.ArgumentParser(prog='img2scad') 40 | parser.add_argument('-o', '--outfile', 41 | help='Output .scad file.') 42 | parser.add_argument('-v', '--varname', 43 | help='Variable to use in .scad file.') 44 | parser.add_argument('-r', '--resize', 45 | help='Resample image to WIDTHxHEIGHT.') 46 | parser.add_argument('infile', help='Input image file.') 47 | opts = parser.parse_args() 48 | 49 | non_alnum = re.compile(r'[^a-zA-Z0-9_]') 50 | if not opts.varname: 51 | if opts.outfile: 52 | opts.varname = os.path.splitext(os.path.basename(opts.outfile))[0] 53 | opts.varname = non_alnum.sub("", opts.varname) 54 | else: 55 | opts.varname = "image_data" 56 | size_pat = re.compile(r'^([0-9][0-9]*)x([0-9][0-9]*)$') 57 | if opts.resize: 58 | m = size_pat.match(opts.resize) 59 | if not m: 60 | print("Expected WIDTHxHEIGHT resize format.", file=sys.stderr) 61 | sys.exit(-1) 62 | opts.resize = (int(m.group(1)), int(m.group(2))) 63 | 64 | if not opts.varname or non_alnum.search(opts.varname): 65 | print("Bad variable name: {}".format(opts.varname), file=sys.stderr) 66 | sys.exit(-1) 67 | 68 | if opts.outfile: 69 | with open(opts.outfile, "w") as outf: 70 | img2scad(opts.infile, opts.varname, opts.resize, outf) 71 | else: 72 | img2scad(opts.infile, opts.varname, opts.resize, sys.stdout) 73 | 74 | sys.exit(0) 75 | 76 | 77 | if __name__ == "__main__": 78 | main() 79 | 80 | -------------------------------------------------------------------------------- /BOSL2/scripts/increment_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERFILE="version.scad" 4 | 5 | if [[ "$(cat "$VERFILE")" =~ BOSL_VERSION.*=.*\[([0-9]+),\ *([0-9]+),\ *([0-9]+)\]\; ]]; then 6 | major=${BASH_REMATCH[1]} minor=${BASH_REMATCH[2]} revision=${BASH_REMATCH[3]} 7 | new_revision=$(( revision+1 )) 8 | 9 | echo "Current Version: $major.$minor.$revision" 10 | echo "New Version: $major.$minor.$new_revision" 11 | 12 | sed -i.bak -e 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$new_revision];/g" "$VERFILE" 13 | rm "$VERFILE".bak 14 | else 15 | echo "Could not extract version number from $VERFILE" >&2 16 | exit 1 17 | fi 18 | -------------------------------------------------------------------------------- /BOSL2/scripts/linecount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lib_comment_lines=$(cat -- *.scad | grep -c '^// ') 4 | tutorial_lines=$(cat tutorials/*.md | grep -c '^ *[^ /]') 5 | 6 | printf '%-20s: %6d\n' \ 7 | 'Documentation Lines' "$(( lib_comment_lines + tutorial_lines ))" \ 8 | 'Example Code Lines' "$(cat examples/*.scad | grep -c '^ *[^ /]')" \ 9 | 'Library Code Lines' "$(cat -- *.scad | grep -c '^ *[^ /]')" \ 10 | 'Support Script Lines' "$(cat scripts/*.sh scripts/*.py | grep -c '^ *[^ /]')" \ 11 | 'Test Code Lines' "$(cat tests/*.scad | grep -c '^ *[^ /]')" 12 | 13 | -------------------------------------------------------------------------------- /BOSL2/scripts/mkdocspdf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OUTFILE_BASE="BOSL2_Docs" 4 | FORMATS="html5" 5 | SOURCES="constants.scad.md transforms.scad.md attachments.scad.md shapes2d.scad.md shapes3d.scad.md drawing.scad.md masks2d.scad.md masks3d.scad.md distributors.scad.md color.scad.md partitions.scad.md mutators.scad.md paths.scad.md regions.scad.md skin.scad.md vnf.scad.md beziers.scad.md rounding.scad.md turtle3d.scad.md math.scad.md linalg.scad.md vectors.scad.md coords.scad.md geometry.scad.md trigonometry.scad.md version.scad.md comparisons.scad.md lists.scad.md utility.scad.md strings.scad.md structs.scad.md fnliterals.scad.md threading.scad.md screws.scad.md screw_drive.scad.md bottlecaps.scad.md ball_bearings.scad.md cubetruss.scad.md gears.scad.md hinges.scad.md joiners.scad.md linear_bearings.scad.md modular_hose.scad.md nema_steppers.scad.md polyhedra.scad.md sliders.scad.md tripod_mounts.scad.md walls.scad.md wiring.scad.md Tutorial-*.md Topics.md AlphaIndex.md" 6 | PANDOC="/usr/local/Cellar/pandoc/3.1/bin/pandoc" 7 | TITLE="Documentation for the Belfry OpenSCAD Library v2" 8 | AUTHOR="Garth Minette" 9 | 10 | if [[ ! -d BOSL2.wiki ]] ; then 11 | echo "Must be in the BOSL2 directory." 12 | exit 255 13 | fi 14 | 15 | cd BOSL2.wiki 16 | 17 | for format in ${FORMATS} ; do 18 | suffix=$(echo ${format} | sed 's/html5/html/') 19 | outfile="${OUTFILE_BASE}.${suffix}" 20 | 21 | echo "Generating ${outfile} ..." 22 | ${PANDOC} -f gfm -t ${format} -o ../${outfile} \ 23 | -s --embed-resources --mathjax --file-scope --pdf-engine=xelatex \ 24 | --columns=100 --epub-cover-image=../images/BOSL2logo.png \ 25 | --toc -N --toc-depth=2 --css=../resources/docs_custom.css \ 26 | --lua-filter=../resources/links-filter-html.lua \ 27 | --resource-path='.:images:images/*' \ 28 | --variable mainfont=Arial --variable sansfont=Arial \ 29 | --metadata title="${TITLE}" \ 30 | --metadata author="${AUTHOR}" \ 31 | --metadata date="$(date -j "+%B %e, %Y")" \ 32 | --metadata geometry=left=3cm,right=3cm,top=2cm,bottom=2cm \ 33 | ${SOURCES} 34 | done 35 | 36 | cd .. 37 | 38 | -------------------------------------------------------------------------------- /BOSL2/scripts/purge_wiki_history.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ ! -d BOSL2.wiki/.git ]] ; then 4 | echo "Must be run from above the BOSL2.wiki repo." >&2 5 | exit 1 6 | fi 7 | 8 | set -e # short-circuit if any command fails 9 | cd BOSL2.wiki 10 | rm -rf .git 11 | git init 12 | git add . 13 | git commit -m "Purged wiki history." 14 | git config pull.rebase false 15 | git remote add origin git@github.com:BelfrySCAD/BOSL2.wiki.git 16 | git push -u --force origin master 17 | -------------------------------------------------------------------------------- /BOSL2/scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OPENSCAD=openscad 4 | if [ "$(uname -s)" == "Darwin" ]; then 5 | OPENSCAD=/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD 6 | fi 7 | 8 | INFILES=("$@") 9 | if (( ${#INFILES[@]} == 0 )); then 10 | INFILES=(tests/test_*.scad) 11 | fi 12 | 13 | OUTCODE=0 14 | for testfile in "${INFILES[@]}"; do 15 | if [[ -f "$testfile" ]] ; then 16 | repname="$(basename "$testfile" | sed 's/^test_//')" 17 | "${OPENSCAD}" -o out.echo --hardwarnings --check-parameters true --check-parameter-ranges true "$testfile" 2>&1 18 | retcode=$? 19 | output=$(cat out.echo) 20 | if (( retcode == 0 )) && [[ "$output" = "" ]]; then 21 | echo "$repname: PASS" 22 | else 23 | echo "$repname: FAIL!" 24 | echo "$output" 25 | OUTCODE=1 26 | fi 27 | rm -f out.echo 28 | fi 29 | done 30 | exit "$OUTCODE" 31 | 32 | -------------------------------------------------------------------------------- /BOSL2/sliders.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // LibFile: sliders.scad 3 | // Simple V-groove based sliders and rails. 4 | // Includes: 5 | // include 6 | // include 7 | // FileGroup: Parts 8 | // FileSummary: Simple sliders and rails. 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | // Section: Modules 13 | 14 | 15 | // Module: slider() 16 | // Synopsis: Creates a V-groove slider. 17 | // SynTags: Geom 18 | // Topics: Parts, Sliders 19 | // See Also: rail() 20 | // Usage: 21 | // slider(l, w, h, [base=], [wall=], [ang=], [$slop=]) [ATTACHMENTS]; 22 | // Description: 23 | // Creates a slider to match a V-groove rail. 24 | // Arguments: 25 | // l = Length (long axis) of slider. 26 | // w = Width of slider. 27 | // h = Height of slider. 28 | // --- 29 | // base = Height of slider base. 30 | // wall = Width of wall behind each side of the slider. 31 | // ang = Overhang angle for slider, to facilitate supportless printig. 32 | // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` 33 | // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` 34 | // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` 35 | // $slop = The printer-specific slop value to make parts fit just right. 36 | // Example: 37 | // slider(l=30, base=10, wall=4, $slop=0.2, spin=90); 38 | function slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP) = no_function("slider"); 39 | module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP) 40 | { 41 | full_width = w + 2*wall; 42 | full_height = h + base; 43 | 44 | attachable(anchor,spin,orient, size=[full_width, l, h+2*base]) { 45 | zrot(90) 46 | down(base+h/2) { 47 | // Base 48 | cuboid([full_width, l, base-get_slop()], chamfer=2, edges=[FRONT,BACK], except_edges=BOT, anchor=BOTTOM); 49 | 50 | // Wall 51 | xflip_copy(offset=w/2+get_slop()) { 52 | cuboid([wall, l, full_height], chamfer=2, edges=RIGHT, except_edges=BOT, anchor=BOTTOM+LEFT); 53 | } 54 | 55 | // Sliders 56 | up(base+h/2) { 57 | xflip_copy(offset=w/2+get_slop()+0.02) { 58 | bev_h = h/2*tan(ang); 59 | prismoid([h, l], [0, l-w], h=bev_h+0.01, orient=LEFT, anchor=BOT); 60 | } 61 | } 62 | } 63 | children(); 64 | } 65 | } 66 | 67 | 68 | 69 | // Module: rail() 70 | // Synopsis: Creates a V-groove rail. 71 | // SynTags: Geom 72 | // Topics: Parts, Sliders 73 | // See Also: slider() 74 | // Usage: 75 | // rail(l, w, h, [chamfer=], [ang=]) [ATTACHMENTS]; 76 | // Description: 77 | // Creates a V-groove rail. 78 | // Arguments: 79 | // l = Length (long axis) of slider. 80 | // w = Width of slider. 81 | // h = Height of slider. 82 | // chamfer = Size of chamfer at end of rail. 83 | // ang = Overhang angle for slider, to facilitate supportless printing. 84 | // --- 85 | // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `BOTTOM` 86 | // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` 87 | // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` 88 | // Example: 89 | // rail(l=100, w=10, h=10); 90 | function rail(l=30, w=10, h=10, chamfer=1.0, ang=30, anchor=BOTTOM, spin=0, orient=UP) = no_function("rail"); 91 | module rail(l=30, w=10, h=10, chamfer=1.0, ang=30, anchor=BOTTOM, spin=0, orient=UP) 92 | { 93 | attack_ang = 30; 94 | attack_len = 2; 95 | 96 | fudge = 1.177; 97 | chamf = sqrt(2) * chamfer; 98 | cosa = cos(ang*fudge); 99 | sina = sin(ang*fudge); 100 | 101 | z1 = h/2; 102 | z2 = z1 - chamf * cosa; 103 | z3 = z1 - attack_len * sin(attack_ang); 104 | z4 = 0; 105 | 106 | x1 = w/2; 107 | x2 = x1 - chamf * sina; 108 | x3 = x1 - chamf; 109 | x4 = x1 - attack_len * sin(attack_ang); 110 | x5 = x2 - attack_len * sin(attack_ang); 111 | x6 = x1 - z1 * sina; 112 | x7 = x4 - z1 * sina; 113 | 114 | y1 = l/2; 115 | y2 = y1 - attack_len * cos(attack_ang); 116 | 117 | attachable(anchor,spin,orient, size=[w, l, h]) { 118 | polyhedron( 119 | convexity=4, 120 | points=[ 121 | [-x5, -y1, z3], 122 | [ x5, -y1, z3], 123 | [ x7, -y1, z4], 124 | [ x4, -y1, -z1-0.05], 125 | [-x4, -y1, -z1-0.05], 126 | [-x7, -y1, z4], 127 | 128 | [-x3, -y2, z1], 129 | [ x3, -y2, z1], 130 | [ x2, -y2, z2], 131 | [ x6, -y2, z4], 132 | [ x1, -y2, -z1-0.05], 133 | [-x1, -y2, -z1-0.05], 134 | [-x6, -y2, z4], 135 | [-x2, -y2, z2], 136 | 137 | [ x5, y1, z3], 138 | [-x5, y1, z3], 139 | [-x7, y1, z4], 140 | [-x4, y1, -z1-0.05], 141 | [ x4, y1, -z1-0.05], 142 | [ x7, y1, z4], 143 | 144 | [ x3, y2, z1], 145 | [-x3, y2, z1], 146 | [-x2, y2, z2], 147 | [-x6, y2, z4], 148 | [-x1, y2, -z1-0.05], 149 | [ x1, y2, -z1-0.05], 150 | [ x6, y2, z4], 151 | [ x2, y2, z2], 152 | ], 153 | faces=[ 154 | [0, 1, 2], 155 | [0, 2, 5], 156 | [2, 3, 4], 157 | [2, 4, 5], 158 | 159 | [0, 13, 6], 160 | [0, 6, 7], 161 | [0, 7, 1], 162 | [1, 7, 8], 163 | [1, 8, 9], 164 | [1, 9, 2], 165 | [2, 9, 10], 166 | [2, 10, 3], 167 | [3, 10, 11], 168 | [3, 11, 4], 169 | [4, 11, 12], 170 | [4, 12, 5], 171 | [5, 12, 13], 172 | [5, 13, 0], 173 | 174 | [14, 15, 16], 175 | [14, 16, 19], 176 | [16, 17, 18], 177 | [16, 18, 19], 178 | 179 | [14, 27, 20], 180 | [14, 20, 21], 181 | [14, 21, 15], 182 | [15, 21, 22], 183 | [15, 22, 23], 184 | [15, 23, 16], 185 | [16, 23, 24], 186 | [16, 24, 17], 187 | [17, 24, 25], 188 | [17, 25, 18], 189 | [18, 25, 26], 190 | [18, 26, 19], 191 | [19, 26, 27], 192 | [19, 27, 14], 193 | 194 | [6, 21, 20], 195 | [6, 20, 7], 196 | [7, 20, 27], 197 | [7, 27, 8], 198 | [8, 27, 26], 199 | [8, 26, 9], 200 | [9, 26, 25], 201 | [9, 25, 10], 202 | [10, 25, 24], 203 | [10, 24, 11], 204 | [11, 24, 23], 205 | [11, 23, 12], 206 | [12, 23, 22], 207 | [12, 22, 13], 208 | [13, 22, 21], 209 | [13, 21, 6], 210 | ] 211 | ); 212 | children(); 213 | } 214 | } 215 | 216 | 217 | 218 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 219 | -------------------------------------------------------------------------------- /BOSL2/std.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // LibFile: std.scad 3 | // File that includes the standard BOSL include files. 4 | // Includes: 5 | // include 6 | ////////////////////////////////////////////////////////////////////// 7 | 8 | assert(version_num()>=20190500, "BOSL2 requires OpenSCAD version 2019.05 or later."); 9 | 10 | include 11 | 12 | include 13 | include 14 | include 15 | include 16 | include 17 | include 18 | include 19 | include 20 | include 21 | include 22 | include 23 | include 24 | include 25 | include 26 | include 27 | include 28 | include 29 | include 30 | include 31 | include 32 | include 33 | include 34 | include 35 | include 36 | include 37 | include 38 | include 39 | 40 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 41 | 42 | -------------------------------------------------------------------------------- /BOSL2/structs.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // LibFile: structs.scad 3 | // This file provides manipulation of "structs". A "struct" is a data structure that 4 | // associates arbitrary keys with values and allows you to get and set values 5 | // by key. 6 | // Includes: 7 | // include 8 | // include 9 | // FileGroup: Data Management 10 | // FileSummary: Structure/Dictionary Manipulation 11 | ////////////////////////////////////////////////////////////////////// 12 | 13 | 14 | // Section: struct operations 15 | // 16 | // A struct is a data structure that associates arbitrary keys (of any type) with values (of any type). 17 | // Structures are implemented as lists of [key, value] pairs. 18 | // 19 | // An empty list `[]` is an empty structure and can be used wherever a structure input is required. 20 | 21 | // Function: struct_set() 22 | // Synopsis: Sets one or more key-value pairs in a struct. 23 | // Topics: Data Structures, Dictionaries 24 | // See Also: struct_set(), struct_remove(), struct_val(), struct_keys(), echo_struct(), is_struct() 25 | // Usage: 26 | // struct2 = struct_set(struct, key, value, [grow=]); 27 | // struct2 = struct_set(struct, [key1, value1, key2, value2, ...], [grow=]); 28 | // Description: 29 | // Sets the key(s) in the structure to the specified value(s), returning a new updated structure. If a 30 | // key exists its value is changed, otherwise the key is added to the structure. If `grow=false` then 31 | // it is an error to set a key not already defined in the structure. If you specify the same key twice 32 | // that is also an error. Note that key order will change when you change a key's value. 33 | // Arguments: 34 | // struct = input structure. 35 | // key = key to set or list of key,value pairs to set 36 | // value = value to set the key to (when giving a single key and value) 37 | // --- 38 | // grow = Set to true to allow structure to grow, or false for new keys to generate an error. Default: true 39 | // Example: Create a struct containing just one key-value pair 40 | // some_struct = struct_set([], "answer", 42); 41 | // // 'some_struct' now contains a single value, 42, under one key, "answer". 42 | // Example: Create a struct containing more than one key-value pair. Note that keys and values need not be the same type. 43 | // some_struct = struct_set([], ["answer", 42, 2, "two", "quote", "What a nice day"]); 44 | // // 'some struct' now contains these key-value pairs: 45 | // // answer: 42 46 | // // 2: two 47 | // // quote: What a nice day 48 | function struct_set(struct, key, value, grow=true) = 49 | is_def(value) ? struct_set(struct,[key,value],grow=grow) 50 | : 51 | assert(is_list(key) && len(key)%2==0, "[key,value] pair list is not a list or has an odd length") 52 | let( 53 | new_entries = [for(i=[0:1:len(key)/2-1]) [key[2*i], key[2*i+1]]], 54 | newkeys = column(new_entries,0), 55 | indlist = search(newkeys, struct,0,0), 56 | badkeys = grow ? (search([undef],new_entries,1,0)[0] != [] ? [undef] : []) 57 | : [for(i=idx(indlist)) if (is_undef(newkeys[i]) || len(indlist[i])==0) newkeys[i]], 58 | ind = flatten(indlist), 59 | dupfind = search(newkeys, new_entries,0,0), 60 | dupkeys = [for(i=idx(dupfind)) if (len(dupfind[i])>1) newkeys[i]] 61 | ) 62 | assert(badkeys==[], str("Unknown or bad key ",_format_key(badkeys[0])," in struct_set")) 63 | assert(dupkeys==[], str("Duplicate key ",_format_key(dupkeys[0])," for struct")) 64 | concat(list_remove(struct,ind), new_entries); 65 | 66 | function _format_key(key) = is_string(key) ? str("\"",key,"\""): key; 67 | 68 | // Function: struct_remove() 69 | // Synopsis: Removes one or more keys from a struct. 70 | // Topics: Data Structures, Dictionaries 71 | // See Also: struct_set(), struct_remove(), struct_val(), struct_keys(), echo_struct(), is_struct() 72 | // Usage: 73 | // struct2 = struct_remove(struct, key); 74 | // Description: 75 | // Remove key or list of keys from a structure. If you want to remove a single key which is a list 76 | // you must pass it as a singleton list, or struct_remove will attempt to remove the listed items as keys. 77 | // If you list the same item multiple times for removal it will be removed without error. 78 | // Arguments: 79 | // struct = input structure 80 | // key = a single key or list of keys to remove. 81 | function struct_remove(struct, key) = 82 | !is_list(key) ? struct_remove(struct, [key]) : 83 | let(ind = search(key, struct)) 84 | list_remove(struct, [for(i=ind) if (i!=[]) i]); 85 | 86 | 87 | // Function: struct_val() 88 | // Synopsis: Returns the value for an key in a struct. 89 | // Topics: Data Structures, Dictionaries 90 | // See Also: struct_set(), struct_remove(), struct_val(), struct_keys(), echo_struct(), is_struct() 91 | // Usage: 92 | // val = struct_val(struct, key, default); 93 | // Description: 94 | // Returns the value for the specified key in the structure, or default value if the key is not present 95 | // Arguments: 96 | // struct = input structure 97 | // key = key whose value to return 98 | // default = default value to return if key is not present. Default: undef 99 | function struct_val(struct, key, default=undef) = 100 | assert(is_def(key),"key is missing") 101 | let(ind = search([key],struct)[0]) 102 | ind == [] ? default : struct[ind][1]; 103 | 104 | 105 | // Function: struct_keys() 106 | // Synopsis: Returns a list of keys for a struct. 107 | // Topics: Data Structures, Dictionaries 108 | // See Also: struct_set(), struct_remove(), struct_val(), struct_keys(), echo_struct(), is_struct() 109 | // Usage: 110 | // keys = struct_keys(struct); 111 | // Description: 112 | // Returns a list of the keys in a structure 113 | // Arguments: 114 | // struct = input structure 115 | function struct_keys(struct) = column(struct,0); 116 | 117 | 118 | // Function&Module: echo_struct() 119 | // Synopsis: Echoes the struct to the console in a formatted manner. 120 | // Topics: Data Structures, Dictionaries 121 | // See Also: struct_set(), struct_remove(), struct_val(), struct_keys(), echo_struct(), is_struct() 122 | // Usage: 123 | // echo_struct(struct, [name]); 124 | // foo = echo_struct(struct, [name]); 125 | // Description: 126 | // Displays a list of structure keys and values, one pair per line, for easier reading. 127 | // Arguments: 128 | // struct = input structure 129 | // name = optional structure name to list at the top of the output. Default: "" 130 | function echo_struct(struct,name="") = 131 | let( keylist = [for(entry=struct) str(" ",entry[0],": ",entry[1],"\n")]) 132 | echo(str("\nStructure ",name,"\n",str_join(keylist))) 133 | undef; 134 | 135 | module echo_struct(struct,name="") { 136 | no_children($children); 137 | dummy = echo_struct(struct,name); 138 | } 139 | 140 | 141 | // Function: is_struct() 142 | // Synopsis: Returns true if the value is a struct. 143 | // Topics: Data Structures, Dictionaries 144 | // See Also: struct_set(), struct_remove(), struct_val(), struct_keys(), echo_struct(), is_struct() 145 | // Usage: 146 | // bool = is_struct(struct); 147 | // Description: 148 | // Returns true if the input is a list of pairs, false otherwise. 149 | function is_struct(x) = 150 | is_list(x) && [for (xx=x) if(!(is_list(xx) && len(xx)==2)) 1] == []; 151 | 152 | 153 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 154 | -------------------------------------------------------------------------------- /BOSL2/tests/README.txt: -------------------------------------------------------------------------------- 1 | This directory contains regression tests scripts to check whether the 2 | library is working correctly. If all the scripts run without 3 | producing any output, then all the tests have passed. 4 | -------------------------------------------------------------------------------- /BOSL2/tests/polyhedra.scad: -------------------------------------------------------------------------------- 1 | include<../std.scad> 2 | include<../polyhedra.scad> 3 | 4 | 5 | if (true) { 6 | 7 | $fn=96; 8 | 9 | // Display of all solids with insphere, midsphere and circumsphere 10 | 11 | for(i=[0:len(_polyhedra_)-1]) { 12 | move_copies([[3*i,0,0]]) // Plain polyhedron 13 | regular_polyhedron(index=i, mr=1,facedown=true); 14 | move_copies([[3*i,3.5,0]]){ // Inner radius means sphere touches faces of the polyhedron 15 | sphere(r=1.005); // Sphere is slightly oversized so you can see it poking out from each face 16 | %regular_polyhedron(index=i, ir=1,facedown=true); 17 | } 18 | move_copies([[3*i,7,0]]){ // Mid radius means the sphere touches the center of each edge 19 | sphere(r=1); 20 | %regular_polyhedron(index=i, mr=1,facedown=true); 21 | } 22 | move_copies([[3*i,11,0]]){ // outer radius means points of the polyhedron are on the sphere 23 | %sphere(r=.99); // Slightly undersized sphere means the points poke out a bit 24 | regular_polyhedron(index=i, or=1,facedown=true); 25 | } 26 | } 27 | } 28 | 29 | 30 | 31 | /////////////////////////////////////////////////////////////////////////////////////////////////// 32 | // 33 | // Examples start here: not part of library 34 | 35 | 36 | 37 | /* 38 | // Test that rounded shapes are the same size as unrounded 39 | shape = "dodecahedron"; 40 | //shape = "cube"; 41 | top_half(cp=[0,0,.2]) 42 | difference(){ 43 | regular_polyhedron(shape); 44 | regular_polyhedron(shape, rounding=0.2,side=1.0000); 45 | } 46 | */ 47 | -------------------------------------------------------------------------------- /BOSL2/tests/test_affine.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | 4 | // 2D 5 | 6 | module test_affine2d_identity() { 7 | assert(affine2d_identity() == [[1,0,0],[0,1,0],[0,0,1]]); 8 | } 9 | test_affine2d_identity(); 10 | 11 | 12 | module test_affine2d_translate() { 13 | assert(affine2d_translate([0,0]) == [[1,0,0],[0,1,0],[0,0,1]]); 14 | assert(affine2d_translate([10,20]) == [[1,0,10],[0,1,20],[0,0,1]]); 15 | assert(affine2d_translate([20,10]) == [[1,0,20],[0,1,10],[0,0,1]]); 16 | } 17 | test_affine2d_translate(); 18 | 19 | 20 | module test_affine2d_scale() { 21 | assert(affine2d_scale([1,1]) == [[1,0,0],[0,1,0],[0,0,1]]); 22 | assert(affine2d_scale([2,3]) == [[2,0,0],[0,3,0],[0,0,1]]); 23 | assert(affine2d_scale([5,4]) == [[5,0,0],[0,4,0],[0,0,1]]); 24 | } 25 | test_affine2d_scale(); 26 | 27 | 28 | module test_affine2d_mirror() { 29 | assert(approx(affine2d_mirror([1,1]),[[0,-1,0],[-1,0,0],[0,0,1]])); 30 | assert(affine2d_mirror([1,0]) == [[-1,0,0],[0,1,0],[0,0,1]]); 31 | assert(affine2d_mirror([0,1]) == [[1,0,0],[0,-1,0],[0,0,1]]); 32 | } 33 | test_affine2d_mirror(); 34 | 35 | 36 | module test_affine2d_zrot() { 37 | for(a = [-360:2/3:360]) { 38 | assert(affine2d_zrot(a) == [[cos(a),-sin(a),0],[sin(a),cos(a),0],[0,0,1]]); 39 | } 40 | } 41 | test_affine2d_zrot(); 42 | 43 | 44 | module test_affine2d_skew() { 45 | for(ya = [-89:3:89]) { 46 | for(xa = [-89:3:89]) { 47 | assert(affine2d_skew(xa=xa, ya=ya) == [[1,tan(xa),0],[tan(ya),1,0],[0,0,1]]); 48 | } 49 | } 50 | } 51 | test_affine2d_skew(); 52 | 53 | 54 | // 3D 55 | 56 | module test_affine3d_identity() { 57 | assert(affine3d_identity() == [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); 58 | } 59 | test_affine3d_identity(); 60 | 61 | 62 | module test_affine3d_translate() { 63 | assert(affine3d_translate([10,20,30]) == [[1,0,0,10],[0,1,0,20],[0,0,1,30],[0,0,0,1]]); 64 | assert(affine3d_translate([3,2,1]) == [[1,0,0,3],[0,1,0,2],[0,0,1,1],[0,0,0,1]]); 65 | } 66 | test_affine3d_translate(); 67 | 68 | 69 | module test_affine3d_scale() { 70 | assert(affine3d_scale([3,2,4]) == [[3,0,0,0],[0,2,0,0],[0,0,4,0],[0,0,0,1]]); 71 | } 72 | test_affine3d_scale(); 73 | 74 | 75 | module test_affine3d_mirror() { 76 | assert(affine3d_mirror([1,0,0]) == [[-1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); 77 | assert(affine3d_mirror([0,1,0]) == [[1,0,0,0],[0,-1,0,0],[0,0,1,0],[0,0,0,1]]); 78 | assert(affine3d_mirror([0,0,1]) == [[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]]); 79 | assert(approx(affine3d_mirror([1,1,1]), [[1/3,-2/3,-2/3,0],[-2/3,1/3,-2/3,0],[-2/3,-2/3,1/3,0],[0,0,0,1]])); 80 | } 81 | test_affine3d_mirror(); 82 | 83 | 84 | module test_affine3d_xrot() { 85 | for(a = [-360:2/3:360]) { 86 | assert(approx(affine3d_xrot(a), [[1,0,0,0],[0,cos(a),-sin(a),0],[0,sin(a),cos(a),0],[0,0,0,1]])); 87 | } 88 | } 89 | test_affine3d_xrot(); 90 | 91 | 92 | module test_affine3d_yrot() { 93 | for(a = [-360:2/3:360]) { 94 | assert(approx(affine3d_yrot(a), [[cos(a),0,sin(a),0],[0,1,0,0],[-sin(a),0,cos(a),0],[0,0,0,1]])); 95 | } 96 | } 97 | test_affine3d_yrot(); 98 | 99 | 100 | module test_affine3d_zrot() { 101 | for(a = [-360:2/3:360]) { 102 | assert(approx(affine3d_zrot(a), [[cos(a),-sin(a),0,0],[sin(a),cos(a),0,0],[0,0,1,0],[0,0,0,1]])); 103 | } 104 | } 105 | test_affine3d_zrot(); 106 | 107 | 108 | module test_affine3d_rot_by_axis() { 109 | for(a = [-360:2/3:360]) { 110 | assert(approx(affine3d_rot_by_axis(RIGHT,a), [[1,0,0,0],[0,cos(a),-sin(a),0],[0,sin(a),cos(a),0],[0,0,0,1]])); 111 | assert(approx(affine3d_rot_by_axis(BACK,a), [[cos(a),0,sin(a),0],[0,1,0,0],[-sin(a),0,cos(a),0],[0,0,0,1]])); 112 | assert(approx(affine3d_rot_by_axis(UP,a), [[cos(a),-sin(a),0,0],[sin(a),cos(a),0,0],[0,0,1,0],[0,0,0,1]])); 113 | } 114 | } 115 | test_affine3d_rot_by_axis(); 116 | 117 | 118 | module test_affine3d_rot_from_to() { 119 | assert(approx(affine3d_rot_from_to(UP,FRONT), affine3d_xrot(90))); 120 | assert(approx(affine3d_rot_from_to(UP,RIGHT), affine3d_yrot(90))); 121 | assert(approx(affine3d_rot_from_to(BACK,LEFT), affine3d_zrot(90))); 122 | } 123 | test_affine3d_rot_from_to(); 124 | 125 | 126 | module test_affine3d_skew() { 127 | assert(affine3d_skew(sxy=2) == [[1,2,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); 128 | assert(affine3d_skew(sxz=2) == [[1,0,2,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); 129 | assert(affine3d_skew(syx=2) == [[1,0,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]); 130 | assert(affine3d_skew(syz=2) == [[1,0,0,0],[0,1,2,0],[0,0,1,0],[0,0,0,1]]); 131 | assert(affine3d_skew(szx=2) == [[1,0,0,0],[0,1,0,0],[2,0,1,0],[0,0,0,1]]); 132 | assert(affine3d_skew(szy=2) == [[1,0,0,0],[0,1,0,0],[0,2,1,0],[0,0,0,1]]); 133 | } 134 | test_affine3d_skew(); 135 | 136 | 137 | module test_affine3d_skew_xy() { 138 | for(ya = [-89:3:89]) { 139 | for(xa = [-89:3:89]) { 140 | assert(affine3d_skew_xy(xa=xa, ya=ya) == [[1,tan(xa),0,0],[tan(ya),1,0,0],[0,0,1,0],[0,0,0,1]]); 141 | } 142 | } 143 | } 144 | test_affine3d_skew_xy(); 145 | 146 | 147 | module test_affine3d_skew_xz() { 148 | for(za = [-89:3:89]) { 149 | for(xa = [-89:3:89]) { 150 | assert(affine3d_skew_xz(xa=xa, za=za) == [[1,0,tan(xa),0],[0,1,0,0],[tan(za),0,1,0],[0,0,0,1]]); 151 | } 152 | } 153 | } 154 | test_affine3d_skew_xz(); 155 | 156 | 157 | module test_affine3d_skew_yz() { 158 | for(za = [-89:3:89]) { 159 | for(ya = [-89:3:89]) { 160 | assert(affine3d_skew_yz(ya=ya, za=za) == [[1,0,0,0],[0,1,tan(ya),0],[0,tan(za),1,0],[0,0,0,1]]); 161 | } 162 | } 163 | } 164 | test_affine3d_skew_yz(); 165 | 166 | 167 | //////////////////////////// 168 | 169 | 170 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 171 | -------------------------------------------------------------------------------- /BOSL2/tests/test_attachments.scad: -------------------------------------------------------------------------------- 1 | include<../std.scad> 2 | 3 | 4 | module test__standard_anchors() { 5 | assert_equal(_standard_anchors(), [[-1,-1,1],[0,-1,1],[1,-1,1],[-1,0,1],[0,0,1],[1,0,1],[-1,1,1],[0,1,1],[1,1,1],[-1,-1,0],[0,-1,0],[1,-1,0],[-1,0,0],[0,0,0],[1,0,0],[-1,1,0],[0,1,0],[1,1,0],[-1,-1,-1],[0,-1,-1],[1,-1,-1],[-1,0,-1],[0,0,-1],[1,0,-1],[-1,1,-1],[0,1,-1],[1,1,-1]]); 6 | } 7 | test__standard_anchors(); 8 | 9 | -------------------------------------------------------------------------------- /BOSL2/tests/test_cubetruss.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../cubetruss.scad> 3 | 4 | 5 | module test_cubetruss_dist() { 6 | assert(cubetruss_dist(5,1,size=30,strut=3) == 138); 7 | assert(cubetruss_dist(3,2,size=30,strut=3) == 87); 8 | assert(cubetruss_dist(5,1,size=20,strut=2) == 92); 9 | assert(cubetruss_dist(3,2,size=20,strut=2) == 58); 10 | } 11 | test_cubetruss_dist(); 12 | 13 | 14 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 15 | -------------------------------------------------------------------------------- /BOSL2/tests/test_distributors.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | 4 | module test_line_copies() { 5 | assert_equal(line_copies(l=100,n=5,p=[0,0,0]), [[-50,0,0],[-25,0,0],[0,0,0],[25,0,0],[50,0,0]]); 6 | assert_equal(line_copies(20,n=5,p=[0,0,0]), [[-40,0,0],[-20,0,0],[0,0,0],[20,0,0],[40,0,0]]); 7 | assert_equal(line_copies(spacing=20,n=5,p=[0,0,0]), [[-40,0,0],[-20,0,0],[0,0,0],[20,0,0],[40,0,0]]); 8 | assert_equal(line_copies(spacing=[0,20],n=5,p=[0,0,0]), [[0,-40,0],[0,-20,0],[0,0,0],[0,20,0],[0,40,0]]); 9 | 10 | assert_equal(line_copies(p1=[0,0],l=100,n=5,p=[0,0,0]), [[0,0,0],[25,0,0],[50,0,0],[75,0,0],[100,0,0]]); 11 | assert_equal(line_copies(p1=[0,0],20,n=5,p=[0,0,0]), [[0,0,0],[20,0,0],[40,0,0],[60,0,0],[80,0,0]]); 12 | assert_equal(line_copies(p1=[0,0],spacing=20,n=5,p=[0,0,0]), [[0,0,0],[20,0,0],[40,0,0],[60,0,0],[80,0,0]]); 13 | assert_equal(line_copies(p1=[0,0],spacing=[0,20],n=5,p=[0,0,0]), [[0,0,0],[0,20,0],[0,40,0],[0,60,0],[0,80,0]]); 14 | } 15 | test_line_copies(); 16 | 17 | 18 | 19 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 20 | -------------------------------------------------------------------------------- /BOSL2/tests/test_drawing.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | module test_turtle() { 4 | assert_approx( 5 | turtle([ 6 | "move", 10, 7 | "ymove", 5, 8 | "xmove", 5, 9 | "xymove", [10,15], 10 | "left", 135, 11 | "untilx", 0, 12 | "turn", 90, 13 | "untily", 0, 14 | "right", 135, 15 | "arcsteps", 5, 16 | "arcright", 15, 30, 17 | "arcleft", 15, 30, 18 | "arcsteps", 0, 19 | "arcrightto", 15, 90, 20 | "arcleftto", 15, 180, 21 | "jump", [10,10], 22 | "xjump", 15, 23 | "yjump", 15, 24 | "angle", 30, 25 | "length", 15, 26 | "right", 27 | "move", 28 | "scale", 2, 29 | "left", 30 | "move", 31 | "addlength", 5, 32 | "repeat", 3, ["move"], 33 | ], $fn=24), 34 | [[0,0],[10,0],[10,5],[15,5],[25,20],[-3.5527136788e-15,45],[-45,0],[-44.8716729206,1.9578928833],[-44.4888873943,3.88228567654],[-43.8581929877,5.74025148548],[-42.9903810568,7.5],[-42.1225691259,9.25974851452],[-41.4918747192,11.1177143235],[-41.1090891929,13.0421071167],[-40.9807621135,15],[-41.0157305757,16.0236362005],[-41.120472923,17.0424997364],[-41.2945007983,18.0518401958],[-41.5370028033,19.0469515674],[-41.8468482818,20.0231941826],[-42.222592591,20.9760163477],[-42.6624838375,21.900975566],[-43.1644710453,22.7937592505],[-43.7262137184,23.6502048317],[-44.345092753,24.4663191649],[-45.0182226494,25.2382971483],[-45.7424649653,25.9625394642],[-46.5144429486,26.6356693606],[-47.3305572818,27.2545483952],[-48.187002863,27.8162910682],[-49.0797865476,28.318278276],[-50.0047457658,28.7581695226],[-50.957567931,29.1339138318],[-51.9338105462,29.4437593102],[-52.9289219177,29.6862613152],[-53.9382623771,29.8602891905],[-54.9571259131,29.9650315379],[-55.9807621135,30],[10,10],[15,10],[15,15],[2.00961894323,22.5],[-27.9903810568,22.5],[-62.9903810568,22.5],[-97.9903810568,22.5],[-132.990381057,22.5]] 35 | ); 36 | } 37 | test_turtle(); 38 | 39 | 40 | module test_arc() { 41 | assert_approx(arc(n=8, d=100, angle=135, cp=[10,10]), [[60,10],[57.1941665154,26.5139530978],[49.0915741234,41.1744900929],[36.6016038258,52.3362099614],[21.1260466978,58.7463956091],[4.40177619483,59.6856104947],[-11.6941869559,55.0484433951],[-25.3553390593,45.3553390593]]); 42 | assert_approx(arc(n=8, d=100, angle=135, cp=[10,10],endpoint=false), [[60,10],[57.8470167866,24.5142338627],[51.5734806151,37.778511651],[41.7196642082,48.6505226681],[29.1341716183,56.1939766256],[14.9008570165,59.7592363336],[0.245483899194,59.0392640202],[-13.5698368413,54.0960632174]]); 43 | assert_approx(arc(n=8, d=100, angle=[45,225], cp=[10,10]), [[45.3553390593,45.3553390593],[26.5139530978,57.1941665154],[4.40177619483,59.6856104947],[-16.6016038258,52.3362099614],[-32.3362099614,36.6016038258],[-39.6856104947,15.5982238052],[-37.1941665154,-6.51395309776],[-25.3553390593,-25.3553390593]]); 44 | assert_approx(arc(n=8, d=100, start=45, angle=135, cp=[10,10]), [[45.3553390593,45.3553390593],[31.6941869559,55.0484433951],[15.5982238052,59.6856104947],[-1.12604669782,58.7463956091],[-16.6016038258,52.3362099614],[-29.0915741234,41.1744900929],[-37.1941665154,26.5139530978],[-40,10]]); 45 | assert_approx(arc(n=8, d=100, start=45, angle=-90, cp=[10,10]), [[45.3553390593,45.3553390593],[52.3362099614,36.6016038258],[57.1941665154,26.5139530978],[59.6856104947,15.5982238052],[59.6856104947,4.40177619483],[57.1941665154,-6.51395309776],[52.3362099614,-16.6016038258],[45.3553390593,-25.3553390593]]); 46 | assert_approx(arc(n=8, width=100, thickness=30), [[50,-3.5527136788e-15],[39.5300788555,13.9348601124],[25.3202618476,24.0284558904],[8.71492362453,29.3258437015],[-8.71492362453,29.3258437015],[-25.3202618476,24.0284558904],[-39.5300788555,13.9348601124],[-50,-1.42108547152e-14]]); 47 | assert_approx(arc(n=8, cp=[10,10], points=[[45,45],[-25,45]]), [[45,45],[36.3342442379,51.9107096148],[26.3479795075,56.7198412457],[15.5419588213,59.1862449514],[4.45804117867,59.1862449514],[-6.34797950747,56.7198412457],[-16.3342442379,51.9107096148],[-25,45]]); 48 | assert_approx(arc(n=24, cp=[10,10], points=[[45,45],[-25,45]], long=true), [[45,45],[51.3889035257,37.146982612],[56.0464336973,28.1583574081],[58.7777575294,18.4101349813],[59.4686187624,8.31010126292],[58.0901174104,-1.71924090789],[54.6999187001,-11.2583458482],[49.4398408296,-19.9081753929],[42.5299224539,-27.3068913894],[34.2592180667,-33.1449920477],[24.9737063235,-37.1782589647],[15.0618171232,-39.2379732261],[4.93818287676,-39.2379732261],[-4.97370632349,-37.1782589647],[-14.2592180667,-33.1449920477],[-22.5299224539,-27.3068913894],[-29.4398408296,-19.9081753929],[-34.6999187001,-11.2583458482],[-38.0901174104,-1.71924090789],[-39.4686187624,8.31010126292],[-38.7777575294,18.4101349813],[-36.0464336973,28.1583574081],[-31.3889035257,37.146982612],[-25,45]]); 49 | assert_approx(arc($fn=24, cp=[10,10], points=[[45,45],[-25,45]], long=true), [[45,45],[53.2421021636,34.0856928585],[58.1827254512,21.3324740498],[59.4446596304,7.71403542491],[56.9315576496,-5.72987274525],[50.8352916125,-17.9728253654],[41.6213035891,-28.0800887515],[29.9930697126,-35.2799863457],[16.8383906815,-39.0228152281],[3.16160931847,-39.0228152281],[-9.9930697126,-35.2799863457],[-21.6213035891,-28.0800887515],[-30.8352916125,-17.9728253654],[-36.9315576496,-5.72987274525],[-39.4446596304,7.71403542491],[-38.1827254512,21.3324740498],[-33.2421021636,34.0856928585],[-25,45]]); 50 | } 51 | test_arc(); 52 | 53 | 54 | 55 | module test_dashed_stroke() { 56 | segs = dashed_stroke([[0,0],[15,0]], dashpat=[3,2], closed=false); 57 | assert_approx(segs,[[[0,0],[2.5,0]],[[4+1/6,0],[6+2/3,0]],[[8+1/3,0],[10+5/6,0]],[[12.5,0],[15,0]]]); 58 | } 59 | test_dashed_stroke(); 60 | 61 | 62 | -------------------------------------------------------------------------------- /BOSL2/tests/test_edges.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | 4 | module test__is_edge_array() { 5 | assert(_is_edge_array([[0,0,0,0],[0,0,0,0],[0,0,0,0]])); 6 | assert(_is_edge_array([[1,1,1,1],[1,1,1,1],[1,1,1,1]])); 7 | assert(!_is_edge_array([[1,1,1],[1,1,1],[1,1,1]])); 8 | assert(!_is_edge_array([[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]])); 9 | assert(!_is_edge_array([[1,1,1,1],[1,1,1,1]])); 10 | assert(!_is_edge_array([1,1,1,1])); 11 | assert(!_is_edge_array("foo")); 12 | assert(!_is_edge_array(42)); 13 | assert(!_is_edge_array(true)); 14 | assert(_is_edge_array(_edges(["X","Y"]))); 15 | } 16 | test__is_edge_array(); 17 | 18 | 19 | module test__edge_set() { 20 | // Edge set pass through 21 | assert(_edge_set([[1,1,1,1],[0,1,0,1],[0,0,0,0]]) == [[1,1,1,1],[0,1,0,1],[0,0,0,0]]); 22 | 23 | // Vectors towards corners 24 | assert(_edge_set([-1,-1,-1]) == [[1,0,0,0],[1,0,0,0],[1,0,0,0]]); 25 | assert(_edge_set([-1,-1, 1]) == [[0,0,1,0],[0,0,1,0],[1,0,0,0]]); 26 | assert(_edge_set([-1, 1,-1]) == [[0,1,0,0],[1,0,0,0],[0,0,1,0]]); 27 | assert(_edge_set([-1, 1, 1]) == [[0,0,0,1],[0,0,1,0],[0,0,1,0]]); 28 | assert(_edge_set([ 1,-1,-1]) == [[1,0,0,0],[0,1,0,0],[0,1,0,0]]); 29 | assert(_edge_set([ 1,-1, 1]) == [[0,0,1,0],[0,0,0,1],[0,1,0,0]]); 30 | assert(_edge_set([ 1, 1,-1]) == [[0,1,0,0],[0,1,0,0],[0,0,0,1]]); 31 | assert(_edge_set([ 1, 1, 1]) == [[0,0,0,1],[0,0,0,1],[0,0,0,1]]); 32 | 33 | // Vectors towards edges 34 | assert(_edge_set([ 0,-1,-1]) == [[1,0,0,0],[0,0,0,0],[0,0,0,0]]); 35 | assert(_edge_set([ 0, 1,-1]) == [[0,1,0,0],[0,0,0,0],[0,0,0,0]]); 36 | assert(_edge_set([ 0,-1, 1]) == [[0,0,1,0],[0,0,0,0],[0,0,0,0]]); 37 | assert(_edge_set([ 0, 1, 1]) == [[0,0,0,1],[0,0,0,0],[0,0,0,0]]); 38 | assert(_edge_set([-1, 0,-1]) == [[0,0,0,0],[1,0,0,0],[0,0,0,0]]); 39 | assert(_edge_set([ 1, 0,-1]) == [[0,0,0,0],[0,1,0,0],[0,0,0,0]]); 40 | assert(_edge_set([-1, 0, 1]) == [[0,0,0,0],[0,0,1,0],[0,0,0,0]]); 41 | assert(_edge_set([ 1, 0, 1]) == [[0,0,0,0],[0,0,0,1],[0,0,0,0]]); 42 | assert(_edge_set([-1,-1, 0]) == [[0,0,0,0],[0,0,0,0],[1,0,0,0]]); 43 | assert(_edge_set([ 1,-1, 0]) == [[0,0,0,0],[0,0,0,0],[0,1,0,0]]); 44 | assert(_edge_set([-1, 1, 0]) == [[0,0,0,0],[0,0,0,0],[0,0,1,0]]); 45 | assert(_edge_set([ 1, 1, 0]) == [[0,0,0,0],[0,0,0,0],[0,0,0,1]]); 46 | 47 | // Vectors towards faces 48 | assert(_edge_set([ 0, 0,-1]) == [[1,1,0,0],[1,1,0,0],[0,0,0,0]]); 49 | assert(_edge_set([ 0, 0, 1]) == [[0,0,1,1],[0,0,1,1],[0,0,0,0]]); 50 | assert(_edge_set([ 0,-1, 0]) == [[1,0,1,0],[0,0,0,0],[1,1,0,0]]); 51 | assert(_edge_set([ 0, 1, 0]) == [[0,1,0,1],[0,0,0,0],[0,0,1,1]]); 52 | assert(_edge_set([-1, 0, 0]) == [[0,0,0,0],[1,0,1,0],[1,0,1,0]]); 53 | assert(_edge_set([ 1, 0, 0]) == [[0,0,0,0],[0,1,0,1],[0,1,0,1]]); 54 | 55 | // Named edge sets 56 | assert(_edge_set("X") == [[1,1,1,1],[0,0,0,0],[0,0,0,0]]); 57 | assert(_edge_set("Y") == [[0,0,0,0],[1,1,1,1],[0,0,0,0]]); 58 | assert(_edge_set("Z") == [[0,0,0,0],[0,0,0,0],[1,1,1,1]]); 59 | assert(_edge_set("NONE") == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); 60 | assert(_edge_set("ALL") == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); 61 | } 62 | test__edge_set(); 63 | 64 | 65 | module test__normalize_edges() { 66 | assert(_normalize_edges([[-2,-2,-2,-2],[-2,-2,-2,-2],[-2,-2,-2,-2]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); 67 | assert(_normalize_edges([[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); 68 | assert(_normalize_edges([[0,0,0,0],[0,0,0,0],[0,0,0,0]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); 69 | assert(_normalize_edges([[1,1,1,1],[1,1,1,1],[1,1,1,1]]) == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); 70 | assert(_normalize_edges([[2,2,2,2],[2,2,2,2],[2,2,2,2]]) == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); 71 | } 72 | test__normalize_edges(); 73 | 74 | 75 | module test__edges() { 76 | assert(_edges("X")==[[1,1,1,1],[0,0,0,0],[0,0,0,0]]); 77 | assert(_edges("Y")==[[0,0,0,0],[1,1,1,1],[0,0,0,0]]); 78 | assert(_edges("Z")==[[0,0,0,0],[0,0,0,0],[1,1,1,1]]); 79 | assert(_edges(["X"])==[[1,1,1,1],[0,0,0,0],[0,0,0,0]]); 80 | assert(_edges(["Y"])==[[0,0,0,0],[1,1,1,1],[0,0,0,0]]); 81 | assert(_edges(["Z"])==[[0,0,0,0],[0,0,0,0],[1,1,1,1]]); 82 | assert(_edges(["X","Y"])==[[1,1,1,1],[1,1,1,1],[0,0,0,0]]); 83 | assert(_edges(["X","Z"])==[[1,1,1,1],[0,0,0,0],[1,1,1,1]]); 84 | assert(_edges(["Y","Z"])==[[0,0,0,0],[1,1,1,1],[1,1,1,1]]); 85 | assert(_edges("ALL",except="X")==[[0,0,0,0],[1,1,1,1],[1,1,1,1]]); 86 | assert(_edges("ALL",except="Y")==[[1,1,1,1],[0,0,0,0],[1,1,1,1]]); 87 | assert(_edges("ALL",except="Z")==[[1,1,1,1],[1,1,1,1],[0,0,0,0]]); 88 | assert(_edges(["Y","Z"],except=[FRONT+RIGHT,FRONT+LEFT])==[[0,0,0,0],[1,1,1,1],[0,0,1,1]]); 89 | } 90 | test__edges(); 91 | 92 | 93 | module test__corner_edge_count() { 94 | edges = _edges([TOP,FRONT+RIGHT]); 95 | assert(_corner_edge_count(edges,TOP+FRONT+RIGHT) == 3); 96 | assert(_corner_edge_count(edges,TOP+FRONT+LEFT) == 2); 97 | assert(_corner_edge_count(edges,BOTTOM+FRONT+RIGHT) == 1); 98 | assert(_corner_edge_count(edges,BOTTOM+FRONT+LEFT) == 0); 99 | } 100 | test__corner_edge_count(); 101 | 102 | 103 | module test__corner_edges() { 104 | edges = _edges([TOP,FRONT+RIGHT]); 105 | assert_equal(_corner_edges(edges,TOP+FRONT+RIGHT), [1,1,1]); 106 | assert_equal(_corner_edges(edges,TOP+FRONT+LEFT), [1,1,0]); 107 | assert_equal(_corner_edges(edges,BOTTOM+FRONT+RIGHT), [0,0,1]); 108 | assert_equal(_corner_edges(edges,BOTTOM+FRONT+LEFT), [0,0,0]); 109 | } 110 | test__corner_edges(); 111 | 112 | 113 | module test__corners() { 114 | assert_equal(_corners(BOT + FRONT + LEFT ), [1,0,0,0,0,0,0,0]); 115 | assert_equal(_corners(BOT + FRONT + RIGHT), [0,1,0,0,0,0,0,0]); 116 | assert_equal(_corners(BOT + BACK + LEFT ), [0,0,1,0,0,0,0,0]); 117 | assert_equal(_corners(BOT + BACK + RIGHT), [0,0,0,1,0,0,0,0]); 118 | assert_equal(_corners(TOP + FRONT + LEFT ), [0,0,0,0,1,0,0,0]); 119 | assert_equal(_corners(TOP + FRONT + RIGHT), [0,0,0,0,0,1,0,0]); 120 | assert_equal(_corners(TOP + BACK + LEFT ), [0,0,0,0,0,0,1,0]); 121 | assert_equal(_corners(TOP + BACK + RIGHT), [0,0,0,0,0,0,0,1]); 122 | 123 | assert_equal(_corners(BOT + FRONT), [1,1,0,0,0,0,0,0]); 124 | assert_equal(_corners(BOT + BACK ), [0,0,1,1,0,0,0,0]); 125 | assert_equal(_corners(TOP + FRONT), [0,0,0,0,1,1,0,0]); 126 | assert_equal(_corners(TOP + BACK ), [0,0,0,0,0,0,1,1]); 127 | assert_equal(_corners(BOT + LEFT ), [1,0,1,0,0,0,0,0]); 128 | assert_equal(_corners(BOT + RIGHT), [0,1,0,1,0,0,0,0]); 129 | assert_equal(_corners(TOP + LEFT ), [0,0,0,0,1,0,1,0]); 130 | assert_equal(_corners(TOP + RIGHT), [0,0,0,0,0,1,0,1]); 131 | assert_equal(_corners(FRONT + LEFT ), [1,0,0,0,1,0,0,0]); 132 | assert_equal(_corners(FRONT + RIGHT), [0,1,0,0,0,1,0,0]); 133 | assert_equal(_corners(BACK + LEFT ), [0,0,1,0,0,0,1,0]); 134 | assert_equal(_corners(BACK + RIGHT), [0,0,0,1,0,0,0,1]); 135 | 136 | assert_equal(_corners(LEFT), [1,0,1,0,1,0,1,0]); 137 | assert_equal(_corners(RIGHT), [0,1,0,1,0,1,0,1]); 138 | assert_equal(_corners(FRONT), [1,1,0,0,1,1,0,0]); 139 | assert_equal(_corners(BACK), [0,0,1,1,0,0,1,1]); 140 | assert_equal(_corners(BOT), [1,1,1,1,0,0,0,0]); 141 | assert_equal(_corners(TOP), [0,0,0,0,1,1,1,1]); 142 | 143 | assert_equal(_corners([BOT + FRONT + LEFT ]), [1,0,0,0,0,0,0,0]); 144 | assert_equal(_corners([BOT + FRONT + RIGHT]), [0,1,0,0,0,0,0,0]); 145 | assert_equal(_corners([BOT + BACK + LEFT ]), [0,0,1,0,0,0,0,0]); 146 | assert_equal(_corners([BOT + BACK + RIGHT]), [0,0,0,1,0,0,0,0]); 147 | assert_equal(_corners([TOP + FRONT + LEFT ]), [0,0,0,0,1,0,0,0]); 148 | assert_equal(_corners([TOP + FRONT + RIGHT]), [0,0,0,0,0,1,0,0]); 149 | assert_equal(_corners([TOP + BACK + LEFT ]), [0,0,0,0,0,0,1,0]); 150 | assert_equal(_corners([TOP + BACK + RIGHT]), [0,0,0,0,0,0,0,1]); 151 | 152 | assert_equal(_corners([BOT + FRONT]), [1,1,0,0,0,0,0,0]); 153 | assert_equal(_corners([BOT + BACK ]), [0,0,1,1,0,0,0,0]); 154 | assert_equal(_corners([TOP + FRONT]), [0,0,0,0,1,1,0,0]); 155 | assert_equal(_corners([TOP + BACK ]), [0,0,0,0,0,0,1,1]); 156 | assert_equal(_corners([BOT + LEFT ]), [1,0,1,0,0,0,0,0]); 157 | assert_equal(_corners([BOT + RIGHT]), [0,1,0,1,0,0,0,0]); 158 | assert_equal(_corners([TOP + LEFT ]), [0,0,0,0,1,0,1,0]); 159 | assert_equal(_corners([TOP + RIGHT]), [0,0,0,0,0,1,0,1]); 160 | assert_equal(_corners([FRONT + LEFT ]), [1,0,0,0,1,0,0,0]); 161 | assert_equal(_corners([FRONT + RIGHT]), [0,1,0,0,0,1,0,0]); 162 | assert_equal(_corners([BACK + LEFT ]), [0,0,1,0,0,0,1,0]); 163 | assert_equal(_corners([BACK + RIGHT]), [0,0,0,1,0,0,0,1]); 164 | 165 | assert_equal(_corners([LEFT]), [1,0,1,0,1,0,1,0]); 166 | assert_equal(_corners([RIGHT]), [0,1,0,1,0,1,0,1]); 167 | assert_equal(_corners([FRONT]), [1,1,0,0,1,1,0,0]); 168 | assert_equal(_corners([BACK]), [0,0,1,1,0,0,1,1]); 169 | assert_equal(_corners([BOT]), [1,1,1,1,0,0,0,0]); 170 | assert_equal(_corners([TOP]), [0,0,0,0,1,1,1,1]); 171 | 172 | assert_equal(_corners([TOP,FRONT+RIGHT]), [0,1,0,0,1,1,1,1]); 173 | } 174 | test__corners(); 175 | 176 | 177 | module test__is_corner_array() { 178 | edges = _edges([TOP,FRONT+RIGHT]); 179 | corners = _corners([TOP,FRONT+RIGHT]); 180 | assert(!_is_corner_array(undef)); 181 | assert(!_is_corner_array(true)); 182 | assert(!_is_corner_array(false)); 183 | assert(!_is_corner_array(INF)); 184 | assert(!_is_corner_array(-INF)); 185 | assert(!_is_corner_array(NAN)); 186 | assert(!_is_corner_array(-4)); 187 | assert(!_is_corner_array(0)); 188 | assert(!_is_corner_array(4)); 189 | assert(!_is_corner_array("foo")); 190 | assert(!_is_corner_array([])); 191 | assert(!_is_corner_array([4,5,6])); 192 | assert(!_is_corner_array([2:3:9])); 193 | assert(!_is_corner_array(edges)); 194 | assert(_is_corner_array(corners)); 195 | } 196 | test__is_corner_array(); 197 | 198 | 199 | module test__normalize_corners() { 200 | assert_equal(_normalize_corners([-2,-2,-2,-2,-2,-2,-2,-2]), [0,0,0,0,0,0,0,0]); 201 | assert_equal(_normalize_corners([-1,-1,-1,-1,-1,-1,-1,-1]), [0,0,0,0,0,0,0,0]); 202 | assert_equal(_normalize_corners([0,0,0,0,0,0,0,0]), [0,0,0,0,0,0,0,0]); 203 | assert_equal(_normalize_corners([1,1,1,1,1,1,1,1]), [1,1,1,1,1,1,1,1]); 204 | assert_equal(_normalize_corners([2,2,2,2,2,2,2,2]), [1,1,1,1,1,1,1,1]); 205 | } 206 | test__normalize_corners(); 207 | 208 | 209 | 210 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 211 | -------------------------------------------------------------------------------- /BOSL2/tests/test_linear_bearings.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../linear_bearings.scad> 3 | 4 | 5 | module test_lmXuu_info() { 6 | assert_equal(lmXuu_info(4), [8, 12]); 7 | assert_equal(lmXuu_info(8), [15, 24]); 8 | assert_equal(lmXuu_info(10), [19, 29]); 9 | assert_equal(lmXuu_info(25), [40, 59]); 10 | assert_equal(lmXuu_info(50), [80, 100]); 11 | assert_equal(lmXuu_info(100), [150, 175]); 12 | } 13 | test_lmXuu_info(); 14 | 15 | 16 | // vim: expandtab shiftwidth=4 softtabstop=4 nowrap 17 | -------------------------------------------------------------------------------- /BOSL2/tests/test_masks2d.scad: -------------------------------------------------------------------------------- 1 | include<../std.scad> 2 | 3 | module test_mask2d_chamfer() { 4 | assert_approx(mask2d_chamfer(x=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,0]]); 5 | assert_approx(mask2d_chamfer(y=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,0]]); 6 | assert_approx(mask2d_chamfer(edge=10),[[7.07106781187,-0.01],[-0.01,-0.01],[-0.01,7.07106781187],[0,7.07106781187],[7.07106781187,0]]); 7 | assert_approx(mask2d_chamfer(x=10,angle=30),[[10,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,0]]); 8 | assert_approx(mask2d_chamfer(y=10,angle=30),[[5.7735026919,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,0]]); 9 | assert_approx(mask2d_chamfer(edge=10,angle=30),[[5,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,0]]); 10 | assert_approx(mask2d_chamfer(x=10,angle=30,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,18.3205080757],[1,18.3205080757],[11,1]]); 11 | assert_approx(mask2d_chamfer(y=10,angle=30,inset=1),[[6.7735026919,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[6.7735026919,1]]); 12 | assert_approx(mask2d_chamfer(edge=10,angle=30,inset=1),[[6,-0.01],[-0.01,-0.01],[-0.01,9.66025403784],[1,9.66025403784],[6,1]]); 13 | assert_approx(mask2d_chamfer(x=10,angle=30,inset=1,excess=1),[[11,-1],[-1,-1],[-1,18.3205080757],[1,18.3205080757],[11,1]]); 14 | assert_approx(mask2d_chamfer(y=10,angle=30,inset=1,excess=1),[[6.7735026919,-1],[-1,-1],[-1,11],[1,11],[6.7735026919,1]]); 15 | assert_approx(mask2d_chamfer(edge=10,angle=30,inset=1,excess=1),[[6,-1],[-1,-1],[-1,9.66025403784],[1,9.66025403784],[6,1]]); 16 | } 17 | test_mask2d_chamfer(); 18 | 19 | 20 | module test_mask2d_cove() { 21 | $fn = 24; 22 | assert_approx(mask2d_cove(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[1.7763568394e-15,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,1.7763568394e-15]]); 23 | assert_approx(mask2d_cove(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[1.7763568394e-15,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,1.7763568394e-15]]); 24 | assert_approx(mask2d_cove(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); 25 | assert_approx(mask2d_cove(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); 26 | assert_approx(mask2d_cove(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); 27 | assert_approx(mask2d_cove(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); 28 | } 29 | test_mask2d_cove(); 30 | 31 | 32 | module test_mask2d_roundover() { 33 | $fn = 24; 34 | assert_approx(mask2d_roundover(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15]]); 35 | assert_approx(mask2d_roundover(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15]]); 36 | assert_approx(mask2d_roundover(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); 37 | assert_approx(mask2d_roundover(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); 38 | assert_approx(mask2d_roundover(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); 39 | assert_approx(mask2d_roundover(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); 40 | } 41 | test_mask2d_roundover(); 42 | 43 | 44 | module test_mask2d_dovetail() { 45 | assert_approx(mask2d_dovetail(x=10),[[0,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); 46 | assert_approx(mask2d_dovetail(y=10),[[0,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); 47 | assert_approx(mask2d_dovetail(edge=10),[[0,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); 48 | assert_approx(mask2d_dovetail(x=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); 49 | assert_approx(mask2d_dovetail(y=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); 50 | assert_approx(mask2d_dovetail(edge=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); 51 | assert_approx(mask2d_dovetail(x=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,18.3205080757],[1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); 52 | assert_approx(mask2d_dovetail(y=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); 53 | assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,9.66025403784],[1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); 54 | assert_approx(mask2d_dovetail(x=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,18.3205080757],[1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); 55 | assert_approx(mask2d_dovetail(y=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,11],[1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); 56 | assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,9.66025403784],[1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); 57 | } 58 | test_mask2d_dovetail(); 59 | 60 | 61 | module test_mask2d_rabbet() { 62 | assert_approx(mask2d_rabbet(10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,10],[10,0]]); 63 | assert_approx(mask2d_rabbet(size=10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,10],[10,0]]); 64 | assert_approx(mask2d_rabbet(size=[10,15]), [[10,-0.01],[-0.01,-0.01],[-0.01,15],[0,15],[10,15],[10,0]]); 65 | assert_approx(mask2d_rabbet(size=[10,15],excess=1), [[10,-1],[-1,-1],[-1,15],[0,15],[10,15],[10,0]]); 66 | } 67 | test_mask2d_rabbet(); 68 | 69 | 70 | module test_mask2d_teardrop() { 71 | $fn=24; 72 | assert_approx(mask2d_teardrop(r=10), [[6.03197753333,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.03197753333,-4.4408920985e-16]]); 73 | assert_approx(mask2d_teardrop(d=20), [[6.03197753333,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.03197753333,-4.4408920985e-16]]); 74 | assert_approx(mask2d_teardrop(r=10,angle=30), [[4.28975301178,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.28975301178,0]]); 75 | assert_approx(mask2d_teardrop(r=10,angle=30,excess=1), [[4.28975301178,-1],[-1,-1],[-1,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.28975301178,0]]); 76 | } 77 | test_mask2d_teardrop(); 78 | 79 | 80 | module test_mask2d_ogee() { 81 | $fn=24; 82 | assert_approx( 83 | mask2d_ogee([ 84 | "xstep",1, "ystep",1, // Starting shoulder. 85 | "fillet",5, "round",5, // S-curve. 86 | "ystep",1, "xstep",1 // Ending shoulder. 87 | ]), 88 | [[12,-0.01],[-0.01,-0.01],[-0.01,12],[1,12],[1,11],[1.32701564615,10.9892946162],[1.6526309611,10.9572243069],[1.97545161008,10.903926402],[2.29409522551,10.8296291314],[2.60719732652,10.7346506475],[2.91341716183,10.6193976626],[3.2114434511,10.4843637077],[3.5,10.3301270189],[3.7778511651,10.1573480615],[4.04380714504,9.96676670146],[4.2967290755,9.75919903739],[4.53553390593,9.53553390593],[4.75919903739,9.2967290755],[4.96676670146,9.04380714504],[5.15734806151,8.7778511651],[5.33012701892,8.5],[5.48436370766,8.2114434511],[5.61939766256,7.91341716183],[5.73465064748,7.60719732652],[5.82962913145,7.29409522551],[5.90392640202,6.97545161008],[5.95722430687,6.6526309611],[5.98929461619,6.32701564615],[6,6],[6.01070538381,5.67298435385],[6.04277569313,5.3473690389],[6.09607359798,5.02454838992],[6.17037086855,4.70590477449],[6.26534935252,4.39280267348],[6.38060233744,4.08658283817],[6.51563629234,3.7885565489],[6.66987298108,3.5],[6.84265193849,3.2221488349],[7.03323329854,2.95619285496],[7.24080096261,2.7032709245],[7.46446609407,2.46446609407],[7.7032709245,2.24080096261],[7.95619285496,2.03323329854],[8.2221488349,1.84265193849],[8.5,1.66987298108],[8.7885565489,1.51563629234],[9.08658283817,1.38060233744],[9.39280267348,1.26534935252],[9.70590477449,1.17037086855],[10.0245483899,1.09607359798],[10.3473690389,1.04277569313],[10.6729843538,1.01070538381],[11,1],[11,0],[12,0]] 89 | ); 90 | } 91 | test_mask2d_ogee(); 92 | 93 | -------------------------------------------------------------------------------- /BOSL2/tests/test_mutators.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | 4 | module test_hsl() { 5 | for (h = [0:30:360]) { 6 | for (s = [0:0.2:1]) { 7 | for (l = [0:0.2:1]) { 8 | c = (1 - abs(2*l-1)) * s; 9 | x = c * (1 - abs(((h/60)%2)-1)); 10 | m = l - c/2; 11 | rgb = [m,m,m] + ( 12 | h<= 60? [c,x,0] : 13 | h<=120? [x,c,0] : 14 | h<=180? [0,c,x] : 15 | h<=240? [0,x,c] : 16 | h<=300? [x,0,c] : 17 | [c,0,x] 18 | ); 19 | assert_approx(hsl(h,s,l), rgb, format("h={}, s={}, l={}", [h,s,l])); 20 | } 21 | } 22 | } 23 | } 24 | test_hsl(); 25 | 26 | 27 | module test_hsv() { 28 | for (h = [0:30:360]) { 29 | for (s = [0:0.2:1]) { 30 | for (v = [0:0.2:1]) { 31 | c = v * s; 32 | x = c * (1 - abs(((h/60)%2)-1)); 33 | m = v - c; 34 | rgb = [m,m,m] + ( 35 | h<= 60? [c,x,0] : 36 | h<=120? [x,c,0] : 37 | h<=180? [0,c,x] : 38 | h<=240? [0,x,c] : 39 | h<=300? [x,0,c] : 40 | [c,0,x] 41 | ); 42 | assert_approx(hsv(h,s,v), rgb, format("h={}, s={}, v={}", [h,s,v])); 43 | } 44 | } 45 | } 46 | } 47 | test_hsv(); 48 | 49 | 50 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 51 | -------------------------------------------------------------------------------- /BOSL2/tests/test_regions.scad: -------------------------------------------------------------------------------- 1 | include<../std.scad> 2 | 3 | module test_is_region() { 4 | assert(is_region([circle(d=10),square(10)])); 5 | assert(is_region([circle(d=10),square(10),circle(d=50)])); 6 | assert(is_region([square(10)])); 7 | assert(!is_region([])); 8 | assert(!is_region(23)); 9 | assert(!is_region(true)); 10 | assert(!is_region("foo")); 11 | } 12 | test_is_region(); 13 | 14 | 15 | 16 | module test_union() { 17 | R1 = [square(10,center=true), square(9,center=true)]; 18 | R2 = [square(9,center=true)]; 19 | assert(are_regions_equal(union(R1,R2), [square(10,center=true)])); 20 | assert(are_regions_equal(union(R2,R1), [square(10,center=true)])); 21 | R8 = [right(8,square(10,center=true)), left(8,square(10,center=true))]; 22 | R9 = [back(8,square(10,center=true)), fwd(8,square(10,center=true))]; 23 | assert(are_regions_equal(union(R9,R8), [[[-5, -5], [-13, -5], [-13, 5], [-5, 5], [-5, 13], [5, 13], [5, 5], [13, 5], [13, -5], [5, -5], [5, -13], [-5, -13]], [[-3, 3], [-3, -3], [3, -3], [3, 3]]])); 24 | assert(are_regions_equal(union(R8,R9), [[[-5, -5], [-13, -5], [-13, 5], [-5, 5], [-5, 13], [5, 13], [5, 5], [13, 5], [13, -5], [5, -5], [5, -13], [-5, -13]], [[-3, 3], [-3, -3], [3, -3], [3, 3]]])); 25 | 26 | } 27 | test_union(); 28 | 29 | 30 | module test_intersection() { 31 | R1 = [square(10,center=true), square(9,center=true)]; 32 | R6 = [square(9.5,center=true), square(9,center=true)]; 33 | assert(are_regions_equal(intersection(R6,R1), R6)); 34 | assert(are_regions_equal(intersection(R1,R6), R6)); 35 | R8 = [right(8,square(10,center=true)), left(8,square(10,center=true))]; 36 | R9 = [back(8,square(10,center=true)), fwd(8,square(10,center=true))]; 37 | assert(are_regions_equal(intersection(R9,R8),[[[-3, -5], [-5, -5], [-5, -3], [-3, -3]], [[-5, 5], [-3, 5], [-3, 3], [-5, 3]], [[5, -5], [3, -5], [3, -3], [5, -3]], [[3, 3], [3, 5], [5, 5], [5, 3]]])); 38 | assert(are_regions_equal(intersection(R8,R9),[[[-3, -5], [-5, -5], [-5, -3], [-3, -3]], [[-5, 5], [-3, 5], [-3, 3], [-5, 3]], [[5, -5], [3, -5], [3, -3], [5, -3]], [[3, 3], [3, 5], [5, 5], [5, 3]]])); 39 | 40 | 41 | } 42 | test_intersection(); 43 | 44 | 45 | module test_difference() { 46 | R5 = [square(10,center=true), square(9,center=true),square(4,center=true)]; 47 | R4 = [square(9,center=true), square(3,center=true)]; 48 | assert(are_regions_equal(difference(R5,R4), 49 | [square(10,center=true), square(9, center=true), square(3,center=true)])); 50 | pathA = [ 51 | [-9,12], [-6,2], [-3,12], [0,2], [3,10], [5,10], [19,-4], [-8,-4], [-12,0] 52 | ]; 53 | pathB = [ 54 | [-12,8], [7,8], [9,6], [7,5], [-3,5], [-5,-6], [-2,-6], [0,-4], 55 | [6,-4], [2,-8], [-7,-8], [-15,0] 56 | ]; 57 | 58 | right=[[[-10, 8], [-9, 12], [-7.8, 8]], [[0, -4], [-4.63636363636, -4], [-3, 5], [-0.9, 5], [0, 2], [1.125, 5], [7, 5], [9, 6], [19, -4], [6, -4]], [[-4.2, 8], [-1.8, 8], [-3, 12]], [[2.25, 8], [3, 10], [5, 10], [7, 8]]]; 59 | assert(are_regions_equal(difference(pathA,pathB),right)); 60 | 61 | R8 = [right(8,square(10,center=true)), left(8,square(10,center=true))]; 62 | R9 = [back(8,square(10,center=true)), fwd(8,square(10,center=true))]; 63 | 64 | assert(are_regions_equal(difference(R9,R8), [[[-5, 5], [-5, 13], [5, 13], [5, 5], [3, 5], [3, 3], [-3, 3], [-3, 5]], [[5, -13], [-5, -13], [-5, -5], [-3, -5], [-3, -3], [3, -3], [3, -5], [5, -5]]])); 65 | 66 | assert(are_regions_equal(difference(R8,R9),[[[-5, -5], [-13, -5], [-13, 5], [-5, 5], [-5, 3], [-3, 3], [-3, -3], [-5, -3]], [[3, -3], [3, 3], [5, 3], [5, 5], [13, 5], [13, -5], [5, -5], [5, -3]]])); 67 | 68 | 69 | } 70 | test_difference(); 71 | 72 | 73 | 74 | module test_exclusive_or() { 75 | R8 = [right(8,square(10,center=true)), left(8,square(10,center=true))]; 76 | R9 = [back(8,square(10,center=true)), fwd(8,square(10,center=true))]; 77 | assert(are_regions_equal(exclusive_or(R8,R9),[[[-5, -5], [-13, -5], [-13, 5], [-5, 5], [-5, 3], [-3, 3], [-3, -3], [-5, -3]], [[-3, -5], [-5, -5], [-5, -13], [5, -13], [5, -5], [3, -5], [3, -3], [-3, -3]], [[-5, 5], [-3, 5], [-3, 3], [3, 3], [3, 5], [5, 5], [5, 13], [-5, 13]], [[3, -3], [3, 3], [5, 3], [5, 5], [13, 5], [13, -5], [5, -5], [5, -3]]],either_winding=true)); 78 | assert(are_regions_equal(exclusive_or(R9,R8),[[[-5, -5], [-13, -5], [-13, 5], [-5, 5], [-5, 3], [-3, 3], [-3, -3], [-5, -3]], [[-3, -5], [-5, -5], [-5, -13], [5, -13], [5, -5], [3, -5], [3, -3], [-3, -3]], [[-5, 5], [-3, 5], [-3, 3], [3, 3], [3, 5], [5, 5], [5, 13], [-5, 13]], [[3, -3], [3, 3], [5, 3], [5, 5], [13, 5], [13, -5], [5, -5], [5, -3]]],either_winding=true)); 79 | 80 | p = turtle(["move",100,"left",144], repeat=4); 81 | p2 = move(-centroid(p),p); 82 | p3 = polygon_parts(p2); 83 | p4 = exclusive_or(p3,square(51,center=true)); 84 | 85 | star_square = [[[-50, -16.2459848116], [-25.5, -16.2459848116], 86 | [-25.5, 1.55430712449]], [[-7.45841874701, 25.5], [-30.9016994375, 87 | 42.5325404176], [-25.3674915789, 25.5]], [[-19.0983005625, 88 | 6.20541401733], [-25.5, 1.55430712449], [-25.5, 25.5], 89 | [-25.3674915789, 25.5]], [[-11.803398875, -16.2459848116], 90 | [-19.0983005625, 6.20541401733], [-3.5527136788e-15, 20.0811415886], 91 | [19.0983005625, 6.20541401733], [11.803398875, -16.2459848116]], 92 | [[7.45841874701, 25.5], [0, 20.0811415886], [-7.45841874701, 25.5]], 93 | [[25.3674915789, 25.5], [7.45841874701, 25.5], [30.9016994375, 94 | 42.5325404176]], [[25.5, 1.55430712449], [19.0983005625, 95 | 6.20541401733], [25.3674915789, 25.5], [25.5, 25.5]], [[25.5, 96 | -16.2459848116], [25.5, 1.55430712449], [50, -16.2459848116]], 97 | [[8.79658707105, -25.5], [11.803398875, -16.2459848116], [25.5, 98 | -16.2459848116], [25.5, -25.5]], [[-8.79658707105, -25.5], 99 | [8.79658707105, -25.5], [0, -52.5731112119]], [[-25.5, 100 | -16.2459848116], [-11.803398875, -16.2459848116], [-8.79658707105, 101 | -25.5], [-25.5, -25.5]]]; 102 | assert(are_regions_equal(exclusive_or(p3,square(51,center=true)),star_square,either_winding=true)); 103 | assert(are_regions_equal(exclusive_or(square(51,center=true),p3),star_square,either_winding=true)); 104 | 105 | } 106 | test_exclusive_or(); 107 | 108 | 109 | 110 | module test_point_in_region(){ 111 | region = [for(i=[2:8]) hexagon(r=i)]; 112 | pir=[for(x=[-6:6],y=[-6:6]) point_in_region([x,y],region)]; 113 | assert_equal(pir, 114 | [-1, -1, -1, 1, 1, -1, 0, -1, 1, 1, -1, -1, -1, -1, 1, 1, 115 | -1, -1, 1, 0, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 0, 116 | -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 0, 1, -1, -1, 1, 117 | 1, -1, -1, 1, -1, 1, 1, -1, 0, -1, 1, 1, -1, 1, -1, -1, 1, 118 | -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 119 | 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 120 | -1, 1, -1, -1, 1, -1, 1, 1, -1, 0, -1, 1, 1, -1, 1, -1, -1, 121 | 1, 1, -1, -1, 1, 0, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 122 | -1, 0, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 0, 1, -1, 123 | -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 0, -1, 1, 1, -1, -1, -1]); 124 | } 125 | test_point_in_region(); 126 | 127 | 128 | module test_make_region(){ 129 | pentagram = turtle(["move",100,"left",144], repeat=4); 130 | region1 = make_region(pentagram); 131 | assert_approx(region1, 132 | [[[0, 0], [38.196601125, 0], [30.9016994375, 22.451398829]], [[50, 133 | 36.3271264003], [19.0983005625, 58.7785252292], [30.9016994375, 134 | 22.451398829]], [[69.0983005625, 22.451398829], [50, 36.3271264003], 135 | [80.9016994375, 58.7785252292]], [[61.803398875, 3.5527136788e-15], 136 | [69.0983005625, 22.451398829], [100, 0]], [[38.196601125, 0], 137 | [61.803398875, 3.94430452611e-31], [50, -36.3271264003]]]); 138 | region2 = make_region(pentagram,nonzero=true); 139 | assert_approx(region2, 140 | [[[0, 0], [38.196601125, 0], [50, -36.3271264003], 141 | [61.803398875, 3.5527136788e-15], [100, 0], 142 | [69.0983005625, 22.451398829], [80.9016994375, 143 | 58.7785252292], [50, 36.3271264003], [19.0983005625, 144 | 58.7785252292], [30.9016994375, 22.451398829]]]); 145 | region3 = make_region([square(10), move([5,5],square(8))]); 146 | assert_equal(region3, [[[10, 0], [0, 0], [0, 10], [5, 10], [5, 5], [10, 5]], [[5, 10], [10, 10], [10, 5], [13, 5], [13, 13], [5, 13]]]); 147 | } 148 | test_make_region(); 149 | 150 | 151 | 152 | module test_region_area(){ 153 | assert_equal(region_area([square(10), right(20,square(8))]), 164); 154 | } 155 | test_region_area(); 156 | 157 | 158 | -------------------------------------------------------------------------------- /BOSL2/tests/test_rounding.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../rounding.scad> 3 | 4 | module test_round_corners() { 5 | 6 | test1 = turtle(["move", 10, "move", 10, "left", 30, "move", 17, "left", 155, "move", 10, "right", 90, "move", 10,"left", 90, "move", 30]); 7 | test2 = turtle(["move", 20, "left", 30, "move", 17, "left", 155, "move", 10, "right", 90, "move", 10,"left", 90, "move", 30]); 8 | 9 | assert_approx(round_corners(test2, cut=.5, $fn=8,closed=true), 10 | [[-0.606288551887,1.51404651037],[0.280235443937,0.414087063263],[1.63092692777,0],[16.2021229436,0],[19.8705904774,0.482962913145],[23.2890580113,1.89893852818],[34.1829092195,8.18850645576],[34.2505074296,8.28809878531],[34.2143124553,8.40289457772],[34.1018154298,8.44570309758],[25.9629982589,7.73364886061],[25.0818786883,8.01146479408],[24.6552785953,8.83095594797],[23.994133744,16.387876178],[23.5675336511,17.2073673319],[22.6864140805,17.4851832654],[-5.00680751277,15.0623403194],[-5.5709968443,14.7138107731],[-5.62744081644,14.0530562616]]); 11 | 12 | assert_approx(round_corners(test2, cut=.5, $fn=8,closed=false), 13 | [[0,0],[16.2021229436,0],[19.8705904774,0.482962913145],[23.2890580113,1.89893852818],[34.1829092195,8.18850645576],[34.2505074296,8.28809878531],[34.2143124553,8.40289457772],[34.1018154298,8.44570309758],[25.9629982589,7.73364886061],[25.0818786883,8.01146479408],[24.6552785953,8.83095594797],[23.994133744,16.387876178],[23.5675336511,17.2073673319],[22.6864140805,17.4851832654],[-5.99691348681,14.975717271]]); 14 | 15 | assert_approx(round_corners(test2, radius=[9,1.5,1.5,3], $fn=8,closed=false),[[0,0],[17.5884572681,0],[19.917828674,0.306667563398],[22.0884572681,1.20577136594],[28.8628496345,5.11696862225],[29.5970120924,6.19860893897],[29.2039100968,7.44536918473],[27.9821160204,7.91029877507],[26.2547769306,7.75917618664],[25.1598619019,8.1044015691],[24.6297512693,9.12273461966],[24.1503946842,14.6018054592],[23.090173419,16.6384715603],[20.9003433617,17.3289223252],[-5.99691348681,14.975717271]]); 16 | 17 | assert_approx(round_corners(test2, radius=[0,9,1.5,1.5,3,0], $fn=8,closed=false),[[0,0],[17.5884572681,0],[19.917828674,0.306667563398],[22.0884572681,1.20577136594],[28.8628496345,5.11696862225],[29.5970120924,6.19860893897],[29.2039100968,7.44536918473],[27.9821160204,7.91029877507],[26.2547769306,7.75917618664],[25.1598619019,8.1044015691],[24.6297512693,9.12273461966],[24.1503946842,14.6018054592],[23.090173419,16.6384715603],[20.9003433617,17.3289223252],[-5.99691348681,14.975717271]]); 18 | 19 | assert_approx(round_corners(test2, radius=[4,9,1.5,1.5,3,5], $fn=8,closed=true), [[-1.00632035384,2.51302092923],[0.46513599873,0.687303493898],[2.70701955022,0],[17.5884572681,0],[19.917828674,0.306667563398],[22.0884572681,1.20577136594],[28.8628496345,5.11696862225],[29.5970120924,6.19860893897],[29.2039100968,7.44536918473],[27.9821160204,7.91029877507],[26.2547769306,7.75917618664],[25.1598619019,8.1044015691],[24.6297512693,9.12273461966],[24.1503946842,14.6018054592],[23.090173419,16.6384715603],[20.9003433617,17.3289223252],[0.712818162855,15.5627427257],[-3.11056954856,13.2008342139],[-3.49307800348,8.72304539674]]); 20 | 21 | assert_approx(round_corners(test1, radius=[4,0,9,1.5,1.5,3,5], $fn=8, closed=true),[[-1.00632035384,2.51302092923],[0.46513599873,0.687303493898],[2.70701955022,0],[10,0],[17.5884572681,0],[19.917828674,0.306667563398],[22.0884572681,1.20577136594],[28.8628496345,5.11696862225],[29.5970120924,6.19860893897],[29.2039100968,7.44536918473],[27.9821160204,7.91029877507],[26.2547769306,7.75917618664],[25.1598619019,8.1044015691],[24.6297512693,9.12273461966],[24.1503946842,14.6018054592],[23.090173419,16.6384715603],[20.9003433617,17.3289223252],[0.712818162855,15.5627427257],[-3.11056954856,13.2008342139],[-3.49307800348,8.72304539674]]); 22 | 23 | assert_approx(round_corners(test1, joint=3, $fn=8, closed=true),[[-1.11523430308,2.78500492805],[0.515477620423,0.76169028093],[3,0],[7,0],[13,0],[17,0],[19.8977774789,0.381499642545],[22.5980762114,1.5],[32.124355653,7],[32.4498754498,7.47958777023],[32.2755782213,8.03238795439],[31.7338477701,8.23853277176],[27.7490689777,7.88990980077],[25.5592389204,8.58036056568],[24.4990176552,10.6170266668],[24.1503946842,14.6018054592],[23.090173419,16.6384715603],[20.9003433617,17.3289223252],[-3.00832939254,15.2371844993],[-4.71130594915,14.1851659419],[-4.88167918373,12.190712343]]); 24 | 25 | assert_approx(round_corners(test1, joint=3, $fn=8, method="chamfer", closed=true), [[-1.11523430308,2.78500492805],[3,0],[7,0],[13,0],[17,0],[22.5980762114,1.5],[32.124355653,7],[31.7338477701,8.23853277176],[27.7490689777,7.88990980077],[24.4990176552,10.6170266668],[24.1503946842,14.6018054592],[20.9003433617,17.3289223252],[-3.00832939254,15.2371844993],[-4.88167918373,12.190712343]]); 26 | 27 | assert_approx(round_corners(test1, joint=3, $fn=4, method="smooth", closed=true),[[-1.11523430308,2.78500492805],[-0.506080589514,1.46865494252],[0.353393568173,0.522188424009],[1.55153656203,0.0761524785012],[3,0],[7,0],[8.5,0],[10,0],[11.5,0],[13,0],[17,0],[18.4890098964,0.041015625],[19.9246392896,0.28125],[21.2880480021,0.791015625],[22.5980762114,1.5],[32.124355653,7],[33.2706335159,7.70183488048],[33.674933057,8.1697248947],[33.0753795745,8.32110126636],[31.7338477701,8.23853277176],[27.7490689777,7.88990980077],[26.3293465324,7.8480447775],[25.2718192958,8.2378271955],[24.7043208711,9.21160321051],[24.4990176552,10.6170266668],[24.1503946842,14.6018054592],[23.9450914683,16.0072289155],[23.3775930436,16.9810049305],[22.320065807,17.3707873485],[20.9003433617,17.3289223252],[-3.00832939254,15.2371844993],[-4.39040765537,15.0374479012],[-5.22744753731,14.5025539523],[-5.32708255097,13.514211823],[-4.88167918373,12.190712343]]); 28 | 29 | assert_approx(round_corners(test1, joint=3, $fn=4, method="smooth", k=.1, closed=true), [[-1.11523430308,2.78500492805],[-0.374134800869,0.998685360916],[0.164916998481,0.243687931204],[1.06619720521,0.0239336361004],[3,0],[7,0],[8.95,0],[10,0],[11.05,0],[13,0],[17,0],[18.9465459674,0.012890625],[19.9648316685,0.13125],[20.9058726414,0.537890625],[22.5980762114,1.5],[32.124355653,7],[33.7650948284,7.95986239101],[34.2335990876,8.34587161753],[33.6284170693,8.39334886112],[31.7338477701,8.23853277176],[27.7490689777,7.88990980077],[25.829925477,7.74788623096],[24.9991076092,7.91282206324],[24.6924075141,8.70237713407],[24.4990176552,10.6170266668],[24.1503946842,14.6018054592],[23.9570048253,16.5164549919],[23.6503047302,17.3060100627],[22.8194868624,17.470945895],[20.9003433617,17.3289223252],[-3.00832939254,15.2371844993],[-4.91564186446,15.0455441488],[-5.63782937704,14.7549077223],[-5.57131429138,13.9792788941],[-4.88167918373,12.190712343]]); 30 | 31 | assert_approx(round_corners(test1, joint=[3,0,3,5,2,2,4], $fn=4, method="smooth", k=[.8,0,.7,.5,0,.4,1], closed=true),[[-1.11523430308,2.78500492805],[-0.605039930997,1.82113212873],[0.494750995442,0.731063793612],[1.91554107964,0.115316610302],[3,0],[10,0],[17,0],[18.2602418609,0.055078125],[19.9045431002,0.35625],[21.4791356824,0.917578125],[22.5980762114,1.5],[30.3923048454,6],[32.3027679503,7.1697248008],[32.9766005188,7.94954149117],[31.9773447146,8.20183544393],[29.7414583739,8.06422128626],[26.7528742796,7.80275405802],[25.3902084366,7.69137858706],[24.8741147528,7.76386137763],[24.713114411,8.25952793415],[24.5861733979,9.62083196871],[24.0632389414,15.5980001573],[23.9283556903,16.6198201409],[23.5934897955,17.2383006602],[22.9262565325,17.4606811745],[21.8965380598,17.4160780679],[-2.01213469444,15.324340242],[-2.97951536307,15.0445310318],[-4.28698915458,13.9242432294],[-4.69675267167,12.2519315552],[-4.50993441604,11.262377367]]); 32 | 33 | assert_approx(round_corners(test2, cut=.6, $fn=4, method="smooth", k=[.8,.7,.5,0,.4,1], closed=true), [[-0.758025389489,1.89296943206],[-0.411245984888,1.23782454268],[0.336282532724,0.496904475915],[1.30199436026,0.0783807655462],[2.03910170463,0],[15.1195326672,0],[17.1697224117,0.0896023299387],[19.8447085729,0.579555495773],[22.4062911263,1.49273668813],[24.2266086926,2.44023366641],[33.3031485364,7.68057638855],[33.9293399672,8.06397643682],[34.1502016939,8.3195765203],[33.8226761739,8.40227076906],[33.0898209499,8.35716505304],[31.5228787369,8.220075373],[26.897837498,7.84205448929],[25.1461574492,8.08806923839],[24.5997041686,9.77041731835],[24.1688520829,14.390836426],[24.116478533,14.9894688406],[23.9403981733,16.3233811597],[23.5032548901,17.1307628876],[22.6322299704,17.4210646163],[21.2880067431,17.3628384763],[-4.80585474409,15.0799214086],[-5.0950068559,14.9962858485],[-5.48581351579,14.6614294736],[-5.6082926909,14.161572005],[-5.55245232227,13.8657921816]]); 34 | 35 | assert_approx(round_corners([[0,0],[10,0],[10,10]], cut=1, method="chamfer",closed=false), [[0,0],[8.58578643763,0],[10,1.41421356237],[10,10]]); 36 | 37 | } 38 | test_round_corners(); 39 | 40 | 41 | 42 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 43 | -------------------------------------------------------------------------------- /BOSL2/tests/test_screw_drive.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../screw_drive.scad> 3 | 4 | 5 | module test_torx_diam() { 6 | assert_approx(torx_diam(10), 2.80); 7 | assert_approx(torx_diam(15), 3.35); 8 | assert_approx(torx_diam(20), 3.95); 9 | assert_approx(torx_diam(25), 4.50); 10 | assert_approx(torx_diam(30), 5.60); 11 | assert_approx(torx_diam(40), 6.75); 12 | } 13 | test_torx_diam(); 14 | 15 | 16 | module test_torx_depth() { 17 | assert_approx(torx_depth(10), 1.142); 18 | assert_approx(torx_depth(15), 1.2); 19 | assert_approx(torx_depth(20), 1.4); 20 | assert_approx(torx_depth(25), 1.61); 21 | assert_approx(torx_depth(30), 2.22); 22 | assert_approx(torx_depth(40), 2.63); 23 | } 24 | test_torx_depth(); 25 | 26 | 27 | 28 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 29 | -------------------------------------------------------------------------------- /BOSL2/tests/test_skin.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../skin.scad> 3 | 4 | 5 | module test_skin() { 6 | profiles = [ 7 | [[-100,-100,0], [0,100,0], [100,-100,0]], 8 | [[-100,-100,100], [-100,100,100], [100,100,100], [100,-100,100]], 9 | ]; 10 | vnf1 = skin(profiles, slices=0, caps=false, method="distance"); 11 | assert_equal(vnf1, [[[-100,-100,0],[0,100,0],[0,100,0],[100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[0,5,4],[0,1,5],[5,2,6],[2,3,6],[6,3,7],[3,0,7],[7,0,4]]]); 12 | 13 | vnf2 = skin(profiles, slices=0, caps=true, method="distance"); 14 | assert_equal(vnf2,[[[-100,-100,0],[0,100,0],[0,100,0],[100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[3,2,1,0],[4,5,6,7],[0,5,4],[0,1,5],[5,2,6],[2,3,6],[6,3,7],[3,0,7],[7,0,4]]]); 15 | } 16 | test_skin(); 17 | 18 | 19 | module test_sweep() { 20 | multi_region = [ 21 | [[10, 0], [ 0, 0], [ 0, 10], [10, 10]], 22 | [[30, 0], [20, 0], [20, 10], [30, 10]] 23 | ]; 24 | transforms = [ up(10), down(10) ]; 25 | 26 | vnf1 = sweep(multi_region,transforms,closed=false,caps=false); 27 | assert(len(vnf1[0])==8*2 && len(vnf1[1])==8*2); 28 | 29 | vnf2 = sweep(multi_region,transforms,closed=false,caps=false,style="quincunx"); 30 | assert(len(vnf2[0])==8*3 && len(vnf2[1])==8*4); 31 | } 32 | test_sweep(); 33 | 34 | 35 | 36 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 37 | -------------------------------------------------------------------------------- /BOSL2/tests/test_structs.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../structs.scad> 3 | 4 | 5 | module test_struct_set() { 6 | st = struct_set([], "Foo", 42); 7 | assert(st == [["Foo",42]]); 8 | st2 = struct_set(st, "Bar", 28); 9 | assert(st2 == [["Foo",42],["Bar",28]]); 10 | st3 = struct_set(st2, "Foo", 91); 11 | assert(st3 == [["Bar",28],["Foo",91]]); 12 | st4 = struct_set(st3, [3,4,5,6]); 13 | assert(st4 == [["Bar", 28],["Foo",91],[3,4],[5,6]]); 14 | st5 = struct_set(st3, [[3,4],[5,6]]); 15 | assert(st5 == [["Bar", 28],["Foo",91],[[3,4],[5,6]]]); 16 | st6 = struct_set(st3, [3,4],true); 17 | assert(st6 == [["Bar", 28],["Foo",91],[[3,4],true]]); 18 | st7 = struct_set(st3, [3,4,[5,7],99]); 19 | assert(st7 == [["Bar", 28],["Foo",91],[3,4],[[5,7],99]]); 20 | st8 = struct_set(st3,[]); 21 | assert(st8==st3); 22 | } 23 | test_struct_set(); 24 | 25 | 26 | module test_struct_remove() { 27 | st = [["Foo",91],["Bar",28],["Baz",9]]; 28 | assert(struct_remove(st, "Foo") == [["Bar",28],["Baz",9]]); 29 | assert(struct_remove(st, "Bar") == [["Foo",91],["Baz",9]]); 30 | assert(struct_remove(st, "Baz") == [["Foo",91],["Bar",28]]); 31 | assert(struct_remove(st, ["Baz","Baz"]) == [["Foo",91],["Bar",28]]); 32 | assert(struct_remove(st, ["Baz","Foo"]) == [["Bar",28]]); 33 | assert(struct_remove(st, []) == st); 34 | assert(struct_remove(st, ["Bar","niggle"]) == [["Foo",91],["Baz",9]]); 35 | assert(struct_remove(st, struct_keys(st)) == []); 36 | } 37 | test_struct_remove(); 38 | 39 | 40 | module test_struct_val() { 41 | st = [["Foo",91],["Bar",28],[true,99],["Baz",9],[[5,4],3], [7,92]]; 42 | assert(struct_val(st,"Foo") == 91); 43 | assert(struct_val(st,"Bar") == 28); 44 | assert(struct_val(st,"Baz") == 9); 45 | assert(struct_val(st,"Baz",5) == 9); 46 | assert(struct_val(st,"Qux") == undef); 47 | assert(struct_val(st,"Qux",5) == 5); 48 | assert(struct_val(st,[5,4])==3); 49 | assert(struct_val(st,true)==99); 50 | assert(struct_val(st,5) == undef); 51 | assert(struct_val(st,7) == 92); 52 | } 53 | test_struct_val(); 54 | 55 | 56 | module test_struct_keys() { 57 | assert(struct_keys([["Foo",3],["Bar",2],["Baz",1]]) == ["Foo","Bar","Baz"]); 58 | assert(struct_keys([["Zee",1],["Why",2],["Exx",3]]) == ["Zee","Why","Exx"]); 59 | assert(struct_keys([["Zee",1],[[3,4],2],["Why",2],[9,1],["Exx",3]]) == ["Zee",[3,4],"Why",9,"Exx"]); 60 | } 61 | test_struct_keys(); 62 | 63 | 64 | module test_echo_struct() { 65 | // Can't yet test echo output 66 | } 67 | test_echo_struct(); 68 | 69 | 70 | module test_is_struct() { 71 | assert(is_struct([["Foo",1],["Bar",2],["Baz",3]])); 72 | assert(!is_struct([["Foo"],["Bar"],["Baz"]])); 73 | assert(!is_struct(["Foo","Bar","Baz"])); 74 | assert(!is_struct([3,4,5])); 75 | assert(!is_struct(3)); 76 | assert(!is_struct(true)); 77 | assert(!is_struct("foo")); 78 | assert(is_struct([])); 79 | } 80 | test_is_struct(); 81 | 82 | 83 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 84 | -------------------------------------------------------------------------------- /BOSL2/tests/test_trigonometry.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | 4 | module test_tri_functions() { 5 | sides = rands(1,100,100,seed_value=8181); 6 | for (p = pair(sides,true)) { 7 | adj = p.x; 8 | opp = p.y; 9 | hyp = norm([opp,adj]); 10 | ang = atan2(opp,adj); 11 | 12 | assert_approx(hyp_ang_to_adj(hyp,ang), adj); 13 | assert_approx(opp_ang_to_adj(opp,ang), adj); 14 | assert_approx(hyp_adj_to_opp(hyp,adj), opp); 15 | assert_approx(hyp_ang_to_opp(hyp,ang), opp); 16 | assert_approx(adj_ang_to_opp(adj,ang), opp); 17 | assert_approx(adj_opp_to_hyp(adj,opp), hyp); 18 | assert_approx(adj_ang_to_hyp(adj,ang), hyp); 19 | assert_approx(opp_ang_to_hyp(opp,ang), hyp); 20 | assert_approx(hyp_adj_to_ang(hyp,adj), ang); 21 | assert_approx(hyp_opp_to_ang(hyp,opp), ang); 22 | assert_approx(adj_opp_to_ang(adj,opp), ang); 23 | } 24 | } 25 | *test_tri_functions(); 26 | 27 | 28 | module test_hyp_opp_to_adj() nil(); // Covered in test_tri_functions() 29 | module test_hyp_ang_to_adj() nil(); // Covered in test_tri_functions() 30 | module test_opp_ang_to_adj() nil(); // Covered in test_tri_functions() 31 | module test_hyp_adj_to_opp() nil(); // Covered in test_tri_functions() 32 | module test_hyp_ang_to_opp() nil(); // Covered in test_tri_functions() 33 | module test_adj_ang_to_opp() nil(); // Covered in test_tri_functions() 34 | module test_adj_opp_to_hyp() nil(); // Covered in test_tri_functions() 35 | module test_adj_ang_to_hyp() nil(); // Covered in test_tri_functions() 36 | module test_opp_ang_to_hyp() nil(); // Covered in test_tri_functions() 37 | module test_hyp_adj_to_ang() nil(); // Covered in test_tri_functions() 38 | module test_hyp_opp_to_ang() nil(); // Covered in test_tri_functions() 39 | module test_adj_opp_to_ang() nil(); // Covered in test_tri_functions() 40 | 41 | 42 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 43 | -------------------------------------------------------------------------------- /BOSL2/tests/test_version.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | 3 | 4 | module test_bosl_version() { 5 | assert(is_vector(bosl_version())); // Returned value is a vector. 6 | assert(len(bosl_version())==3); // of three numbers. 7 | assert(bosl_version()[0]==2); // The major version is 2. 8 | for (v=bosl_version()) { 9 | assert(floor(v)==v); // All version parts are integers. 10 | } 11 | } 12 | test_bosl_version(); 13 | 14 | 15 | module test_bosl_version_num() { 16 | assert(is_num(bosl_version_num())); 17 | v = bosl_version(); 18 | assert(bosl_version_num() == v[0]+v[1]/100+v[2]/1000000); 19 | } 20 | test_bosl_version_num(); 21 | 22 | 23 | module test_bosl_version_str() { 24 | assert(is_string(bosl_version_str())); 25 | v = bosl_version(); 26 | assert(bosl_version_str() == str(v[0],".",v[1],".",v[2])); 27 | } 28 | test_bosl_version_str(); 29 | 30 | 31 | module test_bosl_required() { 32 | bosl_required(2.000001); 33 | bosl_required("2.0.1"); 34 | bosl_required([2,0,1]); 35 | } 36 | test_bosl_required(); 37 | 38 | 39 | module test_version_to_list() { 40 | assert(is_list(version_to_list(2.010001))); 41 | assert(is_list(version_to_list("2.1.1"))); 42 | assert(is_list(version_to_list([2,1,1]))); 43 | assert(version_to_list(2.010001)==[2,1,1]); 44 | assert(version_to_list("2.1.1")==[2,1,1]); 45 | assert(version_to_list([2,1,1])==[2,1,1]); 46 | assert(version_to_list(2.010035)==[2,1,35]); 47 | assert(version_to_list(2.345678)==[2,34,5678]); 48 | assert(version_to_list("2.34.5678")==[2,34,5678]); 49 | assert(version_to_list([2,34,5678])==[2,34,5678]); 50 | assert(version_to_list([2,34,56,78])==[2,34,56]); 51 | } 52 | test_version_to_list(); 53 | 54 | 55 | module test_version_to_str() { 56 | assert(is_string(version_to_str(2.010001))); 57 | assert(is_string(version_to_str("2.1.1"))); 58 | assert(is_string(version_to_str([2,1,1]))); 59 | assert(version_to_str(2.010001)=="2.1.1"); 60 | assert(version_to_str("2.1.1")=="2.1.1"); 61 | assert(version_to_str([2,1,1])=="2.1.1"); 62 | assert(version_to_str(2.345678)=="2.34.5678"); 63 | assert(version_to_str("2.34.5678")=="2.34.5678"); 64 | assert(version_to_str([2,34,5678])=="2.34.5678"); 65 | assert(version_to_str([2,34,56,78])=="2.34.56"); 66 | } 67 | test_version_to_str(); 68 | 69 | 70 | module test_version_to_num() { 71 | assert(is_num(version_to_num(2.010001))); 72 | assert(is_num(version_to_num("2.1.1"))); 73 | assert(is_num(version_to_num([2,1,1]))); 74 | assert(version_to_num(2.010001)==2.010001); 75 | assert(version_to_num("2.1.1")==2.010001); 76 | assert(version_to_num([2,1,1])==2.010001); 77 | assert(version_to_num(2.345678)==2.345678); 78 | assert(version_to_num("2.34.5678")==2.345678); 79 | assert(version_to_num([2,34,5678])==2.345678); 80 | assert(version_to_num([2,34,56,78])==2.340056); 81 | } 82 | test_version_to_num(); 83 | 84 | 85 | module test_version_cmp() { 86 | function diversify(x) = [ 87 | version_to_num(x), 88 | version_to_str(x), 89 | version_to_list(x) 90 | ]; 91 | 92 | module testvercmp(x,y,z) { 93 | for (a = diversify(y)) { 94 | for (b = diversify(x)) { 95 | assert(version_cmp(a,b)>0); 96 | } 97 | for (b = diversify(y)) { 98 | assert(version_cmp(a,b)==0); 99 | } 100 | for (b = diversify(z)) { 101 | assert(version_cmp(a,b)<0); 102 | } 103 | } 104 | } 105 | 106 | testvercmp([2,1,33],[2,1,34],[2,1,35]); 107 | testvercmp([2,2,1],[2,2,34],[2,2,67]); 108 | testvercmp([2,2,34],[2,3,34],[2,4,34]); 109 | testvercmp([2,3,34],[3,3,34],[4,3,34]); 110 | testvercmp([2,3,34],[3,1,1],[4,1,1]); 111 | testvercmp([2,1,1],[3,3,34],[4,1,1]); 112 | testvercmp([2,1,1],[3,1,1],[4,3,34]); 113 | } 114 | test_version_cmp(); 115 | 116 | 117 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 118 | -------------------------------------------------------------------------------- /BOSL2/tests/test_vnf.scad: -------------------------------------------------------------------------------- 1 | include <../std.scad> 2 | include <../vnf.scad> 3 | 4 | 5 | module test_is_vnf() { 6 | assert(is_vnf([[],[]])); 7 | assert(!is_vnf([])); 8 | assert(is_vnf([[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]])); 9 | } 10 | test_is_vnf(); 11 | 12 | 13 | module test_is_vnf_list() { 14 | assert(is_vnf_list([])); 15 | assert(!is_vnf_list([[],[]])); 16 | assert(is_vnf_list([[[],[]]])); 17 | assert(!is_vnf_list([[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]])); 18 | assert(is_vnf_list([[[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]])); 19 | } 20 | test_is_vnf_list(); 21 | 22 | 23 | module test_vnf_vertices() { 24 | vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; 25 | assert(vnf_vertices(vnf) == vnf[0]); 26 | } 27 | test_vnf_vertices(); 28 | 29 | 30 | module test_vnf_faces() { 31 | vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; 32 | assert(vnf_faces(vnf) == vnf[1]); 33 | } 34 | test_vnf_faces(); 35 | 36 | 37 | module test_vnf_from_polygons() { 38 | verts = [[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]]; 39 | faces = [[0,1,2],[0,3,1],[2,3,0],[0,1,0]]; // Last face has zero area 40 | assert(vnf_merge_points( 41 | vnf_from_polygons([for (face=faces) select(verts,face)])) == [verts,select(faces,0,-2)]); 42 | } 43 | test_vnf_from_polygons(); 44 | 45 | 46 | 47 | module test_vnf_volume() { 48 | assert_approx(vnf_volume(cube(100, center=false)), 1000000); 49 | assert(approx(vnf_volume(sphere(d=100, anchor=BOT, $fn=144)) / (4/3*PI*pow(50,3)),1, eps=.001)); 50 | } 51 | test_vnf_volume(); 52 | 53 | 54 | 55 | module test_vnf_area(){ 56 | assert(approx(vnf_area(sphere(d=100, $fn=144)) / (4*PI*50*50),1, eps=1e-3)); 57 | } 58 | test_vnf_area(); 59 | 60 | 61 | module test_vnf_join() { 62 | vnf1 = vnf_from_polygons([[[-1,-1,-1],[1,-1,-1],[0,1,-1]]]); 63 | vnf2 = vnf_from_polygons([[[1,1,1],[-1,1,1],[0,1,-1]]]); 64 | assert(vnf_join([vnf1,vnf2]) == [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[1,1,1],[-1,1,1],[0,1,-1]],[[0,1,2],[3,4,5]]]); 65 | } 66 | test_vnf_join(); 67 | 68 | 69 | module test_vnf_triangulate() { 70 | vnf = [[[-1,-1,0],[1,-1,0],[1,1,0],[-1,1,0]],[[0,1,2,3]]]; 71 | assert(vnf_triangulate(vnf) == [[[-1,-1,0],[1,-1,0],[1,1,0],[-1,1,0]], [[0,1,2],[2,3,0]]]); 72 | } 73 | test_vnf_triangulate(); 74 | 75 | 76 | module test_vnf_vertex_array() { 77 | vnf1 = vnf_vertex_array( 78 | points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], 79 | col_wrap=true, caps=true 80 | ); 81 | vnf2 = vnf_vertex_array( 82 | points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], 83 | col_wrap=true, caps=true, style="alt" 84 | ); 85 | vnf3 = vnf_vertex_array( 86 | points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], 87 | col_wrap=true, caps=true, style="quincunx" 88 | ); 89 | assert(vnf1 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[2,1,0],[3,4,5],[0,4,3],[0,1,4],[1,5,4],[1,2,5],[2,3,5],[2,0,3]]]); 90 | assert(vnf2 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[2,1,0],[3,4,5],[0,1,3],[3,1,4],[1,2,4],[4,2,5],[2,0,5],[5,0,3]]]); 91 | assert(vnf3 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100],[0,-50,50],[-50,25,50],[50,25,50]],[[2,1,0],[3,4,5],[0,6,3],[3,6,4],[4,6,1],[1,6,0],[1,7,4],[4,7,5],[5,7,2],[2,7,1],[2,8,5],[5,8,3],[3,8,0],[0,8,2]]]); 92 | } 93 | test_vnf_vertex_array(); 94 | 95 | 96 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 97 | -------------------------------------------------------------------------------- /BOSL2/tripod_mounts.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////////// 2 | // LibFile: tripod_mounts.scad 3 | // Mount plates for tripods. Currently only the Manfrotto RC2 plate. 4 | // Includes: 5 | // include 6 | // include 7 | // FileGroup: Parts 8 | // FileSummary: Tripod mount plates: RC2 9 | ////////////////////////////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | // Module: manfrotto_rc2_plate() 13 | // Synopsis: Creates a Manfrotto RC2 tripod quick release mount plate. 14 | // SynTags: Geom 15 | // Topics: Parts 16 | // See Also: threaded_rod() 17 | // Usage: 18 | // manfrotto_rc2_plate([chamfer],[anchor],[orient],[spin]) [ATTACHMENTS]; 19 | // Description: 20 | // Creates a Manfrotto RC2 quick release mount plate to mount to a tripod. The chamfer argument 21 | // lets you control whether the model edges are chamfered. By default all edges are chamfered, 22 | // but you can set it to "bot" to chamfer only the bottom, so that connections to a model larger 23 | // than the plate doin't have a V-groove at the junction. The plate is 10.5 mm thick. 24 | // Arguments: 25 | // chamfer = "none" for no chamfer, "all" for full chamfering, and "bot" or "bottom" for bottom chamfering. Default: "all". 26 | // Examples: 27 | // manfrotto_rc2_plate(); 28 | // manfrotto_rc2_plate("bot"); 29 | module manfrotto_rc2_plate(chamfer="all",anchor,orient,spin) 30 | { 31 | chsize=0.5; 32 | 33 | dummy = assert(in_list(chamfer, ["bot","bottom","all","none"]), "chamfer must be \"all\", \"bottom\", \"bot\", or \"none\""); 34 | chamf_top = chamfer=="all"; 35 | chamf_bot = in_list(chamfer, ["bot","bottom","all"]); 36 | 37 | length = 52.5; 38 | innerlen=43; 39 | 40 | topwid = 37.4; 41 | botwid = 42.4; 42 | 43 | thickness = 10.5; 44 | 45 | flat_height=3; 46 | angled_size=5; 47 | angled_height = thickness - flat_height*2; 48 | angled_width = sqrt(angled_size^2-angled_height^2); 49 | 50 | corner_space = 25; 51 | corner_space_ht = 4.5; 52 | 53 | left_top=2; 54 | 55 | pts = turtle([ 56 | "move",botwid, 57 | "left", 58 | "move", flat_height, 59 | "xymove", [-angled_width, angled_height], 60 | "move", flat_height, 61 | "left", 62 | "move", topwid, 63 | "left", 64 | "move", left_top, 65 | "jump", [0,flat_height] 66 | ]); 67 | 68 | 69 | cutout_len=26; 70 | 71 | 72 | facet = [ 73 | back(-left_top,select(pts,-3)), 74 | each fwd(1.5,select(pts,-2,-1)), 75 | [-10,-left_top+select(pts,-1).y], 76 | left(10,back(-flat_height,select(pts,-3))) 77 | ]; 78 | 79 | attachable(anchor,spin,orient,size=[botwid,length,thickness],size2=[topwid,length],shift=[.64115/2,0]){ 80 | tag_scope() 81 | down(thickness/2) 82 | diff() 83 | linear_sweep(pts,h=length,convexity=4,orient=FWD,anchor=FWD){ 84 | tag("remove"){ 85 | zflip_copy() 86 | down(.01)fwd(.01)left(.01)position(LEFT+FRONT+BOT) 87 | cuboid([corner_space,(length-innerlen)/2,thickness+.02], chamfer=-chsize, 88 | orient=FWD,anchor=TOP+LEFT+FWD,edges=chamf_top?"ALL":TOP); 89 | fwd(left_top)position(LEFT+BACK)linear_sweep(h=cutout_len,facet,convexity=4,anchor=RIGHT+BACK); 90 | } 91 | if (chamf_bot){ 92 | edge_mask(FRONT+LEFT)chamfer_edge_mask(length,chsize); 93 | edge_mask(FRONT+RIGHT)chamfer_edge_mask(length,chsize); 94 | edge_mask(FRONT+TOP)chamfer_edge_mask(length,chsize); 95 | edge_mask(FRONT+BOT)chamfer_edge_mask(length,chsize); 96 | edge_mask(TOP+RIGHT)chamfer_edge_mask(length,chsize); 97 | edge_mask(BOT+RIGHT)chamfer_edge_mask(length,chsize); 98 | zflip_copy(){ 99 | right(corner_space)edge_mask(TOP+LEFT) chamfer_edge_mask(length,chsize); 100 | down((length-innerlen)/2)edge_mask(TOP+LEFT) chamfer_edge_mask(length,chsize); 101 | } 102 | } 103 | if (chamf_top){ 104 | edge_mask(BACK+LEFT) chamfer_edge_mask(length,chsize); 105 | edge_mask(BACK+RIGHT) chamfer_edge_mask(length,chsize); 106 | edge_mask(BACK+TOP) chamfer_edge_mask(length,chsize); 107 | edge_mask(BACK+BOT) chamfer_edge_mask(length,chsize); 108 | } 109 | } 110 | children(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /BOSL2/tutorials/Distributors.md: -------------------------------------------------------------------------------- 1 | # Distributors Tutorial 2 | 3 | 4 | 5 | ## Distributors 6 | 7 | Distributors are modules that are useful for placing multiple copies of a 8 | child across a line, area, volume, or ring. Many transforms have one or 9 | more distributive variation. 10 | 11 | Transforms | Related Distributors 12 | ----------------------- | --------------------- 13 | `left()`, `right()` | `xcopies()` 14 | `fwd()`, `back()` | `ycopies()` 15 | `down()`, `up()` | `zcopies()` 16 | `move()`, `translate()` | `move_copies()`, `line_copies()`, `grid_copies()` 17 | `xrot()` | `xrot_copies()` 18 | `yrot()` | `yrot_copies()` 19 | `zrot()` | `zrot_copies()` 20 | `rot()`, `rotate()` | `rot_copies()`, `arc_copies()` 21 | `xflip()` | `xflip_copy()` 22 | `yflip()` | `yflip_copy()` 23 | `zflip()` | `zflip_copy()` 24 | `mirror()` | `mirror_copy()` 25 | 26 | 27 | ### Transform Distributors 28 | Using `xcopies()`, you can make a line of evenly spaced copies of a shape 29 | centered along the X axis. To make a line of 5 spheres, spaced every 20 30 | units along the X axis, do: 31 | ```openscad-2D 32 | include 33 | xcopies(20, n=5) sphere(d=10); 34 | ``` 35 | Note that the first expected argument to `xcopies()` is the spacing argument, 36 | so you do not need to supply the `spacing=` argument name. 37 | 38 | Similarly, `ycopies()` makes a line of evenly spaced copies centered along the 39 | Y axis. To make a line of 5 spheres, spaced every 20 units along the Y 40 | axis, do: 41 | ```openscad-2D 42 | include 43 | ycopies(20, n=5) sphere(d=10); 44 | ``` 45 | 46 | And, `zcopies()` makes a line of evenly spaced copies centered along the Z axis. 47 | To make a line of 5 spheres, spaced every 20 units along the Z axis, do: 48 | ```openscad-3D 49 | include 50 | zcopies(20, n=5) sphere(d=10); 51 | ``` 52 | 53 | If you don't give the `n=` argument to `xcopies()`, `ycopies()` or `zcopies()`, 54 | then it defaults to 2 (two) copies. This actually is the most common usage: 55 | ```openscad-2D 56 | include 57 | xcopies(20) sphere(d=10); 58 | ``` 59 | 60 | ```openscad-2D 61 | include 62 | ycopies(20) sphere(d=10); 63 | ``` 64 | 65 | ```openscad-3D 66 | include 67 | zcopies(20) sphere(d=10); 68 | ``` 69 | 70 | If you don't know the spacing you want, but instead know how long a line you 71 | want the copies distributed over, you can use the `l=` argument instead of 72 | the `spacing=` argument: 73 | ```openscad-2D 74 | include 75 | xcopies(l=100, n=5) sphere(d=10); 76 | ``` 77 | 78 | ```openscad-2D 79 | include 80 | ycopies(l=100, n=5) sphere(d=10); 81 | ``` 82 | 83 | ```openscad-3D 84 | include 85 | zcopies(l=100, n=5) sphere(d=10); 86 | ``` 87 | 88 | If you don't want the line of copies centered on the origin, you can give a 89 | starting point `sp=`, and the line of copies will start there. For `xcopies()`, 90 | the line of copies will extend to the right of the starting point. 91 | ```openscad-2D 92 | include 93 | xcopies(20, n=5, sp=[0,0,0]) sphere(d=10); 94 | ``` 95 | 96 | For `ycopies()`, the line of copies will extend to the back of the starting point. 97 | ```openscad-2D 98 | include 99 | ycopies(20, n=5, sp=[0,0,0]) sphere(d=10); 100 | ``` 101 | 102 | For `zcopies()`, the line of copies will extend upwards from the starting point. 103 | ```openscad-3D 104 | include 105 | zcopies(20, n=5, sp=[0,0,0]) sphere(d=10); 106 | ``` 107 | 108 | If you need to distribute copies along an arbitrary line, you can use the 109 | `line_copies()` command. You can give both the direction vector and the spacing 110 | of the line of copies with the `spacing=` argument: 111 | ```openscad-3D 112 | include 113 | line_copies(spacing=(BACK+RIGHT)*20, n=5) sphere(d=10); 114 | ``` 115 | 116 | With the `p1=` argument, you can specify the starting point of the line: 117 | ```openscad-3D 118 | include 119 | line_copies(spacing=(BACK+RIGHT)*20, n=5, p1=[0,0,0]) sphere(d=10); 120 | ``` 121 | 122 | If you give both `p1=` and `p2=`, you can nail down both the start and 123 | endpoints of the line of copies: 124 | ```openscad-2D 125 | include 126 | line_copies(p1=[0,100,0], p2=[100,0,0], n=4) 127 | sphere(d=10); 128 | ``` 129 | 130 | The `grid_copies()` command will let you spread copies across both the X and Y 131 | axes at the same time: 132 | ```openscad-2D 133 | include 134 | grid_copies(20, n=6) sphere(d=10); 135 | ``` 136 | 137 | The spacing can be separately specified for both the X and Y axes, as can 138 | the count of rows and columns: 139 | ```openscad-2D 140 | include 141 | grid_copies([20,30], n=[6,4]) sphere(d=10); 142 | ``` 143 | 144 | Another neat trick of `grid_copies()`, is that you can stagger the output: 145 | ```openscad-2D 146 | include 147 | grid_copies(20, n=[12,6], stagger=true) sphere(d=10); 148 | ``` 149 | 150 | You can get the alternate stagger pattern if you set `stagger="alt"`: 151 | ```openscad-2D 152 | include 153 | grid_copies(20, n=[12,6], stagger="alt") sphere(d=10); 154 | ``` 155 | 156 | By default, if you give a scalar for the spacing value, staggering will give 157 | you a hexagonal grid, with the spacing being the distance from an item to all 158 | six of the surrounding items. If you give the spacing as a 2-item vector, 159 | then that will force the X and Y spacings between columns and rows instead. 160 | ```openscad-2D 161 | include 162 | grid_copies([20,20], n=6, stagger=true) sphere(d=10); 163 | ``` 164 | 165 | You can alternately specify a grid using size and spacing: 166 | ```openscad-2D 167 | include 168 | grid_copies(20, size=100) sphere(d=10); 169 | ``` 170 | 171 | ```openscad-2D 172 | include 173 | grid_copies(20, size=[100,80]) sphere(d=10); 174 | ``` 175 | 176 | ```openscad-2D 177 | include 178 | grid_copies(20, size=[100,80], stagger=true) sphere(d=10); 179 | ``` 180 | 181 | You can also make grids by specifying size and column/row count: 182 | ```openscad-2D 183 | include 184 | grid_copies(n=5, size=100) sphere(d=10); 185 | ``` 186 | 187 | ```openscad-2D 188 | include 189 | grid_copies(n=[4,5], size=100) sphere(d=10); 190 | ``` 191 | 192 | ```openscad-2D 193 | include 194 | grid_copies(n=[4,5], size=[100,80]) sphere(d=10); 195 | ``` 196 | 197 | Finally, the `grid_copies()` command will let you give a polygon or region shape 198 | to fill with items. Only the items in the grid whose center would be inside 199 | the polygon or region will be created. To fill a star shape with items, you 200 | can do something like: 201 | ```openscad-3D 202 | include 203 | poly = [for (i=[0:11]) polar_to_xy(50*(i%2+1), i*360/12-90)]; 204 | grid_copies(5, stagger=true, inside=poly) { 205 | cylinder(d=4,h=10,spin=90,$fn=6); 206 | } 207 | ``` 208 | 209 | 210 | ### Rotational Distributors 211 | You can make six copies of a cone, rotated around a center: 212 | ```openscad-3D 213 | include 214 | zrot_copies(n=6) yrot(90) cylinder(h=50,d1=0,d2=20); 215 | ``` 216 | 217 | To Be Completed 218 | 219 | 220 | -------------------------------------------------------------------------------- /BOSL2/tutorials/FractalTree.md: -------------------------------------------------------------------------------- 1 | # Fractal Tree Tutorial 2 | 3 | 4 | 5 | ### Start with a Tree Trunk 6 | 7 | Firstoff, include the BOSL2 library, then make a starting module that just has a tapered cylinder for the tree trunk. 8 | 9 | ```openscad-3D 10 | include 11 | module tree(l=1500, sc=0.7) 12 | cylinder(h=l, d1=l/5, d2=l/5*sc); 13 | tree(); 14 | ``` 15 | 16 | ### Attaching a Branch 17 | 18 | You can attach a branch to the top of the trunk by using `attach()` as a child of the trunk cylinder. 19 | 20 | ```openscad-3D 21 | include 22 | module tree(l=1500, sc=0.7) 23 | cylinder(h=l, d1=l/5, d2=l/5*sc) 24 | attach(TOP) 25 | yrot(30) cylinder(h=l*sc, d1=l/5*sc, d2=l/5*sc*sc); 26 | tree(); 27 | ``` 28 | 29 | ### Replicating the Branch 30 | 31 | Instead of attaching each branch individually, you can make multiple copies of one branch, that are rotated relative to each other. 32 | 33 | ```openscad-3D 34 | include 35 | module tree(l=1500, sc=0.7) 36 | cylinder(h=l, d1=l/5, d2=l/5*sc) 37 | attach(TOP) 38 | zrot_copies(n=2) // Replicate that branch 39 | yrot(30) cylinder(h=l*sc, d1=l/5*sc, d2=l/5*sc*sc); 40 | tree(); 41 | ``` 42 | 43 | ### Use Recursion 44 | 45 | Since branches look much like the main trunk, we can make the tree recursive. Don't forget the termination clause, or else it'll try to recurse forever! 46 | 47 | ```openscad-Med 48 | include 49 | module tree(l=1500, sc=0.7, depth=10) 50 | cylinder(h=l, d1=l/5, d2=l/5*sc) 51 | attach(TOP) 52 | if (depth>0) { // Important! 53 | zrot_copies(n=2) 54 | yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); 55 | } 56 | tree(); 57 | ``` 58 | 59 | ### Make it Not Flat 60 | 61 | A flat planar tree isn't what we want, so lets bush it out a bit by rotating each level 90 degrees. 62 | 63 | ```openscad-Med 64 | include 65 | module tree(l=1500, sc=0.7, depth=10) 66 | cylinder(h=l, d1=l/5, d2=l/5*sc) 67 | attach(TOP) 68 | if (depth>0) { 69 | zrot(90) // Bush it out 70 | zrot_copies(n=2) 71 | yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); 72 | } 73 | tree(); 74 | ``` 75 | 76 | ### Adding Leaves 77 | 78 | Let's add leaves. They look much like squashed versions of the standard teardrop() module, so lets use that. 79 | 80 | ```openscad-Big 81 | include 82 | module tree(l=1500, sc=0.7, depth=10) 83 | cylinder(h=l, d1=l/5, d2=l/5*sc) 84 | attach(TOP) 85 | if (depth>0) { 86 | zrot(90) 87 | zrot_copies(n=2) 88 | yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); 89 | } else { 90 | yscale(0.67) 91 | teardrop(d=l*3, l=1, anchor=BOT, spin=90); 92 | } 93 | tree(); 94 | ``` 95 | 96 | ### Adding Color 97 | 98 | We can finish this off with some color. The `color()` module will force all it's children and 99 | their descendants to the new color, even if they were colored before. The `recolor()` module, 100 | however, will only color children and decendants that don't already have a color set by a more 101 | nested `recolor()`. 102 | 103 | ```openscad-Big 104 | include 105 | module tree(l=1500, sc=0.7, depth=10) 106 | recolor("lightgray") 107 | cylinder(h=l, d1=l/5, d2=l/5*sc) 108 | attach(TOP) 109 | if (depth>0) { 110 | zrot(90) 111 | zrot_copies(n=2) 112 | yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); 113 | } else { 114 | recolor("springgreen") 115 | yscale(0.67) 116 | teardrop(d=l*3, l=1, anchor=BOT, spin=90); 117 | } 118 | tree(); 119 | ``` 120 | 121 | 122 | -------------------------------------------------------------------------------- /BOSL2/tutorials/Mutators.md: -------------------------------------------------------------------------------- 1 | # Mutators Tutorial 2 | 3 | 4 | 5 | ## 3D Space Halving 6 | Sometimes you want to take a 3D shape like a sphere, and cut it in half. 7 | The BOSL2 library provides a number of ways to do this: 8 | 9 | ```openscad-3D 10 | include 11 | left_half() 12 | sphere(d=100); 13 | ``` 14 | 15 | ```openscad-3D 16 | include 17 | right_half() 18 | sphere(d=100); 19 | ``` 20 | 21 | ```openscad-3D 22 | include 23 | front_half() 24 | sphere(d=100); 25 | ``` 26 | 27 | ```openscad-3D 28 | include 29 | back_half() 30 | sphere(d=100); 31 | ``` 32 | 33 | ```openscad-3D 34 | include 35 | bottom_half() 36 | sphere(d=100); 37 | ``` 38 | 39 | ```openscad-3D 40 | include 41 | top_half() 42 | sphere(d=100); 43 | ``` 44 | 45 | You can use the `half_of()` module if you want to split space in a way not aligned with an axis: 46 | 47 | ```openscad-3D 48 | include 49 | half_of([-1,0,-1]) 50 | sphere(d=100); 51 | ``` 52 | 53 | The plane of dissection can be shifted along the axis of any of these operators: 54 | 55 | ```openscad-3D 56 | include 57 | left_half(x=20) 58 | sphere(d=100); 59 | ``` 60 | 61 | ```openscad-3D 62 | include 63 | back_half(y=-20) 64 | sphere(d=100); 65 | ``` 66 | 67 | ```openscad-3D 68 | include 69 | bottom_half(z=20) 70 | sphere(d=100); 71 | ``` 72 | 73 | ```openscad-3D 74 | include 75 | half_of([-1,0,-1], cp=[20,0,20]) 76 | sphere(d=100); 77 | ``` 78 | 79 | By default, these operators can be applied to objects that fit in a cube 1000 on a side. If you need 80 | to apply these halving operators to objects larger than this, you can give the size in the `s=` 81 | argument: 82 | 83 | ```openscad-3D 84 | include 85 | bottom_half(s=2000) 86 | sphere(d=1500); 87 | ``` 88 | 89 | ## 2D Plane Halving 90 | To cut 2D shapes in half, you will need to add the `planar=true` argument: 91 | 92 | ```openscad-3D 93 | include 94 | left_half(planar=true) 95 | circle(d=100); 96 | ``` 97 | 98 | ```openscad-3D 99 | include 100 | right_half(planar=true) 101 | circle(d=100); 102 | ``` 103 | 104 | ```openscad-3D 105 | include 106 | front_half(planar=true) 107 | circle(d=100); 108 | ``` 109 | 110 | ```openscad-3D 111 | include 112 | back_half(planar=true) 113 | circle(d=100); 114 | ``` 115 | 116 | ## Chained Mutators 117 | If you have a set of shapes that you want to do pair-wise hulling of, you can use `chain_hull()`: 118 | 119 | ```openscad-3D 120 | include 121 | chain_hull() { 122 | cube(5, center=true); 123 | translate([30, 0, 0]) sphere(d=15); 124 | translate([60, 30, 0]) cylinder(d=10, h=20); 125 | translate([60, 60, 0]) cube([10,1,20], center=false); 126 | } 127 | ``` 128 | 129 | ## Extrusion Mutators 130 | The OpenSCAD `linear_extrude()` module can take a 2D shape and extrude it vertically in a line: 131 | 132 | ```openscad-3D 133 | include 134 | linear_extrude(height=30) 135 | zrot(45) 136 | square(40,center=true); 137 | ``` 138 | 139 | The `rotate_extrude()` module can take a 2D shape and rotate it around the Z axis. 140 | 141 | ```openscad-3D 142 | include 143 | rotate_extrude() 144 | left(50) zrot(45) 145 | square(40,center=true); 146 | ``` 147 | 148 | In a similar manner, the BOSL2 `cylindrical_extrude()` module can take a 2d shape and extrude it 149 | out radially from the center of a cylinder: 150 | 151 | ```openscad-3D 152 | include 153 | cylindrical_extrude(or=40, ir=35) 154 | text(text="Hello World!", size=10, halign="center", valign="center"); 155 | ``` 156 | 157 | 158 | ## Offset Mutators 159 | 160 | ### Minkowski Difference 161 | Openscad provides the `minkowski()` module to trace a shape over the entire surface of another shape: 162 | 163 | ```openscad-3D 164 | include 165 | minkowski() { 166 | union() { 167 | cube([100,33,33], center=true); 168 | cube([33,100,33], center=true); 169 | cube([33,33,100], center=true); 170 | } 171 | sphere(r=8); 172 | } 173 | ``` 174 | 175 | However, it doesn't provide the inverse of this operation; to remove a shape from the entire surface 176 | of another object. For this, the BOSL2 library provides the `minkowski_difference()` module: 177 | 178 | ```openscad-3D 179 | include 180 | minkowski_difference() { 181 | union() { 182 | cube([100,33,33], center=true); 183 | cube([33,100,33], center=true); 184 | cube([33,33,100], center=true); 185 | } 186 | sphere(r=8); 187 | } 188 | ``` 189 | 190 | To perform a `minkowski_difference()` on 2D shapes, you need to supply the `planar=true` argument: 191 | 192 | ```openscad-2D 193 | include 194 | minkowski_difference(planar=true) { 195 | union() { 196 | square([100,33], center=true); 197 | square([33,100], center=true); 198 | } 199 | circle(r=8); 200 | } 201 | ``` 202 | 203 | ### Round2d 204 | The `round2d()` module lets you take a 2D shape and round inside and outside corners. The inner concave corners are rounded to the radius `ir=`, while the outer convex corners are rounded to the radius `or=`: 205 | 206 | ```openscad-2D 207 | include 208 | round2d(or=8) 209 | star(6, step=2, d=100); 210 | ``` 211 | 212 | ```openscad-2D 213 | include 214 | round2d(ir=12) 215 | star(6, step=2, d=100); 216 | ``` 217 | 218 | ```openscad-2D 219 | include 220 | round2d(or=8,ir=12) 221 | star(6, step=2, d=100); 222 | ``` 223 | 224 | You can use `r=` to effectively set both `ir=` and `or=` to the same value: 225 | 226 | ```openscad-2D 227 | include 228 | round2d(r=8) 229 | star(6, step=2, d=100); 230 | ``` 231 | 232 | ### Shell2d 233 | With the `shell2d()` module, you can take an arbitrary shape, and get the shell outline of it. 234 | With a positive thickness, the shell is offset outwards from the original shape: 235 | 236 | ```openscad-2D 237 | include 238 | shell2d(thickness=5) 239 | star(5,step=2,d=100); 240 | color("blue") 241 | stroke(star(5,step=2,d=100),closed=true); 242 | ``` 243 | 244 | With a negative thickness, the shell if inset from the original shape: 245 | 246 | ```openscad-2D 247 | include 248 | shell2d(thickness=-5) 249 | star(5,step=2,d=100); 250 | color("blue") 251 | stroke(star(5,step=2,d=100),closed=true); 252 | ``` 253 | 254 | You can give a pair of thickness values if you want it both inset and outset from the original shape: 255 | 256 | ```openscad-2D 257 | include 258 | shell2d(thickness=[-5,5]) 259 | star(5,step=2,d=100); 260 | color("blue") 261 | stroke(star(5,step=2,d=100),closed=true); 262 | ``` 263 | 264 | You can add rounding to the outside by passing a radius to the `or=` argument. 265 | 266 | ```openscad-2D 267 | include 268 | shell2d(thickness=-5,or=5) 269 | star(5,step=2,d=100); 270 | ``` 271 | 272 | If you need to pass different radii for the convex and concave corners of the outside, you can pass them as `or=[CONVEX,CONCAVE]`: 273 | 274 | ```openscad-2D 275 | include 276 | shell2d(thickness=-5,or=[5,10]) 277 | star(5,step=2,d=100); 278 | ``` 279 | 280 | A radius of 0 can be used to specify no rounding: 281 | 282 | ```openscad-2D 283 | include 284 | shell2d(thickness=-5,or=[5,0]) 285 | star(5,step=2,d=100); 286 | ``` 287 | 288 | You can add rounding to the inside by passing a radius to the `ir=` argument. 289 | 290 | ```openscad-2D 291 | include 292 | shell2d(thickness=-5,ir=5) 293 | star(5,step=2,d=100); 294 | ``` 295 | 296 | If you need to pass different radii for the convex and concave corners of the inside, you can pass them as `ir=[CONVEX,CONCAVE]`: 297 | 298 | ```openscad-2D 299 | include 300 | shell2d(thickness=-5,ir=[8,3]) 301 | star(5,step=2,d=100); 302 | ``` 303 | 304 | You can use `or=` and `ir=` together to get nice combined rounding effects: 305 | 306 | ```openscad-2D 307 | include 308 | shell2d(thickness=-5,or=[7,2],ir=[7,2]) 309 | star(5,step=2,d=100); 310 | ``` 311 | 312 | ```openscad-2D 313 | include 314 | shell2d(thickness=-5,or=[5,0],ir=[5,0]) 315 | star(5,step=2,d=100); 316 | ``` 317 | 318 | 319 | ### Round3d 320 | ### Offset3d 321 | (To be Written) 322 | 323 | 324 | ## Color Manipulators 325 | The built-in OpenSCAD `color()` module can let you set the RGB color of an object, but it's often 326 | easier to select colors using other color schemes. You can use the HSL or Hue-Saturation-Lightness 327 | color scheme with the `hsl()` module: 328 | 329 | ```openscad-3D 330 | include 331 | n = 10; size = 100/n; 332 | for (a=count(n), b=count(n), c=count(n)) { 333 | let( h=360*a/n, s=1-b/(n-1), l=c/(n-1)) 334 | translate(size*[a,b,c]) { 335 | hsl(h,s,l) cube(size); 336 | } 337 | } 338 | ``` 339 | 340 | You can use the HSV or Hue-Saturation-Value color scheme with the `hsv()` module: 341 | 342 | ```openscad-3D 343 | include 344 | n = 10; size = 100/n; 345 | for (a=count(n), b=count(n), c=count(n)) { 346 | let( h=360*a/n, s=1-b/(n-1), v=c/(n-1)) 347 | translate(size*[a,b,c]) { 348 | hsv(h,s,v) cube(size); 349 | } 350 | } 351 | ``` 352 | 353 | 354 | -------------------------------------------------------------------------------- /BOSL2/tutorials/Transforms.md: -------------------------------------------------------------------------------- 1 | # Transforms Tutorial 2 | 3 | 4 | 5 | ## Translation 6 | The `translate()` command is very simple: 7 | ```openscad 8 | include 9 | #sphere(d=20); 10 | translate([0,0,30]) sphere(d=20); 11 | ``` 12 | 13 | But at a glance, or when the formula to calculate the move is complex, it can be difficult to see 14 | just what axis is being moved along, and in which direction. It's also a bit verbose for such a 15 | frequently used command. For these reasons, BOSL2 provides you with shortcuts for each direction. 16 | These shortcuts are `up()`, `down()`, `fwd()`, `back()`, `left()`, and `right()`: 17 | ```openscad 18 | include 19 | #sphere(d=20); 20 | up(30) sphere(d=20); 21 | ``` 22 | 23 | ```openscad 24 | include 25 | #sphere(d=20); 26 | down(30) sphere(d=20); 27 | ``` 28 | 29 | ```openscad 30 | include 31 | #sphere(d=20); 32 | fwd(30) sphere(d=20); 33 | ``` 34 | 35 | ```openscad 36 | include 37 | #sphere(d=20); 38 | back(30) sphere(d=20); 39 | ``` 40 | 41 | ```openscad 42 | include 43 | #sphere(d=20); 44 | left(30) sphere(d=20); 45 | ``` 46 | 47 | ```openscad 48 | include 49 | #sphere(d=20); 50 | right(30) sphere(d=20); 51 | ``` 52 | 53 | There is also a more generic `move()` command that can work just like `translate()`: 54 | ```openscad 55 | include 56 | #sphere(d=20); 57 | move([30,-10]) sphere(d=20); 58 | ``` 59 | 60 | ## Scaling 61 | The `scale()` command is also fairly simple: 62 | ```openscad 63 | include 64 | scale(2) cube(10, center=true); 65 | ``` 66 | 67 | ```openscad 68 | include 69 | scale([1,2,3]) cube(10, center=true); 70 | ``` 71 | 72 | If you want to only change the scaling on one axis, though, BOSL2 provides clearer 73 | commands to do just that; `xscale()`, `yscale()`, and `zscale()`: 74 | ```openscad 75 | include 76 | xscale(2) cube(10, center=true); 77 | ``` 78 | ```openscad 79 | include 80 | yscale(2) cube(10, center=true); 81 | ``` 82 | ```openscad 83 | include 84 | zscale(2) cube(10, center=true); 85 | ``` 86 | 87 | 88 | ## Rotation 89 | The `rotate()` command is fairly straightforward: 90 | ```openscad 91 | include 92 | rotate([0,30,0]) cube(20, center=true); 93 | ``` 94 | 95 | It is also a bit verbose, and can, at a glance, be difficult to tell just how it is rotating. 96 | BOSL2 provides shortcuts for rotating around each axis, for clarity; `xrot()`, `yrot()`, and `zrot()`: 97 | ```openscad 98 | include 99 | xrot(30) cube(20, center=true); 100 | ``` 101 | 102 | ```openscad 103 | include 104 | yrot(30) cube(20, center=true); 105 | ``` 106 | 107 | ```openscad 108 | include 109 | zrot(30) cube(20, center=true); 110 | ``` 111 | 112 | The `rot()` command is a more generic rotation command, and shorter to type than `rotate()`: 113 | ```openscad 114 | include 115 | rot([0,30,15]) cube(20, center=true); 116 | ``` 117 | 118 | All of the rotation shortcuts can take a `cp=` argument, that lets you specify a 119 | centerpoint to rotate around: 120 | ```openscad 121 | include 122 | cp = [0,0,40]; 123 | color("blue") move(cp) sphere(d=3); 124 | #cube(20, center=true); 125 | xrot(45, cp=cp) cube(20, center=true); 126 | ``` 127 | 128 | ```openscad 129 | include 130 | cp = [0,0,40]; 131 | color("blue") move(cp) sphere(d=3); 132 | #cube(20, center=true); 133 | yrot(45, cp=cp) cube(20, center=true); 134 | ``` 135 | 136 | ```openscad 137 | include 138 | cp = [0,40,0]; 139 | color("blue") move(cp) sphere(d=3); 140 | #cube(20, center=true); 141 | zrot(45, cp=cp) cube(20, center=true); 142 | ``` 143 | 144 | You can also do a new trick with it. You can rotate from pointing in one direction, towards another. 145 | You give these directions using vectors: 146 | ```openscad 147 | include 148 | #cylinder(d=10, h=50); 149 | rot(from=[0,0,1], to=[1,0,1]) cylinder(d=10, h=50); 150 | ``` 151 | 152 | There are several direction vectors constants and aliases you can use for clarity: 153 | 154 | Constant | Value | Direction 155 | ------------------------------ | ------------ | -------------- 156 | `CENTER`, `CTR` | `[ 0, 0, 0]` | Centered 157 | `LEFT` | `[-1, 0, 0]` | Towards X- 158 | `RIGHT` | `[ 1, 0, 0]` | Towards X+ 159 | `FWD`, `FORWARD`, `FRONT` | `[ 0,-1, 0]` | Towards Y- 160 | `BACK` | `[ 0, 1, 0]` | Towards Y+ 161 | `DOWN`, `BOTTOM`, `BOT` | `[ 0, 0,-1]` | Towards Z- 162 | `UP`, `TOP` | `[ 0, 0, 1]` | Towards Z+ 163 | 164 | This lets you rewrite the above vector rotation more clearly as: 165 | ```openscad 166 | include 167 | #cylinder(d=10, h=50); 168 | rot(from=UP, to=UP+RIGHT) cylinder(d=10, h=50); 169 | ``` 170 | 171 | 172 | ## Mirroring 173 | The standard `mirror()` command works like this: 174 | ```openscad 175 | include 176 | #yrot(60) cylinder(h=50, d1=20, d2=10); 177 | mirror([1,0,0]) yrot(60) cylinder(h=50, d1=20, d2=10); 178 | ``` 179 | 180 | BOSL2 provides shortcuts for mirroring across the standard axes; `xflip()`, `yflip()`, and `zflip()`: 181 | ```openscad 182 | include 183 | #yrot(60) cylinder(h=50, d1=20, d2=10); 184 | xflip() yrot(60) cylinder(h=50, d1=20, d2=10); 185 | ``` 186 | 187 | ```openscad 188 | include 189 | #xrot(60) cylinder(h=50, d1=20, d2=10); 190 | yflip() xrot(60) cylinder(h=50, d1=20, d2=10); 191 | ``` 192 | 193 | ```openscad 194 | include 195 | #cylinder(h=50, d1=20, d2=10); 196 | zflip() cylinder(h=50, d1=20, d2=10); 197 | ``` 198 | 199 | All of the flip commands can offset where the mirroring is performed: 200 | ```openscad 201 | include 202 | #zrot(30) cube(20, center=true); 203 | xflip(x=-20) zrot(30) cube(20, center=true); 204 | color("blue",0.25) left(20) cube([0.1,50,50], center=true); 205 | ``` 206 | 207 | ```openscad 208 | include 209 | #zrot(30) cube(20, center=true); 210 | yflip(y=20) zrot(30) cube(20, center=true); 211 | color("blue",0.25) back(20) cube([40,0.1,40], center=true); 212 | ``` 213 | 214 | ```openscad 215 | include 216 | #xrot(30) cube(20, center=true); 217 | zflip(z=-20) xrot(30) cube(20, center=true); 218 | color("blue",0.25) down(20) cube([40,40,0.1], center=true); 219 | ``` 220 | 221 | 222 | ## Skewing 223 | One transform that OpenSCAD does not perform natively is skewing. 224 | BOSL2 provides the `skew()` command for that. You give it multipliers 225 | for the skews you want to perform. The arguments used all start with `s`, 226 | followed by the axis you want to skew along, followed by the axis that 227 | the skewing will increase along. For example, to skew along the X axis as 228 | you get farther along the Y axis, use the `sxy=` argument. If you give it 229 | a multiplier of `0.5`, then for each unit further along the Y axis you get, 230 | you will add `0.5` units of skew to the X axis. Giving a negative multiplier 231 | reverses the direction it skews: 232 | ```openscad 233 | include 234 | skew(sxy=0.5) cube(10,center=false); 235 | ``` 236 | 237 | ```openscad 238 | include 239 | skew(sxz=-0.5) cube(10,center=false); 240 | ``` 241 | 242 | ```openscad 243 | include 244 | skew(syx=-0.5) cube(10,center=false); 245 | ``` 246 | 247 | ```openscad 248 | include 249 | skew(syz=0.5) cube(10,center=false); 250 | ``` 251 | 252 | ```openscad 253 | include 254 | skew(szx=-0.5) cube(10,center=false); 255 | ``` 256 | 257 | ```openscad 258 | include 259 | skew(szy=0.5) cube(10,center=false); 260 | ``` 261 | 262 | 263 | -------------------------------------------------------------------------------- /BOSL2/tutorials/VNF.md: -------------------------------------------------------------------------------- 1 | # VNF Tutorial 2 | 3 | 4 | 5 | ## What's a VNF? 6 | The acronym VNF stands for Vertices 'N' Faces. You have probably already come across the concept of vertices and faces when working with the OpenSCAD built-in module `polyhedron()`. A `polyhedron()` in it's simplest form takes two arguments, the first being a list of vertices, and the second a list of faces, where each face is a lists of indices into the list of vertices. For example, to make a cube, you can do: 7 | 8 | ```openscad-3D 9 | include 10 | verts = [ 11 | [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1], 12 | [-1,-1, 1], [1,-1, 1], [1,1, 1], [-1,1, 1] 13 | ]; 14 | faces = [ 15 | [0,1,2], [0,2,3], //BOTTOM 16 | [0,4,5], [0,5,1], //FRONT 17 | [1,5,6], [1,6,2], //RIGHT 18 | [2,6,7], [2,7,3], //BACK 19 | [3,7,4], [3,4,0], //LEFT 20 | [6,4,7], [6,5,4] //TOP 21 | ]; 22 | polyhedron(verts, faces); 23 | ``` 24 | 25 | A VNF structure (usually just called a VNF) is just a two item list where the first item is the list of vertices, and the second item is the list of faces. It's easier to pass a VNF to a function than it is to pass both the vertices and faces separately. 26 | 27 | The equivalent to the `polyhedron()` module that takes a VNF instead is `vnf_polyhedron()`. To make the same cube as a VNF, you can do it like: 28 | 29 | ```openscad-3D 30 | include 31 | vnf = [ 32 | [ 33 | [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1], 34 | [-1,-1, 1], [1,-1, 1], [1,1, 1], [-1,1, 1], 35 | ], 36 | [ 37 | [0,1,2], [0,2,3], //BOTTOM 38 | [0,4,5], [0,5,1], //FRONT 39 | [1,5,6], [1,6,2], //RIGHT 40 | [2,6,7], [2,7,3], //BACK 41 | [3,7,4], [3,4,0], //LEFT 42 | [6,4,7], [6,5,4] //TOP 43 | ] 44 | ]; 45 | vnf_polyhedron(vnf); 46 | ``` 47 | 48 | ## Assembling a Polyhedron in Parts 49 | A VNF does not have to contain a complete polyhedron, and the vertices contained in it do not have to be unique. This allows the true power of VNFs: You can use the `vnf_join()` function to take multiple partial-polyhedron VNFs and merge them into a more complete VNF. This lets you construct a complex polyhedron in parts, without having to keep track of all the vertices you created in other parts of it. 50 | 51 | As an example, consider a roughly spherical polyhedron with vertices at the top and bottom poles. You can break it down into three major parts: The top cap, the bottom cap, and the side wall. The top and bottom caps both have a ring of vertices linked to the top or bottom vertex in triangles, while the sides are multiple rings of vertices linked in squares. Lets create the top cap first: 52 | 53 | ```openscad-3D,ThrownTogether 54 | include 55 | cap_vnf = [ 56 | [[0,0,1], for (a=[0:30:359.9]) spherical_to_xyz(1,a,30)], // Vertices 57 | [for (i=[1:12]) [0, i%12+1, i]] // Faces 58 | ]; 59 | vnf_polyhedron(cap_vnf); 60 | ``` 61 | 62 | The bottom cap is exactly the same, just mirrored: 63 | 64 | ```openscad-3D,ThrownTogether 65 | include 66 | cap_vnf = [ 67 | [[0,0,1], for (a=[0:30:359.9]) spherical_to_xyz(1,a,30)], // Vertices 68 | [for (i=[1:12]) [0, i%12+1, i]] // Faces 69 | ]; 70 | cap_vnf2 = zflip(cap_vnf); 71 | vnf_polyhedron(cap_vnf2); 72 | ``` 73 | 74 | To create the sides, we can make use of the `vnf_vertex_array()` function to turn a row-column grid of vertices into a VNF. The `col_wrap=true` argument tells it to connect the vertices of the last column to the vertices of the first column. The `caps=false` argument tells it that we don't want it to create caps for the ends of the first and last rows: 75 | 76 | ```openscad-3D,ThrownTogether 77 | include 78 | wall_vnf = vnf_vertex_array( 79 | points=[ 80 | for (phi = [30:30:179.9]) [ 81 | for (theta = [0:30:359.9]) 82 | spherical_to_xyz(1,theta,phi) 83 | ] 84 | ], 85 | col_wrap=true, caps=false 86 | ); 87 | vnf_polyhedron(wall_vnf); 88 | ``` 89 | 90 | Putting all the parts together with `vnf_join()`, we get: 91 | 92 | ```openscad-3D,ThrownTogether 93 | include 94 | cap_vnf = [ 95 | [[0,0,1], for (a=[0:30:359.9]) spherical_to_xyz(1,a,30)], // Vertices 96 | [for (i=[1:12]) [0, i%12+1, i]] // Faces 97 | ]; 98 | cap_vnf2 = zflip(cap_vnf); 99 | wall_vnf = vnf_vertex_array( 100 | points=[ 101 | for (phi = [30:30:179.9]) [ 102 | for (theta = [0:30:359.9]) 103 | spherical_to_xyz(1,theta,phi) 104 | ] 105 | ], 106 | col_wrap=true, caps=false 107 | ); 108 | vnf = vnf_join([cap_vnf,cap_vnf2,wall_vnf]); 109 | vnf_polyhedron(vnf); 110 | ``` 111 | 112 | Which is now a complete manifold polyhedron. 113 | 114 | 115 | ## Debugging a VNF 116 | One of the critical tasks in creating a polyhedron is making sure that all of your faces are facing the correct way. This is also true for VNFs. The best way to find reversed faces is simply to select the View→Thrown Together menu item in OpenSCAD while viewing your polyhedron or VNF. Any purple faces are reversed, and you will need to fix them. For example, one of the two top face triangles on this cube is reversed: 117 | 118 | ```openscad-3D,ThrownTogether 119 | include 120 | vnf = [ 121 | [ 122 | [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1], 123 | [-1,-1, 1], [1,-1, 1], [1,1, 1], [-1,1, 1], 124 | ], 125 | [ 126 | [0,1,2], [0,2,3], //BOTTOM 127 | [0,4,5], [0,5,1], //FRONT 128 | [1,5,6], [1,6,2], //RIGHT 129 | [2,6,7], [2,7,3], //BACK 130 | [3,7,4], [3,4,0], //LEFT 131 | [6,4,7], [6,4,5] //TOP 132 | ] 133 | ]; 134 | vnf_polyhedron(vnf); 135 | ``` 136 | 137 | Another way to find problems with your VNF, is to use the `vnf_validate()` module, which will ECHO problems to the console, and will attempt to display where the issue is. This can find a lot more types of non-manifold errors, but can be slow: 138 | 139 | 140 | ```openscad-3D,ThrownTogether 141 | include 142 | vnf = [ 143 | [ 144 | [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1], 145 | [-1,-1, 1], [1,-1, 1], [1,1, 1], [-1,1, 1], 146 | ], 147 | [ 148 | [0,1,2], [0,2,3], //BOTTOM 149 | [0,4,5], //FRONT 150 | [1,5,6], [1,6,2], //RIGHT 151 | [2,6,7], [2,7,3], //BACK 152 | [3,7,4], [3,4,0], //LEFT 153 | [6,4,7], [6,4,5] //TOP 154 | ] 155 | ]; 156 | vnf_validate(vnf, size=0.1); 157 | ``` 158 | 159 | ```text 160 | ECHO: "ERROR REVERSAL (violet): Faces Reverse Across Edge at [[-1, -1, 1], [1, -1, 1]]" 161 | ECHO: "ERROR REVERSAL (violet): Faces Reverse Across Edge at [[1, -1, 1], [1, 1, 1]]" 162 | ECHO: "ERROR REVERSAL (violet): Faces Reverse Across Edge at [[1, 1, 1], [-1, -1, 1]]" 163 | ``` 164 | 165 | The `vnf_validate()` module will stop after displaying the first found problem type, so once you fix those issues, you will want to run it again to display any other remaining issues. For example, the reversed face in the above example is hiding a non-manifold hole in the front face: 166 | 167 | ```openscad-3D,ThrownTogether 168 | include 169 | vnf = [ 170 | [ 171 | [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1], 172 | [-1,-1, 1], [1,-1, 1], [1,1, 1], [-1,1, 1], 173 | ], 174 | [ 175 | [0,1,2], [0,2,3], //BOTTOM 176 | [0,4,5], //FRONT 177 | [1,5,6], [1,6,2], //RIGHT 178 | [2,6,7], [2,7,3], //BACK 179 | [3,7,4], [3,4,0], //LEFT 180 | [6,4,7], [6,5,4] //TOP 181 | ] 182 | ]; 183 | vnf_validate(vnf, size=0.1); 184 | ``` 185 | 186 | ```text 187 | ECHO: "ERROR HOLE_EDGE (red): Edge bounds Hole at [[-1, -1, -1], [1, -1, -1]]" 188 | ECHO: "ERROR HOLE_EDGE (red): Edge bounds Hole at [[-1, -1, -1], [1, -1, 1]]" 189 | ECHO: "ERROR HOLE_EDGE (red): Edge bounds Hole at [[1, -1, -1], [1, -1, 1]]" 190 | ``` 191 | 192 | -------------------------------------------------------------------------------- /BOSL2/version.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // LibFile: version.scad 3 | // File that provides functions to manage versioning. 4 | // Includes: 5 | // include 6 | // FileGroup: Data Management 7 | // FileSummary: Parse and compare semantic versions. 8 | // FileFootnotes: STD=Included in std.scad 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | BOSL_VERSION = [2,0,716]; 13 | 14 | 15 | // Section: BOSL Library Version Functions 16 | 17 | 18 | // Function: bosl_version() 19 | // Synopsis: Returns the BOSL2 version as a list. 20 | // Topics: Versioning 21 | // See Also: bosl_version_num(), bosl_version_str(), bosl_required() 22 | // Usage: 23 | // ver = bosl_version(); 24 | // Description: 25 | // Returns a list with three integer elements, [MAJOR,MINOR,REV], 26 | // representing the Major, Minor, and Build Revision numbers. 27 | // For example, version 2.1.43 will be returned as `[2,1,43]`. 28 | function bosl_version() = BOSL_VERSION; 29 | 30 | 31 | // Function: bosl_version_num() 32 | // Synopsis: Returns the BOSL2 version as a float. 33 | // Topics: Versioning 34 | // See Also: bosl_version(), bosl_version_str(), bosl_required() 35 | // Usage: 36 | // ver = bosl_version_num(); 37 | // Description: 38 | // Returns a floating point number of the version, formatted like M.mmrrrr where M is the major version number, 39 | // each m is a zero-padded digit of the minor version number, and each r is a zero-padded digit of the build 40 | // revision number. For example, version 2.1.43 will be returned as `2.010043`. 41 | function bosl_version_num() = version_to_num(BOSL_VERSION); 42 | 43 | 44 | // Function: bosl_version_str() 45 | // Synopsis: Returns the BOSL2 version as a string. 46 | // Topics: Versioning 47 | // See Also: bosl_version(), bosl_version_num(), bosl_required() 48 | // Usage: 49 | // ver = bosl_version_str(); 50 | // Description: 51 | // Returns a string of the version, formatted like "MAJOR.MINOR.REV". 52 | // For example, version 2.1.43 will be returned as `"2.1.43"`. 53 | function bosl_version_str() = version_to_str(BOSL_VERSION); 54 | 55 | 56 | // Module: bosl_required() 57 | // Synopsis: Asserts that the current version of the library is at least the given version. 58 | // Topics: Versioning 59 | // See Also: version_to_num(), version_to_str(), version_to_list(), version_cmp() 60 | // Usage: 61 | // bosl_required(version); 62 | // Description: 63 | // Given a version as a list, number, or string, asserts that the currently installed BOSL library is at least the given version. 64 | // Arguments: 65 | // version = version required 66 | module bosl_required(version) { 67 | no_children($children); 68 | assert( 69 | version_cmp(bosl_version(), version) >= 0, 70 | str( 71 | "BOSL ", bosl_version_str(), " is installed, but BOSL ", 72 | version_to_str(version), " or better is required." 73 | ) 74 | ); 75 | } 76 | 77 | 78 | // Section: Generic Version Functions 79 | 80 | function _version_split_str(x, _i=0, _out=[], _num=0) = 81 | _i>=len(x)? concat(_out,[_num]) : 82 | let( 83 | cval = ord(x[_i]) - ord("0"), 84 | numend = cval<0 || cval>9, 85 | _out = numend? concat(_out, [_num]) : _out, 86 | _num = numend? 0 : (10*_num + cval) 87 | ) 88 | _version_split_str(x, _i=_i+1, _out=_out, _num=_num); 89 | 90 | 91 | // Function: version_to_list() 92 | // Synopsis: Splits a version into a list of integer version parts. 93 | // Topics: Versioning 94 | // See Also: version_to_num(), version_to_str(), version_cmp(), bosl_required() 95 | // Usage: 96 | // ver = version_to_list(x); 97 | // Description: 98 | // Given a version string, number, or list, returns the list of version integers [MAJOR,MINOR,REVISION]. 99 | // Arguments: 100 | // x = version to convert 101 | // Example: 102 | // v1 = version_to_list("2.1.43"); // Returns: [2,1,43] 103 | // v2 = version_to_list(2.120234); // Returns: [2,12,234] 104 | // v3 = version_to_list([2,3,4]); // Returns: [2,3,4] 105 | // v4 = version_to_list([2,3,4,5]); // Returns: [2,3,4] 106 | function version_to_list(version) = 107 | is_list(version)? [default(version[0],0), default(version[1],0), default(version[2],0)] : 108 | is_string(version)? _version_split_str(version) : 109 | is_num(version)? [floor(version), floor(version*100%100), floor(version*1000000%10000+0.5)] : 110 | assert(is_num(version) || is_vector(version) || is_string(version)) 0; 111 | 112 | 113 | // Function: version_to_str() 114 | // Synopsis: Coerces a version into a standard version string. 115 | // Topics: Versioning 116 | // See Also: version_to_num(), version_to_list(), version_cmp(), bosl_required() 117 | // Usage: 118 | // str = version_to_str(version); 119 | // Description: 120 | // Takes a version string, number, or list, and returns the properly formatter version string for it. 121 | // Arguments: 122 | // version = version to convert 123 | // Example: 124 | // v1 = version_to_str([2,1,43]); // Returns: "2.1.43" 125 | // v2 = version_to_str(2.010043); // Returns: "2.1.43" 126 | // v3 = version_to_str(2.340789); // Returns: "2.34.789" 127 | // v4 = version_to_str("2.3.89"); // Returns: "2.3.89" 128 | function version_to_str(version) = 129 | let(version = version_to_list(version)) 130 | str(version[0],".",version[1],".",version[2]); 131 | 132 | 133 | // Function: version_to_num() 134 | // Synopsis: Coerces a version into a standard version float. 135 | // Topics: Versioning 136 | // See Also: version_cmp(), version_to_str(), version_to_list(), bosl_required() 137 | // Usage: 138 | // str = version_to_num(version); 139 | // Description: 140 | // Takes a version string, number, or list, and returns the properly formatter version number for it. 141 | // Arguments: 142 | // version = version to convert 143 | // Example: 144 | // v1 = version_to_num([2,1,43]); // Returns: 2.010043 145 | // v2 = version_to_num([2,34,567]); // Returns: 2.340567 146 | // v3 = version_to_num(2.120567); // Returns: 2.120567 147 | // v4 = version_to_num("2.6.79"); // Returns: 2.060079 148 | function version_to_num(version) = 149 | let(version = version_to_list(version)) 150 | (version[0]*1000000 + version[1]*10000 + version[2])/1000000; 151 | 152 | 153 | // Function: version_cmp() 154 | // Synopsis: Compares two versions. 155 | // Topics: Versioning 156 | // See Also: version_to_num(), version_to_str(), version_to_list(), bosl_required() 157 | // Usage: 158 | // cmp = version_cmp(a,b); 159 | // Description: 160 | // Given a pair of versions, in any combination of string, integer, or list, compares them, and returns the relative value of them. 161 | // Returns an integer <0 if a0 if a>b. 162 | // Example: 163 | // cmp1 = version_cmp(2.010034, "2.1.33"); // Returns: >0 164 | // cmp2 = version_cmp(2.010034, "2.1.34"); // Returns: 0 165 | // cmp3 = version_cmp(2.010034, "2.1.35"); // Returns: <0 166 | function version_cmp(a,b) = 167 | let( 168 | a = version_to_list(a), 169 | b = version_to_list(b), 170 | cmps = [for (i=[0:1:2]) if(a[i]!=b[i]) a[i]-b[i]] 171 | ) cmps==[]? 0 : cmps[0]; 172 | 173 | 174 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 175 | -------------------------------------------------------------------------------- /BOSL2/wiring.scad: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // LibFile: wiring.scad 3 | // Rendering for routed wire bundles 4 | // Includes: 5 | // include 6 | // include 7 | // FileGroup: Parts 8 | // FileSummary: Routed bundles of wires. 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | include 12 | 13 | 14 | /// Function: _hex_offset_ring() 15 | /// Usage: 16 | /// _hex_offset_ring(d, lev) 17 | /// Description: 18 | /// Returns a hexagonal ring of points, with a spacing of `d`. 19 | /// If `lev=0`, returns a single point at `[0,0]`. All greater 20 | /// levels return `6 * lev` points. 21 | /// Arguments: 22 | /// d = Base unit diameter to build rings upon. 23 | /// lev = How many rings to produce. 24 | /// Example: 25 | /// _hex_offset_ring(d=1, lev=3); // Returns a hex ring of 18 points. 26 | function _hex_offset_ring(d, lev=0) = 27 | (lev == 0)? [[0,0]] : 28 | reverse(subdivide_path(hexagon(r=lev*d), refine=lev)); 29 | 30 | 31 | /// Function: _hex_offsets() 32 | /// Usage: 33 | /// _hex_offsets(n, d) 34 | /// Description: 35 | /// Returns the centerpoints for the optimal hexagonal packing 36 | /// of at least `n` circular items, of diameter `d`. Will return 37 | /// enough points to fill out the last ring, even if that is more 38 | /// than `n` points. 39 | /// Arguments: 40 | /// n = Number of items to bundle. 41 | /// d = How far to space each point away from others. 42 | function _hex_offsets(n, d, lev=0, arr=[]) = 43 | (len(arr) >= n)? arr : 44 | _hex_offsets( 45 | n=n, 46 | d=d, 47 | lev=lev+1, 48 | arr=concat(arr, _hex_offset_ring(d, lev=lev)) 49 | ); 50 | 51 | 52 | 53 | // Section: Modules 54 | 55 | 56 | // Module: wire_bundle() 57 | // Synopsis: Creates a wire bundle for a given number of wires. 58 | // SynTags: Geom 59 | // Topics: Wiring 60 | // See Also: path_sweep(), path_sweep2d() 61 | // Usage: 62 | // wire_bundle(path, wires, [wirediam], [rounding], [wirenum=], [corner_steps=]); 63 | // Description: 64 | // Returns a 3D object representing a bundle of wires that follow a given path, 65 | // with the corners rounded to a given radius. There are 17 base wire colors. 66 | // If you have more than 17 wires, colors will get re-used. 67 | // Arguments: 68 | // path = The 3D path that the wire bundle should follow. 69 | // wires = The number of wires in the wire bundle. 70 | // wirediam = The diameter of each wire in the bundle. 71 | // rounding = The radius that the path corners will be rounded to. 72 | // --- 73 | // wirenum = The first wire's offset into the color table. 74 | // corner_steps = The corner roundings in the path will be converted into this number of segments. 75 | // Example: 76 | // wire_bundle([[50,0,-50], [50,50,-50], [0,50,-50], [0,0,-50], [0,0,0]], rounding=10, wires=13); 77 | module wire_bundle(path, wires, wirediam=2, rounding=10, wirenum=0, corner_steps=15) { 78 | no_children($children); 79 | colors = [ 80 | [0.2, 0.2, 0.2], [1.0, 0.2, 0.2], [0.0, 0.8, 0.0], [1.0, 1.0, 0.2], 81 | [0.3, 0.3, 1.0], [1.0, 1.0, 1.0], [0.7, 0.5, 0.0], [0.5, 0.5, 0.5], 82 | [0.2, 0.9, 0.9], [0.8, 0.0, 0.8], [0.0, 0.6, 0.6], [1.0, 0.7, 0.7], 83 | [1.0, 0.5, 1.0], [0.5, 0.6, 0.0], [1.0, 0.7, 0.0], [0.7, 1.0, 0.5], 84 | [0.6, 0.6, 1.0], 85 | ]; 86 | sides = max(segs(wirediam/2), 8); 87 | offsets = _hex_offsets(wires, wirediam); 88 | rounded_path = round_corners(path, radius=rounding, $fn=(corner_steps+1)*4, closed=false); 89 | attachable(){ 90 | for (i = [0:1:wires-1]) { 91 | extpath = move(offsets[i], p=circle(d=wirediam, $fn=sides)); 92 | color(colors[(i+wirenum)%len(colors)]) { 93 | path_sweep(extpath, rounded_path); 94 | } 95 | } 96 | union(); 97 | } 98 | } 99 | 100 | 101 | 102 | // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap 103 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # PolyDiceGenerator 2 | 3 | A customizable **Polyhedral Dice Generator** for OpenSCAD. 4 | 5 | ## PolyDiceGenerator v0.27.5 6 | 7 | - Updated included BOSL2 library to 2.0.716 8 | - Thanks to [arnoesterhuizen](https://github.com/arnoesterhuizen) for creating the pull request that this update is based on. 9 | 10 | ## PolyDiceGenerator v0.27.4 11 | 12 | - Added d4i - an infinity edge d4 13 | 14 | ## PolyDiceGenerator v0.27.3 15 | 16 | - Fixed d4c/d4p corner rounding/clipping bug 17 | - Added per-die spacing adjustment for double digit numbers 18 | 19 | ## PolyDiceGenerator v0.27.2 20 | 21 | - Fixed d4c/d4p render issues with OpenSCAD 2021.01 22 | 23 | ## PolyDiceGenerator v0.27.1 24 | 25 | - Tweaked arc generation values to improve render times 26 | - Updated included BOSL2 library to 2.0.525 27 | - BOSL2 update fixed d10/d00 rounding scale/alignmet bug 28 | 29 | ## PolyDiceGenerator v0.27.0 30 | 31 | - Fixed number rotate bug when resizing d10/d00 32 | - Added ability to change d4c/d6 pip shapes 'd(n)_pip_sides' 33 | - Tweaked arc generation values to improve performance 34 | - Updated included BOSL2 library to v2.0.499 35 | 36 | ## PolyDiceGenerator v0.26.11 37 | 38 | - Fixed problem with bumpers not being unioned to dice. 39 | - Updated arc generation values to improve performance. 40 | 41 | ## PolyDiceGenerator v0.26.10 42 | 43 | - Added ability to draw bumpers around specified faces. 44 | 45 | ## PolyDiceGenerator v0.26.9 46 | 47 | - Added d3 - a rounded triangular prism 48 | 49 | ## PolyDiceGenerator v0.26.8 50 | 51 | - Added d12r - a rhombic dodecahedron 52 | 53 | ## PolyDiceGenerator v0.26.7 54 | 55 | - Added stroke adjustment for numbers, underscores, and symbols 56 | 57 | ## PolyDiceGenerator v0.26.6 58 | 59 | - Added fix for quoted font names when copied from Help > Font List 60 | 61 | ## PolyDiceGenerator v0.26.5 62 | 63 | - Added per-die size adjustment for each individual text element 64 | - Removed pre-built distributions for simplification 65 | - DiceLab distribution values can be found in Hidden variables section 66 | - Variable names have changed, existing Customizer presets will be affected 67 | 68 | ## PolyDiceGenerator v0.26.4 69 | 70 | - Added alternative shapes for d2 coin 71 | - Added d4p a pyramid shaped d4 72 | - Added BOSL2 library v2.0.402 73 | 74 | ## PolyDiceGenerator v0.26.3 75 | 76 | - Added per-die vertical push, horizontal push, and depth arrays allowing adjustment of individual elements 77 | 78 | ## PolyDiceGenerator v0.26.2 79 | 80 | - Added d2 - a two sided coin 81 | - Added rotation adjustment for pip symbols 82 | - Added d4c pips and pip symbol option 83 | - Fixed d00 hard coded "0" bug 84 | 85 | ## PolyDiceGenerator v0.26.1 86 | 87 | - Added d00 #10 horizontal push and padding adjustments if using angled numbers 88 | 89 | ## PolyDiceGenerator v0.26.0 90 | 91 | - Changed edge rounding and number spacing to sliders allowing decimal values in Customizer 92 | - Added underscore horizontal push adjustment 93 | - Added corner rounding adjustment 94 | - Added corner clipping adjustment 95 | 96 | ## PolyDiceGenerator v0.25.1 97 | 98 | - Added bosl version echo 99 | - Updated bosl link to installation section 100 | 101 | ## PolyDiceGenerator v0.25.0 102 | 103 | - Renamed project to PolyDiceGenerator 104 | - Pushed project to github 105 | - Created documentation [README.md](README.md) 106 | - Due to recent BOSL2 updates v2.0.402 is currently required 107 | - Readded option for d00 symbols 108 | - Updated kerning to scale as a % of number size 109 | 110 | ## DiceRPGenerator v0.24.3 (2020-08-25) 111 | 112 | - Fixed d12 #8 rotation 113 | 114 | ## DiceRPGenerator v0.24.2 (2020-08-24) 115 | 116 | - Moved number spacing and number 4 horizontal push to per dice settings 117 | - Fixed d6 and d8 kerning 118 | 119 | ## DiceRPGenerator v0.24.1 120 | 121 | - Fixed rotations broken after BOSL2 update - v2.0.402 is required 122 | - Added d6 corner rounding - by Maryanne DellaSalla 123 | - Added selector for "Standard N+1", "DiceLab", and "Custom" distributions 124 | - Workaround for Customizer bug when symbol "undef" entries become quoted 125 | - Symbols now override number values - no need to blank out numbers when adding symbols 126 | - Added adjustments for symbol size, vertical push, and horizontal push 127 | - Added double digit number spacing adjustment...kerning 128 | - Added horizontal number push adjustment 129 | - Added additional horizontal push adjustment for number 4s 130 | - Readded underscore size and push adjustments 131 | 132 | ## DiceRPGenerator v0.23.1 133 | 134 | - Variable rewrite for better Customizer usage 135 | - Fixed symbols as pips Customizer bug 136 | - Added d10 and d00 length adjustment 137 | - Merged Number depth into a single variable 138 | 139 | ## DiceRPGenerator v0.22 140 | 141 | - Updated numbering to DiceLab layout 142 | - Added d4c - a crystal shaped d4 143 | - Added d4c body and point length adjustments 144 | - Added d6 and d00 angled text option 145 | - Added d6 pip option 146 | - Added d6 symbols for pips option 147 | - Added d00 "0" padding and size adjustment if using angled numbers 148 | - Added d00 vertical adjustment if using angled numbers 149 | - Updated underscore size and push to self-adjust with text size - works pretty well depending on font 150 | - Removed d4 and d00 underscores 151 | 152 | ## DiceRPGenerator v0.21 153 | 154 | - By Czertwy 155 | 156 | ## Modified 157 | 158 | - By bronkula 159 | 160 | ## Original 161 | 162 | - By TimEdwards 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | PolyDiceGenerator Copyright (c) 2020, charmaur 4 | All rights reserved. 5 | 6 | BOSL2 Copyright (c) 2017-2020, Revar Desmera 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /images/bumpers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/bumpers.png -------------------------------------------------------------------------------- /images/dice_render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/dice_render.png -------------------------------------------------------------------------------- /images/render_v0.26.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.26.0.png -------------------------------------------------------------------------------- /images/render_v0.26.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.26.2.png -------------------------------------------------------------------------------- /images/render_v0.26.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.26.4.png -------------------------------------------------------------------------------- /images/render_v0.26.8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.26.8.png -------------------------------------------------------------------------------- /images/render_v0.26.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.26.9.png -------------------------------------------------------------------------------- /images/render_v0.27.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.27.4.png -------------------------------------------------------------------------------- /images/render_v0.27.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charmaur/PolyDiceGenerator/8fa6c99e285a2b19dc5c38054f14b568e1da9767/images/render_v0.27.5.png --------------------------------------------------------------------------------