From 2e2ba8f573f1fdc89a6111e1661d3c3190fd360a Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 15:23:28 -0700 Subject: [PATCH 01/35] on mobile device, only third selection mode is allowed --- src/components/App.tsx | 7 +++++++ src/components/CellControls.tsx | 22 +++++++++++++++++++--- src/lib/PointCanvas.ts | 7 ++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 2da880af..33a5cf07 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -27,6 +27,12 @@ const initialViewerState = ViewerState.fromUrlHash(window.location.hash); console.log("initial viewer state: ", initialViewerState); clearUrlHash(); +function isMobileDevice(): boolean { + return /Mobi|Android|iPad|iPhone/i.test(navigator.userAgent); +} +export const isMobile = !isMobileDevice(); +console.log("isMobileDevice", isMobile); + const drawerWidth = 256; const playbackFPS = 16; const playbackIntervalMs = 1000 / playbackFPS; @@ -302,6 +308,7 @@ export default function App() { setSelectionMode={(value: PointSelectionMode) => { dispatchCanvas({ type: ActionType.SELECTION_MODE, selectionMode: value }); }} + isMobile={isMobile} /> diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index 37f4d855..79b5d4fc 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -16,6 +16,7 @@ interface CellControlsProps { setPointBrightness: (value: number) => void; selectionMode: PointSelectionMode; setSelectionMode: (value: PointSelectionMode) => void; + isMobile: boolean; } export default function CellControls(props: CellControlsProps) { @@ -25,6 +26,20 @@ export default function CellControls(props: CellControlsProps) { { icon: "Globe", tooltipText: "Sphere", value: PointSelectionMode.SPHERE }, ]; + // Intercept onChange of selection buttons to prevent the first two buttons from being selected on mobile devices + const handleSegmentedControlChange = (_e: React.MouseEvent, newValue: PointSelectionMode | null) => { + // If isMobile is true and the selected value corresponds to the first or second button, do nothing + if ( + props.isMobile && + (newValue === PointSelectionMode.BOX || newValue === PointSelectionMode.SPHERICAL_CURSOR) + ) { + window.alert("This selection mode is not available on mobile devices."); + console.log("Mobile device detected, preventing selection of box or spherical cursor"); + return; // Prevent selection + } + props.setSelectionMode(newValue!); // Otherwise, update the selection mode + }; + return ( @@ -47,9 +62,10 @@ export default function CellControls(props: CellControlsProps) { { - props.setSelectionMode(v); - }} + onChange={handleSegmentedControlChange} + // onChange={(_e, v) => { + // props.setSelectionMode(v); + // }} value={props.selectionMode} /> diff --git a/src/lib/PointCanvas.ts b/src/lib/PointCanvas.ts index 44b1d6f9..70cb8de1 100644 --- a/src/lib/PointCanvas.ts +++ b/src/lib/PointCanvas.ts @@ -25,6 +25,7 @@ import { PointSelector, PointSelectionMode } from "@/lib/PointSelector"; import { ViewerState } from "./ViewerState"; import { numberOfValuesPerPoint } from "./TrackManager"; +import { isMobile } from "@/components/App.tsx"; import config from "../../CONFIG.ts"; const pointSize = config.settings.point_size; @@ -149,7 +150,11 @@ export class PointCanvas { // Set up selection this.selector = new PointSelector(this.scene, this.renderer, this.camera, this.controls, this.points); - this.setSelectionMode(PointSelectionMode.BOX); + if (isMobile) { + this.setSelectionMode(PointSelectionMode.SPHERE); + } else { + this.setSelectionMode(PointSelectionMode.BOX); + } } shallowCopy(): PointCanvas { From 3559298d42485636329fa99ebdfc419cdf3e58cf Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 15:28:28 -0700 Subject: [PATCH 02/35] typo --- src/components/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 33a5cf07..dc11cc72 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -30,7 +30,7 @@ clearUrlHash(); function isMobileDevice(): boolean { return /Mobi|Android|iPad|iPhone/i.test(navigator.userAgent); } -export const isMobile = !isMobileDevice(); +export const isMobile = isMobileDevice(); console.log("isMobileDevice", isMobile); const drawerWidth = 256; From 8532e3da4169453172dd43020ab3d8a471d27bc8 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 15:41:06 -0700 Subject: [PATCH 03/35] added alert to check if mobile is detected --- src/components/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/App.tsx b/src/components/App.tsx index dc11cc72..eed8e5a5 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -31,6 +31,7 @@ function isMobileDevice(): boolean { return /Mobi|Android|iPad|iPhone/i.test(navigator.userAgent); } export const isMobile = isMobileDevice(); +window.alert("Mobile device detected: " + isMobile); console.log("isMobileDevice", isMobile); const drawerWidth = 256; From bea014c0616c694438a559f25838db5b7e440db2 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 15:46:26 -0700 Subject: [PATCH 04/35] iPad wasnt detected --- src/components/App.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index eed8e5a5..99bfa365 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -28,8 +28,21 @@ console.log("initial viewer state: ", initialViewerState); clearUrlHash(); function isMobileDevice(): boolean { - return /Mobi|Android|iPad|iPhone/i.test(navigator.userAgent); + const ua = navigator.userAgent || navigator.vendor || window.opera; + + // Detect Android, iPhone, or iPod + if (/Mobi|Android|iPhone|iPod/i.test(ua)) { + return true; + } + + // Detect iPads in iPadOS, where Safari identifies the device as a Mac + if (/Macintosh/i.test(ua) && "ontouchend" in document) { + return true; + } + + return false; } + export const isMobile = isMobileDevice(); window.alert("Mobile device detected: " + isMobile); console.log("isMobileDevice", isMobile); From 9191f98d5518ab10a46ea2f7051d415943240012 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 15:48:58 -0700 Subject: [PATCH 05/35] iPad stil not detected --- src/components/App.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 99bfa365..76004c1f 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -28,7 +28,7 @@ console.log("initial viewer state: ", initialViewerState); clearUrlHash(); function isMobileDevice(): boolean { - const ua = navigator.userAgent || navigator.vendor || window.opera; + const ua = navigator.userAgent || navigator.vendor; // Detect Android, iPhone, or iPod if (/Mobi|Android|iPhone|iPod/i.test(ua)) { @@ -42,7 +42,6 @@ function isMobileDevice(): boolean { return false; } - export const isMobile = isMobileDevice(); window.alert("Mobile device detected: " + isMobile); console.log("isMobileDevice", isMobile); From d5b5541968ad81df2f80719fc0995b496aae1360 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 16:18:44 -0700 Subject: [PATCH 06/35] initialize sphere selector in middle of point cloud --- src/components/App.tsx | 1 - src/lib/SpherePointSelector.ts | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 76004c1f..a90cbd26 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -43,7 +43,6 @@ function isMobileDevice(): boolean { return false; } export const isMobile = isMobileDevice(); -window.alert("Mobile device detected: " + isMobile); console.log("isMobileDevice", isMobile); const drawerWidth = 256; diff --git a/src/lib/SpherePointSelector.ts b/src/lib/SpherePointSelector.ts index cdf1225c..805ffc3a 100644 --- a/src/lib/SpherePointSelector.ts +++ b/src/lib/SpherePointSelector.ts @@ -15,6 +15,7 @@ import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { TransformControls } from "three/examples/jsm/Addons.js"; import { SelectionChanged } from "@/lib/PointSelector"; +import { ViewerState } from "./ViewerState.ts"; // Selecting with a sphere, with optional transform controls. export class SpherePointSelector { @@ -63,6 +64,9 @@ export class SpherePointSelector { this.draggingChanged = this.draggingChanged.bind(this); this.cursorControl.addEventListener("dragging-changed", this.draggingChanged); + + const cameraTarget = new ViewerState().cameraTarget; + this.cursor.position.set(cameraTarget[0], cameraTarget[1], cameraTarget[2]); } dispose() { From 9852c66cb29e4fbc7120a0a027af80f2ed8b5f27 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Mon, 30 Sep 2024 18:54:50 -0700 Subject: [PATCH 07/35] added select-cells button when on Mobile device, to place shift+click" --- src/components/App.tsx | 3 +++ src/components/CellControls.tsx | 12 ++++++++++-- src/hooks/usePointCanvas.ts | 11 ++++++++++- src/lib/PointCanvas.ts | 5 +++++ src/lib/SpherePointSelector.ts | 24 ++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index a90cbd26..193c93c9 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -321,6 +321,9 @@ export default function App() { dispatchCanvas({ type: ActionType.SELECTION_MODE, selectionMode: value }); }} isMobile={isMobile} + MobileSelectCells={() => { + dispatchCanvas({ type: ActionType.MOBILE_SELECT_CELLS }); + }} /> diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index 79b5d4fc..fe8e8c0f 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -1,5 +1,5 @@ import { Box, Stack } from "@mui/material"; -import { InputSlider, SegmentedControl, SingleButtonDefinition } from "@czi-sds/components"; +import { InputSlider, SegmentedControl, SingleButtonDefinition, Button } from "@czi-sds/components"; import { FontS, SmallCapsButton, ControlLabel } from "@/components/Styled"; import { PointSelectionMode } from "@/lib/PointSelector"; @@ -17,6 +17,7 @@ interface CellControlsProps { selectionMode: PointSelectionMode; setSelectionMode: (value: PointSelectionMode) => void; isMobile: boolean; + MobileSelectCells: () => void; } export default function CellControls(props: CellControlsProps) { @@ -69,8 +70,15 @@ export default function CellControls(props: CellControlsProps) { value={props.selectionMode} /> + + {props.isMobile && ( + + )} + Date: Tue, 1 Oct 2024 10:13:30 -0700 Subject: [PATCH 08/35] changed sections in leftSideBar --- src/components/leftSidebar/TrackControls.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/leftSidebar/TrackControls.tsx b/src/components/leftSidebar/TrackControls.tsx index d11c206e..3bcf6b89 100644 --- a/src/components/leftSidebar/TrackControls.tsx +++ b/src/components/leftSidebar/TrackControls.tsx @@ -2,7 +2,7 @@ import { TrackManager } from "@/lib/TrackManager"; import { InputSlider, InputToggle } from "@czi-sds/components"; import { Box, Stack } from "@mui/material"; -import { ControlLabel } from "@/components/Styled"; +import { ControlLabel, FontS } from "@/components/Styled"; interface TrackControlsProps { trackManager: TrackManager | null; @@ -19,9 +19,10 @@ export default function TrackControls(props: TrackControlsProps) { return ( + Visualization options Date: Tue, 1 Oct 2024 11:08:32 -0700 Subject: [PATCH 09/35] renamed tooltips + cleanup --- src/components/CellControls.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index fe8e8c0f..3265177b 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -23,8 +23,8 @@ interface CellControlsProps { export default function CellControls(props: CellControlsProps) { const buttonDefinition: SingleButtonDefinition[] = [ { icon: "Cube", tooltipText: "Box", value: PointSelectionMode.BOX }, - { icon: "Starburst", tooltipText: "Spherical cursor", value: PointSelectionMode.SPHERICAL_CURSOR }, - { icon: "Globe", tooltipText: "Sphere", value: PointSelectionMode.SPHERE }, + { icon: "Starburst", tooltipText: "Sphere", value: PointSelectionMode.SPHERICAL_CURSOR }, + { icon: "Globe", tooltipText: "Adjustable sphere", value: PointSelectionMode.SPHERE }, ]; // Intercept onChange of selection buttons to prevent the first two buttons from being selected on mobile devices @@ -64,9 +64,6 @@ export default function CellControls(props: CellControlsProps) { id="selection-mode-control" buttonDefinition={buttonDefinition} onChange={handleSegmentedControlChange} - // onChange={(_e, v) => { - // props.setSelectionMode(v); - // }} value={props.selectionMode} /> From 9cf0e05685047cbd73455cfffca5a293da49ebb6 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 11:23:50 -0700 Subject: [PATCH 10/35] detect desktop/tablet/phone --- src/components/App.tsx | 213 +++++++++++++++++++++++------------------ src/lib/PointCanvas.ts | 4 +- 2 files changed, 122 insertions(+), 95 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 193c93c9..acaff3d7 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -27,23 +27,44 @@ const initialViewerState = ViewerState.fromUrlHash(window.location.hash); console.log("initial viewer state: ", initialViewerState); clearUrlHash(); -function isMobileDevice(): boolean { +function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: boolean } { const ua = navigator.userAgent || navigator.vendor; - // Detect Android, iPhone, or iPod - if (/Mobi|Android|iPhone|iPod/i.test(ua)) { - return true; - } + // Detect iPads, iPhones, and iPods based on the user agent string + const isiPad = + /iPad/.test(ua) || (navigator.maxTouchPoints && navigator.maxTouchPoints > 1 && /Macintosh/.test(ua)); + const isiPhoneOrIPod = /iPhone|iPod/.test(ua); - // Detect iPads in iPadOS, where Safari identifies the device as a Mac - if (/Macintosh/i.test(ua) && "ontouchend" in document) { - return true; - } + // Detect Android phones and tablets + const isAndroidPhone = /Android/.test(ua) && /Mobile/.test(ua); + const isAndroidTablet = /Android/.test(ua) && !/Mobile/.test(ua); - return false; + // Screen size check (tablets typically have a wider screen) + const isSmallScreen = window.screen.width <= 768; + + // Determine if it's a phone, tablet, or desktop + const isPhone = isiPhoneOrIPod || isAndroidPhone || isSmallScreen; + const isTablet = isiPad || isAndroidTablet || (!isPhone && isSmallScreen && window.screen.width > 600); // Optional: Add a threshold for larger screens + const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet + + return { + isPhone: isPhone, + isTablet: isTablet && !isPhone, // To avoid small phones being miscategorized as tablets + isMobile: !isDesktop, + }; } -export const isMobile = isMobileDevice(); -console.log("isMobileDevice", isMobile); + +export const detectedDevice = detectDeviceType(); +console.log("detectDeviceType: ", detectDeviceType()); +window.alert( + "detected device type (desktop | tablet | phone) = (" + + !detectDeviceType().isMobile + + " | " + + detectDeviceType().isTablet + + " | " + + detectDeviceType().isPhone + + ")", +); const drawerWidth = 256; const playbackFPS = 16; @@ -271,98 +292,104 @@ export default function App() { return ( {/* TODO: components *could* go deeper still for organization */} - - - {brandingLogoPath && } - {brandingLogoPath && brandingName && } - {brandingName &&

{brandingName}

}{" "} -
- - { - dispatchCanvas({ type: ActionType.REMOVE_ALL_TRACKS }); - }} - getTrackDownloadData={getTrackDownloadData} - numSelectedCells={numSelectedCells} - numSelectedTracks={numSelectedTracks} - trackManager={trackManager} - pointBrightness={canvas.pointBrightness} - setPointBrightness={(brightness: number) => { - dispatchCanvas({ type: ActionType.POINT_BRIGHTNESS, brightness }); + { - dispatchCanvas({ type: ActionType.SELECTION_MODE, selectionMode: value }); - }} - isMobile={isMobile} - MobileSelectCells={() => { - dispatchCanvas({ type: ActionType.MOBILE_SELECT_CELLS }); - }} - /> + > + {brandingLogoPath && } + {brandingLogoPath && brandingName && } + {brandingName &&

{brandingName}

}{" "} +
+ + { + dispatchCanvas({ type: ActionType.REMOVE_ALL_TRACKS }); + }} + getTrackDownloadData={getTrackDownloadData} + numSelectedCells={numSelectedCells} + numSelectedTracks={numSelectedTracks} + trackManager={trackManager} + pointBrightness={canvas.pointBrightness} + setPointBrightness={(brightness: number) => { + dispatchCanvas({ type: ActionType.POINT_BRIGHTNESS, brightness }); + }} + selectionMode={canvas.selector.selectionMode} + setSelectionMode={(value: PointSelectionMode) => { + dispatchCanvas({ type: ActionType.SELECTION_MODE, selectionMode: value }); + }} + isMobile={detectedDevice.isMobile} + MobileSelectCells={() => { + dispatchCanvas({ type: ActionType.MOBILE_SELECT_CELLS }); + }} + /> + + + + 0} + trackManager={trackManager} + trackHighlightLength={trackHighlightLength} + selectionMode={canvas.selector.selectionMode} + showTracks={canvas.showTracks} + setShowTracks={(show: boolean) => { + dispatchCanvas({ type: ActionType.SHOW_TRACKS, showTracks: show }); + }} + showTrackHighlights={canvas.showTrackHighlights} + setShowTrackHighlights={(show: boolean) => { + dispatchCanvas({ + type: ActionType.SHOW_TRACK_HIGHLIGHTS, + showTrackHighlights: show, + }); + }} + setTrackHighlightLength={(length: number) => { + dispatchCanvas({ + type: ActionType.MIN_MAX_TIME, + minTime: canvas.curTime - length / 2, + maxTime: canvas.curTime + length / 2, + }); + }} + /> + + + + +
- - - 0} - trackManager={trackManager} - trackHighlightLength={trackHighlightLength} - selectionMode={canvas.selector.selectionMode} - showTracks={canvas.showTracks} - setShowTracks={(show: boolean) => { - dispatchCanvas({ type: ActionType.SHOW_TRACKS, showTracks: show }); - }} - showTrackHighlights={canvas.showTrackHighlights} - setShowTrackHighlights={(show: boolean) => { - dispatchCanvas({ type: ActionType.SHOW_TRACK_HIGHLIGHTS, showTrackHighlights: show }); - }} - setTrackHighlightLength={(length: number) => { - dispatchCanvas({ - type: ActionType.MIN_MAX_TIME, - minTime: canvas.curTime - length / 2, - maxTime: canvas.curTime + length / 2, - }); - }} - /> - - - - - -
-
+ + )} + Date: Tue, 1 Oct 2024 11:54:45 -0700 Subject: [PATCH 11/35] removed device alert --- src/components/App.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index acaff3d7..481175dd 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -56,15 +56,16 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo export const detectedDevice = detectDeviceType(); console.log("detectDeviceType: ", detectDeviceType()); -window.alert( - "detected device type (desktop | tablet | phone) = (" + - !detectDeviceType().isMobile + - " | " + - detectDeviceType().isTablet + - " | " + - detectDeviceType().isPhone + - ")", -); +// for debugging: show the detected device type in an alert +// window.alert( +// "detected device type (desktop | tablet | phone) = (" + +// !detectDeviceType().isMobile + +// " | " + +// detectDeviceType().isTablet + +// " | " + +// detectDeviceType().isPhone + +// ")", +// ); const drawerWidth = 256; const playbackFPS = 16; From 7bf871027e4ffe5dddb45706f6a438b37e355e3a Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 13:24:27 -0700 Subject: [PATCH 12/35] scale canvas to screen (tablet/phone/desktop) to prevent the playBackControls to be hidden --- src/components/App.tsx | 124 ++++++++++++++++++- src/components/overlays/ColorMap.tsx | 2 +- src/components/overlays/TimestampOverlay.tsx | 2 +- 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 481175dd..16474f32 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -290,8 +290,29 @@ export default function App() { return trackData.map((row) => row.map(formatter.format)); }; + // Resize the canvas dynamically based on window size + const updateCanvasSize = useCallback(() => { + const sceneElement = sceneDivRef.current; + if (sceneElement) { + const windowHeight = window.innerHeight; + const playbackHeight = 100; // Assume playback controls take around 100px of height + const availableHeight = windowHeight - playbackHeight; + + sceneElement.style.height = `${availableHeight}px`; // Set the canvas height dynamically + } + }, [sceneDivRef]); + + useEffect(() => { + window.addEventListener("resize", updateCanvasSize); + updateCanvasSize(); // Call it initially + return () => { + window.removeEventListener("resize", updateCanvasSize); + }; + }, [updateCanvasSize]); + return ( - + // + {/* TODO: components *could* go deeper still for organization */} {!detectedDevice.isPhone && ( )} - + */} + + + + {/* + 0} /> + + {/* The playback controls */} + {/* + + + { + dispatchCanvas({ type: ActionType.AUTO_ROTATE, autoRotate }); + }} + setPlaying={setPlaying} + setCurTime={(curTime: number) => { + dispatchCanvas({ type: ActionType.CUR_TIME, curTime }); + }} + /> + */} + + + {/* The canvas (Scene component) */} + + 0} /> + + + + + {/* The playback controls */} + + { + dispatchCanvas({ type: ActionType.AUTO_ROTATE, autoRotate }); + }} + setPlaying={setPlaying} + setCurTime={(curTime: number) => { + dispatchCanvas({ type: ActionType.CUR_TIME, curTime }); + }} + /> + + + ); } diff --git a/src/components/overlays/ColorMap.tsx b/src/components/overlays/ColorMap.tsx index 4c854e8e..8350762d 100644 --- a/src/components/overlays/ColorMap.tsx +++ b/src/components/overlays/ColorMap.tsx @@ -11,7 +11,7 @@ export const ColorMap = () => { { size="small" sx={{ position: "absolute", - bottom: "4.75rem", + bottom: "0.5rem", left: "16.5rem", backgroundColor: "rgba(255, 255, 255, 0.6)", zIndex: 100, From d1bce74462f17c99ac8686fc0bbf798924c3ac90 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 13:36:46 -0700 Subject: [PATCH 13/35] added margins for phone/tablet --- src/components/App.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 16474f32..3df08f95 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -43,9 +43,14 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo const isSmallScreen = window.screen.width <= 768; // Determine if it's a phone, tablet, or desktop - const isPhone = isiPhoneOrIPod || isAndroidPhone || isSmallScreen; - const isTablet = isiPad || isAndroidTablet || (!isPhone && isSmallScreen && window.screen.width > 600); // Optional: Add a threshold for larger screens - const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet + // const isPhone = isiPhoneOrIPod || isAndroidPhone || isSmallScreen; + // const isTablet = isiPad || isAndroidTablet || (!isPhone && isSmallScreen && window.screen.width > 600); // Optional: Add a threshold for larger screens + // const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet + + const isPhone = false; + const isTablet = true; + const isDesktop = false; // It's a desktop if it's neither a phone nor a tablet + console.log("isPhone: ", isPhone); return { isPhone: isPhone, @@ -519,8 +524,8 @@ export default function App() { sx={{ flexGrow: 0, padding: ".5em", - height: "50px", - paddingLeft: !detectedDevice.isMobile ? `${drawerWidth}px` : 0, // Ensure playback controls are visible + height: "100px", + paddingLeft: !detectedDevice.isPhone ? `${drawerWidth}px` : 0, // Ensure playback controls are visible }} > Date: Tue, 1 Oct 2024 13:45:16 -0700 Subject: [PATCH 14/35] corrected typos --- src/components/App.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 3df08f95..5327c7f8 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -43,14 +43,14 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo const isSmallScreen = window.screen.width <= 768; // Determine if it's a phone, tablet, or desktop - // const isPhone = isiPhoneOrIPod || isAndroidPhone || isSmallScreen; - // const isTablet = isiPad || isAndroidTablet || (!isPhone && isSmallScreen && window.screen.width > 600); // Optional: Add a threshold for larger screens - // const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet + const isPhone = isiPhoneOrIPod || isAndroidPhone || isSmallScreen; + const isTablet = isiPad || isAndroidTablet || (!isPhone && isSmallScreen && window.screen.width > 600); // Optional: Add a threshold for larger screens + const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet - const isPhone = false; - const isTablet = true; - const isDesktop = false; // It's a desktop if it's neither a phone nor a tablet - console.log("isPhone: ", isPhone); + // manually asign labels for debugging + // const isPhone = false; + // const isTablet = true; + // const isDesktop = false; return { isPhone: isPhone, @@ -446,8 +446,6 @@ export default function App() { /> */} - - {/* - */} + */}
-
); } From 06df6d1456c97a5b6940efe83bd86b2964b2b721 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 13:57:30 -0700 Subject: [PATCH 15/35] clean up --- src/components/App.tsx | 101 ++--------------------------------------- 1 file changed, 3 insertions(+), 98 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 5327c7f8..1655f777 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -295,28 +295,7 @@ export default function App() { return trackData.map((row) => row.map(formatter.format)); }; - // Resize the canvas dynamically based on window size - const updateCanvasSize = useCallback(() => { - const sceneElement = sceneDivRef.current; - if (sceneElement) { - const windowHeight = window.innerHeight; - const playbackHeight = 100; // Assume playback controls take around 100px of height - const availableHeight = windowHeight - playbackHeight; - - sceneElement.style.height = `${availableHeight}px`; // Set the canvas height dynamically - } - }, [sceneDivRef]); - - useEffect(() => { - window.addEventListener("resize", updateCanvasSize); - updateCanvasSize(); // Call it initially - return () => { - window.removeEventListener("resize", updateCanvasSize); - }; - }, [updateCanvasSize]); - return ( - // {/* TODO: components *could* go deeper still for organization */} {!detectedDevice.isPhone && ( @@ -416,83 +395,9 @@ export default function App() { )} - - {/* - 0} /> - - - - { - dispatchCanvas({ type: ActionType.AUTO_ROTATE, autoRotate }); - }} - setPlaying={setPlaying} - setCurTime={(curTime: number) => { - dispatchCanvas({ type: ActionType.CUR_TIME, curTime }); - }} - /> - - */} - - {/* - 0} /> - - - {/* The playback controls */} - {/* - - - { - dispatchCanvas({ type: ActionType.AUTO_ROTATE, autoRotate }); - }} - setPlaying={setPlaying} - setCurTime={(curTime: number) => { - dispatchCanvas({ type: ActionType.CUR_TIME, curTime }); - }} - /> - */} - + {/* Box for Scene + playBackControls */} - {/* The canvas (Scene component) */} + {/* The canvas (Scene + colormap + timestamp) */} From 3381a086aea78262d0a96fcb813be3d04c7b2be1 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 14:13:04 -0700 Subject: [PATCH 16/35] don't reduce point brightness when no cells are selected --- src/hooks/usePointCanvas.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/usePointCanvas.ts b/src/hooks/usePointCanvas.ts index e0178034..5a97b160 100644 --- a/src/hooks/usePointCanvas.ts +++ b/src/hooks/usePointCanvas.ts @@ -174,13 +174,15 @@ function reducer(canvas: PointCanvas, action: PointCanvasAction): PointCanvas { newCanvas.updateAllTrackHighlights(); break; case ActionType.ADD_SELECTED_POINT_IDS: { - newCanvas.pointBrightness = 0.8; - newCanvas.resetPointColors(); - // newCanvas.highlightPoints(action.selectedPointIndices); const newSelectedPointIds = new Set(canvas.selectedPointIds); for (const trackId of action.selectedPointIds) { newSelectedPointIds.add(trackId); } + if (action.selectedPointIds.size !== 0) { + // only reduce pointBrightness if there are selected points + newCanvas.pointBrightness = 0.8; + } + newCanvas.resetPointColors(); newCanvas.selectedPointIds = newSelectedPointIds; newCanvas.highlightPoints(action.selectedPointIndices); break; From 074ec8ad9f40d60ecf644daf8d2cac2e6d41eef2 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 14:28:52 -0700 Subject: [PATCH 17/35] remove spherical selector when no mode is selected --- src/components/App.tsx | 2 +- src/lib/PointSelector.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 1655f777..73f4ece0 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -427,7 +427,7 @@ export default function App() { sx={{ flexGrow: 0, padding: ".5em", - height: detectedDevice.isMobile ? "150px" : "50px", + height: detectedDevice.isMobile ? "150px" : "50px", // leaving extra space for mobile paddingLeft: !detectedDevice.isPhone ? `${drawerWidth}px` : 0, // Ensure playback controls are visible }} > diff --git a/src/lib/PointSelector.ts b/src/lib/PointSelector.ts index f82b2704..d8faa82b 100644 --- a/src/lib/PointSelector.ts +++ b/src/lib/PointSelector.ts @@ -97,7 +97,7 @@ export class PointSelector { setSelectionMode(mode: PointSelectionMode) { console.debug("PointSelector.setSelectionMode: ", mode); this.selectionMode = mode; - this.sphereSelector.setVisible(mode !== PointSelectionMode.BOX, mode === PointSelectionMode.SPHERE); + this.sphereSelector.setVisible((mode === PointSelectionMode.SPHERICAL_CURSOR || mode === PointSelectionMode.SPHERE), mode === PointSelectionMode.SPHERE); } handleEvent(event: Event) { From ac990b00448ee000b92a010f48d90ed3d5d4eb7f Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 14:48:57 -0700 Subject: [PATCH 18/35] disable any selector on phone --- src/components/App.tsx | 4 ++-- src/components/CellControls.tsx | 2 +- src/components/leftSidebar/ControlInstructions.tsx | 2 +- src/components/leftSidebar/LeftSidebarWrapper.tsx | 2 +- src/lib/PointCanvas.ts | 6 ++++-- src/lib/PointSelector.ts | 9 ++++++--- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 73f4ece0..26f03150 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -48,8 +48,8 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet // manually asign labels for debugging - // const isPhone = false; - // const isTablet = true; + // const isPhone = true; + // const isTablet = false; // const isDesktop = false; return { diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index 3265177b..9c817cd9 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -14,7 +14,7 @@ interface CellControlsProps { trackManager: TrackManager | null; pointBrightness: number; setPointBrightness: (value: number) => void; - selectionMode: PointSelectionMode; + selectionMode: PointSelectionMode | null; setSelectionMode: (value: PointSelectionMode) => void; isMobile: boolean; MobileSelectCells: () => void; diff --git a/src/components/leftSidebar/ControlInstructions.tsx b/src/components/leftSidebar/ControlInstructions.tsx index 7dd2dbb0..a595bad1 100644 --- a/src/components/leftSidebar/ControlInstructions.tsx +++ b/src/components/leftSidebar/ControlInstructions.tsx @@ -3,7 +3,7 @@ import { Callout } from "@czi-sds/components"; import { PointSelectionMode } from "@/lib/PointSelector"; interface ControlInstructionsProps { - selectionMode: PointSelectionMode; + selectionMode: PointSelectionMode | null; } export default function ControlInstructions(props: ControlInstructionsProps) { diff --git a/src/components/leftSidebar/LeftSidebarWrapper.tsx b/src/components/leftSidebar/LeftSidebarWrapper.tsx index d82a6b79..40f858d8 100644 --- a/src/components/leftSidebar/LeftSidebarWrapper.tsx +++ b/src/components/leftSidebar/LeftSidebarWrapper.tsx @@ -12,7 +12,7 @@ interface LeftSidebarWrapperProps { showTrackHighlights: boolean; setShowTrackHighlights: (showTrackHighlights: boolean) => void; setTrackHighlightLength: (trackHighlightLength: number) => void; - selectionMode: PointSelectionMode; + selectionMode: PointSelectionMode | null; } export default function LeftSidebarWrapper({ diff --git a/src/lib/PointCanvas.ts b/src/lib/PointCanvas.ts index 9f2a17f3..ee972d2a 100644 --- a/src/lib/PointCanvas.ts +++ b/src/lib/PointCanvas.ts @@ -150,8 +150,10 @@ export class PointCanvas { // Set up selection this.selector = new PointSelector(this.scene, this.renderer, this.camera, this.controls, this.points); - if (detectedDevice.isMobile) { + if (detectedDevice.isTablet) { this.setSelectionMode(PointSelectionMode.SPHERE); + } else if (detectedDevice.isPhone) { + this.setSelectionMode(null); // no selection functionality on phone } else { this.setSelectionMode(PointSelectionMode.BOX); } @@ -191,7 +193,7 @@ export class PointCanvas { this.controls.target.fromArray(state.cameraTarget); } - setSelectionMode(mode: PointSelectionMode) { + setSelectionMode(mode: PointSelectionMode | null) { this.selector.setSelectionMode(mode); } diff --git a/src/lib/PointSelector.ts b/src/lib/PointSelector.ts index d8faa82b..3605c47b 100644 --- a/src/lib/PointSelector.ts +++ b/src/lib/PointSelector.ts @@ -32,7 +32,7 @@ export class PointSelector { readonly boxSelector: BoxPointSelector; readonly sphereSelector: SpherePointSelector; - selectionMode: PointSelectionMode = PointSelectionMode.BOX; + selectionMode: PointSelectionMode | null = PointSelectionMode.BOX; // To notify external observers about changes to the current selection. selectionChanged: SelectionChanged = (_selection: number[]) => {}; @@ -94,10 +94,13 @@ export class PointSelector { this.selectionChanged(selection); } - setSelectionMode(mode: PointSelectionMode) { + setSelectionMode(mode: PointSelectionMode | null) { console.debug("PointSelector.setSelectionMode: ", mode); this.selectionMode = mode; - this.sphereSelector.setVisible((mode === PointSelectionMode.SPHERICAL_CURSOR || mode === PointSelectionMode.SPHERE), mode === PointSelectionMode.SPHERE); + this.sphereSelector.setVisible( + mode === PointSelectionMode.SPHERICAL_CURSOR || mode === PointSelectionMode.SPHERE, + mode === PointSelectionMode.SPHERE, + ); } handleEvent(event: Event) { From d98919e11ef8d25c310a5e721bbfd2bff3680f48 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 15:00:59 -0700 Subject: [PATCH 19/35] on iPad: only show the select cells button if the selection mode is actually selected. The button disappears when the user deselects any mode --- src/components/App.tsx | 4 ++-- src/components/CellControls.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 26f03150..73f4ece0 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -48,8 +48,8 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet // manually asign labels for debugging - // const isPhone = true; - // const isTablet = false; + // const isPhone = false; + // const isTablet = true; // const isDesktop = false; return { diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index 9c817cd9..1d9cf144 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -68,7 +68,7 @@ export default function CellControls(props: CellControlsProps) { /> - {props.isMobile && ( + {props.isMobile && props.selectionMode === PointSelectionMode.SPHERE && ( From 27e8988b1ce80597c4510c1d9a57ef09e4027724 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 15:09:50 -0700 Subject: [PATCH 20/35] selection info box disappears when no mode is selected --- src/components/leftSidebar/LeftSidebarWrapper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/leftSidebar/LeftSidebarWrapper.tsx b/src/components/leftSidebar/LeftSidebarWrapper.tsx index 40f858d8..d7b507c7 100644 --- a/src/components/leftSidebar/LeftSidebarWrapper.tsx +++ b/src/components/leftSidebar/LeftSidebarWrapper.tsx @@ -39,7 +39,7 @@ export default function LeftSidebarWrapper({ setTrackHighlightLength={setTrackHighlightLength} /> )} - + {selectionMode !== null && } ); } From 8b08ed41f21c9a9868019f914cfbac873083567b Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 1 Oct 2024 15:12:49 -0700 Subject: [PATCH 21/35] center the select cells button on --- src/components/CellControls.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index 1d9cf144..c8fee9c5 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -67,7 +67,7 @@ export default function CellControls(props: CellControlsProps) { value={props.selectionMode} /> - + {props.isMobile && props.selectionMode === PointSelectionMode.SPHERE && ( )} + + `${value}`} + onChange={(_, value) => { + props.setSelectorScale(value as number); + }} + /> diff --git a/src/hooks/usePointCanvas.ts b/src/hooks/usePointCanvas.ts index 5a97b160..d1403539 100644 --- a/src/hooks/usePointCanvas.ts +++ b/src/hooks/usePointCanvas.ts @@ -20,6 +20,7 @@ enum ActionType { ADD_SELECTED_POINT_IDS = "ADD_SELECTED_POINT_IDS", UPDATE_WITH_STATE = "UPDATE_WITH_STATE", MOBILE_SELECT_CELLS = "MOBILE_SELECT_CELLS", + SELECTOR_SCALE = "SELECTOR_SCALE", } interface AutoRotate { @@ -97,6 +98,11 @@ interface MobileSelectCells { type: ActionType.MOBILE_SELECT_CELLS; } +interface SelectorScale { + type: ActionType.SELECTOR_SCALE; + scale: number; +} + // setting up a tagged union for the actions type PointCanvasAction = | AutoRotate @@ -113,7 +119,8 @@ type PointCanvasAction = | MinMaxTime | AddSelectedPointIds | UpdateWithState - | MobileSelectCells; + | MobileSelectCells + | SelectorScale; function reducer(canvas: PointCanvas, action: PointCanvasAction): PointCanvas { console.debug("usePointCanvas.reducer: ", action); @@ -193,6 +200,9 @@ function reducer(canvas: PointCanvas, action: PointCanvasAction): PointCanvas { case ActionType.MOBILE_SELECT_CELLS: newCanvas.MobileSelectCells(); break; + case ActionType.SELECTOR_SCALE: + newCanvas.setSelectorScale(action.scale); + break; default: console.warn("usePointCanvas reducer - unknown action type: %s", action); return canvas; diff --git a/src/lib/PointCanvas.ts b/src/lib/PointCanvas.ts index ee972d2a..f8ac7fb5 100644 --- a/src/lib/PointCanvas.ts +++ b/src/lib/PointCanvas.ts @@ -376,7 +376,12 @@ export class PointCanvas { } MobileSelectCells() { - // if used on Mobile Device, this will select the cells upon button click + // if used on tablet, this will select the cells upon button click this.selector.sphereSelector.MobileFindAndSelect(); } + + setSelectorScale(scale: number) { + // on tablet: this will set the size of the sphere selector upon the user using the slider + this.selector.sphereSelector.cursor.scale.set(scale, scale, scale); + } } From f47a0be93c1ef513259acb32ecc9c6b2fe86051d Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 11:04:47 -0700 Subject: [PATCH 25/35] fixed bug in selector scale slider --- src/components/App.tsx | 3 ++- src/components/CellControls.tsx | 46 ++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 267715cc..5c2e6c8d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -348,13 +348,14 @@ export default function App() { setSelectionMode={(value: PointSelectionMode) => { dispatchCanvas({ type: ActionType.SELECTION_MODE, selectionMode: value }); }} - isMobile={detectedDevice.isMobile} + isTablet={detectedDevice.isTablet} MobileSelectCells={() => { dispatchCanvas({ type: ActionType.MOBILE_SELECT_CELLS }); }} setSelectorScale={(scale: number) => { dispatchCanvas({ type: ActionType.SELECTOR_SCALE, scale }); }} + selectorScale={canvas.selector.sphereSelector.cursor.scale.x} /> diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index bfca8f6e..184d158f 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -16,9 +16,10 @@ interface CellControlsProps { setPointBrightness: (value: number) => void; selectionMode: PointSelectionMode | null; setSelectionMode: (value: PointSelectionMode) => void; - isMobile: boolean; + isTablet: boolean; MobileSelectCells: () => void; setSelectorScale: (value: number) => void; + selectorScale: number; } export default function CellControls(props: CellControlsProps) { @@ -32,7 +33,7 @@ export default function CellControls(props: CellControlsProps) { const handleSegmentedControlChange = (_e: React.MouseEvent, newValue: PointSelectionMode | null) => { // If isMobile is true and the selected value corresponds to the first or second button, do nothing if ( - props.isMobile && + props.isTablet && (newValue === PointSelectionMode.BOX || newValue === PointSelectionMode.SPHERICAL_CURSOR) ) { window.alert("This selection mode is not available on mobile devices."); @@ -69,27 +70,36 @@ export default function CellControls(props: CellControlsProps) { /> - {props.isMobile && props.selectionMode === PointSelectionMode.SPHERE && ( + {props.isTablet && props.selectionMode === PointSelectionMode.SPHERE && ( )} - - `${value}`} - onChange={(_, value) => { - props.setSelectorScale(value as number); - }} - /> + {(props.selectionMode === PointSelectionMode.SPHERICAL_CURSOR || + props.selectionMode === PointSelectionMode.SPHERE) && + props.isTablet && ( + <> +
+ +
+ `${value}`} + onChange={(_, value) => { + props.setSelectorScale(value as number); + }} + value={props.selectorScale} + /> + + )} From 7ccee15b562f077029ee7a4e03c5f7073caac5a5 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 14:07:30 -0700 Subject: [PATCH 26/35] updated ControlInstructions syntax --- .../leftSidebar/ControlInstructions.tsx | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/components/leftSidebar/ControlInstructions.tsx b/src/components/leftSidebar/ControlInstructions.tsx index a595bad1..cf9c4104 100644 --- a/src/components/leftSidebar/ControlInstructions.tsx +++ b/src/components/leftSidebar/ControlInstructions.tsx @@ -15,24 +15,48 @@ export default function ControlInstructions(props: ControlInstructionsProps) { case PointSelectionMode.SPHERICAL_CURSOR: instructionText = ( <> -

Hold Shift and a sphere will follow your cursor, centering on nearby points.

-

Shift-Click to select cells within the sphere.

-

Additional controls:

-

Ctrl+scroll: scale sphere

-

s: show/hide sphere

+

+ Hold Shift and the selector will follow your cursor. +

+

+ Shift-Click to select cells within the sphere. +

+ Additional controls: +
    +
  • + ctrl+scroll: scale sphere +
  • +
  • + s: show/hide sphere +
  • +
); break; case PointSelectionMode.SPHERE: instructionText = ( <> -

Shift-click to select cells within the sphere.

-

Additional controls:

-

w: position mode

-

e: rotation mode

-

r: scale mode

-

Ctrl+scroll: scale

-

s: show/hide sphere

+

+ Shift-click to select cells within the selector. +

+ Additional controls: +
    +
  • + w: position +
  • +
  • + e: rotation +
  • +
  • + r: scale +
  • +
  • + Ctrl+scroll: scale +
  • +
  • + s: show/hide selector +
  • +
); break; From 1f1d5ebe77ac3f8e434dae935472fe73025eed3a Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 14:29:03 -0700 Subject: [PATCH 27/35] added conditional iPad statement to the ConrolInstructions --- src/components/App.tsx | 1 + src/components/CellControls.tsx | 2 +- src/components/leftSidebar/ControlInstructions.tsx | 4 ++++ src/components/leftSidebar/LeftSidebarWrapper.tsx | 4 +++- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 5c2e6c8d..e805f061 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -383,6 +383,7 @@ export default function App() { maxTime: canvas.curTime + length / 2, }); }} + isTablet={detectedDevice.isTablet} />
diff --git a/src/components/CellControls.tsx b/src/components/CellControls.tsx index 184d158f..f1c98107 100644 --- a/src/components/CellControls.tsx +++ b/src/components/CellControls.tsx @@ -31,7 +31,7 @@ export default function CellControls(props: CellControlsProps) { // Intercept onChange of selection buttons to prevent the first two buttons from being selected on mobile devices const handleSegmentedControlChange = (_e: React.MouseEvent, newValue: PointSelectionMode | null) => { - // If isMobile is true and the selected value corresponds to the first or second button, do nothing + // If isTablet is true and the selected value corresponds to the first or second button, do nothing if ( props.isTablet && (newValue === PointSelectionMode.BOX || newValue === PointSelectionMode.SPHERICAL_CURSOR) diff --git a/src/components/leftSidebar/ControlInstructions.tsx b/src/components/leftSidebar/ControlInstructions.tsx index cf9c4104..60079260 100644 --- a/src/components/leftSidebar/ControlInstructions.tsx +++ b/src/components/leftSidebar/ControlInstructions.tsx @@ -4,6 +4,7 @@ import { PointSelectionMode } from "@/lib/PointSelector"; interface ControlInstructionsProps { selectionMode: PointSelectionMode | null; + isTablet: boolean; } export default function ControlInstructions(props: ControlInstructionsProps) { @@ -36,6 +37,9 @@ export default function ControlInstructions(props: ControlInstructionsProps) { case PointSelectionMode.SPHERE: instructionText = ( <> + {props.isTablet && ( +

If using a tablet without keyboard, use the UI controls above to scale the selector

+ )}

Shift-click to select cells within the selector.

diff --git a/src/components/leftSidebar/LeftSidebarWrapper.tsx b/src/components/leftSidebar/LeftSidebarWrapper.tsx index d7b507c7..d7f23c8c 100644 --- a/src/components/leftSidebar/LeftSidebarWrapper.tsx +++ b/src/components/leftSidebar/LeftSidebarWrapper.tsx @@ -13,6 +13,7 @@ interface LeftSidebarWrapperProps { setShowTrackHighlights: (showTrackHighlights: boolean) => void; setTrackHighlightLength: (trackHighlightLength: number) => void; selectionMode: PointSelectionMode | null; + isTablet: boolean; } export default function LeftSidebarWrapper({ @@ -25,6 +26,7 @@ export default function LeftSidebarWrapper({ setShowTrackHighlights, setTrackHighlightLength, selectionMode, + isTablet, }: LeftSidebarWrapperProps) { return ( <> @@ -39,7 +41,7 @@ export default function LeftSidebarWrapper({ setTrackHighlightLength={setTrackHighlightLength} /> )} - {selectionMode !== null && } + {selectionMode !== null && } ); } From 5d184aaeee1bf552a96591bfaf8b725c06543a0b Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 14:30:38 -0700 Subject: [PATCH 28/35] minor textual change --- src/components/leftSidebar/ControlInstructions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/leftSidebar/ControlInstructions.tsx b/src/components/leftSidebar/ControlInstructions.tsx index 60079260..353a6535 100644 --- a/src/components/leftSidebar/ControlInstructions.tsx +++ b/src/components/leftSidebar/ControlInstructions.tsx @@ -38,7 +38,7 @@ export default function ControlInstructions(props: ControlInstructionsProps) { instructionText = ( <> {props.isTablet && ( -

If using a tablet without keyboard, use the UI controls above to scale the selector

+

If using a tablet without keyboard, use the UI controls above to select cells and scale the selector

)}

Shift-click to select cells within the selector. From d835f959e32c6275035f375c1c18635328d6a6bd Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 14:47:28 -0700 Subject: [PATCH 29/35] fixed lint issue --- src/components/leftSidebar/ControlInstructions.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/leftSidebar/ControlInstructions.tsx b/src/components/leftSidebar/ControlInstructions.tsx index 353a6535..78a4d89f 100644 --- a/src/components/leftSidebar/ControlInstructions.tsx +++ b/src/components/leftSidebar/ControlInstructions.tsx @@ -38,7 +38,10 @@ export default function ControlInstructions(props: ControlInstructionsProps) { instructionText = ( <> {props.isTablet && ( -

If using a tablet without keyboard, use the UI controls above to select cells and scale the selector

+

+ If using a tablet without keyboard, use the UI controls above to select cells and scale the + selector +

)}

Shift-click to select cells within the selector. From 47a3af070e06513f9555bebb539cf34855f5c305 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 16:15:41 -0700 Subject: [PATCH 30/35] popup on phone --- src/components/App.tsx | 13 +++++++++---- src/components/leftSidebar/ControlInstructions.tsx | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index e805f061..fba94a37 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -41,15 +41,16 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo // Screen size check (tablets typically have a wider screen) const isSmallScreen = window.screen.width <= 768; + const hasTouch = navigator.maxTouchPoints > 1; // Determine if it's a phone, tablet, or desktop const isPhone = isiPhoneOrIPod || isAndroidPhone || isSmallScreen; - const isTablet = isiPad || isAndroidTablet || (!isPhone && isSmallScreen && window.screen.width > 600); // Optional: Add a threshold for larger screens + const isTablet = isiPad || isAndroidTablet || hasTouch; const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet // manually asign labels for debugging - // const isPhone = false; - // const isTablet = true; + // const isPhone = true; + // const isTablet = false; // const isDesktop = false; return { @@ -60,7 +61,11 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo } export const detectedDevice = detectDeviceType(); -console.log("detectDeviceType: ", detectDeviceType()); +console.debug("detectDeviceType: ", detectedDevice); +if (detectedDevice.isPhone) { + window.alert("Note: for full functionality, please use a tablet or desktop device."); +} + // for debugging: show the detected device type in an alert // window.alert( // "detected device type (desktop | tablet | phone) = (" + diff --git a/src/components/leftSidebar/ControlInstructions.tsx b/src/components/leftSidebar/ControlInstructions.tsx index 78a4d89f..b5de958d 100644 --- a/src/components/leftSidebar/ControlInstructions.tsx +++ b/src/components/leftSidebar/ControlInstructions.tsx @@ -40,7 +40,7 @@ export default function ControlInstructions(props: ControlInstructionsProps) { {props.isTablet && (

If using a tablet without keyboard, use the UI controls above to select cells and scale the - selector + selector (use keyboard for full functionality)

)}

From 9a76187647b7d85014b253ae6f8ce35e7c5437e9 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 16:27:59 -0700 Subject: [PATCH 31/35] changed phone popup from alert to confirm --- src/components/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index fba94a37..8a24b133 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -63,11 +63,11 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo export const detectedDevice = detectDeviceType(); console.debug("detectDeviceType: ", detectedDevice); if (detectedDevice.isPhone) { - window.alert("Note: for full functionality, please use a tablet or desktop device."); + window.confirm("Note: for full functionality, please use a tablet or desktop device."); } // for debugging: show the detected device type in an alert -// window.alert( +// window.confirm( // "detected device type (desktop | tablet | phone) = (" + // !detectDeviceType().isMobile + // " | " + From 2242ed7aee9406d20b59e04d24a96ab96c40551d Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Wed, 2 Oct 2024 16:31:19 -0700 Subject: [PATCH 32/35] change to popup --- src/components/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 8a24b133..8df821db 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -63,7 +63,7 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo export const detectedDevice = detectDeviceType(); console.debug("detectDeviceType: ", detectedDevice); if (detectedDevice.isPhone) { - window.confirm("Note: for full functionality, please use a tablet or desktop device."); + window.confirm("Note: for full functionality, please use a tablet or desktop device. Press 'OK' to continue "); } // for debugging: show the detected device type in an alert From d0fd8b75b533f68ffdae011dd20d48fe405f5533 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 8 Oct 2024 11:13:47 -0700 Subject: [PATCH 33/35] changed data path --- CONFIG.ts | 2 +- src/components/App.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONFIG.ts b/CONFIG.ts index 8a41c32d..b00e013c 100644 --- a/CONFIG.ts +++ b/CONFIG.ts @@ -10,7 +10,7 @@ const config = { // When opening the viewer, or refreshing the page, the viewer will revert to the following default dataset data:{ // Default dataset URL (must be publically accessible) - default_dataset: "https://public.czbiohub.org/royerlab/zoo/Zebrafish/tracks_centered_bundle.zarr/" + default_dataset: "https://public.czbiohub.org/royerlab/zoo/Zebrafish/tracks_centered_iso_bundle.zarr/" }, // Default settings for certain parameters diff --git a/src/components/App.tsx b/src/components/App.tsx index 8df821db..bcba4415 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -49,8 +49,8 @@ function detectDeviceType(): { isPhone: boolean; isTablet: boolean; isMobile: bo const isDesktop = !isPhone && !isTablet; // It's a desktop if it's neither a phone nor a tablet // manually asign labels for debugging - // const isPhone = true; - // const isTablet = false; + // const isPhone = false; + // const isTablet = true; // const isDesktop = false; return { From ef61364f3b64d4f1082526e789aa27d4b5a5f3fa Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 8 Oct 2024 12:04:53 -0700 Subject: [PATCH 34/35] keep preview when setting brightness or changing the selector size on iPad --- src/hooks/usePointCanvas.ts | 2 ++ src/lib/PointCanvas.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/usePointCanvas.ts b/src/hooks/usePointCanvas.ts index 42eb0de1..9e60d686 100644 --- a/src/hooks/usePointCanvas.ts +++ b/src/hooks/usePointCanvas.ts @@ -177,6 +177,7 @@ function reducer(canvas: PointCanvas, action: PointCanvasAction): PointCanvas { newCanvas.pointBrightness = action.brightness; newCanvas.resetPointColors(); newCanvas.updateSelectedPointIndices(); + newCanvas.updatePreviewPoints(); break; case ActionType.POINT_SIZES: newCanvas.pointSize = action.pointSize; @@ -263,6 +264,7 @@ function reducer(canvas: PointCanvas, action: PointCanvasAction): PointCanvas { break; case ActionType.SELECTOR_SCALE: newCanvas.setSelectorScale(action.scale); + newCanvas.updatePreviewPoints(); break; default: console.warn("usePointCanvas reducer - unknown action type: %s", action); diff --git a/src/lib/PointCanvas.ts b/src/lib/PointCanvas.ts index 7805482c..d34b3c09 100644 --- a/src/lib/PointCanvas.ts +++ b/src/lib/PointCanvas.ts @@ -263,7 +263,6 @@ export class PointCanvas { } updatePreviewPoints() { - console.log("line 266 in PointCanvas.ts"); this.selector.sphereSelector.findPointsWithinSelector(); } From edbe9bfbddc5147cc38b2f4cf0998e103a8e4f69 Mon Sep 17 00:00:00 2001 From: TeunHuijben Date: Tue, 8 Oct 2024 15:48:18 -0700 Subject: [PATCH 35/35] fixed that DataControls stay on pays and dont scroll --- src/components/App.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 46df1808..575cf1cd 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -383,7 +383,13 @@ export default function App() { {brandingLogoPath && brandingName && } {brandingName &&

{brandingName}

}{" "} - + { dispatchCanvas({ type: ActionType.REMOVE_ALL_TRACKS }); @@ -413,9 +419,7 @@ export default function App() { }} selectorScale={canvas.selector.sphereSelector.cursor.scale.x} /> - - - + 0} trackManager={trackManager}