Use js-bezier's BBox struct and functions.
rodzic
2b0d69bb87
commit
6d911e1654
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)}
|
||||||
>
|
>
|
||||||
|
|
14
lib/specs.ts
14
lib/specs.ts
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Ładowanie…
Reference in New Issue