# Bumo
**Bu**ild123d **m**utables **o**bjects

## Introduction
Bumo is a Python package that work with the [Build123d](https://github.com/gumyr/build123d) CAD library.
It essentially consists in a new class `Builder` used to update a CAD model: instead of creating a new instance of a CAD object each time an operation is made, the builder mutates to its new shape.
This slight difference on the behavior allows to:
- get more intuitive results when altering object attributes or when working with class inheritance;
- keep track of all the CAD object history, which can be used for various features, such as faces coloration based on operations.
The following instructions assumes you already know the basics of Build123d: if necessary please take a look at the [Build123d docs](https://build123d.readthedocs.io/en/latest/) before to continue.
## Installation
This package [is registred on Pypi](https://pypi.org/project/bumo/), so you can either install it with Poetry:
poetry add bumo
or with pip:
pip install bumo
## Getting started
### Simple example
Let's start with some basic operations:
```py
import build123d as _
from ocp_vscode import show_object
from bumo import Builder
b = Builder()
b += _.Box(8, 8, 2)
b -= _.Cylinder(2, 15)
b *= _.Rotation(0, 25, 0)
b &= _.Cylinder(4, 8)
show_object(b(), clear=True)
```
Wich will produce this:

For now there are no big differences here compared to the classical way to use Build123d, but let's analyze these 4 parts anyway:
1. **Imports**: respectively, the Build123d CAD library, the [ocp-vscode viewer](https://github.com/bernhard-42/vscode-ocp-cad-viewer/issues), and Bumo (I have a personal preference for named imports over wildcard imports, but do as you wish);
2. **Builder instantiation**;
3. **Applying mutations**: respectively, `fuse`, `substract`, `move`, and `intersect` (note that their counterparts `+`, `-`, `*`, `&` are not available);
4. **Show**: using ocp-vscode here, but any viewer should work (note that we must call the builder (`b()`) when passing it to the show function).
Parts 1. and 4. will always be the same here, so let's ignore them and focus on the builder-related stuff for the next examples.
### Listing mutations
You can print the list of mutations and their properties:
```py
b.info()
```
This will produce a table like this:

There is one row per muation, and their colors match the faces colors, which is convenient to quicly make a link between the operations and the altered faces.
Column details:
- **Idx**: mutation index;
- **Id**: mutation id;
- **Type**: operation type;
- **F+**, **F~**, **F-**: amount of added/altered/removed faces on this mutation;
- **E+**, **E~**, **E-**: amount of added/altered/removed edges on this mutation;
### Listing shapes
The info method is accessible from any mutation shapes attribute (`faces_altered`, `edges_removed`, etc.), for instance:
```py
b = Builder()
b += _.Box(1, 2, 3)
b.last.faces_added.info()
```
Will produce:
```
╒════════╤════════════════╤════════╤════════════════╤═══════════════╕
│ Hash │ Type │ Area │ Position │ Orientation │
╞════════╪════════════════╪════════╪════════════════╪═══════════════╡
│ 634e76 │ GeomType.PLANE │ 6 │ [-0.5, -1, -2] │ [-0, 0, -0] │
│ 75e78b │ GeomType.PLANE │ 6 │ [-0.5, -1, -2] │ [-0, 0, -0] │
│ fdc160 │ GeomType.PLANE │ 3 │ [-0.5, -1, -2] │ [-0, 0, -0] │
│ 7843d9 │ GeomType.PLANE │ 3 │ [-0.5, -1, -2] │ [-0, 0, -0] │
│ fc164c │ GeomType.PLANE │ 2 │ [-0.5, -1, -2] │ [-0, 0, -0] │
│ f6c299 │ GeomType.PLANE │ 2 │ [-0.5, -1, -2] │ [-0, 0, -0] │
╘════════╧════════════════╧════════╧════════════════╧═══════════════╛
```
### Extended syntax
The example above could also have been written like this:
```py
b = Builder()
b.add(_.Box(8, 8, 2))
b.sub(_.Cylinder(2, 15))
b.move(_.Rotation(0, 25, 0))
b.intersect(_.Cylinder(4, 8))
```
This syntax allows to store the mutation itself into an object for later use.
### Reusing mutations
`Mutation` objects can be used to retrieve the added, altered, removed and untouched faces or edges on this mutation (for instance when working with fillets and chamfers), and they can be accessed either with:
- the return value of a mutation (ex. `hole = b.sub()`);
- querrying a builder index (ex. `b[2]`);
- using the last attribute (ex. `b.last`);
```py
b = Builder()
b.add(_.Box(12, 12, 2))
b.add(_.Box(8, 8, 4))
b.fillet(b.last.edges_added(), 0.4)
hole = b.sub(_.Cylinder(3, 4))
b.chamfer(hole.edges_added()[0], 0.3)
```

### Using the debug mode
You can turn one or several mutations in debug mode, so all the other faces will be translucent, either by:
- passing DEBUG to the `mode` argument of a mutation method (ex: `b.add(..., mode=DEBUG)`);
- passing DEBUG to mutation assignment operator (ex: `b += ..., DEBUG`);
- passing faces (even removed ones) to the debug method (ex: `b.debug(...)`).
```py
from bumo import Builder, DEBUG
b += _.Box(12, 12, 2)
b += _.Box(8, 8, 4)
# b += _.Box(8, 8, 4), DEBUG
b.fillet(b.last.edges_added, 0.4)
hole = b.sub(_.Cylinder(3, 4))
b.chamfer(hole.edges_added[0], 0.3, mode=DEBUG)
b.debug(b[2].faces_altered[0])
# b.debug(hole.faces_removed())
```

### Changing colors
By default, mutations are colored using a color palette. Using the same `mode` used earlier, you can pass a specific color instead of the auto-generated-one:
```py
b = Builder()
b += _.Box(12, 12, 2), "orange"
b += _.Box(8, 8, 4), "green"
b += _.Cylinder(3, 4), "violet"
```

### Mutation with builders
If necessary it is possible to pass an other builder to a mutation:
```py
b = Builder()
b.add(_.Box(12, 12, 2))
obj2 = Builder()
obj2.add(_.Box(8, 8, 4))
b.add(obj2)
b.sub(_.Cylinder(3, 4))
```
### Configuring the builder
You can configure Bumo according to your needs:
```py
from bumo import config
config.DEBUG_ALPHA = 0.5
config.COLOR_PALETTE = ColorPalette.INFERNO
```
Options are:
- **COLOR_PALETTE**: The color palette to use when auto_color is enabled (`ColorPalette.VIRIDIS`);
- **DEBUG_ALPHA**: The alpha value used for translucent shapes in debug mode (`0.2`);
- **DEFAULT_COLOR**: The default color to be used when a color is passed to a mutation (`Color("orange")`);
- **DEFAULT_DEBUG_COLOR**: The default color to be used when using the debug mode (default: `Color("red")`);
- **INFO_COLOR**: Set to False to disable terminal colors in the info table (default: `True`);
- **INFO_TABLE_FORMAT** = The [table format](https://github.com/astanin/python-tabulate?tab=readme-ov-file#table-format) used in the info table (default: `"fancy_outline"`);
- **COLUMNS_MUTATIONS**: The columns to display in mutations info tables, among: idx, label, type, color_hex, color_name, f+, f~, f-, e+, e~, e- (default: `["idx", "label", "type", "f+", "f~", "f-", "e+", "e~", "e-"]`);
- **COLUMNS_SHAPES**: The columns to display in shapes info tables, among: hash, type, area, color_hex, color_name. (default: `["hash", "type", "area", "position", "orientation"]`).
Raw data
{
"_id": null,
"home_page": "https://codeberg.org/roipoussiere/bumo",
"name": "bumo",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.13,>=3.11",
"maintainer_email": null,
"keywords": "cad, build123d",
"author": "Nathana\u00ebl Jourdane",
"author_email": "njourdane@protonmail.com",
"download_url": "https://files.pythonhosted.org/packages/d3/6a/05181bdfca0877760532d9e3e21787e0e85274725c995c6f5a93ccd43ffc/bumo-0.1.3.tar.gz",
"platform": null,
"description": "# Bumo\n\n**Bu**ild123d **m**utables **o**bjects\n\n\n\n## Introduction\n\nBumo is a Python package that work with the [Build123d](https://github.com/gumyr/build123d) CAD library.\n\nIt essentially consists in a new class `Builder` used to update a CAD model: instead of creating a new instance of a CAD object each time an operation is made, the builder mutates to its new shape.\n\nThis slight difference on the behavior allows to:\n- get more intuitive results when altering object attributes or when working with class inheritance;\n- keep track of all the CAD object history, which can be used for various features, such as faces coloration based on operations.\n\nThe following instructions assumes you already know the basics of Build123d: if necessary please take a look at the [Build123d docs](https://build123d.readthedocs.io/en/latest/) before to continue.\n\n## Installation\n\nThis package [is registred on Pypi](https://pypi.org/project/bumo/), so you can either install it with Poetry:\n\n poetry add bumo\n\nor with pip:\n\n pip install bumo\n\n## Getting started\n\n### Simple example\n\nLet's start with some basic operations:\n\n```py\nimport build123d as _\nfrom ocp_vscode import show_object\nfrom bumo import Builder\n\nb = Builder()\n\nb += _.Box(8, 8, 2)\nb -= _.Cylinder(2, 15)\nb *= _.Rotation(0, 25, 0)\nb &= _.Cylinder(4, 8)\n\nshow_object(b(), clear=True)\n```\n\nWich will produce this:\n\n\n\nFor now there are no big differences here compared to the classical way to use Build123d, but let's analyze these 4 parts anyway:\n\n1. **Imports**: respectively, the Build123d CAD library, the [ocp-vscode viewer](https://github.com/bernhard-42/vscode-ocp-cad-viewer/issues), and Bumo (I have a personal preference for named imports over wildcard imports, but do as you wish);\n2. **Builder instantiation**;\n3. **Applying mutations**: respectively, `fuse`, `substract`, `move`, and `intersect` (note that their counterparts `+`, `-`, `*`, `&` are not available);\n4. **Show**: using ocp-vscode here, but any viewer should work (note that we must call the builder (`b()`) when passing it to the show function).\n\nParts 1. and 4. will always be the same here, so let's ignore them and focus on the builder-related stuff for the next examples.\n\n### Listing mutations\n\nYou can print the list of mutations and their properties:\n\n```py\nb.info()\n```\n\nThis will produce a table like this:\n\n\n\nThere is one row per muation, and their colors match the faces colors, which is convenient to quicly make a link between the operations and the altered faces.\n\nColumn details:\n- **Idx**: mutation index;\n- **Id**: mutation id;\n- **Type**: operation type;\n- **F+**, **F~**, **F-**: amount of added/altered/removed faces on this mutation;\n- **E+**, **E~**, **E-**: amount of added/altered/removed edges on this mutation;\n\n### Listing shapes\n\nThe info method is accessible from any mutation shapes attribute (`faces_altered`, `edges_removed`, etc.), for instance:\n\n```py\nb = Builder()\nb += _.Box(1, 2, 3)\nb.last.faces_added.info()\n```\n\nWill produce:\n\n```\n\u2552\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2555\n\u2502 Hash \u2502 Type \u2502 Area \u2502 Position \u2502 Orientation \u2502\n\u255e\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561\n\u2502 634e76 \u2502 GeomType.PLANE \u2502 6 \u2502 [-0.5, -1, -2] \u2502 [-0, 0, -0] \u2502\n\u2502 75e78b \u2502 GeomType.PLANE \u2502 6 \u2502 [-0.5, -1, -2] \u2502 [-0, 0, -0] \u2502\n\u2502 fdc160 \u2502 GeomType.PLANE \u2502 3 \u2502 [-0.5, -1, -2] \u2502 [-0, 0, -0] \u2502\n\u2502 7843d9 \u2502 GeomType.PLANE \u2502 3 \u2502 [-0.5, -1, -2] \u2502 [-0, 0, -0] \u2502\n\u2502 fc164c \u2502 GeomType.PLANE \u2502 2 \u2502 [-0.5, -1, -2] \u2502 [-0, 0, -0] \u2502\n\u2502 f6c299 \u2502 GeomType.PLANE \u2502 2 \u2502 [-0.5, -1, -2] \u2502 [-0, 0, -0] \u2502\n\u2558\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255b\n```\n\n### Extended syntax\n\nThe example above could also have been written like this:\n\n```py\nb = Builder()\nb.add(_.Box(8, 8, 2))\nb.sub(_.Cylinder(2, 15))\nb.move(_.Rotation(0, 25, 0))\nb.intersect(_.Cylinder(4, 8))\n```\n\nThis syntax allows to store the mutation itself into an object for later use.\n\n### Reusing mutations\n\n`Mutation` objects can be used to retrieve the added, altered, removed and untouched faces or edges on this mutation (for instance when working with fillets and chamfers), and they can be accessed either with:\n- the return value of a mutation (ex. `hole = b.sub()`);\n- querrying a builder index (ex. `b[2]`);\n- using the last attribute (ex. `b.last`);\n\n```py\nb = Builder()\nb.add(_.Box(12, 12, 2))\nb.add(_.Box(8, 8, 4))\nb.fillet(b.last.edges_added(), 0.4)\nhole = b.sub(_.Cylinder(3, 4))\nb.chamfer(hole.edges_added()[0], 0.3)\n```\n\n\n\n### Using the debug mode\n\nYou can turn one or several mutations in debug mode, so all the other faces will be translucent, either by:\n\n- passing DEBUG to the `mode` argument of a mutation method (ex: `b.add(..., mode=DEBUG)`);\n- passing DEBUG to mutation assignment operator (ex: `b += ..., DEBUG`);\n- passing faces (even removed ones) to the debug method (ex: `b.debug(...)`).\n\n```py\nfrom bumo import Builder, DEBUG\n\nb += _.Box(12, 12, 2)\nb += _.Box(8, 8, 4)\n# b += _.Box(8, 8, 4), DEBUG\nb.fillet(b.last.edges_added, 0.4)\nhole = b.sub(_.Cylinder(3, 4))\nb.chamfer(hole.edges_added[0], 0.3, mode=DEBUG)\nb.debug(b[2].faces_altered[0])\n# b.debug(hole.faces_removed())\n```\n\n\n\n### Changing colors\n\nBy default, mutations are colored using a color palette. Using the same `mode` used earlier, you can pass a specific color instead of the auto-generated-one:\n\n```py\nb = Builder()\nb += _.Box(12, 12, 2), \"orange\"\nb += _.Box(8, 8, 4), \"green\"\nb += _.Cylinder(3, 4), \"violet\"\n```\n\n\n\n### Mutation with builders\n\nIf necessary it is possible to pass an other builder to a mutation:\n\n```py\nb = Builder()\nb.add(_.Box(12, 12, 2))\n\nobj2 = Builder()\nobj2.add(_.Box(8, 8, 4))\n\nb.add(obj2)\nb.sub(_.Cylinder(3, 4))\n```\n\n### Configuring the builder\n\nYou can configure Bumo according to your needs:\n\n```py\nfrom bumo import config\n\nconfig.DEBUG_ALPHA = 0.5\nconfig.COLOR_PALETTE = ColorPalette.INFERNO\n```\n\nOptions are:\n\n- **COLOR_PALETTE**: The color palette to use when auto_color is enabled (`ColorPalette.VIRIDIS`);\n- **DEBUG_ALPHA**: The alpha value used for translucent shapes in debug mode (`0.2`);\n- **DEFAULT_COLOR**: The default color to be used when a color is passed to a mutation (`Color(\"orange\")`);\n- **DEFAULT_DEBUG_COLOR**: The default color to be used when using the debug mode (default: `Color(\"red\")`);\n- **INFO_COLOR**: Set to False to disable terminal colors in the info table (default: `True`);\n- **INFO_TABLE_FORMAT** = The [table format](https://github.com/astanin/python-tabulate?tab=readme-ov-file#table-format) used in the info table (default: `\"fancy_outline\"`);\n- **COLUMNS_MUTATIONS**: The columns to display in mutations info tables, among: idx, label, type, color_hex, color_name, f+, f~, f-, e+, e~, e- (default: `[\"idx\", \"label\", \"type\", \"f+\", \"f~\", \"f-\", \"e+\", \"e~\", \"e-\"]`);\n- **COLUMNS_SHAPES**: The columns to display in shapes info tables, among: hash, type, area, color_hex, color_name. (default: `[\"hash\", \"type\", \"area\", \"position\", \"orientation\"]`).\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "BUild123d Mutables Objects",
"version": "0.1.3",
"project_urls": {
"Homepage": "https://codeberg.org/roipoussiere/bumo",
"Repository": "https://codeberg.org/roipoussiere/bumo"
},
"split_keywords": [
"cad",
" build123d"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2085ba897b0bcdc5882f23982a6408583929259d2d0b13d0e55141b0e74921b4",
"md5": "3260174e7dbde747311bd9672d94aee0",
"sha256": "1dd6e702d3b37c540c74323701b286748e4abc980a5365bb5b9b9304b5e8b2f0"
},
"downloads": -1,
"filename": "bumo-0.1.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3260174e7dbde747311bd9672d94aee0",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.13,>=3.11",
"size": 17981,
"upload_time": "2025-01-22T22:43:43",
"upload_time_iso_8601": "2025-01-22T22:43:43.006788Z",
"url": "https://files.pythonhosted.org/packages/20/85/ba897b0bcdc5882f23982a6408583929259d2d0b13d0e55141b0e74921b4/bumo-0.1.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d36a05181bdfca0877760532d9e3e21787e0e85274725c995c6f5a93ccd43ffc",
"md5": "2876c118a3adee0ccd23782885fa326b",
"sha256": "ed5d7190fd463427616fdac03a97bb6757eea382ad01822dc283845eab17a448"
},
"downloads": -1,
"filename": "bumo-0.1.3.tar.gz",
"has_sig": false,
"md5_digest": "2876c118a3adee0ccd23782885fa326b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.13,>=3.11",
"size": 19106,
"upload_time": "2025-01-22T22:43:45",
"upload_time_iso_8601": "2025-01-22T22:43:45.050846Z",
"url": "https://files.pythonhosted.org/packages/d3/6a/05181bdfca0877760532d9e3e21787e0e85274725c995c6f5a93ccd43ffc/bumo-0.1.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-22 22:43:45",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": true,
"codeberg_user": "roipoussiere",
"codeberg_project": "bumo",
"lcname": "bumo"
}