Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions pylabrobot/visualizer3D/browser_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,32 @@ async def test_a_tree_that_changes_shape_keeps_the_models_it_had(self):
self.assertGreater(len(seen), 0, "nothing was sampled while the scene was rebuilt")
self.assertEqual(min(seen), drawn, f"the models went away and came back: {seen}")

async def test_a_facility_framed_from_far_off_is_drawn(self):
"""Both cameras had fixed clip planes - 20 m deep - and only the perspective one was refitted
when a view was framed. A lab framed whole stands further off than that, so its plan view, the
home button and the projection toggle drew nothing at all."""
lab = Facility(name="lab", size_x=14_000, size_y=22_000, size_z=3_000)
for i in range(4):
bench = Resource(name=f"bench_{i}", size_x=8_000, size_y=1_600, size_z=900, category="bench")
lab.assign_child_resource(bench, location=Coordinate(3_000, 2_500 + i * 5_000, 0))
fs_port, ws_port = free_ports(2)
viewer = Viewer3D(lab, open_browser=False, fs_port=fs_port, ws_port=ws_port)
await viewer.start()
try:
async with Browser() as browser:
await browser.open(viewer.url)
await browser.settle("window.plrViewer && window.plrViewer.resources().includes('bench_3')")
await browser.frames()
self.assertGreater(await browser.drawn_fraction("#viewport"), 0.02, "the opening plan view")
await browser.evaluate("document.getElementById('view-projection').click()")
await browser.frames()
self.assertGreater(await browser.drawn_fraction("#viewport"), 0.02, "after the toggle")
await browser.evaluate("document.getElementById('home-button').click()")
await browser.frames()
self.assertGreater(await browser.drawn_fraction("#viewport"), 0.02, "after home")
finally:
await viewer.stop()


@unittest.skipUnless(CHROME, "no headless browser to drive")
class SimulationTests(unittest.IsolatedAsyncioTestCase):
Expand Down
20 changes: 13 additions & 7 deletions pylabrobot/visualizer3D/static/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ function sizeOrthographic(distance) {
orthographicCamera.updateProjectionMatrix();
}

// Clip planes from how far the camera stands from what it frames. Fixed planes cut away anything
// framed from further off than they reach: a whole facility is framed from 29 m above.
function fitClipping(distance) {
perspectiveCamera.near = Math.max(distance / 1000, 0.1);
perspectiveCamera.far = distance * 50;
orthographicCamera.near = -distance * 50;
orthographicCamera.far = distance * 50;
}

// The axis presets live on the view helper: click an axis there and the camera animates onto it.
// What the helper cannot do is choose a projection, so that button stays.
export const projectionButton = document.getElementById("view-projection");
Expand All @@ -141,6 +150,7 @@ export function setProjection(kind) {
const target = controls.target.clone();
const position = camera.position.clone();
const distance = position.distanceTo(target);
fitClipping(distance);

projection = kind;
camera = kind === "orthographic" ? orthographicCamera : perspectiveCamera;
Expand Down Expand Up @@ -227,13 +237,9 @@ export function frameBox(box, direction) {

controls.target.copy(centre);
camera.position.copy(centre).add(direction.clone().normalize().multiplyScalar(distance));
if (projection === "orthographic") {
fitOrthographic(w, h);
} else {
camera.near = Math.max(distance / 1000, 0.1);
camera.far = distance * 50;
camera.updateProjectionMatrix();
}
fitClipping(distance);
if (projection === "orthographic") fitOrthographic(w, h);
else camera.updateProjectionMatrix();
// A drag leaves OrbitControls holding a rotation it has not finished applying: with damping on it
// spends that residue over the following frames and only decays it, so it outlives the pointer
// going up. Framing writes the camera straight to where it belongs, and the residue then turns it
Expand Down
Loading