Skip to content

Commit

Permalink
Merge pull request #1059 from silx-kit/select-zoom-center
Browse files Browse the repository at this point in the history
Center ratio-respecting rectangle around drawn rectangle
  • Loading branch information
loichuder authored Apr 6, 2022
2 parents b90e318 + c6b99a0 commit 7982187
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 32 deletions.
44 changes: 32 additions & 12 deletions apps/storybook/src/SelectToZoom.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,53 @@
import {
DataCurve,
Pan,
VisCanvas,
Zoom,
SelectToZoom,
ResetZoomButton,
HeatmapMesh,
getDomain,
} from '@h5web/lib';
import type { ModifierKey as ModifierKeyType } from '@h5web/lib';
import { mockValues, ScaleType } from '@h5web/shared';
import type { Meta, Story } from '@storybook/react';
import { range } from 'lodash';
import ndarray from 'ndarray';

import FillHeight from './decorators/FillHeight';

const X = range(-5, 5, 0.01);
const Y = X.map((x) => Math.exp((-x * x) / 0.2));
const cols = 41;
const rows = 20;
const values = ndarray(Float32Array.from(mockValues.twoD.flat()), [rows, cols]);
const domain = getDomain(values);

function GaussianCurve() {
return <DataCurve abscissas={X} ordinates={Y} color="blue" />;
function Image() {
return (
<HeatmapMesh
values={values}
domain={domain || [0, 1]}
colorMap="Viridis"
scaleType={ScaleType.Linear}
/>
);
}

interface TemplateProps {
modifierKey: ModifierKeyType;
keepRatio?: boolean;
}

const Template: Story<TemplateProps> = (args) => {
const { keepRatio } = args;
return (
<VisCanvas
abscissaConfig={{ visDomain: [-5, 5], showGrid: true }}
ordinateConfig={{ visDomain: [-0.5, 1.5], showGrid: true }}
visRatio={keepRatio ? cols / rows : undefined}
>
<Pan />
<Zoom />
<SelectToZoom {...args} />
<ResetZoomButton />
<GaussianCurve />
<Image />
</VisCanvas>
);
};
Expand All @@ -42,16 +56,22 @@ export const Default = Template.bind({});
Default.args = {
modifierKey: 'Control',
};
Default.argTypes = {
modifierKey: {
control: { type: 'inline-radio' },
options: ['Alt', 'Control', 'Shift'],
},

export const KeepRatio = Template.bind({});
KeepRatio.args = {
modifierKey: 'Control',
keepRatio: true,
};

export default {
title: 'Building Blocks/SelectToZoom',
component: SelectToZoom,
decorators: [FillHeight],
parameters: { layout: 'fullscreen' },
argTypes: {
modifierKey: {
control: { type: 'inline-radio' },
options: ['Alt', 'Control', 'Shift'],
},
},
} as Meta;
10 changes: 7 additions & 3 deletions packages/lib/src/interactions/RatioSelectionRect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Vector2 } from 'three';

import { useAxisSystemContext } from '../vis/shared/AxisSystemContext';
import SelectionRect from './SelectionRect';
import { clampRectangleToVis, getRatioEndPoint } from './utils';
import { clampRectangleToVis, getRatioRespectingRectangle } from './utils';

interface Props extends SVGProps<SVGRectElement> {
startPoint: Vector2;
Expand All @@ -16,9 +16,13 @@ function RatioSelectionRect(props: Props) {

const { dataToWorld, worldToData, visSize } = useAxisSystemContext();

const ratioEndPoint = getRatioEndPoint(startPoint, endPoint, ratio);
const [ratioStartPoint, ratioEndPoint] = getRatioRespectingRectangle(
startPoint,
endPoint,
ratio
);
const [newStartPoint, newEndPoint] = clampRectangleToVis(
dataToWorld(startPoint),
dataToWorld(ratioStartPoint),
dataToWorld(ratioEndPoint),
visSize
);
Expand Down
11 changes: 5 additions & 6 deletions packages/lib/src/interactions/SelectToZoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SelectionRect from './SelectionRect';
import SelectionTool from './SelectionTool';
import { useMoveCameraTo } from './hooks';
import type { Interaction, Selection } from './models';
import { getEnclosedRectangle, getRatioEndPoint } from './utils';
import { getEnclosedRectangle, getRatioRespectingRectangle } from './utils';

interface Props extends Interaction {
keepRatio?: boolean;
Expand All @@ -32,12 +32,11 @@ function SelectToZoom(props: Props) {
const { startPoint: dataStartPoint, endPoint: dataEndPoint } = selection;

// Work in world coordinates as we need to act on the world camera
const startPoint = dataToWorld(dataStartPoint);
const endPoint = dataToWorld(
const [startPoint, endPoint] = (
keepRatio
? getRatioEndPoint(dataStartPoint, dataEndPoint, dataRatio)
: dataEndPoint
);
? getRatioRespectingRectangle(dataStartPoint, dataEndPoint, dataRatio)
: [dataStartPoint, dataEndPoint]
).map(dataToWorld);

if (startPoint.x === endPoint.x && startPoint.y === endPoint.y) {
return;
Expand Down
24 changes: 13 additions & 11 deletions packages/lib/src/interactions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function boundPointToFOV(
return new Vector2(boundedX, boundedY);
}

export function getRatioEndPoint(
export function getRatioRespectingRectangle(
startPoint: Vector2,
endPoint: Vector2,
ratio: number
Expand All @@ -31,17 +31,19 @@ export function getRatioEndPoint(
const originalRatio =
Math.abs(endPoint.x - startPoint.x) / Math.abs(endPoint.y - startPoint.y);

if (originalRatio < ratio) {
return new Vector2(
startPoint.x + widthSign * height * ratio,
startPoint.y + heightSign * height
);
}
const shiftX = widthSign * (originalRatio < ratio ? height * ratio : width);
const shiftY = heightSign * (originalRatio < ratio ? height : width / ratio);

return new Vector2(
startPoint.x + widthSign * width,
startPoint.y + (heightSign * width) / ratio
);
const centerPoint = endPoint
.clone()
.sub(startPoint)
.divideScalar(2)
.add(startPoint);

return [
new Vector2(centerPoint.x - shiftX / 2, centerPoint.y - shiftY / 2),
new Vector2(centerPoint.x + shiftX / 2, centerPoint.y + shiftY / 2),
];
}

export function getDefaultInteractions(
Expand Down

0 comments on commit 7982187

Please sign in to comment.