Skip to content

Commit da9dd33

Browse files
committed
scale path
1 parent b8e4cfa commit da9dd33

16 files changed

+470
-33
lines changed

dist/index.d.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,16 @@ declare module 'mz-svg' {
290290
export const minifyPath: (d?: string, decimalPlaces?: number) => string | undefined;
291291
export const beautifyPath: (d?: string, decimalPlaces?: number) => string | undefined;
292292
export const getPathBBox: (d?: string, decimalPlaces?: number) => IBBox | null;
293+
294+
export type Vector2 = [number, number];
295+
export type Vector3 = [number, number, number];
296+
export type Vector4 = [number, number, number, number];
297+
export type Vector = number[];
298+
293299
export const translatePath: (d: string, x: number, y: number, decimalPlaces?: number) => string;
294-
export const rotatePathAroundDot: (d: string, cx: number, cy: number, angleDegrees: number, decimalPlaces?: number) => string;
300+
export const rotatePathAroundPoint: (d: string, transformOrigin: Vector2, angleDegrees: number, decimalPlaces?: number) => string;
295301
export const rotatePath: (d: string, angleDegrees: number, decimalPlaces?: number) => string;
302+
export const scalePathAroundPoint: (d: string, scaleVector: Vector2, transformOrigin: Vector2, decimalPlaces?: number) => string;
303+
export const scalePath: (d: string, scaleVector: Vector2, decimalPlaces?: number) => string;
304+
296305
}

dist/mz-svg.esm.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mz-svg.esm.js.map

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mz-svg.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mz-svg.min.js.map

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mz-svg.node.cjs

+2-2
Large diffs are not rendered by default.

dist/mz-svg.node.cjs.map

+3-3
Large diffs are not rendered by default.

src/main/path/transform.ts

+155-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { parsePath } from './index';
22
import { maximizeAbsolutePath, pathDataToAbsolute, pathDataToRelative, pathDataToString } from './convert';
3-
import { Vector3, m2RotateAroundPointH, degreesToRadians } from 'mz-math';
3+
import { Vector3, m2RotateAroundPointH, degreesToRadians, Vector2, m2ScaleAtPointH } from 'mz-math';
44
import { EPathDataCommand } from './interfaces';
55
import { getPathBBox } from './bbox';
66

7+
// ---------------------- TRANSLATE ----------------------
8+
79
export const translatePath = (d: string, x: number, y: number, decimalPlaces = 2) => {
810
if(!d) return d;
911

@@ -20,17 +22,19 @@ export const translatePath = (d: string, x: number, y: number, decimalPlaces = 2
2022
return pathDataToString(relative, true, decimalPlaces);
2123
};
2224

23-
const rotateDot = (x: number, y: number, cx: number, cy: number, angleRad: number, decimalPlaces = 2) => {
25+
// ---------------------- ROTATE -------------------------
26+
27+
const rotateDot = (point: Vector2, transformOrigin: Vector2, angleRad: number, decimalPlaces = 2) => {
2428
return m2RotateAroundPointH(
2529
angleRad,
26-
[cx, cy, 1],
27-
[x, y, 1],
30+
[transformOrigin[0], transformOrigin[1], 1],
31+
[point[0], point[1], 1],
2832
true,
2933
decimalPlaces
3034
);
3135
};
3236

33-
export const rotatePathAroundDot = (d: string, cx: number, cy: number, angleDegrees: number, decimalPlaces = 2) => {
37+
export const rotatePathAroundPoint = (d: string, transformOrigin: Vector2, angleDegrees: number, decimalPlaces = 2) => {
3438
if(!d) return d;
3539

3640
const parsed = parsePath(d);
@@ -51,35 +55,35 @@ export const rotatePathAroundDot = (d: string, cx: number, cy: number, angleDegr
5155
// 2 params (x, y)
5256

5357
// get the new position after rotation
54-
const pos: Vector3 = rotateDot(item.params[0], item.params[1], cx, cy, angleRad, decimalPlaces);
58+
const pos: Vector3 = rotateDot([item.params[0], item.params[1]], transformOrigin, angleRad, decimalPlaces);
5559
item.params[0] = pos[0];
5660
item.params[1] = pos[1];
5761
break;
5862
}
5963

6064
case EPathDataCommand.CubicCurveToAbs:{
6165
// 6 parameters
62-
const pos1: Vector3 = rotateDot(item.params[0], item.params[1], cx, cy, angleRad, decimalPlaces);
66+
const pos1: Vector3 = rotateDot([item.params[0], item.params[1]], transformOrigin, angleRad, decimalPlaces);
6367
item.params[0] = pos1[0];
6468
item.params[1] = pos1[1];
6569

66-
const pos2: Vector3 = rotateDot(item.params[2], item.params[3], cx, cy, angleRad, decimalPlaces);
70+
const pos2: Vector3 = rotateDot([item.params[2], item.params[3]], transformOrigin, angleRad, decimalPlaces);
6771
item.params[2] = pos2[0];
6872
item.params[3] = pos2[1];
6973

70-
const pos3: Vector3 = rotateDot(item.params[4], item.params[5], cx, cy, angleRad, decimalPlaces);
74+
const pos3: Vector3 = rotateDot([item.params[4], item.params[5]], transformOrigin, angleRad, decimalPlaces);
7175
item.params[4] = pos3[0];
7276
item.params[5] = pos3[1];
7377
break;
7478
}
7579

7680
case EPathDataCommand.QuadraticCurveToAbs:{
7781
// 4 parameters
78-
const pos1: Vector3 = rotateDot(item.params[0], item.params[1], cx, cy, angleRad, decimalPlaces);
82+
const pos1: Vector3 = rotateDot([item.params[0], item.params[1]], transformOrigin, angleRad, decimalPlaces);
7983
item.params[0] = pos1[0];
8084
item.params[1] = pos1[1];
8185

82-
const pos2: Vector3 = rotateDot(item.params[2], item.params[3], cx, cy, angleRad, decimalPlaces);
86+
const pos2: Vector3 = rotateDot([item.params[2], item.params[3]], transformOrigin, angleRad, decimalPlaces);
8387
item.params[2] = pos2[0];
8488
item.params[3] = pos2[1];
8589
break;
@@ -91,7 +95,7 @@ export const rotatePathAroundDot = (d: string, cx: number, cy: number, angleDegr
9195
item.params[0] = pos1[0];
9296
item.params[1] = pos1[1];*/
9397

94-
const pos2: Vector3 = rotateDot(item.params[5], item.params[6], cx, cy, angleRad, decimalPlaces);
98+
const pos2: Vector3 = rotateDot([item.params[5], item.params[6]], transformOrigin, angleRad, decimalPlaces);
9599
item.params[5] = pos2[0];
96100
item.params[6] = pos2[1];
97101
break;
@@ -115,5 +119,143 @@ export const rotatePath = (d: string, angleDegrees: number, decimalPlaces = 2) =
115119
const cx = x + w / 2;
116120
const cy = y + h / 2;
117121

118-
return rotatePathAroundDot(d, cx, cy, angleDegrees, decimalPlaces);
122+
return rotatePathAroundPoint(d, [cx, cy], angleDegrees, decimalPlaces);
123+
};
124+
125+
// ---------------------- SCALE --------------------------
126+
127+
const scaleDot = (point: Vector2, scaleVector: Vector2, transformOrigin: Vector2, decimalPlaces = 2) => {
128+
return m2ScaleAtPointH(
129+
[scaleVector[0], scaleVector[1], 1],
130+
[transformOrigin[0], transformOrigin[1], 1],
131+
[point[0], point[1], 1],
132+
decimalPlaces
133+
);
119134
};
135+
136+
export const scalePathAroundPoint = (d: string, scaleVector: Vector2, transformOrigin: Vector2, decimalPlaces = 2) => {
137+
if(!d) return d;
138+
139+
const parsed = parsePath(d);
140+
if(parsed.errors.length > 0) return d;
141+
142+
const abs = pathDataToAbsolute(parsed);
143+
if(!abs || abs.commands.length <= 0) return d;
144+
145+
const max = maximizeAbsolutePath(abs);
146+
for(const item of max.commands){
147+
148+
switch(item.command){
149+
150+
case EPathDataCommand.MoveToAbs:
151+
case EPathDataCommand.LineToAbs:{
152+
// 2 params (x, y)
153+
154+
const pos: Vector3 = scaleDot(
155+
[item.params[0], item.params[1]],
156+
scaleVector,
157+
transformOrigin,
158+
decimalPlaces
159+
);
160+
161+
item.params[0] = pos[0];
162+
item.params[1] = pos[1];
163+
break;
164+
}
165+
166+
case EPathDataCommand.CubicCurveToAbs:{
167+
// 6 parameters
168+
const pos1: Vector3 = scaleDot(
169+
[item.params[0], item.params[1]],
170+
scaleVector,
171+
transformOrigin,
172+
decimalPlaces
173+
);
174+
item.params[0] = pos1[0];
175+
item.params[1] = pos1[1];
176+
177+
const pos2: Vector3 = scaleDot(
178+
[item.params[2], item.params[3]],
179+
scaleVector,
180+
transformOrigin,
181+
decimalPlaces
182+
);
183+
item.params[2] = pos2[0];
184+
item.params[3] = pos2[1];
185+
186+
const pos3: Vector3 = scaleDot(
187+
[item.params[4], item.params[5]],
188+
scaleVector,
189+
transformOrigin,
190+
decimalPlaces
191+
);
192+
item.params[4] = pos3[0];
193+
item.params[5] = pos3[1];
194+
break;
195+
}
196+
197+
case EPathDataCommand.QuadraticCurveToAbs:{
198+
// 4 parameters
199+
const pos1: Vector3 = scaleDot(
200+
[item.params[0], item.params[1]],
201+
scaleVector,
202+
transformOrigin,
203+
decimalPlaces
204+
);
205+
item.params[0] = pos1[0];
206+
item.params[1] = pos1[1];
207+
208+
const pos2: Vector3 = scaleDot(
209+
[item.params[2], item.params[3]],
210+
scaleVector,
211+
transformOrigin,
212+
decimalPlaces
213+
);
214+
item.params[2] = pos2[0];
215+
item.params[3] = pos2[1];
216+
break;
217+
}
218+
219+
case EPathDataCommand.ArcAbs:{
220+
// rx ry x-axis-rotation large-arc-flag sweep-flag x y
221+
/*
222+
const pos1: Vector3 = scaleDot(
223+
[item.params[0], item.params[1]],
224+
scaleVector,
225+
transformOrigin,
226+
decimalPlaces
227+
);
228+
item.params[0] = pos1[0];
229+
item.params[1] = pos1[1];*/
230+
231+
const pos2: Vector3 = scaleDot(
232+
[item.params[5], item.params[6]],
233+
scaleVector,
234+
transformOrigin,
235+
decimalPlaces
236+
);
237+
item.params[5] = pos2[0];
238+
item.params[6] = pos2[1];
239+
break;
240+
}
241+
}
242+
}
243+
244+
//const rel = pathDataToRelative(abs);
245+
return pathDataToString(abs, false, decimalPlaces);
246+
};
247+
248+
export const scalePath = (d: string, scaleVector: Vector2, decimalPlaces = 2) => {
249+
250+
const bbox = getPathBBox(d);
251+
252+
const x = bbox?.x ?? 0;
253+
const y = bbox?.y ?? 0;
254+
const w = bbox?.w ?? 0;
255+
const h = bbox?.h ?? 0;
256+
257+
const cx = x + w / 2;
258+
const cy = y + h / 2;
259+
260+
return scalePathAroundPoint(d, scaleVector, [cx, cy], decimalPlaces);
261+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>mzSVG</title>
6+
<style>
7+
body{
8+
display: flex;
9+
}
10+
11+
svg{
12+
border: 1px solid #d2d2d2;
13+
margin: 2rem auto;
14+
cursor: pointer;
15+
}
16+
</style>
17+
</head>
18+
<body>
19+
<script src="../../../dist/mz-svg.min.js"></script>
20+
<script>
21+
const $svg = mzSVG.createSVG({
22+
width: 800,
23+
height: 600
24+
});
25+
26+
const $rect = mzSVG.createPath({
27+
d: 'M 100 200 C 100 50 200 150 300 250',
28+
stroke: '#4ca989',
29+
fill: 'none',
30+
});
31+
32+
const $rect1 = mzSVG.createPath({
33+
d: 'M 100 200 C 100 50 200 150 300 250',
34+
stroke: '#000',
35+
fill: 'none',
36+
});
37+
38+
$svg.append($rect);
39+
$svg.append($rect1);
40+
41+
document.body.append($svg);
42+
43+
let d = $rect1.getAttribute('d');
44+
d = mzSVG.scalePath(d, [2, 2]);
45+
d = mzSVG.rotatePath(d, 45);
46+
$rect1.setAttribute('d', d);
47+
48+
const bbox = mzSVG.getPathBBox(d);
49+
$svg.append(mzSVG.createRect({
50+
x: bbox.x,
51+
y: bbox.y,
52+
width: bbox.w,
53+
height: bbox.h,
54+
fill: 'none',
55+
stroke: 'blue'
56+
}));
57+
58+
</script>
59+
</body>
60+
</html>

test-browser/examples/path/rotate-path-arc.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
document.body.append($svg);
4545

46-
/*const $rect1 = mzSVG.createPath({
46+
const $rect1 = mzSVG.createPath({
4747
d: d1,
4848
stroke: '#000',
4949
fill: 'none',
@@ -52,7 +52,7 @@
5252

5353
let d = $rect1.getAttribute('d');
5454
d = mzSVG.rotatePath(d, 90);
55-
$rect1.setAttribute('d', d);*/
55+
$rect1.setAttribute('d', d);
5656

5757
</script>
5858
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>mzSVG</title>
6+
<style>
7+
body{
8+
display: flex;
9+
}
10+
11+
svg{
12+
border: 1px solid #d2d2d2;
13+
margin: 2rem auto;
14+
cursor: pointer;
15+
}
16+
</style>
17+
</head>
18+
<body>
19+
<script src="../../../dist/mz-svg.min.js"></script>
20+
<script>
21+
const $svg = mzSVG.createSVG({
22+
width: 800,
23+
height: 600
24+
});
25+
26+
const $rect = mzSVG.createPath({
27+
d: 'M 100 200 C 100 50 200 150 300 250',
28+
stroke: '#4ca989',
29+
fill: 'none',
30+
});
31+
32+
const $rect1 = mzSVG.createPath({
33+
d: 'M 100 200 C 100 50 200 150 300 250',
34+
stroke: '#000',
35+
fill: 'none',
36+
});
37+
38+
$svg.append($rect);
39+
$svg.append($rect1);
40+
41+
document.body.append($svg);
42+
43+
let d = $rect1.getAttribute('d');
44+
d = mzSVG.scalePath(d, [2, 2]);
45+
$rect1.setAttribute('d', d);
46+
47+
48+
</script>
49+
</body>
50+
</html>

0 commit comments

Comments
 (0)