Cell maps#

A cell map is an immutable coordinate layout: a box, a cylinder, a spherical band, or a curvilinear volume on top of a surface. Every structure builder takes one. The returned objects are pyvcad.CellMap or pyvcad.CellMapCollection instances.

Analytic maps#

pyvcad_metamaterials.rectangular_cell_map(bounds, cell_size=None, cells=None, linear=True)[source]#

Create an axis-aligned finite cell map.

Exactly one of cell_size and cells is required. Requested cell sizes are converted to integer counts with ceiling so the supplied bounds remain exact and opposite boundaries never drift.

pyvcad_metamaterials.cylindrical_cell_map(center, inner_radius, outer_radius, height, cells=None, axis=<pyvcad.pyvcad.Vec3 object>, reference_direction=<pyvcad.pyvcad.Vec3 object>, start_angle_degrees=0.0, sweep_degrees=360.0, linear=False, arc_count=None, axial_cell_size=None, radial_cell_size=None)[source]#

Create an analytic annular-cylinder cell map.

Provide either explicit cells=(circumferential, axial, radial) or the complete sizing group arc_count, axial_cell_size, and radial_cell_size. Target sizes are rounded up to counts so the requested radii and height remain exact.

pyvcad_metamaterials.spherical_cell_map(center, inner_radius, outer_radius, latitude_bounds_degrees, cells=None, north_axis=<pyvcad.pyvcad.Vec3 object>, reference_direction=<pyvcad.pyvcad.Vec3 object>, start_longitude_degrees=0.0, longitude_sweep_degrees=360.0, linear=False, longitude_count=None, latitude_count=None, radial_cell_size=None)[source]#

Create an analytic spherical-shell band cell map.

Provide either explicit cells=(longitude, latitude, radial) or the complete sizing group longitude_count, latitude_count, and radial_cell_size. Radial target size is rounded up to a count so the requested radii remain exact.

Surface maps#

pyvcad_metamaterials.cell_map_from_surface(surface_or_surfaces, cells, height, linear=False, trim=<TrimMode.boundary: 2>, standoff=0.0)[source]#

Create one signed-height map per parametric surface.

Accepts a single ParametricSurface (a CADFace or TriangleMeshSurface), or a non-empty iterable that mixes any surface types. A single surface returns a pv.CellMap; an iterable returns a pv.CellMapCollection with per-surface cell counts broadcast from cells.

height is the signed thickness of the map in mm along the surface normal. It may be:

  • a number, for a constant height (the original behaviour);

  • a pv.FloatAttribute (or a numba callable), sampled once per cell-map column at the column’s base point on the surface, for a variable height;

  • a per-vertex array (list or NumPy array with one value per vertex) for a pv.TriangleMeshSurface, interpolated across the patch. surface_gap computes one by ray casting to a target such as the inside of a shell, so the cells end on it.

A variable height must stay finite, non-zero, and of one sign over the whole map. With several surfaces, height is either broadcast to all of them or given as a list with one entry per surface.

trim selects how the map is restricted to the surface’s true domain: a pv.TrimMode or one of the strings "none", "cells", "boundary".

standoff is a signed stand-off distance along the surface normal at which the map begins, so it spans [standoff, standoff + height] off the surface.

pyvcad_metamaterials.cell_map_from_cad_face(face_or_faces, cells, height, linear=False, trim=<TrimMode.boundary: 2>, standoff=0.0)[source]#

Create one native-UV signed-height map per CAD face.

pyvcad_metamaterials.cell_map_between_surfaces(first_surface, second_surface, cells, linear=False, trim=<TrimMode.boundary: 2>, auto_align=True, first_origin=None, flip_first=False, second_origin=None, flip_second=False, standoff=0.0)[source]#

Create a curvilinear volume between two parametric surfaces of any type.

first_surface and second_surface may be any mix of CADFace and TriangleMeshSurface. In advanced mode, flip_first and flip_second request opposing UV orientation changes. Origins are normalized (u, v) seam shifts and non-zero values are valid only for periodic directions.

standoff shifts the first surface along its oriented normal (in mm) before the two surfaces are interpolated, so the volume starts a clearance away from it. Zero (the default) leaves the map unchanged; the second surface never moves.

pyvcad_metamaterials.cell_map_between_cad_faces(first_face, second_face, cells, linear=False, trim=<TrimMode.boundary: 2>, auto_align=True, first_origin=None, flip_first=False, second_origin=None, flip_second=False, standoff=0.0)[source]#

Create a curvilinear volume between two compatible CAD faces.

In advanced mode, flip_first and flip_second request opposing UV orientation changes. Origins are normalized (u, v) seam shifts and non-zero values are valid only for periodic CAD directions. standoff shifts the first face along its oriented normal before interpolating.

Map tools#

pyvcad_metamaterials.warp_cell_map(cell_map, u_scale=0.0, v_scale=0.0, w_scale=0.0, lock_u_bounds=True, lock_v_bounds=True, lock_w_bounds=True)[source]#

Warp relative U/V/W spacing with bounded positive exponential weights.

Each cell’s share of the map along an axis is weighted by exp(clamp(scale, -4, 4)), sampled on the unwarped base map. A scale of 0 keeps the original spacing; larger values give that region more room. The base map is not modified.

Parameters:
  • cell_map (pv.CellMap | pv.CellMapCollection) – Base map or maps.

  • u_scale (float | pv.FloatAttribute | callable) – Relative spacing controls per axis, default 0.0. These are weights, not cell sizes in mm. cell_map.logical_position is a convenient input.

  • v_scale (float | pv.FloatAttribute | callable) – Relative spacing controls per axis, default 0.0. These are weights, not cell sizes in mm. cell_map.logical_position is a convenient input.

  • w_scale (float | pv.FloatAttribute | callable) – Relative spacing controls per axis, default 0.0. These are weights, not cell sizes in mm. cell_map.logical_position is a convenient input.

  • lock_u_bounds (bool) – Keep the two outer faces on that axis fixed and redistribute the cells between them (default True).

  • lock_v_bounds (bool) – Keep the two outer faces on that axis fixed and redistribute the cells between them (default True).

  • lock_w_bounds (bool) – Keep the two outer faces on that axis fixed and redistribute the cells between them (default True).

Returns:

The warped map, same kind as the input.

Return type:

pv.CellMap | pv.CellMapCollection

Raises:

RuntimeError – If a map was built with TrimMode.boundary (rebuild it with trim="none" before warping), or if the warp would fold the domain.

Example

>>> warped = mm.warp_cell_map(cell_map, u_scale=cell_map.logical_position.x * 0.2)
pyvcad_metamaterials.rim(cell_map, width=1.0)[source]#

Build a solid rim along the edges of each surface map.

The rim is a signed distance field from the map’s edge wall (the surface’s cropped boundary swept through the map height for boundary-trimmed maps, and the exposed lateral cell faces otherwise). It extends width inward from the edge, so unioning it with a lattice built on the same map seals the lattice’s open edge cells. width may be a constant or a pv.FloatAttribute sampled through the map.

pyvcad_metamaterials.surface_gap(surface, target, max_distance, offset=0.0, missing='raise', tolerance=0.001, voxel_size=0.25)[source]#

Measure the gap from every vertex of a mesh patch along its normal to a target.

This is the head-to-shell gap that drives an exact-fit variable-height map: with gap = mm.surface_gap(head, shell, 60.0), mm.cell_map_from_surface(head, cells, height=gap) grows cells from the head patch that end on the shell.

Parameters:
  • surface (pv.TriangleMeshSurface) – Patch whose vertices and vertex normals cast the rays.

  • target – What the rays hit. A pv.TriangleMeshSurface or pv.SurfaceMesh is hit by exact ray-triangle intersection, so open or non-watertight meshes (a shell’s inner surface exported from CAD) work. Any other pyvcad node is sphere-traced against its signed distance field (a private clone is prepared with voxel_size).

  • max_distance (float) – Longest ray in mm.

  • offset (float) – Subtracted from every distance, for example a stand-off or a clearance.

  • missing – What to do for rays that hit nothing within max_distance: "raise" (default) raises ValueError, "nearest" copies the value of the nearest vertex that did hit, and a number is used as is.

  • tolerance (float) – Hit tolerance in mm for sphere tracing.

  • voxel_size (float) – Voxel size in mm used to prepare a node target.

Returns:

One distance in mm per surface vertex, minus offset.

Return type:

numpy.ndarray

pyvcad_metamaterials.domain(cell_map)[source]#

Return the active volume of a cell map as a solid node (pv.CellMapDomain).

The node’s field is the map’s signed domain distance in mm: negative inside the active, trimmed map volume and positive outside, including trimmed margins and holes. Use it to clip another node to a liner volume (pv.Intersection), as a keep-out (pv.Difference), or as the host of a stochastic lattice. A pv.CellMapCollection returns the union of its maps’ domains.

pyvcad_metamaterials.map_node(cell_map, node, space='physical', scale=None, clip_to_domain=True)[source]#

Wrap any node through a cell map so geometry designed flat follows the map.

The node is authored in a flat child box and evaluated at the map’s logical coordinates scaled to child units. With space="physical" (default) the scale is the map’s average millimetres per cell along U, V, and W, so the child box pv.MappedNode.child_bounds covers the whole map in approximate millimetres; space="logical" uses cell units, and scale (a pv.Vec3 or 3-tuple) sets explicit child units per cell. Typical uses are pads, bosses, labels, or cut-outs placed on a curved liner. Distances are first-order Jacobian estimates: accurate near the surface (safe for meshing) but approximate far from it.

By default the wrapped node ends where the map ends (its field is clipped with the map’s domain distance). Pass clip_to_domain=False when the node marks a zone of the map, for example a region for zones() or overlap(): inside the domain it then keeps its own depth, so growing or shrinking the region by a band width works as expected.

Raw OCP shapes#

CadQuery shapes are converted with pyvcad.CADModel.from_cadquery. Raw OCP shapes use the optional pyvcad_metamaterials.occ adapters, which need the cad extra (pip install "pyvcad-metamaterials[cad]").

pyvcad_metamaterials.occ.body(shape)[source]#

Convert a raw OCP shape into a topology-preserving CADModel.

pyvcad_metamaterials.occ.face(shape)[source]#

Convert one raw OCP face into a CADFace.

pyvcad_metamaterials.occ.faces(shapes)[source]#

Convert raw OCP faces into independent CADFace handles.