Skip to content

Commit a1244e4

Browse files
Merge pull request #100 from kishore08-07/scaling
Added Scaling options
2 parents b000c66 + 970ca6a commit a1244e4

File tree

1 file changed

+99
-11
lines changed
  • site/src/components/ShapeBuilder

1 file changed

+99
-11
lines changed

site/src/components/ShapeBuilder/index.js

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
// /* global window */
22
import React, { useEffect, useRef, useState } from "react";
33
import { Wrapper, CanvasContainer, OutputBox, StyledSVG, CopyButton } from "./shapeBuilder.styles";
4-
import { Button, Typography, Box, CopyIcon } from "@sistent/sistent";
4+
import { Button, Typography, Box, CopyIcon, Select, MenuItem, Slider, FormControl } from "@sistent/sistent";
55
import { SVG, extend as SVGextend } from "@svgdotjs/svg.js";
66
import draw from "@svgdotjs/svg.draw.js";
77

88
SVGextend(SVG.Polygon, draw);
99

10+
const SCALE_PRESETS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3];
11+
const MIN_SCALE = 0.1;
12+
const MAX_SCALE = 3;
13+
1014
const ShapeBuilder = () => {
1115
const boardRef = useRef(null);
1216
const polyRef = useRef(null);
1317
const keyHandlersRef = useRef({});
18+
const basePointsRef = useRef(null);
1419
const [result, setResult] = useState("");
1520
const [error, setError] = useState(null);
1621
const [showCopied, setShowCopied] = useState(false);
22+
const [scale, setScale] = useState(1);
23+
const [currentPreset, setCurrentPreset] = useState(1);
1724

1825
const handleCopyToClipboard = async () => {
1926
if (!result.trim()) return;
@@ -54,23 +61,55 @@ const ShapeBuilder = () => {
5461
}
5562
};
5663

57-
const handleMaximize = () => {
64+
const applyScale = (newScale) => {
5865
const poly = polyRef.current;
5966
if (!poly) return;
6067

6168
const points = getPlottedPoints(poly);
62-
if (!points) return;
63-
const xs = points.map(p => p[0]);
64-
const ys = points.map(p => p[1]);
69+
if (!points || points.length === 0) return;
70+
71+
if (!basePointsRef.current) {
72+
basePointsRef.current = points;
73+
}
74+
75+
const basePoints = basePointsRef.current;
76+
77+
const xs = basePoints.map(p => p[0]);
78+
const ys = basePoints.map(p => p[1]);
79+
const centerX = (Math.max(...xs) + Math.min(...xs)) / 2;
80+
const centerY = (Math.max(...ys) + Math.min(...ys)) / 2;
6581

66-
const width = Math.abs(Math.max(...xs) - Math.min(...xs));
67-
const height = Math.abs(Math.max(...ys) - Math.min(...ys));
82+
const scaledPoints = basePoints.map(([x, y]) => {
83+
const dx = x - centerX;
84+
const dy = y - centerY;
85+
return [centerX + dx * newScale, centerY + dy * newScale];
86+
});
6887

69-
poly.size(width > height ? 520 : undefined, height >= width ? 520 : undefined);
70-
poly.move(0, 0);
88+
poly.plot(scaledPoints);
7189
showCytoArray();
7290
};
7391

92+
const handleScaleChange = (newScale) => {
93+
const clampedScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, newScale));
94+
setScale(clampedScale);
95+
96+
const matchingPreset = SCALE_PRESETS.find(p => Math.abs(p - clampedScale) < 0.01);
97+
setCurrentPreset(matchingPreset || clampedScale);
98+
99+
applyScale(clampedScale);
100+
};
101+
102+
const handlePresetChange = (event) => {
103+
const newPreset = event.target.value;
104+
setCurrentPreset(newPreset);
105+
setScale(newPreset);
106+
applyScale(newPreset);
107+
};
108+
109+
const handleSliderChange = (event, newValue) => {
110+
handleScaleChange(newValue);
111+
};
112+
74113
const handleKeyDown = (e) => {
75114
const poly = polyRef.current;
76115
if (!poly) return;
@@ -144,7 +183,10 @@ const ShapeBuilder = () => {
144183
poly.remove();
145184
detachKeyListeners();
146185
polyRef.current = null;
186+
basePointsRef.current = null;
147187
setResult("");
188+
setScale(1);
189+
setCurrentPreset(1);
148190
initializeDrawing();
149191
};
150192

@@ -154,6 +196,10 @@ const ShapeBuilder = () => {
154196

155197
poly.draw("done");
156198
poly.fill("#00B39F");
199+
const points = getPlottedPoints(poly);
200+
if (points && points.length > 0) {
201+
basePointsRef.current = points;
202+
}
157203
showCytoArray();
158204
};
159205

@@ -201,10 +247,52 @@ const ShapeBuilder = () => {
201247
)}
202248
</CanvasContainer>
203249

204-
<Box sx={{ display: "flex", justifyContent: "center", gap: 2, mt: 3, mb: 3, flexWrap: "wrap" }}>
250+
<Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", gap: 2, mt: 3, mb: 3, flexWrap: "wrap" }}>
205251
<Button variant="contained" onClick={clearShape}>Clear</Button>
206252
<Button variant="contained" onClick={closeShape}>Close Shape</Button>
207-
<Button variant="contained" onClick={handleMaximize}>Maximize</Button>
253+
254+
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5, ml: 2 }}>
255+
<FormControl size="small" sx={{ minWidth: 80 }}>
256+
<Select
257+
id="scale-preset-select"
258+
value={currentPreset}
259+
onChange={handlePresetChange}
260+
displayEmpty
261+
aria-label="Scale preset"
262+
sx={{
263+
color: '#fff',
264+
'& .MuiSelect-icon': {
265+
color: '#fff'
266+
}
267+
}}
268+
>
269+
{SCALE_PRESETS.map((preset) => (
270+
<MenuItem key={preset} value={preset}>
271+
{preset}×
272+
</MenuItem>
273+
))}
274+
</Select>
275+
</FormControl>
276+
277+
<Box sx={{ width: 150, display: "flex", alignItems: "center", gap: 1 }}>
278+
<Slider
279+
value={scale}
280+
onChange={handleSliderChange}
281+
min={MIN_SCALE}
282+
max={MAX_SCALE}
283+
step={0.01}
284+
valueLabelDisplay="auto"
285+
valueLabelFormat={(value) => `${value.toFixed(2)}×`}
286+
marks={SCALE_PRESETS.map(value => ({ value, label: "" }))}
287+
aria-label="Scale slider"
288+
sx={{ flexGrow: 1 }}
289+
/>
290+
</Box>
291+
292+
<Typography variant="body2" sx={{ minWidth: "50px", fontWeight: 500 }}>
293+
{scale.toFixed(2)}×
294+
</Typography>
295+
</Box>
208296
</Box>
209297

210298
<OutputBox>

0 commit comments

Comments
 (0)