svg-path-simplify 0.3.6 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +38 -85
- package/dist/svg-path-simplify.esm.js +1405 -528
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1405 -528
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +76 -15
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/docs/api.md +127 -0
- package/index.html +216 -277
- package/package.json +1 -1
- package/src/constants.js +8 -1
- package/src/pathData_simplify_cubic.js +0 -8
- package/src/pathSimplify-main.js +25 -9
- package/src/svg_flatten_transforms.js +1 -1
- package/src/svgii/convert_colors.js +52 -5
- package/src/svgii/convert_units.js +25 -10
- package/src/svgii/pathData_analyze.js +33 -13
- package/src/svgii/pathData_convert.js +62 -6
- package/src/svgii/pathData_parse_els.js +21 -5
- package/src/svgii/pathData_toPolygon.js +3 -1
- package/src/svgii/pathData_transform.js +307 -0
- package/src/svgii/svg-styles-getTransforms.js +119 -8
- package/src/svgii/svg-styles-to-attributes-const.js +19 -4
- package/src/svgii/svg_cleanup.js +319 -97
- package/src/svgii/svg_el_parse_style_props.js +218 -76
- package/src/svgii/transform_qr_decompose.js +74 -0
- package/src/svgii/pathData_scale.js +0 -42
- package/src/svgii/svg-styles-to-attributes.js +0 -236
package/package.json
CHANGED
package/src/constants.js
CHANGED
|
@@ -6,4 +6,11 @@ export const {
|
|
|
6
6
|
export const rad2Deg = 180/Math.PI
|
|
7
7
|
export const deg2rad = Math.PI/180
|
|
8
8
|
export const root2 = 1.4142135623730951
|
|
9
|
-
export const svgNs = 'http://www.w3.org/2000/svg'
|
|
9
|
+
export const svgNs = 'http://www.w3.org/2000/svg'
|
|
10
|
+
|
|
11
|
+
// 1/2.54
|
|
12
|
+
export const inch2cm = 0.39370078;
|
|
13
|
+
|
|
14
|
+
// 1/72
|
|
15
|
+
export const inch2pt = 0.01388889;
|
|
16
|
+
|
package/src/pathSimplify-main.js
CHANGED
|
@@ -12,7 +12,7 @@ import { optimizeClosePath, pathDataToTopLeft } from './svgii/pathData_reorder';
|
|
|
12
12
|
import { reversePathData } from './svgii/pathData_reverse';
|
|
13
13
|
import { addExtremePoints, splitSubpaths } from './svgii/pathData_split';
|
|
14
14
|
import { pathDataToD } from './svgii/pathData_stringify';
|
|
15
|
-
import { detectAccuracy, roundPathData} from './svgii/rounding';
|
|
15
|
+
import { detectAccuracy, roundPathData } from './svgii/rounding';
|
|
16
16
|
import { refineAdjacentExtremes } from './svgii/pathData_simplify_refineExtremes';
|
|
17
17
|
import { cleanUpSVG, removeEmptySVGEls, stringifySVG } from './svgii/svg_cleanup';
|
|
18
18
|
import { refineRoundedCorners } from './svgii/pathData_simplify_refineCorners';
|
|
@@ -99,9 +99,12 @@ export function svgPathSimplify(input = '', {
|
|
|
99
99
|
scaleTo = 0,
|
|
100
100
|
crop = false,
|
|
101
101
|
alignToOrigin = false,
|
|
102
|
+
|
|
103
|
+
// flatten transforms
|
|
102
104
|
convertTransforms = false,
|
|
103
105
|
|
|
104
106
|
|
|
107
|
+
|
|
105
108
|
//svg path optimizations
|
|
106
109
|
decimals = 3,
|
|
107
110
|
autoAccuracy = true,
|
|
@@ -114,24 +117,28 @@ export function svgPathSimplify(input = '', {
|
|
|
114
117
|
reversePath = false,
|
|
115
118
|
|
|
116
119
|
//svg cleanup options
|
|
120
|
+
minifyRgbColors = false,
|
|
117
121
|
removePrologue = true,
|
|
118
122
|
removeHidden = true,
|
|
119
123
|
removeUnused = true,
|
|
120
124
|
cleanupDefs = true,
|
|
121
125
|
cleanupClip = true,
|
|
122
126
|
cleanupSVGAtts = true,
|
|
123
|
-
|
|
127
|
+
|
|
124
128
|
stylesToAttributes = false,
|
|
125
129
|
fixHref = false,
|
|
126
130
|
legacyHref = false,
|
|
127
131
|
removeNameSpaced = true,
|
|
128
|
-
attributesToGroup = false,
|
|
129
|
-
removeOffCanvas = false,
|
|
130
132
|
|
|
133
|
+
//attributesToGroup = false,
|
|
134
|
+
removeOffCanvas = false,
|
|
135
|
+
unGroup = false,
|
|
131
136
|
mergePaths = false,
|
|
132
137
|
|
|
133
138
|
// shape conversions
|
|
134
139
|
shapesToPaths = false,
|
|
140
|
+
|
|
141
|
+
|
|
135
142
|
//toPaths || toShapes
|
|
136
143
|
shapeConvert = 0,
|
|
137
144
|
convert_rects = false,
|
|
@@ -146,6 +153,8 @@ export function svgPathSimplify(input = '', {
|
|
|
146
153
|
addViewBox = false,
|
|
147
154
|
addDimensions = false,
|
|
148
155
|
|
|
156
|
+
removeComments = true,
|
|
157
|
+
|
|
149
158
|
} = {}) {
|
|
150
159
|
|
|
151
160
|
|
|
@@ -230,12 +239,19 @@ export function svgPathSimplify(input = '', {
|
|
|
230
239
|
// mode:1 – process complete svg DOM
|
|
231
240
|
else {
|
|
232
241
|
|
|
242
|
+
// convert all shapes to paths
|
|
243
|
+
if (shapesToPaths) {
|
|
244
|
+
shapeConvert = true
|
|
245
|
+
convert_rects = true
|
|
246
|
+
convert_ellipses = true
|
|
247
|
+
convert_poly = true
|
|
248
|
+
convert_lines = true
|
|
249
|
+
}
|
|
233
250
|
|
|
234
251
|
//sanitize
|
|
235
252
|
let svgPropObject = cleanUpSVG(input, {
|
|
236
253
|
removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, cleanUpStrokes, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, convertTransforms, legacyHref, cleanupDefs, cleanupClip, addViewBox, removeOffCanvas, addDimensions,
|
|
237
|
-
shapeConvert, convert_rects, convert_ellipses, convert_poly, convert_lines
|
|
238
|
-
|
|
254
|
+
shapeConvert, convert_rects, convert_ellipses, convert_poly, convert_lines, minifyRgbColors, unGroup, convertTransforms
|
|
239
255
|
}
|
|
240
256
|
);
|
|
241
257
|
|
|
@@ -580,8 +596,8 @@ export function svgPathSimplify(input = '', {
|
|
|
580
596
|
|
|
581
597
|
if (autoAccuracy) {
|
|
582
598
|
accuracyArr = accuracyArr.sort().reverse();
|
|
583
|
-
let decimalsMid = accuracyArr[Math.floor(accuracyArr.length*0.5)]
|
|
584
|
-
decimals = Math.floor(
|
|
599
|
+
let decimalsMid = accuracyArr[Math.floor(accuracyArr.length * 0.5)]
|
|
600
|
+
decimals = Math.floor((accuracyArr[0] + decimalsMid) * 0.5)
|
|
585
601
|
//decimals = detectAccuracy(pathData)
|
|
586
602
|
//console.log('decimals', decimals, 'decimalsMid', decimalsMid, accuracyArr);
|
|
587
603
|
pathOptions.decimals = decimals
|
|
@@ -711,7 +727,7 @@ export function svgPathSimplify(input = '', {
|
|
|
711
727
|
}
|
|
712
728
|
|
|
713
729
|
|
|
714
|
-
svg = stringifySVG(svg, omitNamespace);
|
|
730
|
+
svg = stringifySVG(svg, { omitNamespace, removeComments });
|
|
715
731
|
|
|
716
732
|
|
|
717
733
|
//svgSizeOpt = new Blob([svg]).size
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseCSSTransform, parseTransform } from "./svgii/svg-styles-getTransforms";
|
|
2
|
-
import { getElAttributes, getElementProps, getElStyleProps } from "
|
|
2
|
+
import { getElAttributes, getElementProps, getElStyleProps } from "../__back/svg-styles-to-attributes";
|
|
3
3
|
|
|
4
4
|
export function flattenTransforms(svg) {
|
|
5
5
|
|
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
export function parseColor(str) {
|
|
3
|
+
let type = str.startsWith('#') ? 'rgbHex' : (str.includes('(') ? 'fn' : typeof str);
|
|
4
|
+
let col = {}
|
|
5
|
+
let mode = null;
|
|
6
|
+
let colObj = { mode: null, values: [] }
|
|
7
|
+
if (type === 'rgbHex') {
|
|
8
|
+
col = hex2Rgb(str)
|
|
9
|
+
mode = 'rgba';
|
|
10
|
+
}
|
|
11
|
+
else if (type === 'fn') {
|
|
12
|
+
let colVals = str.split(/\(|\)/).filter(Boolean)
|
|
13
|
+
if (colVals.length < 2) return str;
|
|
14
|
+
|
|
15
|
+
mode = colVals[0];
|
|
16
|
+
let colorComponents = colVals[1].split(/,| /).filter(Boolean).map(Number)
|
|
17
|
+
|
|
18
|
+
let keys = mode.split('');
|
|
19
|
+
keys.forEach((k, i) => {
|
|
20
|
+
let val = colorComponents[i]
|
|
21
|
+
if (mode === 'rgba' && k === 'a') {
|
|
22
|
+
val = Math.floor(val * 255)
|
|
23
|
+
}
|
|
24
|
+
col[k] = val;
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
else if (type === 'string') {
|
|
28
|
+
colObj.mode = 'keyword'
|
|
29
|
+
colObj.values = [str]
|
|
30
|
+
return colObj
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (mode === 'rgba' || mode === 'rgb') {
|
|
34
|
+
col.a = !col.a ? 255 : col.a;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
colObj.mode = mode
|
|
38
|
+
colObj.values = Object.values(col)
|
|
39
|
+
|
|
40
|
+
return colObj;
|
|
41
|
+
}
|
|
42
|
+
|
|
1
43
|
export function hex2Rgb(hex = '') {
|
|
2
44
|
// Remove # if present
|
|
3
45
|
if (hex.startsWith('#')) hex = hex.substring(1);
|
|
@@ -30,13 +72,18 @@ export function hex2Rgb(hex = '') {
|
|
|
30
72
|
|
|
31
73
|
}
|
|
32
74
|
|
|
33
|
-
export function rgba2Hex({ r, g, b, a = 255 }) {
|
|
75
|
+
export function rgba2Hex({ r = 0, g = 0, b = 0, a = 255, values = [] }) {
|
|
34
76
|
// Helper function to convert number to 2-digit hex
|
|
35
77
|
const toHex = (num) => {
|
|
36
78
|
const hex = Math.min(255, Math.max(0, Math.round(num))).toString(16);
|
|
37
79
|
return hex.length === 1 ? '0' + hex : hex;
|
|
38
80
|
};
|
|
39
81
|
|
|
82
|
+
// convert from number array input
|
|
83
|
+
if (!r && !g && !b && values.length) {
|
|
84
|
+
[r, g, b, a = 255] = values;
|
|
85
|
+
}
|
|
86
|
+
|
|
40
87
|
// Get hex values
|
|
41
88
|
let rHex = toHex(r);
|
|
42
89
|
let gHex = toHex(g);
|
|
@@ -51,7 +98,7 @@ export function rgba2Hex({ r, g, b, a = 255 }) {
|
|
|
51
98
|
}
|
|
52
99
|
|
|
53
100
|
// Check for 4-character RGBA short notation (e.g., #ffff)
|
|
54
|
-
if (aHex && allowsShort
|
|
101
|
+
if (aHex && allowsShort) {
|
|
55
102
|
return `#${rHex[0]}${gHex[0]}${bHex[0]}${aHex[0]}`;
|
|
56
103
|
}
|
|
57
104
|
|
|
@@ -67,7 +114,7 @@ export function rgba2Hex({ r, g, b, a = 255 }) {
|
|
|
67
114
|
|
|
68
115
|
|
|
69
116
|
export function hsl2Rgb(hsla = {}) {
|
|
70
|
-
let {h, s, l, a = 1} = hsla;
|
|
117
|
+
let { h, s, l, a = 1 } = hsla;
|
|
71
118
|
|
|
72
119
|
// Normalize
|
|
73
120
|
h = ((h % 360) + 360) % 360; // wrap hue
|
|
@@ -90,9 +137,9 @@ export function hsl2Rgb(hsla = {}) {
|
|
|
90
137
|
let r = (r1 + m) * 255;
|
|
91
138
|
let g = (g1 + m) * 255;
|
|
92
139
|
let b = (b1 + m) * 255;
|
|
93
|
-
a = Math.floor(a*255);
|
|
140
|
+
a = Math.floor(a * 255);
|
|
94
141
|
|
|
95
142
|
[r, g, b] = [r, g, b].map((val) => +val.toFixed(0));
|
|
96
143
|
|
|
97
|
-
return {r,g,b,a}
|
|
144
|
+
return { r, g, b, a }
|
|
98
145
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deg2rad, rad2Deg, root2 } from "../constants";
|
|
1
|
+
import { deg2rad, inch2cm, inch2pt, rad2Deg, root2 } from "../constants";
|
|
2
2
|
import { hex2Rgb, hsl2Rgb, rgba2Hex } from "./convert_colors";
|
|
3
3
|
import { autoRound } from "./rounding";
|
|
4
4
|
import { horizontalProps, verticalProps } from "./svg-styles-to-attributes-const";
|
|
@@ -56,21 +56,21 @@ export function normalizeUnits(value = null, {
|
|
|
56
56
|
// only required for circle r normalization when height!=width
|
|
57
57
|
normalizedDiagonal = width === height ? false : normalizedDiagonal;
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
let type = typeof value;
|
|
60
60
|
if (!value) return value;
|
|
61
61
|
|
|
62
62
|
// check if value is string
|
|
63
|
-
let
|
|
64
|
-
let
|
|
63
|
+
let isNum = type === 'number' ? true : isNumericValue(value)
|
|
64
|
+
let isArray = type === 'string' ? value.split(/,| /).length > 1 : false;
|
|
65
|
+
let isFunction = type === 'string' ? value.includes('(') : false;
|
|
65
66
|
//console.log(isArray);
|
|
66
|
-
let isNum = isNumericValue(value)
|
|
67
67
|
if (!isNum || isArray || isFunction) return value
|
|
68
68
|
|
|
69
69
|
// check unit if not specified
|
|
70
70
|
unit = !unit ? getUnit(value) : unit;
|
|
71
71
|
|
|
72
72
|
let val = parseFloat(value);
|
|
73
|
-
let scale=1;
|
|
73
|
+
let scale = 1;
|
|
74
74
|
let scaleRoot = Math.sqrt(width * width + height * height) / root2
|
|
75
75
|
|
|
76
76
|
|
|
@@ -92,7 +92,6 @@ export function normalizeUnits(value = null, {
|
|
|
92
92
|
}
|
|
93
93
|
break;
|
|
94
94
|
|
|
95
|
-
|
|
96
95
|
case "rad":
|
|
97
96
|
scale = rad2Deg;
|
|
98
97
|
break;
|
|
@@ -103,15 +102,31 @@ export function normalizeUnits(value = null, {
|
|
|
103
102
|
case "in":
|
|
104
103
|
scale = dpi;
|
|
105
104
|
break;
|
|
105
|
+
|
|
106
106
|
case "pt":
|
|
107
|
-
|
|
107
|
+
// 1/72
|
|
108
|
+
scale = dpi * inch2pt;
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
case "pc":
|
|
112
|
+
// 1/6
|
|
113
|
+
scale = dpi * 0.16666667;
|
|
108
114
|
break;
|
|
115
|
+
|
|
109
116
|
case "cm":
|
|
110
|
-
|
|
117
|
+
// 1/2.54
|
|
118
|
+
scale = inch2cm * dpi;
|
|
111
119
|
break;
|
|
112
120
|
case "mm":
|
|
113
|
-
scale = ((1 / 2.54) * dpi)
|
|
121
|
+
//scale = ((1 / 2.54) * dpi) * 0.1;
|
|
122
|
+
scale = inch2cm * dpi * 0.1
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
// has anyone ever used it?
|
|
126
|
+
case "Q":
|
|
127
|
+
scale = inch2cm * dpi * 0.025;
|
|
114
128
|
break;
|
|
129
|
+
|
|
115
130
|
// just a default approximation
|
|
116
131
|
case "em":
|
|
117
132
|
case "rem":
|
|
@@ -55,10 +55,10 @@ export function analyzePathData(pathData = [], {
|
|
|
55
55
|
let len = pathData.length;
|
|
56
56
|
|
|
57
57
|
// threshold for corner angles: 10 deg
|
|
58
|
-
let thresholdCorner = Math.PI * 2 / 360 * 10
|
|
58
|
+
//let thresholdCorner = Math.PI * 2 / 360 * 10
|
|
59
59
|
|
|
60
60
|
// define angle threshold for semi extremes
|
|
61
|
-
let thresholdAngle = detectSemiExtremes ? 0.01 : 0.05
|
|
61
|
+
//let thresholdAngle = detectSemiExtremes ? 0.01 : 0.05
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
for (let c = 2; len && c <= len; c++) {
|
|
@@ -86,7 +86,7 @@ export function analyzePathData(pathData = [], {
|
|
|
86
86
|
// check flatness of command
|
|
87
87
|
let toleranceFlat = 0.01;
|
|
88
88
|
let thresholdLength = dimA * 0.1
|
|
89
|
-
let threshold = thresholdLength*0.01
|
|
89
|
+
let threshold = thresholdLength * 0.01
|
|
90
90
|
let areaThresh = squareDist * toleranceFlat;
|
|
91
91
|
let isFlat = Math.abs(cptArea) < areaThresh;
|
|
92
92
|
|
|
@@ -109,14 +109,29 @@ export function analyzePathData(pathData = [], {
|
|
|
109
109
|
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x)
|
|
110
110
|
let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y)
|
|
111
111
|
|
|
112
|
-
let horizontal = (dy === 0 || dy
|
|
113
|
-
let vertical = (dx === 0 || dx
|
|
112
|
+
//let horizontal = (dy === 0 || dy<=threshold ) && dx > 0
|
|
113
|
+
//let vertical = (dx === 0 || dx<=threshold ) && dy > 0
|
|
114
|
+
let horizontal = (dy === 0 || dy <= threshold) && dx > 0
|
|
115
|
+
let vertical = (dx === 0 || dx <= threshold) && dy > 0
|
|
116
|
+
|
|
114
117
|
|
|
115
118
|
if (horizontal || vertical) {
|
|
116
119
|
hasExtremes = true;
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
// is extreme relative to bounding box
|
|
123
|
+
|
|
124
|
+
// (cp1.x===p0.x && cp1.y!==p0.y ) ||
|
|
125
|
+
if ((cp1.x === p0.x && cp1.y !== p0.y) || (cp1.y === p0.y && cp1.x !== p0.x)) {
|
|
126
|
+
//hasExtremes = true;
|
|
127
|
+
//renderPoint(markers, p0 )
|
|
128
|
+
//p0.extreme = true
|
|
129
|
+
//let comP = pathDataProps[pathDataProps.length-1]
|
|
130
|
+
pathDataProps[pathDataProps.length - 1].extreme = true
|
|
131
|
+
//console.log(comP);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
120
135
|
if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
|
|
121
136
|
hasExtremes = true;
|
|
122
137
|
}
|
|
@@ -126,6 +141,7 @@ export function analyzePathData(pathData = [], {
|
|
|
126
141
|
let couldHaveExtremes = bezierhasExtreme(null, commandPts)
|
|
127
142
|
if (couldHaveExtremes) {
|
|
128
143
|
let tArr = getTatAngles(commandPts)
|
|
144
|
+
|
|
129
145
|
if (tArr.length && (tArr[0] > 0.2)) {
|
|
130
146
|
hasExtremes = true;
|
|
131
147
|
}
|
|
@@ -181,24 +197,28 @@ export function analyzePathData(pathData = [], {
|
|
|
181
197
|
|
|
182
198
|
let signChange2 = (areaCpt < 0 && com.cptArea > 0) || (areaCpt > 0 && com.cptArea < 0) ? true : false;
|
|
183
199
|
|
|
184
|
-
let isCorner
|
|
200
|
+
let isCorner = !isFlat && signChange2;
|
|
185
201
|
if (isCorner) com.corner = true;
|
|
186
202
|
}
|
|
187
203
|
}
|
|
188
204
|
|
|
189
|
-
|
|
190
|
-
|
|
205
|
+
pathDataProps.push(com)
|
|
206
|
+
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
//debug = true;
|
|
211
|
+
|
|
212
|
+
if (debug) {
|
|
213
|
+
pathDataProps.forEach(com=>{
|
|
191
214
|
//if (com.semiExtreme) renderPoint(markers, com.p, 'blue', '2%', '0.5')
|
|
192
215
|
if (com.directionChange) renderPoint(markers, com.p, 'orange', '1.5%', '0.5')
|
|
193
216
|
if (com.corner) renderPoint(markers, com.p, 'magenta', '1.5%', '0.5')
|
|
194
217
|
if (com.extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5')
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
pathDataProps.push(com)
|
|
199
|
-
|
|
218
|
+
})
|
|
200
219
|
}
|
|
201
220
|
|
|
221
|
+
|
|
202
222
|
//pathDataProps.push(comLast)
|
|
203
223
|
|
|
204
224
|
|
|
@@ -51,6 +51,8 @@ export function parsePathDataNormalized(d,
|
|
|
51
51
|
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
52
52
|
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
53
53
|
|
|
54
|
+
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
55
|
+
|
|
54
56
|
// normalize
|
|
55
57
|
pathData = normalizePathData(pathData,
|
|
56
58
|
{
|
|
@@ -77,11 +79,14 @@ export function convertPathData(pathData, {
|
|
|
77
79
|
hasShorthands = true,
|
|
78
80
|
hasQuadratics = true,
|
|
79
81
|
hasArcs = true,
|
|
82
|
+
optimizeArcs = true,
|
|
80
83
|
testTypes = false
|
|
81
84
|
|
|
82
85
|
|
|
83
86
|
} = {}) {
|
|
84
87
|
|
|
88
|
+
|
|
89
|
+
|
|
85
90
|
// pathdata properties - test= true adds a manual test
|
|
86
91
|
if (testTypes) {
|
|
87
92
|
//console.log('test for conversions');
|
|
@@ -99,26 +104,76 @@ export function convertPathData(pathData, {
|
|
|
99
104
|
toShorthands = toLonghands ? false : toShorthands
|
|
100
105
|
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
if (
|
|
107
|
+
if (toAbsolute) pathData = pathDataToAbsolute(pathData);
|
|
108
|
+
if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
// minify semicircle radii
|
|
112
|
+
if (optimizeArcs) pathData = optimizeArcPathData(pathData);
|
|
104
113
|
|
|
105
114
|
|
|
106
115
|
//if(decimals>-1 && decimals<2) pathData = roundPathData(pathData, decimals);
|
|
107
116
|
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
108
|
-
if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
|
|
109
|
-
|
|
110
|
-
if (toAbsolute) pathData = pathDataToAbsolute(pathData);
|
|
111
117
|
|
|
112
118
|
if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData)
|
|
113
119
|
|
|
120
|
+
//console.log(toShorthands, toRelative, decimals);
|
|
121
|
+
if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
|
|
122
|
+
|
|
123
|
+
|
|
114
124
|
// pre round - before relative conversion to minimize distortions
|
|
115
125
|
if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
|
|
126
|
+
|
|
127
|
+
|
|
116
128
|
if (toRelative) pathData = pathDataToRelative(pathData);
|
|
117
129
|
if (decimals > -1) pathData = roundPathData(pathData, decimals);
|
|
118
130
|
|
|
119
131
|
return pathData
|
|
120
132
|
}
|
|
121
133
|
|
|
134
|
+
/**
|
|
135
|
+
*
|
|
136
|
+
* @param {*} pathData
|
|
137
|
+
* @returns
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
export function optimizeArcPathData(pathData = []) {
|
|
141
|
+
pathData.forEach((com, i) => {
|
|
142
|
+
let { type, values } = com;
|
|
143
|
+
if (type === 'A') {
|
|
144
|
+
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
145
|
+
let comPrev = pathData[i - 1]
|
|
146
|
+
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
147
|
+
let M = { x: x0, y: y0 };
|
|
148
|
+
let p = { x, y };
|
|
149
|
+
//largeArc=true
|
|
150
|
+
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
151
|
+
|
|
152
|
+
// rx and ry are large enough
|
|
153
|
+
if (rx >= 1 && (x === x0 || y === y0)) {
|
|
154
|
+
let diff = Math.abs(rx - ry) / rx;
|
|
155
|
+
|
|
156
|
+
// rx~==ry
|
|
157
|
+
if (diff < 0.01) {
|
|
158
|
+
|
|
159
|
+
// test radius against mid point
|
|
160
|
+
let pMid = interpolate(M, p, 0.5)
|
|
161
|
+
let distM = getDistance(pMid, M)
|
|
162
|
+
let rDiff = Math.abs(distM - rx) / rx
|
|
163
|
+
|
|
164
|
+
// half distance between mid and start point should be ~ equal
|
|
165
|
+
if(rDiff<0.01){
|
|
166
|
+
pathData[i].values[0] = 1;
|
|
167
|
+
pathData[i].values[1] = 1;
|
|
168
|
+
pathData[i].values[2] = 0;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
return pathData;
|
|
175
|
+
}
|
|
176
|
+
|
|
122
177
|
|
|
123
178
|
|
|
124
179
|
/**
|
|
@@ -139,6 +194,7 @@ export function normalizePathData(pathData = [],
|
|
|
139
194
|
} = {}
|
|
140
195
|
) {
|
|
141
196
|
|
|
197
|
+
|
|
142
198
|
return convertPathData(pathData, { toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy, hasRelatives, hasShorthands, hasQuadratics, hasArcs, testTypes, decimals: -1 })
|
|
143
199
|
}
|
|
144
200
|
|
|
@@ -577,7 +633,7 @@ export function pathDataToShorthands(pathData, decimals = -1, test = false) {
|
|
|
577
633
|
let dy1 = (p0.y - cpPrev.y)
|
|
578
634
|
|
|
579
635
|
//adjust maxDist
|
|
580
|
-
maxDist = getDistManhattan(cpPrev, cpFirst) * 0.
|
|
636
|
+
maxDist = getDistManhattan(cpPrev, cpFirst) * 0.01
|
|
581
637
|
|
|
582
638
|
// reflected cp
|
|
583
639
|
let cpR = { x: cpPrev.x + dx1 * 2, y: cpPrev.y + dy1 * 2 }
|
|
@@ -11,8 +11,10 @@ import { getPolyBBox } from './geometry_bbox.js';
|
|
|
11
11
|
import { getPathDataVerbose } from './pathData_analyze.js';
|
|
12
12
|
import { parsePathDataNormalized } from './pathData_convert.js';
|
|
13
13
|
import { parsePathDataString, stringifyPathData } from './pathData_parse.js';
|
|
14
|
+
import { transformPathData } from './pathData_transform.js';
|
|
14
15
|
import { autoRound, roundTo } from './rounding.js';
|
|
15
16
|
import { attLookup } from './svg-styles-to-attributes-const.js';
|
|
17
|
+
import { qrDecomposeMatrix } from './transform_qr_decompose.js';
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
export function pathElToShape(el, {
|
|
@@ -151,39 +153,53 @@ export function shapeElToPath(el, { width = 0,
|
|
|
151
153
|
convert_rects = false,
|
|
152
154
|
convert_ellipses = false,
|
|
153
155
|
convert_poly = false,
|
|
154
|
-
convert_lines = false
|
|
156
|
+
convert_lines = false,
|
|
157
|
+
//matrix={a:1, b:0, c:0, d:1, e:0, f:0},
|
|
158
|
+
matrix=null
|
|
155
159
|
|
|
156
160
|
} = {}) {
|
|
157
161
|
|
|
158
162
|
let nodeName = el.nodeName.toLowerCase();
|
|
163
|
+
//console.log('shapeElToPath', nodeName);
|
|
164
|
+
|
|
159
165
|
|
|
160
166
|
if (
|
|
161
|
-
nodeName === 'path' ||
|
|
167
|
+
nodeName === 'path' && !matrix ||
|
|
162
168
|
nodeName === 'rect' && !convert_rects ||
|
|
163
169
|
(nodeName === 'circle' || nodeName === 'ellipse') && !convert_ellipses ||
|
|
164
170
|
(nodeName === 'polygon' || nodeName === 'polyline') && !convert_poly ||
|
|
165
171
|
(nodeName === 'line') && !convert_lines
|
|
166
172
|
) return el;
|
|
167
173
|
|
|
174
|
+
|
|
168
175
|
let pathData = getPathDataFromEl(el, { width, height });
|
|
176
|
+
|
|
177
|
+
// shape attributes – obsolete for path els
|
|
178
|
+
let exclude = ['d', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
179
|
+
|
|
180
|
+
// transform pathData
|
|
181
|
+
if(matrix && Object.values(matrix).join('')!=='100100'){
|
|
182
|
+
pathData = transformPathData(pathData, matrix)
|
|
183
|
+
exclude.push('transform', 'transform-origin')
|
|
184
|
+
}
|
|
185
|
+
|
|
169
186
|
let d = pathData.map(com => { return `${com.type} ${com.values} ` }).join(' ')
|
|
170
187
|
let attributes = [...el.attributes].map(att => att.name);
|
|
171
188
|
|
|
172
|
-
|
|
173
189
|
let pathN = document.createElementNS(svgNs, 'path');
|
|
174
190
|
pathN.setAttribute('d', d);
|
|
175
191
|
|
|
176
|
-
let exclude = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
177
192
|
|
|
193
|
+
// copy attributes
|
|
178
194
|
attributes.forEach(att => {
|
|
179
195
|
if (!exclude.includes(att)) {
|
|
180
|
-
//console.log(att, attributes, exclude);
|
|
181
196
|
let val = el.getAttribute(att);
|
|
182
197
|
pathN.setAttribute(att, val)
|
|
183
198
|
}
|
|
184
199
|
})
|
|
185
200
|
|
|
186
201
|
//el.replaceWith(pathN)
|
|
202
|
+
//console.log(pathN.outerHTML, d);
|
|
187
203
|
return pathN
|
|
188
204
|
|
|
189
205
|
}
|
|
@@ -129,9 +129,11 @@ simplifyRDP=1,
|
|
|
129
129
|
poly = poly.map(pt => { return [pt.x, pt.y] })
|
|
130
130
|
}
|
|
131
131
|
else if(polyFormat==='string'){
|
|
132
|
-
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).join(' ')
|
|
132
|
+
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).flat().join(' ')
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
//console.log(pathData);
|
|
136
|
+
|
|
135
137
|
return { pathData, poly }
|
|
136
138
|
|
|
137
139
|
}
|