Skip to content

Commit

Permalink
Fix of: [NGRM] - The default initial view should not be a top view #1621
Browse files Browse the repository at this point in the history
  • Loading branch information
nilscb authored Jul 28, 2023
1 parent 70d5468 commit 2529360
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
TooltipCallback,
ViewStateType,
ViewsType,
BoundingBox3D,
} from "./components/Map";
import { WellsLayer, MapLayer, AxesLayer, Grid3DLayer } from "./layers";
import InfoCard from "./components/InfoCard";
Expand Down Expand Up @@ -757,3 +758,92 @@ LightsStory.parameters = {
},
},
};

const zoomBox3D: BoundingBox3D = [-325, -450, -25, 125, 150, 125];
//const zoomBox3D: BoundingBox3D = [-100, -100, -100, 100, 100, 100];

export const AutoZoomToBoxStory = (args: SubsurfaceViewerProps) => {
const [rotX, setRotX] = React.useState(0);
const [rotZ, setRotZ] = React.useState(0);

const cameraPosition: ViewStateType = {
rotationX: rotX,
rotationOrbit: rotZ,
zoom: zoomBox3D,
target: [0, 0, 0],
};

const props = {
...args,
cameraPosition,
};

return (
<Root>
<label>{"Rotation X Axis "}</label>
<Slider
defaultValue={50}
valueLabelDisplay={"auto"}
onChange={(_event: Event, value: number | number[]) => {
const angle = 2 * ((value as number) / 100 - 0.5) * 90;
setRotX(angle);
}}
/>
<label>{"Rotation Z Axis "}</label>
<Slider
defaultValue={50}
valueLabelDisplay={"auto"}
onChange={(_event: Event, value: number | number[]) => {
const angle = 2 * ((value as number) / 100 - 0.5) * 180;
setRotZ(angle);
}}
/>
<div className={classes.main}>
<SubsurfaceViewer {...props} />
</div>
</Root>
);
};

AutoZoomToBoxStory.args = {
id: "DeckGL-Map",
layers: [
new AxesLayer({
id: "polyhedral-cells-axes",
bounds: zoomBox3D,
ZIncreasingDownwards: false,
}),
new SimpleMeshLayer({
id: "sphere",
data: [{}],
mesh: new SphereGeometry({
nlat: 100,
nlong: 100,
radius: 10,
}),
wireframe: false,
getPosition: [0, 0, 0],
getColor: [255, 255, 255],
material: true,
}),
],
views: {
layout: [1, 1],
viewports: [
{
id: "view_1",
show3D: true,
},
],
},
};

AutoZoomToBoxStory.parameters = {
docs: {
inlineStories: false,
iframeHeight: 500,
description: {
story: "",
},
},
};
131 changes: 120 additions & 11 deletions typescript/packages/subsurface-viewer/src/components/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ import {
} from "@deck.gl/core/typed";
import { LightingEffect } from "@deck.gl/core/typed";
import { LineLayer } from "@deck.gl/layers/typed";
import { Matrix4 } from "@math.gl/core";
import { fovyToAltitude } from "@math.gl/web-mercator";

type BoundingBox = [number, number, number, number, number, number];
export type BoundingBox3D = [number, number, number, number, number, number];

const minZoom3D = -12;
const maxZoom3D = 12;
Expand Down Expand Up @@ -156,8 +158,8 @@ function parseLights(lights?: LightsType): LightingEffect[] | undefined {
return effects;
}

function addBoundingBoxes(b1: BoundingBox, b2: BoundingBox): BoundingBox {
const boxDefault: BoundingBox = [0, 0, 0, 1, 1, 1];
function addBoundingBoxes(b1: BoundingBox3D, b2: BoundingBox3D): BoundingBox3D {
const boxDefault: BoundingBox3D = [0, 0, 0, 1, 1, 1];

if (typeof b1 === "undefined" || typeof b2 === "undefined") {
return boxDefault;
Expand All @@ -177,7 +179,7 @@ function addBoundingBoxes(b1: BoundingBox, b2: BoundingBox): BoundingBox {
return [xmin, ymin, zmin, xmax, ymax, zmax];
}

function boundingBoxCenter(box: BoundingBox): [number, number, number] {
function boundingBoxCenter(box: BoundingBox3D): [number, number, number] {
const xmin = box[0];
const ymin = box[1];
const zmin = box[2];
Expand Down Expand Up @@ -267,7 +269,7 @@ export interface ViewportType {

export interface ViewStateType {
target: number[];
zoom: number;
zoom: number | BoundingBox3D;
rotationX: number;
rotationOrbit: number;
minZoom?: number;
Expand Down Expand Up @@ -449,6 +451,101 @@ function adjustCameraTarget(
return vs;
}

function calculateZoomFromBBox3D(
camera: ViewStateType | undefined,
deck?: Deck
): ViewStateType | undefined {
const DEGREES_TO_RADIANS = Math.PI / 180;
const RADIANS_TO_DEGREES = 180 / Math.PI;
const IDENTITY = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];

const camera_ = cloneDeep(camera);

if (typeof camera_ === "undefined" || !Array.isArray(camera_.zoom)) {
return camera;
}

if (typeof deck === "undefined") {
camera_.zoom = 0;
camera_.target = [0, 0, 0];
return camera_;
}

const width = deck.width;
const height = deck.height;

// camera fov eye position. see deck.gl file orbit-viewports.ts
const fovy = 50; // default in deck.gl. May also be set construction OrbitView
const fD = fovyToAltitude(fovy);

const bbox = camera_.zoom;

const xMin = bbox[0];
const yMin = bbox[1];
const zMin = bbox[2];

const xMax = bbox[3];
const yMax = bbox[4];
const zMax = bbox[5];

const target = [
xMin + (xMax - xMin) / 2,
yMin + (yMax - yMin) / 2,
zMin + (zMax - zMin) / 2,
];

const cameraFovVertical = 50;
const angle_ver = (cameraFovVertical / 2) * DEGREES_TO_RADIANS;
const L = height / 2 / Math.sin(angle_ver);
const r = L * Math.cos(angle_ver);
const cameraFov = 2 * Math.atan(width / 2 / r) * RADIANS_TO_DEGREES;
const angle_hor = (cameraFov / 2) * DEGREES_TO_RADIANS;

const points: [number, number, number][] = [];
points.push([xMin, yMin, zMin]);
points.push([xMin, yMax, zMin]);
points.push([xMax, yMax, zMin]);
points.push([xMax, yMin, zMin]);
points.push([xMin, yMin, zMax]);
points.push([xMin, yMax, zMax]);
points.push([xMax, yMax, zMax]);
points.push([xMax, yMin, zMax]);

let zoom = 999;
for (const point of points) {
const x_ = (point[0] - target[0]) / height;
const y_ = (point[1] - target[1]) / height;
const z_ = (point[2] - target[2]) / height;

const m = new Matrix4(IDENTITY);
m.rotateX(camera_.rotationX * DEGREES_TO_RADIANS);
m.rotateZ(camera_.rotationOrbit * DEGREES_TO_RADIANS);

const [x, y, z] = m.transformAsVector([x_, y_, z_]);
if (y >= 0) {
// These points will actually appear further away when zooming in.
continue;
}

const fwX = fD * Math.tan(angle_hor);
let y_new = fwX / (Math.abs(x) / y - fwX / fD);
const zoom_x = Math.log2(y_new / y);

const fwY = fD * Math.tan(angle_ver);
y_new = fwY / (Math.abs(z) / y - fwY / fD);
const zoom_z = Math.log2(y_new / y);

// it needs to be inside view volume in both directions.
zoom = zoom_x < zoom ? zoom_x : zoom;
zoom = zoom_z < zoom ? zoom_z : zoom;
}

camera_.zoom = zoom;
camera_.target = target;

return camera_;
}

const Map: React.FC<MapProps> = ({
id,
layers,
Expand All @@ -473,7 +570,7 @@ const Map: React.FC<MapProps> = ({
}: MapProps) => {
const deckRef = useRef<DeckGLRef>(null);

const bboxInitial: BoundingBox = [0, 0, 0, 1, 1, 1];
const bboxInitial: BoundingBox3D = [0, 0, 0, 1, 1, 1];

// Deck.gl View's and viewStates as input to Deck.gl
const [deckGLViews, setDeckGLViews] = useState<View[]>([]);
Expand All @@ -482,9 +579,9 @@ const Map: React.FC<MapProps> = ({
);

const [reportedBoundingBox, setReportedBoundingBox] =
useState<BoundingBox>(bboxInitial);
useState<BoundingBox3D>(bboxInitial);
const [reportedBoundingBoxAcc, setReportedBoundingBoxAcc] =
useState<BoundingBox>(bboxInitial);
useState<BoundingBox3D>(bboxInitial);

const [deckGLLayers, setDeckGLLayers] = useState<LayersList>([]);

Expand All @@ -495,6 +592,15 @@ const Map: React.FC<MapProps> = ({
bottom: 0,
});

const [camera, setCamera] = useState<ViewStateType>();
React.useEffect(() => {
const camera = calculateZoomFromBBox3D(
cameraPosition,
deckRef.current?.deck
);
setCamera(camera);
}, [cameraPosition, deckRef?.current?.deck]);

// Used for scaling in z direction using arrow keys.
const [scaleZ, setScaleZ] = useState<number>(1);
const [scaleZUp, setScaleZUp] = useState<number>(Number.MAX_VALUE);
Expand Down Expand Up @@ -528,7 +634,7 @@ const Map: React.FC<MapProps> = ({
views,
viewPortMargins,
bounds,
cameraPosition,
camera,
reportedBoundingBoxAcc,
deckRef.current?.deck
);
Expand All @@ -538,7 +644,7 @@ const Map: React.FC<MapProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
bounds,
cameraPosition,
camera,
deckRef?.current?.deck,
reportedBoundingBoxAcc,
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -940,7 +1046,10 @@ const Map: React.FC<MapProps> = ({
{scale?.visible ? (
<DistanceScale
{...scale}
zoom={viewStates[Object.keys(viewStates)[0]]?.zoom ?? -5}
zoom={
(viewStates[Object.keys(viewStates)[0]]
?.zoom as number) ?? -5
}
scaleUnit={coordinateUnit}
style={scale.cssStyle ?? {}}
/>
Expand Down

0 comments on commit 2529360

Please sign in to comment.