{"id":42419304,"url":"https://github.com/louiscarl/pysomo","last_synced_at":"2026-01-28T02:53:46.695Z","repository":{"id":57456430,"uuid":"293107777","full_name":"louiscarl/pysomo","owner":"louiscarl","description":"A small solid modeling library. With pysomo you can create 3D models in Python and export them to various 3D file formats. https://pypi.org/project/pysomo/","archived":false,"fork":false,"pushed_at":"2020-09-08T04:52:43.000Z","size":1181,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-26T21:57:36.933Z","etag":null,"topics":["3d","cad","csg","modeling","obj","solid","stl"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/louiscarl.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-05T15:58:03.000Z","updated_at":"2023-04-15T19:47:52.000Z","dependencies_parsed_at":"2022-09-14T05:10:52.744Z","dependency_job_id":null,"html_url":"https://github.com/louiscarl/pysomo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/louiscarl/pysomo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louiscarl%2Fpysomo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louiscarl%2Fpysomo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louiscarl%2Fpysomo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louiscarl%2Fpysomo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/louiscarl","download_url":"https://codeload.github.com/louiscarl/pysomo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louiscarl%2Fpysomo/sbom","scorecard":{"id":599668,"data":{"date":"2025-08-11","repo":{"name":"github.com/louiscarl/pysomo","commit":"1a45c53cb7d0c3a75fb6938e56cade3176a1f07f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/15 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/python-app.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-app.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/louiscarl/pysomo/python-app.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-app.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/louiscarl/pysomo/python-app.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/python-app.yml:25","Warn: pipCommand not pinned by hash: .github/workflows/python-app.yml:26","Warn: pipCommand not pinned by hash: .github/workflows/python-app.yml:27","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T00:06:02.044Z","repository_id":57456430,"created_at":"2025-08-21T00:06:02.045Z","updated_at":"2025-08-21T00:06:02.045Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28835330,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T02:10:51.810Z","status":"ssl_error","status_checked_at":"2026-01-28T02:10:50.806Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","cad","csg","modeling","obj","solid","stl"],"created_at":"2026-01-28T02:53:46.533Z","updated_at":"2026-01-28T02:53:46.681Z","avatar_url":"https://github.com/louiscarl.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pysomo\n\n![PyPI version](https://img.shields.io/pypi/pyversions/pysomo)\n![Tests](https://img.shields.io/github/workflow/status/louiscarl/pysomo/Python%20application?label=tests)\n\n_pysomo_ (for **so**lid **mo**deling) is a small solid modeling library. This library allows you to create 3D models in Python and export them to various 3D file formats.\n\n## Requirements\n_pysomo_ creates an _xcsg_ file, an XML file that can be parsed by the _xcsg_ application to create export your models in various file formats. _pysomo_ does not include _xcsg_, it can be downloaded separately: [download xcsg](https://github.com/arnholm/xcsg/releases).\n\n## Installation\n1. To install, simply run `pip install pysomo`\n2. To export from xcsg format to a 3D model file, the [_xcsg_ application](https://github.com/arnholm/xcsg) must be in the same directory as your application.\n\n## Examples\n\n### Extrusion\nThe following code creates a coin with a square hole in the middle.\n```python\nimport pysomo as somo\n\n\n# First let's create the round part of the coin.\ncoin_circle = somo.Circle(30)\n# This creates the base solid of the coin.\ncoin = coin_circle.linear_extrude(2)\n# Create the solid to use as the extrusion. Note that we use the offset method\n# to create a smaller circle from the base. This will give us the rim.\ncoin_extr = coin_circle.offset(-5, True).linear_extrude(1)\n\n\n# Now we use the subtraction operator to exclude our shapes from the coin.\ncoin = coin - coin_extr.translate(0, 0, 1.5) - coin_extr.translate(0, 0, -0.5)\n\n\n# Let's now create the square hole in the coin.\nsquare = somo.Square(20)\nsquare_rim = square.linear_extrude(2)\nsquare_hole = square.offset(-2, False).linear_extrude(2)\n\n\n# Our final coin is the base coin with a square removed.\ncoin = coin + square_rim - square_hole\n\n\n# Now we export to a file. The Root is responsible for building the xcsg file.\nroot = somo.Root(coin)\n# The Exporter reads the root file and uses the xcsg application.\nsomo.Exporter(r\"coin.obj\").export_obj(root)\n\n```\nThis creates the coin below.\n\n![Extrusion solid](https://github.com/louiscarl/pysomo/raw/master/img/coin.png \"Extrusion of a square from a larger circle.\")\n\n### Stairs\nThe following code builds a staircase up to the maximum height allowed by a building code, if we were to use the maximum step height allowed. It demonstrates modelisation via code. It also demonstrates that figures do not mutate when an operation is applied. Instead, every operation returns a new figure.\n```python\nimport pysomo as somo\n\n\ndef to_meters(inches):\n    return 0.0254 * inches\n\n\n# Let's say these are the building code requirements for stairs, in inches.\nmin_clear_width = to_meters(3 * 12)\nmax_stair_height = to_meters(163)\nmax_riser_height = to_meters(7 + 1 / 2)\nmin_tread_depth = to_meters(11)\n\n\n# Build the staircase steps by union of every step.\nstep = somo.Cuboid(\n    min_clear_width,\n    max_riser_height,\n    min_tread_depth,\n    center='false')\nsteps = step  # Initially, the stairs are a single step\nstep_count = 1  # Number of steps in the stairs\n\n\n# Let's keep adding steps while the height is code compliant.\nwhile (step_count + 1) * max_riser_height \u003c max_stair_height:\n    # Note that every operation in fact returns a new solid and does not\n    # modify the original step, so you can reuse solids for every translation.\n    steps += step.translate(\n        0,\n        max_riser_height * step_count,\n        min_tread_depth * step_count)\n    step_count += 1\n\n\n# Build the stringers. Vertices are calculated from the steps we built above.\nstairs_height = max_riser_height * step_count\nstairs_depth = min_tread_depth * step_count\nvertices = [\n    (min_clear_width, 0, 0),\n    (min_clear_width, 0, min_tread_depth),\n    (min_clear_width, stairs_height, stairs_depth),\n    (min_clear_width, stairs_height - max_riser_height, stairs_depth),\n    (0, 0, 0),\n    (0, 0, min_tread_depth),\n    (0, stairs_height, stairs_depth),\n    (0, stairs_height - max_riser_height, stairs_depth),\n]\nstringers = somo.Polyhedron(vertices)\n\n\n# The union of steps and stringers creates the stairwell.\nroot = somo.Root(steps + stringers)\n\n\n# Export to obj format.\nsomo.Exporter(r\"stairs.obj\").export_obj(root)\n```\nThis creates the staircase below.\n\n![Stairs](https://github.com/louiscarl/pysomo/raw/master/img/stairs.png \"Generated stairs at a compliant height.\")\n\nAn advantage in this style of 3d modeling is the simplicity of changing your models through variables. Let's say we added a zero to the maximum height allowed:\n\n![Stairs](https://github.com/louiscarl/pysomo/raw/master/img/superstairs.png \"Generated staircase that is way too high.\")\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flouiscarl%2Fpysomo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flouiscarl%2Fpysomo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flouiscarl%2Fpysomo/lists"}