Skip to content

Commit

Permalink
Optimize switch cases based on simple-icons data (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
mondeja authored Feb 9, 2023
1 parent 0d1a785 commit 0cf9839
Show file tree
Hide file tree
Showing 14 changed files with 345 additions and 44 deletions.
1 change: 0 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
*
!LICENSE
!src/
!dist/
!package.json
!CHANGELOG.md
Expand Down
40 changes: 23 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,68 @@
# CHANGELOG

## [1.2.3] - 2022-12-23
## 1.2.4 - 2023-02-09

- Optimized proccesing of segments based on [Simple Icons] data.

[Simple Icons]: https://github.com/simple-icons/simple-icons

## 1.2.3 - 2022-12-23

- Fixed CLI not being executed in some versions of Node.js < v16.

## [1.2.2] - 2022-05-26
## 1.2.2 - 2022-05-26

- Fixed edge case computing cubic Bézier curves bounding boxes.

## [1.2.1] - 2022-05-12
## 1.2.1 - 2022-05-12

- Fixed error computing cubic Bézier curves bounding boxes.

## [1.2.0] - 2022-05-07
## 1.2.0 - 2022-05-07

- Use default export for better interoperability.

## [1.1.0] - 2022-05-05
## 1.1.0 - 2022-05-05

- Add support for Typescript.

## [1.0.2] - 2022-01-11
## 1.0.2 - 2022-01-11

- Add basic options `--version` and `--help` to CLI.

## [1.0.1] - 2021-06-21
## 1.0.1 - 2021-06-21

- Fixed error computing limits for cubic Bèzier curves of length 0.

## [1.0.0] - 2021-06-03
## 1.0.0 - 2021-06-03

- Testing with 100% coverage.
- Make `svgPathBbox` function the default export.

## [0.2.0] - 2020-12-22
## 0.2.0 - 2020-12-22

- Removed almost all public API functions (only keep `svgPathBbox` function).
- Removed `polf` dependency.
- Optimized quadratic Bézier curves minimum and maximum values computation.
- Optimized cubic Bézier curves minimum and maximum values computation.
- Optimized lineal segments minimum and maximum values computation.

## [0.1.5] - 2020-11-26
## 0.1.5 - 2020-11-26

- Documentation improved.
- Switch CI to Github Actions.

## [0.1.4] - 2020-11-23
## 0.1.4 - 2020-11-23

- Document and export `quadraticBezierCurveBbox` function.
- Remove development file from NPM package.
- Update acknowledgments.

## [0.1.1] - 2020-11-19
## 0.1.1 - 2020-11-19

- Fix error computing bounding boxes for Q, T and some C commands.

## [0.0.47] - 2020-07-23
## 0.0.47 - 2020-07-23

- Separate point on line functions in another package.
- Replaced svg-path-parser dependency with svgpath to optimize parsing time.
Expand All @@ -65,25 +71,25 @@
- Fix errors in utility functions.
- Add tests for utilities and command line client.

## [0.0.28] - 2020-05-22
## 0.0.28 - 2020-05-22

- Update LICENSE.
- Fix error converting quaratic to Bézier coordinates.
- Add tests for some bounding boxes functions.

## [0.0.26] - 2020-05-21
## 0.0.26 - 2020-05-21

- Removed `quadraticBezierCurveBbox` function.
- Optimized quadratic Bézier curve bounding box computation.
- Optimized cubic Bézier curve bounding box algorithm.
- Fixed error on V and H commands computing SVG path bbox.

## [0.0.20] - 2020-05-17
## 0.0.20 - 2020-05-17

- Add function to obtain an array of numbers from SVG path.
- Multiple paths as arguments for command line script.

## [0.0.13] - 2020-05-17
## 0.0.13 - 2020-05-17

- Add command line interface.
- Add linting.
Expand Down
21 changes: 18 additions & 3 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";
exports.__esModule = true;
// WARNING: This file is autogenerated, edit lib/index.template.ts
var svgPath = require("svgpath");
// Precision for consider cubic polynom as quadratic one
var CBEZIER_MINMAX_EPSILON = 0.00000001;
Expand Down Expand Up @@ -75,8 +76,7 @@ function svgPathBbox(d) {
.unshort()
.iterate(function (seg, _, x, y) {
switch (seg[0]) {
case "M":
case "L": {
case "M": {
if (min[0] > seg[1]) {
min[0] = seg[1];
}
Expand All @@ -91,6 +91,15 @@ function svgPathBbox(d) {
}
break;
}
case "H": {
if (min[0] > seg[1]) {
min[0] = seg[1];
}
if (max[0] < seg[1]) {
max[0] = seg[1];
}
break;
}
case "V": {
if (min[1] > seg[1]) {
min[1] = seg[1];
Expand All @@ -100,13 +109,19 @@ function svgPathBbox(d) {
}
break;
}
case "H": {
case "L": {
if (min[0] > seg[1]) {
min[0] = seg[1];
}
if (min[1] > seg[2]) {
min[1] = seg[2];
}
if (max[0] < seg[1]) {
max[0] = seg[1];
}
if (max[1] < seg[2]) {
max[1] = seg[2];
}
break;
}
case "C": {
Expand Down
74 changes: 74 additions & 0 deletions lib/cases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const ML = `{
if (min[0] > seg[1]) {
min[0] = seg[1];
}
if (min[1] > seg[2]) {
min[1] = seg[2];
}
if (max[0] < seg[1]) {
max[0] = seg[1];
}
if (max[1] < seg[2]) {
max[1] = seg[2];
}
break;
}`;

export default {
M: ML,
L: ML,
H: `{
if (min[0] > seg[1]) {
min[0] = seg[1];
}
if (max[0] < seg[1]) {
max[0] = seg[1];
}
break;
}`,
V: `{
if (min[1] > seg[1]) {
min[1] = seg[1];
}
if (max[1] < seg[1]) {
max[1] = seg[1];
}
break;
}`,
C: `{
const cxMinMax = minmaxC([x, seg[1], seg[3], seg[5]]);
if (min[0] > cxMinMax[0]) {
min[0] = cxMinMax[0];
}
if (max[0] < cxMinMax[1]) {
max[0] = cxMinMax[1];
}
const cyMinMax = minmaxC([y, seg[2], seg[4], seg[6]]);
if (min[1] > cyMinMax[0]) {
min[1] = cyMinMax[0];
}
if (max[1] < cyMinMax[1]) {
max[1] = cyMinMax[1];
}
break;
}`,
Q: `{
const qxMinMax = minmaxQ([x, seg[1], seg[3]]);
if (min[0] > qxMinMax[0]) {
min[0] = qxMinMax[0];
}
if (max[0] < qxMinMax[1]) {
max[0] = qxMinMax[1];
}
const qyMinMax = minmaxQ([y, seg[2], seg[4]]);
if (min[1] > qyMinMax[0]) {
min[1] = qyMinMax[0];
}
if (max[1] < qyMinMax[1]) {
max[1] = qyMinMax[1];
}
break;
}`,
}
103 changes: 103 additions & 0 deletions lib/index.template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"use strict";

import * as svgPath from "svgpath";

type minMax = [min: number, max: number];
export type BBox = [minX: number, minY: number, maxX: number, maxY: number];

// Precision for consider cubic polynom as quadratic one
const CBEZIER_MINMAX_EPSILON = 0.00000001;

// https://github.com/kpym/SVGPathy/blob/acd1a50c626b36d81969f6e98e8602e128ba4302/lib/box.js#L89
function minmaxQ(A: [number, number, number]): minMax {
const min = Math.min(A[0], A[2]),
max = Math.max(A[0], A[2]);

if (A[1] > A[0] ? A[2] >= A[1] : A[2] <= A[1]) {
// if no extremum in ]0,1[
return [min, max];
}

// check if the extremum E is min or max
const E = (A[0] * A[2] - A[1] * A[1]) / (A[0] - 2 * A[1] + A[2]);
return E < min ? [E, max] : [min, E];
}

// https://github.com/kpym/SVGPathy/blob/acd1a50c626b36d81969f6e98e8602e128ba4302/lib/box.js#L127
function minmaxC(A: [number, number, number, number]): minMax {
const K = A[0] - 3 * A[1] + 3 * A[2] - A[3];

// if the polynomial is (almost) quadratic and not cubic
if (Math.abs(K) < CBEZIER_MINMAX_EPSILON) {
if (A[0] === A[3] && A[0] === A[1]) {
// no curve, point targeting same location
return [A[0], A[3]];
}

return minmaxQ([
A[0],
-0.5 * A[0] + 1.5 * A[1],
A[0] - 3 * A[1] + 3 * A[2],
]);
}

// the reduced discriminant of the derivative
const T =
-A[0] * A[2] +
A[0] * A[3] -
A[1] * A[2] -
A[1] * A[3] +
A[1] * A[1] +
A[2] * A[2];

// if the polynomial is monotone in [0,1]
if (T <= 0) {
return [Math.min(A[0], A[3]), Math.max(A[0], A[3])];
}
const S = Math.sqrt(T);

// potential extrema
let min = Math.min(A[0], A[3]),
max = Math.max(A[0], A[3]);

const L = A[0] - 2 * A[1] + A[2];
// check local extrema
for (let R = (L + S) / K, i = 1; i <= 2; R = (L - S) / K, i++) {
if (R > 0 && R < 1) {
// if the extrema is for R in [0,1]
const Q =
A[0] * (1 - R) * (1 - R) * (1 - R) +
A[1] * 3 * (1 - R) * (1 - R) * R +
A[2] * 3 * (1 - R) * R * R +
A[3] * R * R * R;
if (Q < min) {
min = Q;
}
if (Q > max) {
max = Q;
}
}
}

return [min, max];
}

/**
* Compute bounding boxes of SVG paths.
* @param {String} d SVG path for which their bounding box will be computed.
* @returns {BBox}
*/
export default function svgPathBbox(d: string): BBox {
const min = [Infinity, Infinity],
max = [-Infinity, -Infinity];
svgPath(d)
.abs()
.unarc()
.unshort()
.iterate((seg, _, x, y) => {
switch (seg[0]) {
/*cases*/
}
}, true);
return [min[0], min[1], max[0], max[1]];
}
26 changes: 16 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0cf9839

Please sign in to comment.