From d735a9f3d80e8b3357865d0f67f958ccbfed6c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Thu, 26 May 2022 11:18:30 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20error=20computing=20B=C3=A9zier=20curves?= =?UTF-8?q?=20bounding=20boxes=20(#97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ dist/index.js | 10 +++++----- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- scripts/si-benchmark.ts | 8 +++++++- src/index.ts | 11 ++++++----- tests/cases/bbox.ts | 8 ++++++++ 7 files changed, 41 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f8c0ac..79a476a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## [1.2.2] - 2022-05-26 + +- Fixed edge case computing cubic Bézier curves bounding boxes. + ## [1.2.1] - 2022-05-12 - Fixed error computing cubic Bézier curves bounding boxes. diff --git a/dist/index.js b/dist/index.js index 7944f45..eede4eb 100644 --- a/dist/index.js +++ b/dist/index.js @@ -16,13 +16,13 @@ function minmaxQ(A) { } // https://github.com/kpym/SVGPathy/blob/acd1a50c626b36d81969f6e98e8602e128ba4302/lib/box.js#L127 function minmaxC(A) { - if (A[0] === A[1] && A[0] === A[3]) { - // no curve, point targeting same location - return [A[0], A[3]]; - } - // if the polynomial is (almost) quadratic and not cubic var 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], diff --git a/package-lock.json b/package-lock.json index 7406242..c776874 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "svg-path-bbox", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "svg-path-bbox", - "version": "1.2.0", + "version": "1.2.1", "license": "BSD-3-Clause", "dependencies": { "svgpath": "^2.2.0" @@ -34,7 +34,7 @@ "prettier": "2.6.2", "puppeteer": "14.0.0", "rimraf": "3.0.2", - "simple-icons": "6.21.0", + "simple-icons": "6.23.0", "svg-path-bounding-box": "1.0.4", "ts-jest": "28.0.2", "ts-node": "10.7.0", @@ -7821,9 +7821,9 @@ "dev": true }, "node_modules/simple-icons": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-6.21.0.tgz", - "integrity": "sha512-+DRxJwF66C5ZOgYUIy4BfD+algfVaRKfKT7Qs8TtvPSCfc7+BUoKh5Udfa0zPHsUc8BmAuflcTrQqVv+5XpY6g==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-6.23.0.tgz", + "integrity": "sha512-9ql+6OdW5Dnfx1/z5z8MYO2y+OM4Oj4XBvQY5Nlye4eW+Nf5igHzHIKKtTmFkvCmR0lrddZpe1F/89chgoUXhg==", "dev": true, "engines": { "node": ">=0.12.18" @@ -15171,9 +15171,9 @@ "dev": true }, "simple-icons": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-6.21.0.tgz", - "integrity": "sha512-+DRxJwF66C5ZOgYUIy4BfD+algfVaRKfKT7Qs8TtvPSCfc7+BUoKh5Udfa0zPHsUc8BmAuflcTrQqVv+5XpY6g==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-6.23.0.tgz", + "integrity": "sha512-9ql+6OdW5Dnfx1/z5z8MYO2y+OM4Oj4XBvQY5Nlye4eW+Nf5igHzHIKKtTmFkvCmR0lrddZpe1F/89chgoUXhg==", "dev": true }, "sisteransi": { diff --git a/package.json b/package.json index ee5911b..99191cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svg-path-bbox", - "version": "1.2.1", + "version": "1.2.2", "description": "Compute bounding boxes of SVG paths.", "keywords": [ "svg", @@ -62,7 +62,7 @@ "prettier": "2.6.2", "puppeteer": "14.0.0", "rimraf": "3.0.2", - "simple-icons": "6.21.0", + "simple-icons": "6.23.0", "svg-path-bounding-box": "1.0.4", "ts-jest": "28.0.2", "ts-node": "10.7.0", diff --git a/scripts/si-benchmark.ts b/scripts/si-benchmark.ts index 874f8ac..ecf5ec0 100644 --- a/scripts/si-benchmark.ts +++ b/scripts/si-benchmark.ts @@ -1,12 +1,18 @@ import * as icons from "simple-icons/icons"; +import type { SimpleIcon } from "simple-icons"; import runLibrariesBenchmark from "./benchmark"; const EPOCHS = [1000]; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const FILTER = (icon: SimpleIcon) => true; + if (require.main === module) { runLibrariesBenchmark( - Object.values(icons).map((icon) => [icon.title, icon.path]), + Object.values(icons) + .filter(FILTER) + .map((icon) => [icon.title, icon.path]), EPOCHS ); } diff --git a/src/index.ts b/src/index.ts index 6b49b09..6dc7a23 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,14 +25,15 @@ function minmaxQ(A: [number, number, number]): minMax { // https://github.com/kpym/SVGPathy/blob/acd1a50c626b36d81969f6e98e8602e128ba4302/lib/box.js#L127 function minmaxC(A: [number, number, number, number]): minMax { - if (A[0] === A[1] && A[0] === A[3]) { - // no curve, point targeting same location - return [A[0], A[3]]; - } + const K = A[0] - 3 * A[1] + 3 * A[2] - A[3]; // if the polynomial is (almost) quadratic and not cubic - const K = A[0] - 3 * A[1] + 3 * A[2] - A[3]; 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], diff --git a/tests/cases/bbox.ts b/tests/cases/bbox.ts index 7f349f8..1680816 100644 --- a/tests/cases/bbox.ts +++ b/tests/cases/bbox.ts @@ -157,6 +157,14 @@ export const pathologicalCases: CasesTuple = [ [209.59000000000003, 339.90999999999764, 452.7900000000486, 896.1], // svg-path-bounding-box: [ 209.59, 339.91, 452.79, 896.1 ] ], + + // Tapas + // https://github.com/simple-icons/simple-icons/pull/7442#issuecomment-1137971253 + [ + "M7.67 1.56c.282-.134.542-.338.81-.513.253-.163.54-.436.894-.33.103.296-.162.503-.331.662-.538.511-1.154.975-1.72 1.456A240.349 240.349 0 0 1 1.5 7.598a7.406 7.406 0 0 1-.612.445c-.183.118-.456.359-.71.165.071-.337.306-.567.512-.778.213-.216.414-.446.629-.66-.248-.427-.473-.821-.662-1.274-.186-.449-.378-.971-.38-1.554-.002-1.109.635-2.043 1.34-2.68C2.34.61 3.306.066 4.429.006 6.015-.078 6.933.71 7.67 1.56zm5.012 18.075v.198c-.278.01-.532-.01-.795-.016v-.198c.277-.008.535.006.795.016zm-1.59 0v.198c-.282-.012-.52.021-.792.018v-.198a9.53 9.53 0 0 1 .793-.018zm3.177.05c-.007.067.013.158-.017.199-.251-.02-.518-.024-.778-.033v-.198c.275.003.542.009.795.032zm-4.763 0v.199c-.274.002-.512.039-.795.032v-.197c.28.001.516-.036.795-.034zm5.555.034c.255.033.544.029.793.064.013.084-.014.129-.015.2-.255-.033-.544-.03-.794-.067a.703.703 0 0 0 .016-.197zm-7.142.065v.2c-.26.02-.517.046-.778.065-.022-.05-.018-.126-.017-.198.265-.024.521-.053.795-.067zm8.73.067c.269.023.537.048.793.082-.02.058-.004.148-.032.199-.25-.036-.518-.053-.778-.083-.01-.083.017-.128.017-.198zm-10.319.082c-.006.08.03.113.017.199-.259.022-.568.082-.793.082.012-.077-.02-.114-.018-.182.252-.045.529-.066.794-.099zm12.684.199c.012.084-.027.114-.017.196-.256-.044-.54-.063-.794-.114.01-.058.025-.109.017-.182.228.008.545.062.795.1zm-14.288 0c.06.022.033.133.05.196-.259.04-.517.08-.777.117a.68.68 0 0 1-.034-.197c.253-.038.515-.072.761-.116zm15.86.233a.628.628 0 0 1-.034.213c-.247-.055-.52-.083-.777-.132a.702.702 0 0 1 .034-.197c.263.032.503.09.776.116zm-17.414.016c.02.057.036.116.034.196-.263.04-.503.105-.778.133-.004-.073-.034-.12-.033-.197.275-.028.515-.092.777-.132zm18.208.132c.255.052.508.109.778.148-.004.072-.034.119-.034.197-.28-.021-.495-.11-.778-.133-.018-.041.016-.15.034-.212zM22.669 16.726c.156.092.47.098.595.246.099.115.144.486.182.744.203 1.296.287 2.808.332 4.219.008.266.016.583.016.891 0 .298.06.704 0 .91-.041.147-.24.194-.363.264a56.558 56.558 0 0 0-.065-2.843c-.124-.101-.444-.047-.464-.166-.044-.252.267-.09.447-.065-.045-1.272-.177-2.46-.33-3.623-.147-.074-.336-.105-.498-.164-.252.259-.636.939-1.223.81-.22-.047-.363-.342-.464-.545a3.243 3.243 0 0 1-.265-.744c-4.88-.936-11.589-1.016-16.502-.05-.153.655-.43 2.053-1.34 1.52a2.014 2.014 0 0 1-.81-.991 8.31 8.31 0 0 1-.547.133c-.192 1.084-.288 2.268-.346 3.489.166.01.416-.122.595-.1.004.066.028.114.033.18-.166.106-.437.105-.645.166a45.286 45.286 0 0 0-.066 2.976c-.08.022-.273-.122-.347-.213.064-2.301.179-4.553.363-6.732.28-.087.568-.17.844-.264-.04-.383-.117-.827.05-1.09.14-.224.531-.352.81-.432.99-.28 1.979-.05 2.63.413.14.102.247.239.396.299.025-.09-.094-.15-.149-.199-.567-.511-1.498-.958-2.612-.761-.348-1.09-.79-2.142-.794-3.538-.005-1.553.562-2.899 1.205-3.953.66-1.078 1.541-1.954 2.498-2.645a11.504 11.504 0 0 1 8.087-2.051c3.01.369 5.008 1.79 6.45 3.853.69.99 1.248 2.174 1.62 3.524.374 1.352.378 3.098-.05 4.53-1.383-.283-2.637.15-3.125 1.026-.004.015-.016.017-.016.033.498-.678 1.736-1.168 2.976-.86.328.082.746.2.908.43.224.317.122.989-.016 1.373zM16.22 9.382c.055.383.227.783.445.944.376.27.602.001.63-.38.035-.504-.174-1.1-.431-1.324-.105-.09-.299-.145-.412-.115-.256.065-.283.528-.232.875zm-8.649 1.092c-.033.556.16 1.277.529 1.472.43.227.633-.095.661-.495.045-.626-.273-1.714-.86-1.605-.25.047-.313.339-.33.628zm6.83 2.579c-.266.06-.633-.058-.926-.117a22.333 22.333 0 0 0-.91-.164c-.567-.088-1.344-.211-1.9.1-.198.11-.444.351-.465.662-.027.46.342.791.612.993.323.237.663.399 1.092.527.917.278 2.293.353 3.075.017.735-.316 1.706-1.062 1.72-2.05.01-.59-.272-1.119-.859-1.042-.65.085-.882.951-1.44 1.074z", + [0.17800000000000005, -0.00016249501660449533, 23.82066666666666, 24], + // svg-path-bounding-box: [ 0.17800000000000005, -0.00016249501660449477, 23.82066666666666, 24 ] + ], ]; export default [...linealCases, ...pathologicalCases];