group
srcSelects, per transducer, which pattern’s emission is output.
The transducers are split by assigning keys (an enum, for example) with TransducerGroups. Transducers with no key produce no output.
There are two composing APIs.
group_compute— receives each key together with itsTransducerMask, computes, and composes the results.group— composes precomputed buffers selected by key. Use it to reuse results or to compute groups in parallel.
group_compute
Section titled “group_compute”#[derive(Clone, Copy, PartialEq, Eq)]enum Side { Left, Right,}
group_compute( &geometry, &groups, |side, mask, phases, intensities| match side { Side::Left => gspat( &NalgebraBackend, &geometry, &foci, wavelength, &GspatOption { mask, ..Default::default() }, phases, intensities, ), Side::Right => { focus(&geometry, target, wavelength, phases); Ok(()) } }, &mut phases, &mut intensities,)?;class Side(Enum): LEFT = auto() RIGHT = auto()
def compute(side: Side, mask: TransducerMask, phases: PhaseBuffer, intensities: IntensityBuffer) -> None: if side is Side.LEFT: gspat(geometry, foci, wavelength, GspatOption(mask=mask), phases, intensities) else: focus(geometry, target, wavelength, phases)
group_compute(geometry, groups, compute, phases, intensities)internal enum Side{ Left, Right,}
Pattern.GroupCompute(geometry, groups, (side, mask, p, i) =>{ if (side == Side.Left) { Holo.Gspat(geometry, foci, wavelength, new GspatOption { Mask = mask }, p, i); } else { Pattern.Focus(geometry, target, wavelength, p); }}, phases, intensities);| Parameter | Type | Description |
|---|---|---|
geometry |
&Geometry |
Geometry |
groups |
&TransducerGroups<K> |
Key of each transducer |
compute |
FnMut(K, TransducerMask, &mut [Vec<Phase>], &mut [Vec<Intensity>]) -> Result<(), E> |
Computes into the scratch buffers (phases and intensities) for a key and its mask |
phases |
&mut [Vec<Phase>] |
Phase output buffer |
intensities |
&mut [Vec<Intensity>] |
Intensity output buffer |
compute is called once per key (in order of first appearance), and only the transducers of that key are written into phases / intensities.
The scratch buffers are reset to phase 0 and the maximum intensity for each key.
Transducers not assigned to any key produce no output (phase 0 and Intensity::MIN).
#[derive(Clone, Copy, PartialEq, Eq)]enum Side { Left, Right,}
let groups = TransducerGroups::new(&geometry, |device, tr| { Some(if device.position(tr).x < center.x { Side::Left } else { Side::Right })});group( &geometry, &groups, |side| match side { Side::Left => &left, Side::Right => &right, }, Phase::ZERO, &mut dst,);class Side(Enum): LEFT = auto() RIGHT = auto()
groups = TransducerGroups( geometry, lambda device, tr: Side.LEFT if device.position(tr)[0] < center[0] else Side.RIGHT,)group(geometry, groups, {Side.LEFT: left, Side.RIGHT: right}, dst)internal enum Side{ Left, Right,}
var groups = new TransducerGroups<Side>(geometry, (device, tr) => device.Position(tr).X < center.X ? Side.Left : Side.Right);Pattern.Group(geometry, groups, side => side == Side.Left ? left : right, dst);| Parameter | Type | Description |
|---|---|---|
geometry |
&Geometry |
Geometry |
groups |
&TransducerGroups<K> |
Key of each transducer |
source |
FnMut(K) -> impl AsRef<[Vec<T>]> |
Returns the source buffer for a key |
null |
T |
Value written to transducers of no key |
dst |
&mut [Vec<T>] |
Output buffer |
T is Phase or Intensity; call it once for each of the phase and intensity buffers.
Python and C# take no null argument: transducers of no key get phase 0 for a phase buffer and Intensity.MIN (no output) for an intensity buffer.
TransducerGroups
Section titled “TransducerGroups”TransducerGroups::new(&geometry, |device, tr| ...) returns a key (None = no group) from a device and transducer index, and keeps the key of each transducer.
Keys are Copy + Eq types in Rust, value types (struct) in C#, and hashable values in Python.
mask(key) returns a TransducerMask for Holo that enables only the transducers with key.
Example
Section titled “Example”use anyhow::Result;
use autd3_rs::geometry::{Autd3, Geometry, TransducerGroups, offset};use autd3_rs::units::{m, mm, s};use autd3_rs_pattern::{focus, group_compute, wavelength};use autd3_rs_pattern_holo::{AmplitudeTarget, GspatOption, NalgebraBackend, Pa, gspat};
#[derive(Clone, Copy, PartialEq, Eq)]enum Side { Left, Right,}
let geometry = Geometry::new(vec![Autd3::default()]);let wavelength = wavelength(340.0 * m / s);let center = geometry.center();
let groups = TransducerGroups::new(&geometry, |device, tr| { Some(if device.position(tr).x < center.x { Side::Left } else { Side::Right })});
let foci = [ AmplitudeTarget { point: center + offset(-50.0 * mm, 0.0 * mm, 150.0 * mm), amplitude: 5e3 * Pa, }, AmplitudeTarget { point: center + offset(-20.0 * mm, 0.0 * mm, 150.0 * mm), amplitude: 5e3 * Pa, },];
let mut phases = geometry.phase_buffer();let mut intensities = geometry.intensity_buffer();group_compute( &geometry, &groups, |side, mask, phases, intensities| match side { Side::Left => gspat( &NalgebraBackend, &geometry, &foci, wavelength, &GspatOption { mask, ..Default::default() }, phases, intensities, ), Side::Right => { focus( &geometry, center + offset(40.0 * mm, 0.0 * mm, 150.0 * mm), wavelength, phases, ); Ok(()) } }, &mut phases, &mut intensities,)?;from enum import Enum, auto
import numpy as np
from autd3.geometry import Autd3, Geometryfrom autd3.units import m, sfrom autd3_pattern import ( IntensityBuffer, PhaseBuffer, TransducerGroups, TransducerMask, focus, group_compute,)from autd3_pattern import wavelength as calc_wavelengthfrom autd3_pattern_holo import AmplitudeTarget, GspatOption, Pa, gspat
class Side(Enum): LEFT = auto() RIGHT = auto()
geometry = Geometry([Autd3([0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0])])wavelength = calc_wavelength(340 * m / s)center = geometry.center()
groups = TransducerGroups( geometry, lambda device, tr: Side.LEFT if device.position(tr)[0] < center[0] else Side.RIGHT,)
foci = [ AmplitudeTarget(point=center + np.array([-50.0, 0.0, 150.0]), amplitude=5e3 * Pa), AmplitudeTarget(point=center + np.array([-20.0, 0.0, 150.0]), amplitude=5e3 * Pa),]
def compute(side: Side, mask: TransducerMask, phases: PhaseBuffer, intensities: IntensityBuffer) -> None: if side is Side.LEFT: gspat(geometry, foci, wavelength, GspatOption(mask=mask), phases, intensities) else: focus(geometry, center + np.array([40.0, 0.0, 150.0]), wavelength, phases)
phases = geometry.phase_buffer()intensities = geometry.intensity_buffer()group_compute(geometry, groups, compute, phases, intensities)using System.Numerics;using AUTD3;using AUTD3.Holo;using static AUTD3.Units;using static AUTD3.Holo.HoloUnits;
internal enum Side{ Left, Right,}
var geometry = new Geometry(new[] { new Autd3(Vector3.Zero) });var wavelength = Pattern.Wavelength(340.0f * m / s);var center = geometry.Center;
var groups = new TransducerGroups<Side>(geometry, (device, tr) => device.Position(tr).X < center.X ? Side.Left : Side.Right);
var foci = new[]{ new AmplitudeTarget(center + new Vector3(-50.0f, 0.0f, 150.0f), 5e3f * Pa), new AmplitudeTarget(center + new Vector3(-20.0f, 0.0f, 150.0f), 5e3f * Pa),};
var phases = geometry.PhaseBuffer();var intensities = geometry.IntensityBuffer();Pattern.GroupCompute(geometry, groups, (side, mask, p, i) =>{ if (side == Side.Left) { Holo.Gspat(geometry, foci, wavelength, new GspatOption { Mask = mask }, p, i); } else { Pattern.Focus(geometry, center + new Vector3(40.0f, 0.0f, 150.0f), wavelength, p); }}, phases, intensities);