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;
115 changes: 115 additions & 0 deletions react-components/src/components/RevealToolbar/SlicerButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*!
* 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';

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

const [{ minHeight, maxHeight, topRatio, bottomRatio }, setSliceState] = useState<{
minHeight: number;
maxHeight: number;
topRatio: number;
bottomRatio: number;
}>({
minHeight: 0,
maxHeight: 0,
topRatio: 1,
bottomRatio: 0
});
haakonflatval-cognite marked this conversation as resolved.
Show resolved Hide resolved

// 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) {
const newTopRatio = getRatioForNewRange(topRatio, minHeight, maxHeight, newMinY, newMaxY);
const newBottomRatio = getRatioForNewRange(
bottomRatio,
minHeight,
maxHeight,
newMinY,
newMaxY
);

setSliceState({
maxHeight: newMaxY,
minHeight: newMinY,
topRatio: newTopRatio,
bottomRatio: newBottomRatio
});
}
}, [viewer, viewer.models.length, lastModel]);

return (
<Dropdown
appendTo={() => document.body}
content={
<StyledMenu>
<RangeSlider
min={0}
max={1}
step={0.01}
setValue={(v: number[]) => {
viewer.setGlobalClippingPlanes([
new Plane(new Vector3(0, 1, 0), -(minHeight + v[0] * (maxHeight - minHeight))),
new Plane(new Vector3(0, -1, 0), minHeight + v[1] * (maxHeight - minHeight))
]);

setSliceState({
maxHeight,
minHeight,
bottomRatio: v[0],
topRatio: v[1]
});
}}
haakonflatval-cognite marked this conversation as resolved.
Show resolved Hide resolved
marks={{}}
value={[bottomRatio, topRatio]}
vertical
/>
</StyledMenu>
}
placement="right-end">
<Button type="ghost" icon="Slice" aria-label="Slice models" />
</Dropdown>
);
};

function getRatioForNewRange(
ratio: number,
oldMin: number,
oldMax: number,
newMin: number,
newMax: number
): number {
if (ratio === 0 || ratio === 1) {
return ratio;
}

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

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

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