Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: slice button for RevealToolbar #3492

Merged
merged 8 commits into from
Jul 21, 2023
45 changes: 24 additions & 21 deletions react-components/src/components/RevealToolbar/RevealToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
* Copyright 2023 Cognite AS
*/

import { type ReactElement } from 'react';
import { type ReactElement, type JSX } from 'react';
import { Button, ToolBar, type ToolBarProps } from '@cognite/cogs.js';
import { FitModelsButton } from './FitModelsButton';
import { SlicerButton } from './SlicerButton';

const defaultStyle: ToolBarProps = {
style: {
Expand All @@ -14,32 +15,34 @@ const defaultStyle: ToolBarProps = {
}
};

export const RevealToolbar = (toolBarProps: ToolBarProps): ReactElement => {
if (toolBarProps.className === undefined && toolBarProps.style === undefined) {
toolBarProps = { ...toolBarProps, ...defaultStyle };
}
return (
<ToolBar {...toolBarProps}>
<>
<Button type="ghost" icon="Layers" aria-label="3D Resource layers" />
const defaultContent = (
<>
<Button type="ghost" icon="Layers" aria-label="3D Resource layers" />

<div className="cogs-toolbar-divider" />

<div className="cogs-toolbar-divider" />
<FitModelsButton />
<Button type="ghost" icon="Collapse" aria-label="Focus asset" />

<FitModelsButton />
<Button type="ghost" icon="Collapse" aria-label="Focus asset" />
<div className="cogs-toolbar-divider" />

<div className="cogs-toolbar-divider" />
<SlicerButton />
<Button type="ghost" icon="Ruler" aria-label="Make measurements" />

<Button type="ghost" icon="Slice" aria-label="Slice models" />
<Button type="ghost" icon="Ruler" aria-label="Make measurements" />
<div className="cogs-toolbar-divider" />

<div className="cogs-toolbar-divider" />
<Button type="ghost" icon="Settings" aria-label="Show settings" />
<Button type="ghost" icon="Help" aria-label="Display help" />
</>
);

<Button type="ghost" icon="Settings" aria-label="Show settings" />
<Button type="ghost" icon="Help" aria-label="Display help" />
</>
</ToolBar>
);
export const RevealToolbar = (
props: ToolBarProps & { toolBarContent?: JSX.Element }
): ReactElement => {
if (props.className === undefined && props.style === undefined) {
props = { ...props, ...defaultStyle };
}
return <ToolBar {...props}>{props.toolBarContent ?? defaultContent}</ToolBar>;
};

RevealToolbar.FitModelsButton = FitModelsButton;
113 changes: 113 additions & 0 deletions react-components/src/components/RevealToolbar/SlicerButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*!
* Copyright 2023 Cognite AS
*/

import { type ReactElement, useState, useEffect } from 'react';

import { Box3, Plane, Vector3 } from 'three';

import { useReveal } from '../RevealContainer/RevealContext';
import { Button, Dropdown, Menu, RangeSlider } from '@cognite/cogs.js';

import styled from 'styled-components';

type SliceState = {
minHeight: number;
maxHeight: number;
topRatio: number;
bottomRatio: number;
};

export const SlicerButton = (): ReactElement => {
const viewer = useReveal();

const [sliceState, setSliceState] = useState<SliceState>({
minHeight: 0,
maxHeight: 0,
topRatio: 1,
bottomRatio: 0
});

const { minHeight, maxHeight, topRatio, bottomRatio } = sliceState;

// Heuristic to increase chance that update is propagated even
// if multiple additions/deletions of models occur.
const lastModel =
viewer.models.length === 0 ? undefined : viewer.models[viewer.models.length - 1];

useEffect(() => {
const box = new Box3();
viewer.models.forEach((model) => box.union(model.getModelBoundingBox()));

const newMaxY = box.max.y;
const newMinY = box.min.y;

if (maxHeight !== newMaxY || minHeight !== newMinY) {
setSliceState(getNewSliceState(sliceState, newMinY, newMaxY));
}
}, [viewer, viewer.models.length, lastModel]);

function changeSlicingState(newValues: number[]): void {
viewer.setGlobalClippingPlanes([
new Plane(new Vector3(0, 1, 0), -(minHeight + newValues[0] * (maxHeight - minHeight))),
new Plane(new Vector3(0, -1, 0), minHeight + newValues[1] * (maxHeight - minHeight))
]);

setSliceState({
maxHeight,
minHeight,
bottomRatio: newValues[0],
topRatio: newValues[1]
});
}

return (
<Dropdown
appendTo={() => document.body}
content={
<StyledMenu>
<RangeSlider
min={0}
max={1}
step={0.01}
setValue={changeSlicingState}
marks={{}}
value={[bottomRatio, topRatio]}
vertical
/>
</StyledMenu>
}
placement="right-end">
<Button type="ghost" icon="Slice" aria-label="Slice models" />
</Dropdown>
);
};

function getNewSliceState(oldSliceState: SliceState, newMin: number, newMax: number): SliceState {
function getRatioForNewRange(ratio: number): number {
if (ratio === 0 || ratio === 1) {
return ratio;
}

const oldMin = oldSliceState.minHeight;
const oldMax = oldSliceState.maxHeight;

const position = oldMin + ratio * (oldMax - oldMin);
const newRatio = (position - newMin) / (newMax - newMin);

return Math.min(1.0, Math.max(0.0, newRatio));
}

return {
maxHeight: newMax,
minHeight: newMin,
topRatio: getRatioForNewRange(oldSliceState.topRatio),
bottomRatio: getRatioForNewRange(oldSliceState.bottomRatio)
};
}

const StyledMenu = styled(Menu)`
height: 512px;
padding: 12px;
min-width: 0px;
`;
2 changes: 2 additions & 0 deletions react-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ export type {
AddReveal3DModelOptions
} from './components/Reveal3DResources/types';
export { RevealToolbar } from './components/RevealToolbar/RevealToolbar';
export { SlicerButton } from './components/RevealToolbar/SlicerButton';
export { FitModelsButton } from './components/RevealToolbar/FitModelsButton';
export { useFdmAssetMappings } from './hooks/useFdmAssetMappings';
export { type FdmAssetMappingsConfig } from './hooks/types';
Loading