Defining and Importing Shapes

This example shows how to manually define two-dimensional shapes, how to import shapes import shapes from OASIS layout files and how modify those shapes.

Imports

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

Masks

A qtcad.builder.Mask is simply a collection of two dimensional shapes with a name and a numerical id. Grouping shapes together in a mask is useful if those shapes will often be used at once. For example in the Adding the gate surfaces in the Gated Quantum Dot tutorial we put all the gate surfaces into one mask and then added them all at once.

class Mask(name: str | None, shapes: list[~qtcad.builder.masks.Shape] = <factory>, id: int = -1)

A collection of multiple Shape instances.

Parameters:
  • name – Optional name of the mask.

  • shapes – Shapes in the mask (optional, default is an empty list).

  • index – Index of the mask. (optional, will be overwritten by qtcad.builder.Builder if -1)

As we see above, we could specify the shapes upon the creation of the mask. Or we could add them later on using Mask.add_shape and Mask.add_shapes.

Let us define a mask that will contain the footprint of our device.

example_mask = Mask(name="example")

Note that we don’t provide an id as qtcad.builder.Builder will take care of assigning a unique one.

Shapes

All shapes are subclasses of the Shape class which provides a common set of properties (such as a name), as well as transformations.

class Shape(name: str | None = '__unset')

An abstract class representing a shape.

Currently implemented are:
auto_name() Self

Automatically set the name of this shape by inspecting the stack trace and looking for an assignment to a variable of this type. If no such assignment can be found, the name is not set.

property barycenter: tuple[float, float]

Returns: The mean point of the shape.

property bbox: tuple[float, float, float, float]

Returns: The bounding box of the shape as a four-tuple (left, right, bottom, top).

centered() Shape
Returns:

A copy of this shape centered at its center point. The result includes all sub-shapes with similar transformations.

id: int = -1

The index of the shape.

Automatically assigned by qtcad.builder.masks.Mask.

name: str | None = '__unset'

The name of the shape.

If no name is specified, it will be automatically assigned by inspecting the stack trace and looking for an assignment to a variable of this type. See also Shape.auto_name.

named(name: str | None) Shape
Returns:

A copy of this shape that has name as the shape’s name.

point_inside(point: tuple[float, float]) bool

Check if a point is inside this shape.

Parameters:

point – The point to check.

Returns:

True if the point is inside the shape, False otherwise.

rotated(angle: float, center: tuple[float, float] | None = None) Shape
Returns:

A copy of this shape rotated by angle degrees around center or around its barycenter if center is None.

scaled(factor: float) Shape
Returns:

A copy of this shape scaled by factor.

transform(translate: tuple[float, float] = (0.0, 0.0), rotate: float | tuple[float, tuple[float, float] | None] = 0.0, scale: float = 1, center: bool = False, name: str | None = '__unset', transformer: Callable[[Shape], Shape] | None = None) Shape

Apply a series of transformations to this shape and return the resulting shape.

Parameters:
  • translate – A tuple (dx, dy) specifying the translation in x and y direction. See qtcad.builder.Shape.translated().

  • rotate – The rotation angle in degrees. If a tuple (angle, center) is given, the shape is rotated by angle degrees around center. If center is None, the shape is rotated around its barycenter. See qtcad.builder.Shape.rotated().

  • scale – The scaling factor. See qtcad.builder.Shape.scaled().

  • center – Whether to center the shape at its barycenter. See qtcad.builder.Shape.centered().

  • name – The name of the new shape. If "__unset", the name will be copied from the original shape. If None, the new shape will be unnamed. See qtcad.builder.Shape.named(). transformer: An optional callable that takes a shape and returns a transformed shape. This is applied last.

translated(dx: float, dy: float) Shape
Returns:

A copy of this shape translated by dx units in the x direction and dy units in the y direction.

Polygons

A qtcad.builder.Polygon is simply a collection of hull points with optional holes:

class Polygon(name: str | None = '__unset', hull: list[tuple[float, float]] = <factory>, holes: list[~qtcad.builder.masks.Polygon] = <factory>)

A polygon with an hull (outline) defined by the points hull and holes that are themselves polygons.

For the most basic form of usage we can create a polygon just by supplying points:

hole = Polygon(hull=[(0, 0), (0, 1), (1, 1)]).centered()
[01:58:58 PM] INFO     Auto-named Polygon instance to 'hole'                                                                                                  masks.py:208

The above creates a polygon named “hole”, which just happens to be a triangle. We can then take this polygon as the template to create a bigger triangle with the “hole” shape as a hole. For convenience we used the Shape.centered method to center the polygon.

triangle = Polygon(hull=hole.scaled(2).hull, holes=[hole])
print(triangle.name)
              INFO     Auto-named Polygon instance to 'triangle'                                                                                              masks.py:208
triangle

Note that triangle was automatically named based on the name of the variable it was assigned to above.

We can then add our shape to the example mask and load that mask into a qtcad.builder.Builder instance. As a demonstration, we extrude a surface based on that shape:

example_mask.add_shape(triangle)

Builder().add_mask(example_mask).extrude(2).view(
    surfaces=True,
    volume_labels=False,
    angles=(-45, 0, 0),
    save="figs/triangle_hole.svg",
    zoom=0.8,
).finalize()
shapes
[01:58:59 PM] INFO     Extruding shape 0 from mask 'example' with name 'triangle' by 2 at z=0                                                              builder.py:2695
              INFO     Creating new physical group with name 'triangle_bottom'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'triangle_side'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'triangle_top'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'triangle'                                                                                    builder.py:2482
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/triangle_hole.svg             builder.py:1863

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

There are several constructors available to create special polygons.

First, there is qtcad.builder.Polygon.box`:

box(width: float, height: float, *args, **kwargs) Polygon

Create a polygon box with width width and height the height with the lower left corner at (0, 0). The rest arguments and keywords are passed on the constructor of qtcad.builder.masks.Polygon.

box = Polygon.box(2, 3).centered().translated(3, 0)
example_mask.add_shape(box)

Builder().add_mask(example_mask).extrude(2).view(
    surfaces=True,
    volume_labels=True,
    angles=(-45, 0, 0),
    save="figs/triangle_and_box.svg",
    font_size=20,
).finalize()
shapes
[01:59:00 PM] INFO     Auto-named Polygon instance to 'box'                                                                                                   masks.py:208
[01:59:01 PM] INFO     Extruding shape 0 from mask 'example' with name 'triangle' by 2 at z=0                                                              builder.py:2695
[01:59:02 PM] INFO     Creating new physical group with name 'triangle_bottom'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'triangle_side'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'triangle_top'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'triangle'                                                                                    builder.py:2482
              INFO     Extruding shape 1 from mask 'example' with name 'box' by 2 at z=0                                                                   builder.py:2695
              INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/triangle_and_box.svg          builder.py:1863

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

To approximate circles (or create regular polygons) qtcad.builder.Polygon.regular` can be used:

regular(num_segments: int, radius: float = 1, *args, **kwargs) Polygon

Create a regular polygon (n-gon) that approximates a circle with num_segments and radius radius.

The rest arguments are passed to the constructor for qtcad.builder.masks.Polygon.

octagon = Polygon.regular(num_segments=8, radius=1).translated(6, 0)
example_mask.add_shape(octagon)

Builder().add_mask(example_mask).extrude(2).view(
    surfaces=True,
    volume_labels=True,
    angles=(-45, 0, 0),
    save="figs/triangle_and_box_and_octa.svg",
    font_size=20,
).finalize()
shapes
[01:59:03 PM] INFO     Auto-named Polygon instance to 'octagon'                                                                                               masks.py:208
[01:59:04 PM] INFO     Extruding shape 0 from mask 'example' with name 'triangle' by 2 at z=0                                                              builder.py:2695
[01:59:05 PM] INFO     Creating new physical group with name 'triangle_bottom'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'triangle_side'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'triangle_top'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'triangle'                                                                                    builder.py:2482
              INFO     Extruding shape 1 from mask 'example' with name 'box' by 2 at z=0                                                                   builder.py:2695
              INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Extruding shape 2 from mask 'example' with name 'octagon' by 2 at z=0                                                               builder.py:2695
              INFO     Creating new physical group with name 'octagon_bottom'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'octagon_side'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'octagon_top'                                                                                 builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'octagon'                                                                                     builder.py:2482
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/triangle_and_box_and_octa.svg builder.py:1863

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

Circles

A Circle is defined by a center point and a radius:

class Circle(name: str | None = '__unset', radius: float = 1, center: tuple[float, float] = (0, 0))

A circle with radius radius and center point center.

circle = Circle(center=(9, 0), radius=1)
example_mask.add_shape(circle)
Builder().add_mask(example_mask).extrude(2).view(
    surfaces=True,
    volume_labels=True,
    angles=(-45, 0, 0),
    save="figs/triangle_and_box_and_octa_and_circle.svg",
    font_size=20,
).finalize()
shapes
[01:59:07 PM] INFO     Auto-named Circle instance to 'circle'                                                                                                 masks.py:208
[01:59:08 PM] INFO     Extruding shape 0 from mask 'example' with name 'triangle' by 2 at z=0                                                              builder.py:2695
              INFO     Creating new physical group with name 'triangle_bottom'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'triangle_side'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'triangle_top'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'triangle'                                                                                    builder.py:2482
              INFO     Extruding shape 1 from mask 'example' with name 'box' by 2 at z=0                                                                   builder.py:2695
              INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:59:09 PM] INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Extruding shape 2 from mask 'example' with name 'octagon' by 2 at z=0                                                               builder.py:2695
              INFO     Creating new physical group with name 'octagon_bottom'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'octagon_side'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'octagon_top'                                                                                 builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'octagon'                                                                                     builder.py:2482
              INFO     Extruding shape 3 from mask 'example' with name 'circle' by 2 at z=0                                                                builder.py:2695
              INFO     Creating new physical group with name 'circle_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'circle_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'circle_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'circle'                                                                                      builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 56)]
  warnings.warn(
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Saving                                                                                                                              builder.py:1863
                       /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/triangle_and_box_and_octa_and_circle
                       .svg

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

Importing shapes from OASIS layouts

Shapes can also be imported from OASIS layout files. Layers in OASIS files are mapped to qtcad.builder.Mask objects. To load oasis files, we use the qtcad.builder.Builder.load_layout method:

Builder.load_layout(file_path: str | Path, cell_name: str | None = None, name_property: str = 'name') Self

[⚡ Utility] Read the cell named cell_name (default is the top cell) from a GDS or an OASIS file file_path and parse its structure. Each layer will become a Mask appropriately named and the ID from the layout file (provided there are no conflicting IDs) containing polygons for each shape found in the layer. The name of each shape is taken from the user property name_property. The length units in the file are assumed to be 10**length_unit_exponent meters (default is nanometres).

Parameters:
  • file_path – The path to the GDS or OASIS file.

  • cell_name – The name of the cell that the device is specified in. If None, use the top cell.

  • name_property – The user property that contains the name of a shape.

The example layout we are loading here contains two layers in a cell named “fancycell”. The first one contains triangle and the second one boxes.

../../../_images/example_layout.png

Fig. 14 The layout in KLayout.

A subset of the shapes are named by setting the "builder_name" custom property.

../../../_images/naming_shapes.png
builder = (
    Builder().load_layout("layouts/demo.oas", cell_name="fancycell").print_mask_tree()
)
[01:59:12 PM] INFO     Adding layer boxes                                                                                                                     masks.py:741
              INFO     Adding layer triangles                                                                                                                 masks.py:741
Layout
├── Mask 1 "boxes"
│   ├── 0  Polygon
│   ├── 1  Polygon  "named box"
│   └── 2  Polygon  "another box"
└── Mask 2 "triangles"
    ├── 0  Polygon
    └── 1  Polygon  "named triangle"

As can be seen above, we loaded the two layers into appropriately named masks, each containing their requisite shapes.

builder.use_mask("triangles").extrude(100).view(
    surfaces=False,
    volume_labels=True,
    angles=(-45, 0, 0),
    save="figs/oasis_triangles.svg",
    font_size=20,
)
shapes
              INFO     Using mask 'triangles'                                                                                                               builder.py:750
              INFO     Extruding shape 0 from mask 'triangles' with name 'None' by 100 at z=0                                                              builder.py:2695
[01:59:13 PM] INFO     Creating new physical group with name 'triangles_bottom'                                                                            builder.py:2482
              INFO     Creating new physical group with name 'triangles_side'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'triangles_top'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'triangles'                                                                                   builder.py:2482
              INFO     Extruding shape 1 from mask 'triangles' with name 'named triangle' by 100 at z=0                                                    builder.py:2695
              INFO     Creating new physical group with name 'named triangle_bottom'                                                                       builder.py:2482
              INFO     Creating new physical group with name 'named triangle_side'                                                                         builder.py:2482
              INFO     Creating new physical group with name 'named triangle_top'                                                                          builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'named triangle'                                                                              builder.py:2482
              INFO     Setting z-coordinate to 100                                                                                                          builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/oasis_triangles.svg           builder.py:1863

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

The unnamed shapes are given default names based on the mask name. Similarly, we can extrude the boxes:

builder.use_mask("boxes").set_z(0).extrude(200).view(
    surfaces=False,
    volume_labels=True,
    angles=(-45, 0, 0),
    save="figs/oasis_boxes.svg",
    font_size=20,
)
shapes
[01:59:14 PM] INFO     Using mask 'boxes'                                                                                                                   builder.py:750
              INFO     Setting z-coordinate to 0                                                                                                            builder.py:849
              INFO     Extruding shape 0 from mask 'boxes' with name 'None' by 200 at z=0                                                                  builder.py:2695
              INFO     Creating new physical group with name 'boxes_bottom'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'boxes_side'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'boxes_top'                                                                                   builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'boxes'                                                                                       builder.py:2482
              INFO     Extruding shape 1 from mask 'boxes' with name 'named box' by 200 at z=0                                                             builder.py:2695
              INFO     Creating new physical group with name 'named box_bottom'                                                                            builder.py:2482
              INFO     Creating new physical group with name 'named box_side'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'named box_top'                                                                               builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'named box'                                                                                   builder.py:2482
              INFO     Extruding shape 2 from mask 'boxes' with name 'another box' by 200 at z=0                                                           builder.py:2695
              INFO     Creating new physical group with name 'another box_bottom'                                                                          builder.py:2482
              INFO     Creating new physical group with name 'another box_side'                                                                            builder.py:2482
              INFO     Creating new physical group with name 'another box_top'                                                                             builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'another box'                                                                                 builder.py:2482
              INFO     Setting z-coordinate to 200                                                                                                          builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/oasis_boxes.svg               builder.py:1863

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

We can also select multiple masks at once using a list argument in qtcad.builder.Builder.use_mask:

Builder.use_mask(mask: int | str | Mask | Callable[[Mask], bool] | list[int | str | Mask | Callable[[Mask], bool]]) Self

[🔧 Modifier] Switch to use another mask as the working one. The mask parameter can be either the ID or name of a mask or a function taking a mask and returning True if the mask is to be selected. A list of the above can also be provided (see MaskContainer.get_mask). If multiple masks are selected, they are merged into a temporary mask.

This operation also selects all shapes in the mask (see also use_shape).

Parameters:

mask – Name or identifier (index) of the mask.

builder.use_mask(["triangles", "boxes"]).view_shapes(
    save="figs/boxes_and_triangles.svg",
    font_size=20,
)
shapes
[01:59:15 PM] INFO     Using mask 'triangles-boxes'                                                                                                         builder.py:750
              INFO     Viewing shapes: [None, 'named triangle', None, 'named box', 'another box'] using a temporary builder instance                       builder.py:1922

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

Modifying shapes loaded into qtcad.builder.Builder

Using qtcad.builder.Builder.copy_shape, we can copy shapes within a mask or add it to another one. While copy can also apply arbitrary transformations to the shapes.

Builder.copy_shape(name: str | None = '__unset', to_mask: int | str | None = None, translate: tuple[float, float] = (0, 0), rotate: float = 0, scale: float = 1, transformer: Callable[[Shape], Shape] | None = None) Self

[⚡ Utility] Copy the currently active shapes (see use_shapes, use_mask), apply transformations (translation, rotation, scaling, …) to them and select the copies. The copies will be added to the current mask or to the mask specified by to_mask (which does not have to exist yet). See also MaskContainer.copy_shapes() and Shape.transform().

Parameters:
  • name – The name of the new shape. If "__unset", the name will be copied from the original shape. If None, the new shape will be unnamed. See Shape.named. If multiple shapes are copied, an index is appended to the name. E.g., "my_shape_0". Use transformer to modify the names in a more flexible way.

  • to_mask – If specified, the copied shapes will be added to the mask with this name or index. Otherwise, they will be added to the current mask. If the specified mask does not exist and str is provided, a new mask will be created with this name.

  • translate – Translation vector in the x-y plane to apply to the copied shapes. See Shape.translated().

  • rotate – The rotation angle in degrees around the barycenter of the selected shapes. If a tuple (angle, center) is given, the selected shapes are rotated around center (which may be None in which case the shapes are rotated about their individual barycenters). See also Shape.rotated().

  • scale – Scaling factor to apply to the copied shapes. See Shape.scaled().

  • transformer – A callable that takes a shape and returns a modified shape. It is applied after the above transformations. This can be used to apply arbitrary transformations to the copied shapes. See Shape.transform. This might be useful to rename the shapes.

As a demonstration, let us copy the box shape we created above, rename, translate, scale it down and rotate it. We can then extrude the new shape and visualize the result.

builder = (
    Builder()
    .add_mask(example_mask)
    .use_shape("box")
    .extrude(1)
    .copy_shape(
        name="copied_box",
        translate=(3, 0),
        scale=0.5,
        rotate=20,
        transformer=lambda shape: shape.named("box_this_is_applied_last"),
    )
    .print_mask_tree()
    .extrude(1)
    .view(
        surfaces=True,
        volume_labels=True,
        angles=(-45, 0, 0),
        save="figs/transformed.svg",
        font_size=20,
    )
)
shapes
[01:59:18 PM] INFO     Extruding shape 1 from mask 'example' with name 'box' by 1 at z=0                                                                   builder.py:2695
[01:59:19 PM] INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Setting z-coordinate to 1                                                                                                            builder.py:849
Layout
└── Mask 0 "example"
    ├── 0  Polygon  "triangle"
    ├── 1  Polygon  "box"
    ├── 2  Polygon  "octagon"
    ├── 3  Circle  "circle"
    └── 4  Polygon  "box_this_is_applied_last"
              INFO     Extruding shape 4 from mask 'example' with name 'box_this_is_applied_last' by 1 at z=1                                              builder.py:2695
              INFO     Creating new physical group with name 'box_this_is_applied_last_bottom'                                                             builder.py:2482
              INFO     Creating new physical group with name 'box_this_is_applied_last_side'                                                               builder.py:2482
              INFO     Creating new physical group with name 'box_this_is_applied_last_top'                                                                builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box_this_is_applied_last'                                                                    builder.py:2482
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/transformed.svg               builder.py:1863

We can also remove them from the current mask using qtcad.builder.Builder.remove_shapes:

Builder.remove_shapes() Self

[⚡ Utility] Remove the currently selected shapes from the current mask. Selects all the shapes left in the current mask.

Here we remove the shape we just copied (copying selects the copied shapes).

builder.remove_shapes().print_mask_tree()


# By passing a function to :any:`qtcad.builder.Builder.use_shapes` we can
# filter the shapes we want to use.

builder.use_shapes(
    lambda shape: shape.name is not None and "box" in shape.name
).remove_shapes().print_mask_tree()
[01:59:20 PM] INFO     Removing shape 4 'box_this_is_applied_last' from mask 0 'example'.                                                                     masks.py:619
Layout
└── Mask 0 "example"
    ├── 0  Polygon  "triangle"
    ├── 1  Polygon  "box"
    ├── 2  Polygon  "octagon"
    └── 3  Circle  "circle"
              INFO     Removing shape 1 'box' from mask 0 'example'.                                                                                          masks.py:619
Layout
└── Mask 0 "example"
    ├── 0  Polygon  "triangle"
    ├── 1  Polygon  "octagon"
    └── 2  Circle  "circle"

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

We can create new masks. For example we can select shapes and copy them to a new mask.

Builder.copy_shape(name: str | None = '__unset', to_mask: int | str | None = None, translate: tuple[float, float] = (0, 0), rotate: float = 0, scale: float = 1, transformer: Callable[[Shape], Shape] | None = None) Self

[⚡ Utility] Copy the currently active shapes (see use_shapes, use_mask), apply transformations (translation, rotation, scaling, …) to them and select the copies. The copies will be added to the current mask or to the mask specified by to_mask (which does not have to exist yet). See also MaskContainer.copy_shapes() and Shape.transform().

Parameters:
  • name – The name of the new shape. If "__unset", the name will be copied from the original shape. If None, the new shape will be unnamed. See Shape.named. If multiple shapes are copied, an index is appended to the name. E.g., "my_shape_0". Use transformer to modify the names in a more flexible way.

  • to_mask – If specified, the copied shapes will be added to the mask with this name or index. Otherwise, they will be added to the current mask. If the specified mask does not exist and str is provided, a new mask will be created with this name.

  • translate – Translation vector in the x-y plane to apply to the copied shapes. See Shape.translated().

  • rotate – The rotation angle in degrees around the barycenter of the selected shapes. If a tuple (angle, center) is given, the selected shapes are rotated around center (which may be None in which case the shapes are rotated about their individual barycenters). See also Shape.rotated().

  • scale – Scaling factor to apply to the copied shapes. See Shape.scaled().

  • transformer – A callable that takes a shape and returns a modified shape. It is applied after the above transformations. This can be used to apply arbitrary transformations to the copied shapes. See Shape.transform. This might be useful to rename the shapes.

(
    builder.use_shape("triangle")
    .copy_shape(to_mask="only_triangle")
    .print_mask_tree()
    .finalize()
)
Layout
├── Mask 0 "example"
│   ├── 0  Polygon  "triangle"
│   ├── 1  Polygon  "octagon"
│   └── 2  Circle  "circle"
└── Mask 1 "only_triangle"
    └── 0  Polygon  "triangle"

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

Using qtcad.builder.Builder.copy_mask, we can also copy entire masks. Below, we copy the “example” mask to a new mask “example_copy”, applying some transformations in the process:

Builder.copy_mask(new_name: str, **kwargs) Self

[⚡ Utility] Copy current mask to an empty mask named new_name, set the current mask to this newly created one and select all shapes in the new mask.

For the keyword arguments see copy_shape. This method is equivalent to use_mask(current_mask).copy_shape(to_mask=new_name, **kwargs).

Note that the same effect could have been achieved by using qtcad.builder.Builder.use_mask followed by qtcad.builder.Builder.copy_shape.

example_mask = Mask(name="example")
example_mask.add_shapes([triangle, octagon, circle])

Builder().add_mask(example_mask).use_mask("example").extrude(0.1).copy_mask(
    "example_copy", scale=1.2, name="copy", rotate=20, translate=(0, 1)
).print_mask_tree().set_z(2).extrude(0.1).view(
    surfaces=True,
    volume_labels=True,
    angles=(-60, 0, 0),
    save="figs/copied_mask.svg",
    font_size=15,
)
shapes
[01:59:21 PM] INFO     Using mask 'example'                                                                                                                 builder.py:750
              INFO     Extruding shape 0 from mask 'example' with name 'triangle' by 0.1 at z=0                                                            builder.py:2695
[01:59:22 PM] INFO     Creating new physical group with name 'triangle_bottom'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'triangle_side'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'triangle_top'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'triangle'                                                                                    builder.py:2482
              INFO     Extruding shape 1 from mask 'example' with name 'octagon' by 0.1 at z=0                                                             builder.py:2695
              INFO     Creating new physical group with name 'octagon_bottom'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'octagon_side'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'octagon_top'                                                                                 builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'octagon'                                                                                     builder.py:2482
              INFO     Extruding shape 2 from mask 'example' with name 'circle' by 0.1 at z=0                                                              builder.py:2695
              INFO     Creating new physical group with name 'circle_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'circle_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'circle_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'circle'                                                                                      builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 44)]
  warnings.warn(
              INFO     Setting z-coordinate to 0.1                                                                                                          builder.py:849
Layout
├── Mask 0 "example"
│   ├── 0  Polygon  "triangle"
│   ├── 1  Polygon  "octagon"
│   └── 2  Circle  "circle"
└── Mask 1 "example_copy"
    ├── 0  Polygon  "copy_0"
    ├── 1  Polygon  "copy_1"
    └── 2  Circle  "copy_2"
              INFO     Setting z-coordinate to 2                                                                                                            builder.py:849
              INFO     Extruding shape 0 from mask 'example_copy' with name 'copy_0' by 0.1 at z=2                                                         builder.py:2695
              INFO     Creating new physical group with name 'copy_0_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'copy_0_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'copy_0_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'copy_0'                                                                                      builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 44)]
  warnings.warn(
              INFO     Extruding shape 1 from mask 'example_copy' with name 'copy_1' by 0.1 at z=2                                                         builder.py:2695
              INFO     Creating new physical group with name 'copy_1_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'copy_1_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'copy_1_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'copy_1'                                                                                      builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 44)]
  warnings.warn(
              INFO     Extruding shape 2 from mask 'example_copy' with name 'copy_2' by 0.1 at z=2                                                         builder.py:2695
[01:59:23 PM] INFO     Creating new physical group with name 'copy_2_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'copy_2_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'copy_2_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'copy_2'                                                                                      builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 44), (1, 89)]
  warnings.warn(
              INFO     Setting z-coordinate to 2.1                                                                                                          builder.py:849
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/copied_mask.svg               builder.py:1863

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

Note, that the names of the copied shapes have been automatically disambiguated by appending numbered suffixes.

Total running time of the script: (0 minutes 28.006 seconds)

Gallery generated by Sphinx-Gallery