Building composite shapes
Build apertures and chambers from Boolean operations, position independent copies, and assign names to objects. Membership remains independent of any plotting resolution.
a | b, a & b, and a - b form union, intersection, and difference. Operations take snapshots of their operands. The named collection retains references to its objects.
import matplotlib.pyplot as plt
from simgeometry import Circle, Cuboid, GeometryCollection, Rectangle, Sphere
plate = Rectangle((0, 0), (2.0, 1.2))holes = Circle((-0.5, 0), 0.2) | Circle((0.5, 0), 0.2)aperture = plate - holesassert aperture.contains((0, 0))assert not aperture.contains((0.5, 0))aperture.show(resolution=220, labels=False, display=False)plt.title("Plate with two holes")plt.show()
Transform snapshots
Section titled “Transform snapshots”Use translated(), rotated() and scaled() for the same non-mutating API on every shape. Angles are in degrees. Rotation and scaling default to the world origin; pass origin= to choose another centre. Existing primitive translate() methods retain their in-place behavior.
General transformed(matrix, offset) uses world = matrix @ local + offset. Normals transform with the inverse transpose, so they remain correct under nonuniform scaling.
original = Rectangle((0, 0), (1.0, 0.35))geometry = GeometryCollection( { "original": original, "rotated": original.rotated(35).translated((0.1, 0.7)), "scaled": original.scaled((0.7, 1.6)).translated((0.2, -0.7)), })geometry.show(resolution=180, display=False)plt.title("Independent transformed shapes")plt.show()original.translate((5, 0))assert geometry["rotated"].contains((0.1, 0.7))
Names and overlap precedence
Section titled “Names and overlap precedence”Collections return integer labels from evaluate(): zero is background, and labels 1 through N follow insertion order. The last listed object wins at overlapping points. quantity="occupancy" instead returns the union mask. Object names are stored in the result’s labels attribute.
overlap = GeometryCollection({"left": Circle((0, 0), 1), "right": Circle((1, 0), 1)})labels = overlap.evaluate(x=[-0.5, 0.5, 2.5], y=0)assert labels.values.tolist() == [1, 2, 0]print(labels)print(labels.attrs["labels"])<xarray.DataArray 'labels' (x: 3)> Size: 24Barray([1, 2, 0])Coordinates: * x (x) float64 24B -0.5 0.5 2.5 y float64 8B 0.0Attributes: labels: {0: 'background', 1: 'left', 2: 'right'} sampling: direct geometry queries; host arrays; no MPI gather{0: 'background', 1: 'left', 2: 'right'}Three-dimensional composition
Section titled “Three-dimensional composition”Difference and intersection work for 3D objects too. Here a sphere cuts a cavity out of a block; an oblique section lets us inspect the interior. The faceted display is sampled, so refine resolution to check small features.
block = Cuboid((0, 0, 0), (2, 2, 2)) - Sphere((0, 0, 0), 0.75)fig = plt.figure(figsize=(10, 4), layout="constrained")ax = fig.add_subplot(121, projection="3d")block.show_3d( ax=ax, resolution=32, clip=((0, 0, 0), (1, 0, 0)), labels=False, display=False)ax.set_title("Clipped cavity")ax = fig.add_subplot(122)block.slice(origin=(0, 0, 0), normal=(1, 1, 1)).plot( ax=ax, resolution=180, labels=False)ax.set_title("Oblique section")plt.show()
Try an intersection between the block and a rotated box. For inverted objects, supply a finite viewing/sampling region explicitly. Bounds on composed shapes conservatively enclose their operands; they do not certify that the resulting region is nonempty.