diff --git a/lib/bounding-box.ts b/lib/bounding-box.ts index 5553e26..14c7fb2 100644 --- a/lib/bounding-box.ts +++ b/lib/bounding-box.ts @@ -1,43 +1,41 @@ -import { Bezier, Point } from "../vendor/bezier-js"; +import { Bezier, Point, BBox, MinMax } from "../vendor/bezier-js"; import { SVGProps } from "react"; import type { SvgSymbolElement } from "./vocabulary"; import { flatten, float } from "./util"; import { pathToShapes } from "./path"; -export type Bbox = { - minX: number; - minY: number; - maxX: number; - maxY: number; -}; - -export function getBoundingBoxSize(bbox: Bbox): [number, number] { - const width = bbox.maxX - bbox.minX; - const height = bbox.maxY - bbox.minY; +export function getBoundingBoxSize(bbox: BBox): [number, number] { + const width = bbox.x.max - bbox.x.min; + const height = bbox.y.max - bbox.y.min; return [width, height]; } -export function getBoundingBoxCenter(bbox: Bbox): Point { +export function getBoundingBoxCenter(bbox: BBox): Point { const [width, height] = getBoundingBoxSize(bbox); return { - x: bbox.minX + width / 2, - y: bbox.minY + height / 2, + x: bbox.x.min + width / 2, + y: bbox.y.min + height / 2, }; } -export function dilateBoundingBox(bbox: Bbox, amount: number): Bbox { +function dilateMinMax(minmax: MinMax, amount: number): MinMax { return { - minX: bbox.minX - amount, - maxX: bbox.maxX + amount, - minY: bbox.minY - amount, - maxY: bbox.maxY + amount, + min: minmax.min - amount, + max: minmax.max + amount, }; } -export function coalesceBoundingBoxes(bboxes: Bbox[]): Bbox { +export function dilateBoundingBox(bbox: BBox, amount: number): BBox { + return { + x: dilateMinMax(bbox.x, amount), + y: dilateMinMax(bbox.y, amount), + }; +} + +export function coalesceBoundingBoxes(bboxes: BBox[]): BBox { let minX = Infinity; let minY = Infinity; let maxX = -Infinity; @@ -48,64 +46,28 @@ export function coalesceBoundingBoxes(bboxes: Bbox[]): Bbox { } for (let bbox of bboxes) { - if (bbox.minX < minX) { - minX = bbox.minX; + if (bbox.x.min < minX) { + minX = bbox.x.min; } - if (bbox.maxX > maxX) { - maxX = bbox.maxX; + if (bbox.x.max > maxX) { + maxX = bbox.x.max; } - if (bbox.minY < minY) { - minY = bbox.minY; + if (bbox.y.min < minY) { + minY = bbox.y.min; } - if (bbox.maxY > maxY) { - maxY = bbox.maxY; + if (bbox.y.max > maxY) { + maxY = bbox.y.max; } } - return { minX, minY, maxX, maxY }; + return { x: { min: minX, max: maxX }, y: { min: minY, max: maxY } }; } -function getBoundingBoxForPoints(points: Point[]): Bbox { - let minX = Infinity; - let minY = Infinity; - let maxX = -Infinity; - let maxY = -Infinity; - - if (points.length === 0) { - throw new Error(`Must have at least one point!`); - } - - for (let point of points) { - if (point.x < minX) { - minX = point.x; - } - if (point.x > maxX) { - maxX = point.x; - } - if (point.y < minY) { - minY = point.y; - } - if (point.y > maxY) { - maxY = point.y; - } - } - - return { minX, minY, maxX, maxY }; +export function getBoundingBoxForBeziers(beziers: Bezier[]): BBox { + return coalesceBoundingBoxes(beziers.map((b) => b.bbox())); } -function getBezierBoundingBox(bezier: Bezier): Bbox { - const start = bezier.get(0.0); - const end = bezier.get(1.0); - const extrema = bezier.extrema().values.map((t) => bezier.get(t)); - - return getBoundingBoxForPoints([start, end, ...extrema]); -} - -export function getBoundingBoxForBeziers(beziers: Bezier[]): Bbox { - return coalesceBoundingBoxes(beziers.map(getBezierBoundingBox)); -} - -function getPathBoundingBox(props: SVGProps): Bbox { +function getPathBoundingBox(props: SVGProps): BBox { if (!props.d) { throw new Error(`SVG path has no 'd' attribute value!`); } @@ -118,7 +80,7 @@ function getPathBoundingBox(props: SVGProps): Bbox { export function getSvgBoundingBox( element: SvgSymbolElement | SvgSymbolElement[] -): Bbox { +): BBox { if (Array.isArray(element)) { return coalesceBoundingBoxes(element.map(getSvgBoundingBox)); } diff --git a/lib/browser-main.tsx b/lib/browser-main.tsx index 996bd28..855dadd 100644 --- a/lib/browser-main.tsx +++ b/lib/browser-main.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import ReactDOM from "react-dom"; -import { Point } from "../vendor/bezier-js"; -import { Bbox, dilateBoundingBox, getBoundingBoxSize } from "./bounding-box"; +import { Point, BBox } from "../vendor/bezier-js"; +import { dilateBoundingBox, getBoundingBoxSize } from "./bounding-box"; import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "./colors"; import * as colors from "./colors"; import { Specs } from "./specs"; @@ -84,15 +84,15 @@ const AttachmentPoints: React.FC<{ color: string; points: Point[] }> = ( ); -const BoundingBoxes: React.FC<{ fill: string; bboxes: Bbox[] }> = (props) => ( +const BoundingBoxes: React.FC<{ fill: string; bboxes: BBox[] }> = (props) => ( <> {props.bboxes.map((b, i) => { const [width, height] = getBoundingBoxSize(b); return ( = (props) => { return ( diff --git a/lib/specs.ts b/lib/specs.ts index 1fff8c3..49810d4 100644 --- a/lib/specs.ts +++ b/lib/specs.ts @@ -1,9 +1,5 @@ -import { Point } from "../vendor/bezier-js"; -import { - Bbox, - getBoundingBoxCenter, - getBoundingBoxForBeziers, -} from "./bounding-box"; +import { Point, BBox } from "../vendor/bezier-js"; +import { getBoundingBoxCenter, getBoundingBoxForBeziers } from "./bounding-box"; import * as colors from "./colors"; import { pathToShapes } from "./path"; import type { SvgSymbolElement } from "./vocabulary"; @@ -16,7 +12,7 @@ export type Specs = { arm?: Point[]; horn?: Point[]; crown?: Point[]; - nesting?: Bbox[]; + nesting?: BBox[]; }; function getPoints(path: string): Point[] { @@ -31,9 +27,9 @@ function getPoints(path: string): Point[] { return points; } -function getBoundingBoxes(path: string): Bbox[] { +function getBoundingBoxes(path: string): BBox[] { const shapes = pathToShapes(path); - const bboxes: Bbox[] = []; + const bboxes: BBox[] = []; for (let shape of shapes) { bboxes.push(getBoundingBoxForBeziers(shape)); diff --git a/lib/vocabulary.ts b/lib/vocabulary.ts index d738be8..105db32 100644 --- a/lib/vocabulary.ts +++ b/lib/vocabulary.ts @@ -2,12 +2,13 @@ import fs from "fs"; import path from "path"; import cheerio from "cheerio"; import { SVGProps } from "react"; -import { getSvgBoundingBox, Bbox } from "./bounding-box"; +import { BBox } from "../vendor/bezier-js"; +import { getSvgBoundingBox } from "./bounding-box"; import { Specs, extractSpecs } from "./specs"; export type SvgSymbolData = { name: string; - bbox: Bbox; + bbox: BBox; layers: SvgSymbolElement[]; specs?: Specs; };