Skip to content

Commit 9c2e72b

Browse files
Add standoff transform creation tool (OpenwaterHealth#147)
1 parent 45d8630 commit 9c2e72b

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

src/openlifu/geo.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,32 @@ def spherical_coordinate_basis(th:float, phi:float) -> np.ndarray:
215215
[np.cos(th)*np.cos(phi), np.cos(th)*np.sin(phi), -np.sin(th)],
216216
[-np.sin(phi), np.cos(phi), 0],
217217
])
218+
219+
# === General coordinate transformation utiltiies ===
220+
221+
def create_standoff_transform(z_offset:float, dzdy:float) -> np.ndarray:
222+
"""Create a standoff transform based on a z_offset and a dzdy value.
223+
224+
A "standoff transform" applies a displacement in transducer space that moves a transducer to where it would
225+
be situated with the standoff in place. The idea is that if you start with a transform that places a transducer
226+
directly against skin, then pre-composing that transform by a "standoff transform" serves to nudge the transducer
227+
such that there is space for the standoff to be between it and the skin.
228+
229+
This function assumes that the standoff is laterally symmetric, has some thickness, and can raise the bottom of
230+
the transducer a bit more than the top. The `z_offset` is the thickness in the middle of the standoff,
231+
while the `dzdy` is the elevational slope.
232+
233+
Args:
234+
z_offset: Thickness in the middle of the standoff
235+
dzdy: Slope of the standoff, as axial displacement per unit elevational displacement. A positive number
236+
here means that the bottom of the transducer is raised a little bit more than the top.
237+
238+
Returns a 4x4 matrix representing a rigid transform in whatever units z_offset was provided in.
239+
"""
240+
angle = np.arctan(dzdy)
241+
return np.array([
242+
[1,0,0,0],
243+
[0,np.cos(angle),-np.sin(angle),0],
244+
[0,np.sin(angle),np.cos(angle),-z_offset],
245+
[0,0,0,1],
246+
], dtype=float)

tests/test_geo.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Point,
77
cartesian_to_spherical,
88
cartesian_to_spherical_vectorized,
9+
create_standoff_transform,
910
spherical_coordinate_basis,
1011
spherical_to_cartesian,
1112
spherical_to_cartesian_vectorized,
@@ -87,3 +88,15 @@ def test_spherical_coordinate_basis():
8788
assert np.allclose(np.diff(r_hat / point), 0) # verify that r_hat is a scalar multiple of the cartesian coords
8889
assert cartesian_to_spherical_vectorized(point + 0.01*phi_hat)[2] > phi # verify phi_hat points along increasing phi
8990
assert cartesian_to_spherical_vectorized(point + 0.01*theta_hat)[1] > th # verify theta_hat points along increasing theta
91+
92+
def test_create_standoff_transform():
93+
z_offset = 3.2
94+
dzdy = 0.15
95+
t = create_standoff_transform(z_offset, dzdy)
96+
assert np.allclose(t[:3,:3] @ t[:3,:3].T, np.eye(3)) # it's an orthonormal transform
97+
assert np.allclose(np.linalg.det(t[:3,:3]), 1.0) # orientation preserving
98+
assert np.allclose(t @ np.array([0,0,0,1]), np.array([0,0,-z_offset,1.])) # translates the origin correctly
99+
new_x_axis = (t @ np.array([1,0,0,1]) - t @ np.array([0,0,0,1]))[:3]
100+
new_y_axis = (t @ np.array([0,1,0,1]) - t @ np.array([0,0,0,1]))[:3]
101+
assert np.allclose(new_x_axis, np.array([1.,0,0]))
102+
assert new_y_axis[2] > 0 # the y axis was rotated upward, so that the top of the transducer gets closer to the skin

0 commit comments

Comments
 (0)