Use js-bezier's BBox struct and functions.

pull/1/head
Atul Varma 2021-02-14 07:44:52 -05:00
rodzic 2b0d69bb87
commit 6d911e1654
4 zmienionych plików z 45 dodań i 86 usunięć

Wyświetl plik

@ -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 { SVGProps } from "react";
import type { SvgSymbolElement } from "./vocabulary"; import type { SvgSymbolElement } from "./vocabulary";
import { flatten, float } from "./util"; import { flatten, float } from "./util";
import { pathToShapes } from "./path"; import { pathToShapes } from "./path";
export type Bbox = { export function getBoundingBoxSize(bbox: BBox): [number, number] {
minX: number; const width = bbox.x.max - bbox.x.min;
minY: number; const height = bbox.y.max - bbox.y.min;
maxX: number;
maxY: number;
};
export function getBoundingBoxSize(bbox: Bbox): [number, number] {
const width = bbox.maxX - bbox.minX;
const height = bbox.maxY - bbox.minY;
return [width, height]; return [width, height];
} }
export function getBoundingBoxCenter(bbox: Bbox): Point { export function getBoundingBoxCenter(bbox: BBox): Point {
const [width, height] = getBoundingBoxSize(bbox); const [width, height] = getBoundingBoxSize(bbox);
return { return {
x: bbox.minX + width / 2, x: bbox.x.min + width / 2,
y: bbox.minY + height / 2, y: bbox.y.min + height / 2,
}; };
} }
export function dilateBoundingBox(bbox: Bbox, amount: number): Bbox { function dilateMinMax(minmax: MinMax, amount: number): MinMax {
return { return {
minX: bbox.minX - amount, min: minmax.min - amount,
maxX: bbox.maxX + amount, max: minmax.max + amount,
minY: bbox.minY - amount,
maxY: bbox.maxY + 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 minX = Infinity;
let minY = Infinity; let minY = Infinity;
let maxX = -Infinity; let maxX = -Infinity;
@ -48,64 +46,28 @@ export function coalesceBoundingBoxes(bboxes: Bbox[]): Bbox {
} }
for (let bbox of bboxes) { for (let bbox of bboxes) {
if (bbox.minX < minX) { if (bbox.x.min < minX) {
minX = bbox.minX; minX = bbox.x.min;
} }
if (bbox.maxX > maxX) { if (bbox.x.max > maxX) {
maxX = bbox.maxX; maxX = bbox.x.max;
} }
if (bbox.minY < minY) { if (bbox.y.min < minY) {
minY = bbox.minY; minY = bbox.y.min;
} }
if (bbox.maxY > maxY) { if (bbox.y.max > maxY) {
maxY = bbox.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 { export function getBoundingBoxForBeziers(beziers: Bezier[]): BBox {
let minX = Infinity; return coalesceBoundingBoxes(beziers.map((b) => b.bbox()));
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 };
} }
function getBezierBoundingBox(bezier: Bezier): Bbox { function getPathBoundingBox(props: SVGProps<SVGPathElement>): 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<SVGPathElement>): Bbox {
if (!props.d) { if (!props.d) {
throw new Error(`SVG path has no 'd' attribute value!`); throw new Error(`SVG path has no 'd' attribute value!`);
} }
@ -118,7 +80,7 @@ function getPathBoundingBox(props: SVGProps<SVGPathElement>): Bbox {
export function getSvgBoundingBox( export function getSvgBoundingBox(
element: SvgSymbolElement | SvgSymbolElement[] element: SvgSymbolElement | SvgSymbolElement[]
): Bbox { ): BBox {
if (Array.isArray(element)) { if (Array.isArray(element)) {
return coalesceBoundingBoxes(element.map(getSvgBoundingBox)); return coalesceBoundingBoxes(element.map(getSvgBoundingBox));
} }

Wyświetl plik

@ -1,7 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { Point } from "../vendor/bezier-js"; import { Point, BBox } from "../vendor/bezier-js";
import { Bbox, dilateBoundingBox, getBoundingBoxSize } from "./bounding-box"; import { dilateBoundingBox, getBoundingBoxSize } from "./bounding-box";
import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "./colors"; import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "./colors";
import * as colors from "./colors"; import * as colors from "./colors";
import { Specs } from "./specs"; 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) => { {props.bboxes.map((b, i) => {
const [width, height] = getBoundingBoxSize(b); const [width, height] = getBoundingBoxSize(b);
return ( return (
<rect <rect
key={i} key={i}
x={b.minX} x={b.x.min}
y={b.minY} y={b.y.min}
width={width} width={width}
height={height} height={height}
fill={props.fill} fill={props.fill}
@ -155,7 +155,7 @@ const SvgSymbol: React.FC<SvgSymbolProps> = (props) => {
return ( return (
<svg <svg
viewBox={`${bbox.minX} ${bbox.minY} ${width} ${height}`} viewBox={`${bbox.x.min} ${bbox.y.min} ${width} ${height}`}
width={px(width * scale)} width={px(width * scale)}
height={px(height * scale)} height={px(height * scale)}
> >

Wyświetl plik

@ -1,9 +1,5 @@
import { Point } from "../vendor/bezier-js"; import { Point, BBox } from "../vendor/bezier-js";
import { import { getBoundingBoxCenter, getBoundingBoxForBeziers } from "./bounding-box";
Bbox,
getBoundingBoxCenter,
getBoundingBoxForBeziers,
} from "./bounding-box";
import * as colors from "./colors"; import * as colors from "./colors";
import { pathToShapes } from "./path"; import { pathToShapes } from "./path";
import type { SvgSymbolElement } from "./vocabulary"; import type { SvgSymbolElement } from "./vocabulary";
@ -16,7 +12,7 @@ export type Specs = {
arm?: Point[]; arm?: Point[];
horn?: Point[]; horn?: Point[];
crown?: Point[]; crown?: Point[];
nesting?: Bbox[]; nesting?: BBox[];
}; };
function getPoints(path: string): Point[] { function getPoints(path: string): Point[] {
@ -31,9 +27,9 @@ function getPoints(path: string): Point[] {
return points; return points;
} }
function getBoundingBoxes(path: string): Bbox[] { function getBoundingBoxes(path: string): BBox[] {
const shapes = pathToShapes(path); const shapes = pathToShapes(path);
const bboxes: Bbox[] = []; const bboxes: BBox[] = [];
for (let shape of shapes) { for (let shape of shapes) {
bboxes.push(getBoundingBoxForBeziers(shape)); bboxes.push(getBoundingBoxForBeziers(shape));

Wyświetl plik

@ -2,12 +2,13 @@ import fs from "fs";
import path from "path"; import path from "path";
import cheerio from "cheerio"; import cheerio from "cheerio";
import { SVGProps } from "react"; 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"; import { Specs, extractSpecs } from "./specs";
export type SvgSymbolData = { export type SvgSymbolData = {
name: string; name: string;
bbox: Bbox; bbox: BBox;
layers: SvgSymbolElement[]; layers: SvgSymbolElement[];
specs?: Specs; specs?: Specs;
}; };