Scale BIM empty-space wheel zoom
This commit is contained in:
parent
b90142a6c8
commit
9dc5dd4aa0
|
|
@ -303,6 +303,8 @@ let modelVersionHistoryLoading = false;
|
|||
let modelVersionUploading = false;
|
||||
let versionHistoryModal = null;
|
||||
let emptyWheelNavigationRestoreTimer = null;
|
||||
let lastSurfaceDollyFactor = null;
|
||||
let lastSurfaceDollyFactorAt = 0;
|
||||
const GUEST_TOOLBAR_ACTIONS = new Set(["measure", "section", "projection"]);
|
||||
const memoryStore = new Map(); // url -> ArrayBuffer
|
||||
const blobUrls = [];
|
||||
|
|
@ -387,6 +389,7 @@ const ZOOM_SPEED_PERCENT_MAX = 100;
|
|||
const ZOOM_DOLLY_RATE_MIN = 1;
|
||||
const ZOOM_DOLLY_RATE_MAX = 100;
|
||||
const ZOOM_DOLLY_RESPONSE_EXPONENT = 1.45;
|
||||
const LAST_SURFACE_DOLLY_FACTOR_TTL = 1500;
|
||||
|
||||
const normalizeZoomSpeed = (value) => {
|
||||
const numeric = Number(value);
|
||||
|
|
@ -401,6 +404,8 @@ const getZoomDollyRate = (value) => {
|
|||
return ZOOM_DOLLY_RATE_MIN + (Math.pow(t, ZOOM_DOLLY_RESPONSE_EXPONENT) * (ZOOM_DOLLY_RATE_MAX - ZOOM_DOLLY_RATE_MIN));
|
||||
};
|
||||
|
||||
const getBaseMouseWheelDollyRate = () => getZoomDollyRate(activeZoomSpeed);
|
||||
|
||||
const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim());
|
||||
|
||||
const normalizeCustomDisplayColor = (value) => {
|
||||
|
|
@ -466,7 +471,7 @@ const configureCameraControl = () => {
|
|||
control.smartPivot = true;
|
||||
control.zoomOnMouseWheel = true;
|
||||
control.keyboardDollyRate = CAMERA_CONTROL_SETTINGS.keyboardDollyRate;
|
||||
control.mouseWheelDollyRate = getZoomDollyRate(activeZoomSpeed);
|
||||
control.mouseWheelDollyRate = getBaseMouseWheelDollyRate();
|
||||
control.dollyInertia = CAMERA_CONTROL_SETTINGS.dollyInertia;
|
||||
control.dollyMinSpeed = CAMERA_CONTROL_SETTINGS.dollyMinSpeed;
|
||||
control.dollyProximityThreshold = CAMERA_CONTROL_SETTINGS.dollyProximityThreshold;
|
||||
|
|
@ -1341,6 +1346,60 @@ const getCanvasPointFromEvent = (event) => {
|
|||
};
|
||||
};
|
||||
|
||||
const getDollyFactorForDistance = (distance) => {
|
||||
const numeric = Number(distance);
|
||||
if (!Number.isFinite(numeric) || numeric <= 0) return 1;
|
||||
return Math.max(
|
||||
CAMERA_CONTROL_SETTINGS.dollyMinSpeed,
|
||||
numeric / CAMERA_CONTROL_SETTINGS.dollyProximityThreshold,
|
||||
);
|
||||
};
|
||||
|
||||
const getDollyFactorForWorldPos = (worldPos) => {
|
||||
const eye = viewer?.camera?.eye || viewer?.scene?.camera?.eye;
|
||||
if (!eye || !worldPos || worldPos.length < 3) return null;
|
||||
const distance = Math.hypot(
|
||||
Number(worldPos[0]) - Number(eye[0]),
|
||||
Number(worldPos[1]) - Number(eye[1]),
|
||||
Number(worldPos[2]) - Number(eye[2]),
|
||||
);
|
||||
return Number.isFinite(distance) ? getDollyFactorForDistance(distance) : null;
|
||||
};
|
||||
|
||||
const getCameraFocusDollyFactor = () => {
|
||||
const camera = viewer?.camera || viewer?.scene?.camera;
|
||||
const directDistance = Number(camera?.eyeLookDist);
|
||||
if (Number.isFinite(directDistance) && directDistance > 0) {
|
||||
return getDollyFactorForDistance(directDistance);
|
||||
}
|
||||
const eye = camera?.eye;
|
||||
const look = camera?.look;
|
||||
if (!eye || !look) return 1;
|
||||
const distance = Math.hypot(
|
||||
Number(look[0]) - Number(eye[0]),
|
||||
Number(look[1]) - Number(eye[1]),
|
||||
Number(look[2]) - Number(eye[2]),
|
||||
);
|
||||
return Number.isFinite(distance) ? getDollyFactorForDistance(distance) : 1;
|
||||
};
|
||||
|
||||
const rememberSurfaceDollyFactor = (hit) => {
|
||||
const factor = getDollyFactorForWorldPos(hit?.worldPos);
|
||||
if (factor === null) return;
|
||||
lastSurfaceDollyFactor = factor;
|
||||
lastSurfaceDollyFactorAt = window.performance?.now?.() || Date.now();
|
||||
};
|
||||
|
||||
const getEmptyWheelDollyFactor = () => {
|
||||
const focusFactor = getCameraFocusDollyFactor();
|
||||
const now = window.performance?.now?.() || Date.now();
|
||||
const hasFreshSurfaceFactor = (
|
||||
lastSurfaceDollyFactor !== null &&
|
||||
now - lastSurfaceDollyFactorAt <= LAST_SURFACE_DOLLY_FACTOR_TTL
|
||||
);
|
||||
return hasFreshSurfaceFactor ? Math.min(focusFactor, lastSurfaceDollyFactor) : focusFactor;
|
||||
};
|
||||
|
||||
const restorePointerNavigation = () => {
|
||||
if (emptyWheelNavigationRestoreTimer) {
|
||||
window.clearTimeout(emptyWheelNavigationRestoreTimer);
|
||||
|
|
@ -1350,6 +1409,7 @@ const restorePointerNavigation = () => {
|
|||
if (!control) return;
|
||||
control.followPointer = true;
|
||||
control.smartPivot = true;
|
||||
control.mouseWheelDollyRate = getBaseMouseWheelDollyRate();
|
||||
};
|
||||
|
||||
const schedulePointerNavigationRestore = () => {
|
||||
|
|
@ -1362,16 +1422,15 @@ const schedulePointerNavigationRestore = () => {
|
|||
}, 80);
|
||||
};
|
||||
|
||||
const hasWheelSurfaceHit = (event) => {
|
||||
const getWheelSurfaceHit = (event) => {
|
||||
if (!activeModels.length || !viewer?.scene?.pick) return true;
|
||||
const point = getCanvasPointFromEvent(event);
|
||||
if (!point) return true;
|
||||
try {
|
||||
const hit = viewer.scene.pick({
|
||||
return viewer.scene.pick({
|
||||
canvasPos: point.canvasPos,
|
||||
pickSurface: true,
|
||||
});
|
||||
return Boolean(hit?.entity || hit?.worldPos);
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1380,11 +1439,14 @@ const hasWheelSurfaceHit = (event) => {
|
|||
const guardEmptyWheelNavigation = (event) => {
|
||||
const control = viewer?.cameraControl;
|
||||
if (!control) return;
|
||||
if (hasWheelSurfaceHit(event)) {
|
||||
const hit = getWheelSurfaceHit(event);
|
||||
if (hit === true || hit?.entity || hit?.worldPos) {
|
||||
rememberSurfaceDollyFactor(hit === true ? null : hit);
|
||||
restorePointerNavigation();
|
||||
return;
|
||||
}
|
||||
control.followPointer = false;
|
||||
control.mouseWheelDollyRate = getBaseMouseWheelDollyRate() * getEmptyWheelDollyFactor();
|
||||
schedulePointerNavigationRestore();
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue