Deposition and Growth

This example demonstrates the qtcad.builder.Builder.deposit and qtcad.builder.Builder.grow operations that both add material to the top of the model.

Initializing the builder

Below, we initialize a new instance of qtcad.builder.Builder and define a couple of simple shapes we are going to use in the following.

from qtcad.builder import Builder, Polygon, Mask, Circle

footprint = Polygon.box(10, 10, name="base").centered()
circle = Circle(name="circ", radius=1).centered()
thin = Polygon.box(10, 0.4, name="thin").centered()
thick = Polygon.box(10, 2, name="thick").centered()

box_mask = Mask("footprint")
box_mask.add_shape(footprint)
box_mask.add_shape(circle)
box_mask.add_shape(thin)
box_mask.add_shape(thick)

builder = Builder().add_mask(box_mask)
builder.print_mask_tree()
Layout
└── Mask 0 "footprint"
    ├── 0  Polygon  "base"
    ├── 1  Circle  "circ"
    ├── 2  Polygon  "thin"
    └── 3  Polygon  "thick"

<qtcad.builder.builder.Builder object at 0x7f17466f12b0>

Base geometry

Next, select the generate a geometry with an uneven surface. We use qtcad.builder.Builder.set_group_name to tell the Builder to assign all subsequently created entities to the physical group "model_base". We also use qtcad.builder.Builder.copy_shape to create two translated versions of the circle shape. Using qtcad.builder.Builder.save_z and qtcad.builder.Builder.set_z we can save and restore the z-coordinate of the top of the "base" volume.

builder = (
    builder.set_group_name("model_base")
    .set_mesh_size(10)
    .use_mask("footprint")
    .use_shape("base")
    .extrude(2)
    .save_z("box_top")
    .use_shape("circ")
    .extrude(1)
)

builder.view(
    surfaces=False,
    volume_labels=True,
    angles=(-45, 0, 45),
    save="figs/deposit_base.svg",
    font_size=15,
    zoom=0.8,
)
depositing
[01:54:57 PM] INFO     Using mask 'footprint'                                                                                                               builder.py:750
              INFO     Extruding shape 0 from mask 'footprint' with name 'base' by 2 at z=0                                                                builder.py:2695
              INFO     Creating new physical group with name 'model_base_bottom'                                                                           builder.py:2482
              INFO     Creating new physical group with name 'model_base_side'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'model_base_top'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'model_base'                                                                                  builder.py:2482
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Extruding shape 1 from mask 'footprint' with name 'circ' by 1 at z=2                                                                builder.py:2695
[01:54:58 PM] INFO     Fragmenting...                                                                                                                    fragmenter.py:234
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 14)]
  warnings.warn(
[01:54:59 PM] INFO     Setting z-coordinate to 3                                                                                                            builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_base.svg              builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17466f12b0>

Deposition

Now we will use qtcad.builder.Builder.deposit to add material to the top surface. Depositing acts like a having “snow” fall on top of the model.

Builder.deposit(height: float, noclip: bool = False) Self

[🏗️ Operation] Deposit the currently selected shape(s) into the model up from the current z coordinate by height length units. This is done by adding new material atop the topmost surfaces of the model. If noclip=True, the new material is deposited on the whole top surface of the model, rather than clipped by the currently selected shapes.

See also grow, etch and cut.

Parameters:
  • height – The distance by which to deposit the shape(s) up into the model.

  • noclip – Whether to deposit the shape without horizontally clipping to the current shapes.

builder.use_shape("thin").group_from_shape().deposit(1)
[01:55:00 PM] INFO     Depositing on top surfaces clipped to shape 2 with name 'thin' by 1                                                                  builder.py:310
              INFO     Creating new physical group with name 'thin_bottom'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'thin_side'                                                                                   builder.py:2482
              INFO     Creating new physical group with name 'thin_top'                                                                                    builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:55:02 PM] INFO     Creating new physical group with name 'thin'                                                                                        builder.py:2482

<qtcad.builder.builder.Builder object at 0x7f17466f12b0>

By default the current shapes are used to horizontally clip the resulting volume:

builder.view(
    surfaces=False,
    volume_labels=True,
    surface_labels=False,
    angles=(-45, 0, 45),
    save="figs/deposit_result.svg",
    font_size=15,
    zoom=0.8,
)
depositing
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_result.svg            builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17466f12b0>

Before continuing, we create a snapshot of the state of builder and Gmsh before depositing the surface. Currently this operation is only available on POSIX compliant systems that support process forking.

builder_snapshot = builder.snapshot()
[01:55:03 PM] INFO     Forking (emulated)                                                                                                             gmsh_executor.py:346

We can also deposit surfaces in the same manner. Here, we use no_clip=True select the whole top surface of the model. This argument exists for all grow and deposit functions.

Builder.deposit_surface(noclip: bool = False) Self

[🏗️ Operation] Add the currently selected shapes as surfaces on top of the model, following the contours of the top surface. If noclip is True, then the surface is not clipped and can extend past the currently selected shapes.

builder.set_group_name("top_surf").deposit_surface(noclip=True).view(
    surfaces=False,
    volume_labels=False,
    surface_labels=True,
    angles=(-45, 0, 45),
    save="figs/deposit_surf_result.svg",
    font_size=15,
    zoom=0.8,
    groups=["top_surf"],
)
depositing
[01:55:06 PM] INFO     Depositing surface on top of the model                                                                                               builder.py:272
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:55:09 PM] INFO     Creating new physical group with name 'top_surf'                                                                                    builder.py:2482
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_surf_result.svg       builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17466f12b0>
# The same operation as above, but clipped to a thicker version of the
# ``"thin"`` shape we use above.


builder_snapshot.use_shape("thick").deposit_surface().view(
    surfaces=False,
    volume_labels=False,
    surface_labels=True,
    angles=(-45, 0, 48),
    save="figs/deposit_surf_result_clip.svg",
    font_size=15,
    zoom=0.8,
    groups=["thick"],
).finalize()
depositing
[01:55:11 PM] INFO     Depositing surface from shape 3 with name 'thick'                                                                                    builder.py:278
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:55:15 PM] INFO     Creating new physical group with name 'thick'                                                                                       builder.py:2482
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_surf_result_clip.svg  builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f1746576350>

Growing

Another option to add material on top of the model is to “grow” it by adding a layer of uniform thickness atop the model. Again, the created volumes can be optionally clipped by the selected shapes.

Builder.grow(thickness: float, noclip: bool = False, exact: bool = False, num_tries: int = 10, i_know_what_im_doing: bool = False, polygonize: bool = True) Self

[🏗️ Operation] Emulate a physical ‘growth’ operation to add material to the current topmost volumes. In contrast to deposit, this will not only add material to the top of the volumes but also to their sides. The currently active shapes are used to horizontally clip the new volumes unless noclip is True.

The resulting surfaces are labelled similar to the extrude command. However, there is some ambiguity as to what surfaces are to be considered to be on top and what surfaces are on the side. Here, we choose surfaces whose (average) normal vector projected on the x-y plane is 10% larger than its projection on the z-axis to be on the “side”. Surfaces can be created/renamed using assign_to_group, add_surface or deposit_surface.

Warning

Due to problems in OpenCASCADE, this operation might fail. If it does it may help to retry with a different thickness or polygonize=False. The warning can be suppressed with i_know_what_im_doing=True.

Parameters:
  • thickness – The distance by which to grow the selected shapes. Must be positive.

  • noclip – If True, do not clip the created volumes horizontally.

  • exact – If exact=True do not try to slightly adjust the growth thickness in case growth fails and crash straightaway.

  • num_tries – If exact=False, try up to num_tries times to successively slightly reduce the thickness (by 0.1% each time) and retry.

  • i_know_what_im_doing – If False, a warning will be issued because this operation is known to be somewhat unstable due to problems in OpenCASCADE.

  • polygonize – If True, the newly created volumes will be recreated from their hull meshes to avoid problems with curved OpenCASCADE solids (default is True).

First we reset the model to our earlier initial condition:

builder = (
    Builder()
    .add_mask(box_mask)
    .set_group_name("model_base")
    .set_mesh_size(10)
    .use_mask("footprint")
    .use_shape("base")
    .extrude(2)
    .save_z("box_top")
    .use_shape("circ")
    .extrude(1)
)
[01:55:18 PM] INFO     Using mask 'footprint'                                                                                                               builder.py:750
              INFO     Extruding shape 0 from mask 'footprint' with name 'base' by 2 at z=0                                                                builder.py:2695
              INFO     Creating new physical group with name 'model_base_bottom'                                                                           builder.py:2482
              INFO     Creating new physical group with name 'model_base_side'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'model_base_top'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'model_base'                                                                                  builder.py:2482
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Extruding shape 1 from mask 'footprint' with name 'circ' by 1 at z=2                                                                builder.py:2695
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 14)]
  warnings.warn(
              INFO     Setting z-coordinate to 3                                                                                                            builder.py:849

Then, we apply the growth operation with the clipping shape "thick".

builder.set_group_name("growth").use_shape("thin").grow(0.2).view(
    surfaces=False,
    volume_labels=True,
    surface_labels=False,
    angles=(-45, 0, 45),
    save="figs/deposit_groth.svg",
    font_size=15,
    zoom=0.8,
)
depositing
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:376: UserWarning: Due to problems in OpenCASCADE, the grow operation might fail.
If it does it may help to retry with a different thickness or
.``polygonize=False``.
  warnings.warn(
              INFO     Growing top surfaces clipped to shape 2 with name 'thin' by 0.2                                                                     builder.py:3386
[01:55:19 PM] INFO     Creating new physical group with name 'growth_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'growth_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'growth_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:55:20 PM] INFO     Creating new physical group with name 'growth'                                                                                      builder.py:2482
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_groth.svg             builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f1746575090>

The volume that has been added is a film of thickness .5:

builder.view(
    surfaces=False,
    surface_labels=True,
    angles=(-45, 0, 45),
    save="figs/deposit_groth_only.svg",
    font_size=15,
    zoom=0.8,
    groups=["growth"],
)
depositing
[01:55:21 PM] INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_groth_only.svg        builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f1746575090>

Again, the noclip argument allows to grow the whole top surface at once. (Note that in this case clipping to the "base" shape would be closer to our intentions.) We also use polygonize=False to keep the curved surfaces of the grown volume.

(
    builder.set_group_name("growth_noclip")
    .grow(0.5, noclip=True, polygonize=False)
    .view(
        surfaces=False,
        volume_labels=True,
        surface_labels=False,
        angles=(-45, 0, 45),
        save="figs/deposit_groth_noclip.svg",
        font_size=15,
        zoom=0.8,
    )
)
depositing
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:376: UserWarning: Due to problems in OpenCASCADE, the grow operation might fail.
If it does it may help to retry with a different thickness or
.``polygonize=False``.
  warnings.warn(
[01:55:22 PM] INFO     Growing top surfaces by 0.5                                                                                                         builder.py:3391
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3399: UserWarning: Intersection - BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection
  to_grow, merged_boxes = self._get_top_volume(offset)
[01:56:09 PM] INFO     Creating new physical group with name 'growth_noclip_bottom'                                                                        builder.py:2482
              INFO     Creating new physical group with name 'growth_noclip_side'                                                                          builder.py:2482
              INFO     Creating new physical group with name 'growth_noclip_top'                                                                           builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:56:19 PM] INFO     Creating new physical group with name 'growth_noclip'                                                                               builder.py:2482
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_groth_noclip.svg      builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f1746575090>

Note

During the execution of the grow command you may encounter a log message like:

Failed to grow. Shrinking by 0.1%

OpenCASCADE, the CAD kernel that Builder uses through Gmsh, sometimes fails to grow volumes but succeed when the thickness is changed slightly. If you encounter this message and a subsequent crash, trying another growth size is a good first troubleshooting step.

If you want builder to crash straightaway rather than trying to change the thickness, you can pass exact=True.

The grown volume in isolation is:

builder.view(
    surfaces=False,
    volume_labels=True,
    surface_labels=False,
    angles=(-45, 0, 45),
    save="figs/deposit_groth_noclip_only.svg",
    font_size=15,
    zoom=0.8,
    groups=["growth_noclip"],
)
depositing
[01:56:22 PM] INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/deposit_groth_noclip_only.svg builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f1746575090>

Total running time of the script: (1 minutes 28.753 seconds)

Gallery generated by Sphinx-Gallery