iactrace.viz

Visualization functions for displaying results and telescope geometry.

2D Plotting

Functions for displaying sensor images:

iactrace.viz.show_image(image, sensor, ax=None, *, cmap='viridis', vmin=None, vmax=None, norm=None, edgecolor='none', linewidth=0.0, colorbar=False, cbar_label=None)[source]

Render a camera image at actual focal-plane positions.

Builds a single PolyCollection holding every pixel in the camera. Pixel polygons are projected onto the camera (x, y) plane after applying each tile’s position and Euler rotation, so curvature and tile tilt are reflected faithfully.

This draws the image – pixel values on the focal plane. For the camera’s physical 3D geometry (every pixel’s concentrator and photosensor) see iactrace.viz.show_camera().

Args:
image: Pixel image array. Shape (n_sensors, height, width) for

SquareSensorGroup, (n_sensors, n_pixels) for HexagonalSensorGroup.

sensor: The sensor group that produced image. ax: Matplotlib axes. Created with a square aspect figure if None. cmap: Colormap name or instance. vmin, vmax: Explicit colour limits. Default: data min/max. norm: Optional matplotlib.colors.Normalize. Overrides

vmin/vmax when given.

edgecolor: Pixel-boundary colour. Default "none" (no outlines). linewidth: Pixel-boundary line width. colorbar: Attach a vertical colorbar to ax. cbar_label: Colorbar label text.

Returns:

ax (the axes that received the collection).

3D Visualization

Functions for visualizing telescope geometry:

iactrace.viz.show_telescope(telescope, camera=None, *, trajectory=None, **kwargs)[source]

Visualize telescope in 3D.

In Jupyter notebooks, displays interactive 3D view via three.js (client-side). No server-side OpenGL required.

When a camera is supplied, each sensor is drawn as a filled face plus a black wireframe of its pixel grid (square cells or hexagons), so the physical pixel layout can be checked against the rest of the optics.

Args:

telescope: Telescope object camera: Optional Camera object (for rendering sensors) trajectory: Optional recorded ray paths to draw as polylines, in the

world frame (the frame the optics are drawn in). A (steps + 1, N, 3) array as returned by Telescope.trace(..., record_trajectory=True). Trace yourself and pass the result – this function does not run the tracer. Drawn via add_trajectories().

**kwargs: Additional options:
  • mirror_color: RGBA color for mirrors (default: light blue)

  • obstruction_color: RGBA color for obstructions (default: gray)

  • sensor_color: RGBA color for sensors (default: red)

  • lens_color: RGBA color for lenses (default: light green, semi-transparent)

  • show_sensor_grid: Draw per-pixel grid lines for each sensor (default: True; only has an effect when camera is given)

  • sensor_grid_color: RGBA color for the pixel grid (default: black)

Returns:

trimesh.Scene

iactrace.viz.show_camera(camera, *, sensor_idx=None, trajectory=None, clip=True, clip_margin=0.25, entrances=False, detail=0.5, **kwargs)[source]

Visualize the whole camera in 3D: every pixel’s concentrator and photosensor.

Replicates each sensor group’s detection chain – the same geometry show_sensor_chain() draws for one pixel (concentrator walls, photocathode, photodetector body) – at every pixel of every sensor in the group, placed by the sensor’s own position and Euler rotation. The result is the camera as the camera file describes it, in the camera frame.

Each part is emitted as one merged mesh per sensor group, so a few thousand pixels stay a handful of geometries rather than a few thousand.

For a single pixel’s chain in isolation (and for overlaying traced rays on it), use show_sensor_chain(). For a camera image – pixel values on the focal plane – use iactrace.viz.show_image().

Recorded ray paths can be overlaid on the geometry – the final leg onto the camera joined to the scattering through each pixel’s chain:

show_camera(camera, trajectory=camera.trace(rays))
Args:
camera: Camera object; every sensor group is drawn unless sensor_idx

selects one.

sensor_idx: Optional index of a single sensor group to draw. trajectory: Optional camera-frame ray paths to draw as polylines, as

returned by Camera.trace(). As elsewhere, this function draws what it is given and never runs the tracer itself.

clip: How to trim those paths. True (default) fits a box around the

drawn camera geometry, padded by clip_margin; False draws them whole; or pass an explicit (lo, hi) box, each (3,), in the camera frame. Only the paths are clipped – the geometry is always drawn in full.

clip_margin: Padding of the automatic box, as a fraction of the camera’s

larger transverse extent (default 0.25). This is what sets how much of the incoming beam you see converging above the pixels; raise it to show more of the approach, lower it to sit tight on the camera.

entrances: Also draw each pixel’s entrance-aperture face (default

False – with thousands of pixels the filled faces hide the cones behind them).

detail: Tessellation scale, 1.0 being the full per-pixel detail

show_sensor_chain() uses. Defaults to 0.5: the whole-camera mesh is the per-pixel one times a few thousand, and at camera scale the extra facets are invisible. Raise it when zooming in.

**kwargs: cone_color / detector_color / sensor_color /

entrance_color RGBA, as in show_sensor_chain().

Returns:

trimesh.Scene

iactrace.viz.show_sensor_chain(camera, sensor_idx=0, *, trajectory=None, **kwargs)[source]

Visualize a single pixel’s detection chain (“train”) in 3D.

Draws, in the canonical pixel-local frame (entrance aperture at z = 0, axis +z): the pixel entrance aperture, the concentrator walls (if a concentrator is present and exposes Concentrator.cross_sections()), and the photodetector’s sensor surface – its actual photocathode geometry, drawn curved when the photodetector owns a curved DetectionSurface, otherwise a flat active-area polygon at chain.detector_z.

If the photodetector exposes a 3D envelope (PhotoDetector.envelope(), e.g. a PMT), its glass body is lofted around the detector plane as well.

Ray paths are not traced here – run the chain tracer yourself and pass the recorded trajectory (pixel-local frame) to overlay it, e.g.:

trace = trace_chain(cone, chain.surface, rays, record_trajectory=True)
show_sensor_chain(camera, trajectory=trace)  # ChainTrace or its array
Args:

camera: Camera object (the selected sensor group and its chain are read). sensor_idx: Which sensor group to take the pixel geometry and chain from. trajectory: Optional recorded ray paths to draw as polylines, in the

pixel-local frame: a (steps + 1, N, 3) array or any object exposing a trajectory attribute (e.g. a ChainTrace). Drawn via add_trajectories().

**kwargs: entrance_color / cone_color / detector_color /

sensor_color / trace_color RGBA.

Returns:

trimesh.Scene

iactrace.viz.export_mesh(telescope, filename)[source]

Export telescope geometry to 3D file.

Args:

telescope: Telescope object filename: Output path (.glb, .gltf, .stl, .ply, .obj)

iactrace.viz.add_rays(scene, origins, directions, length=10.0, color=None)[source]

Add rays to scene for debugging.

Args:

scene: trimesh.Scene origins: Ray origins (N, 3) directions: Ray directions (N, 3) length: Ray length color: RGBA color for the rays (default: yellow)

Returns:

scene

iactrace.viz.add_points(scene, points, color=None)[source]

Add points to scene.

Args:

scene: trimesh.Scene points: Point coordinates (N, 3) color: RGBA color

Returns:

scene