Orient mandala symbols around their anchor point. (#60)
This orients the mandala symbols around their anchor point, rather than the center of their bounding box (#24).pull/63/head
rodzic
6fa6e0ba8a
commit
131c1f31cb
|
@ -3,9 +3,10 @@ import { BBox, Point } from "../vendor/bezier-js";
|
||||||
import { getAttachmentTransforms } from "./attach";
|
import { getAttachmentTransforms } from "./attach";
|
||||||
import { getBoundingBoxCenter, uniformlyScaleToFit } from "./bounding-box";
|
import { getBoundingBoxCenter, uniformlyScaleToFit } from "./bounding-box";
|
||||||
import { scalePointXY, subtractPoints } from "./point";
|
import { scalePointXY, subtractPoints } from "./point";
|
||||||
import { AttachmentPointType, PointWithNormal } from "./specs";
|
import { AttachmentPointType } from "./specs";
|
||||||
import {
|
import {
|
||||||
createSvgSymbolContext,
|
createSvgSymbolContext,
|
||||||
|
safeGetAttachmentPoint,
|
||||||
SvgSymbolContent,
|
SvgSymbolContent,
|
||||||
SvgSymbolContext,
|
SvgSymbolContext,
|
||||||
SvgSymbolData,
|
SvgSymbolData,
|
||||||
|
@ -21,47 +22,6 @@ import {
|
||||||
|
|
||||||
const DEFAULT_ATTACHMENT_SCALE = 0.5;
|
const DEFAULT_ATTACHMENT_SCALE = 0.5;
|
||||||
|
|
||||||
function getAttachmentPoint(
|
|
||||||
s: SvgSymbolData,
|
|
||||||
type: AttachmentPointType,
|
|
||||||
idx: number = 0
|
|
||||||
): PointWithNormal {
|
|
||||||
const { specs } = s;
|
|
||||||
if (!specs) {
|
|
||||||
throw new AttachmentPointError(`Symbol ${s.name} has no specs.`);
|
|
||||||
}
|
|
||||||
const points = specs[type];
|
|
||||||
if (!(points && points.length > idx)) {
|
|
||||||
throw new AttachmentPointError(
|
|
||||||
`Expected symbol ${s.name} to have at least ${
|
|
||||||
idx + 1
|
|
||||||
} ${type} attachment point(s).`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return points[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AttachmentPointError extends Error {}
|
|
||||||
|
|
||||||
function safeGetAttachmentPoint(
|
|
||||||
s: SvgSymbolData,
|
|
||||||
type: AttachmentPointType,
|
|
||||||
idx: number = 0
|
|
||||||
): PointWithNormal | null {
|
|
||||||
try {
|
|
||||||
return getAttachmentPoint(s, type, idx);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof AttachmentPointError) {
|
|
||||||
console.log(e.message);
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CreatureContextType = SvgSymbolContext & {
|
export type CreatureContextType = SvgSymbolContext & {
|
||||||
attachmentScale: number;
|
attachmentScale: number;
|
||||||
parent: SvgSymbolData | null;
|
parent: SvgSymbolData | null;
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { DEFAULT_BG_COLOR } from "../colors";
|
||||||
import { ExportSvgButton } from "../export-svg";
|
import { ExportSvgButton } from "../export-svg";
|
||||||
import { HoverDebugHelper } from "../hover-debug-helper";
|
import { HoverDebugHelper } from "../hover-debug-helper";
|
||||||
import { NumericSlider } from "../numeric-slider";
|
import { NumericSlider } from "../numeric-slider";
|
||||||
import { reversePoint } from "../point";
|
|
||||||
import {
|
import {
|
||||||
createSvgSymbolContext,
|
createSvgSymbolContext,
|
||||||
|
safeGetAttachmentPoint,
|
||||||
SvgSymbolContent,
|
SvgSymbolContent,
|
||||||
SvgSymbolContext,
|
SvgSymbolContext,
|
||||||
SvgSymbolData,
|
SvgSymbolData,
|
||||||
|
@ -24,8 +24,25 @@ import { SvgVocabulary } from "../svg-vocabulary";
|
||||||
import { SymbolContextWidget } from "../symbol-context-widget";
|
import { SymbolContextWidget } from "../symbol-context-widget";
|
||||||
import { NumericRange, range } from "../util";
|
import { NumericRange, range } from "../util";
|
||||||
import { Random } from "../random";
|
import { Random } from "../random";
|
||||||
|
import { PointWithNormal } from "../specs";
|
||||||
|
import { getAttachmentTransforms } from "../attach";
|
||||||
|
|
||||||
const EYE = SvgVocabulary.get("eye");
|
const EYE = SvgVocabulary.get("eye_vertical");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the anchor point of the given symbol; if it doesn't have
|
||||||
|
* an anchor point, return a reasonable default one by taking the
|
||||||
|
* center of the symbol and having the normal point along the positive
|
||||||
|
* x-axis.
|
||||||
|
*/
|
||||||
|
function getAnchorOrCenter(symbol: SvgSymbolData): PointWithNormal {
|
||||||
|
return (
|
||||||
|
safeGetAttachmentPoint(symbol, "anchor") || {
|
||||||
|
point: getBoundingBoxCenter(symbol.bbox),
|
||||||
|
normal: { x: 1, y: 0 },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const MandalaCircle: React.FC<
|
const MandalaCircle: React.FC<
|
||||||
{
|
{
|
||||||
|
@ -34,13 +51,21 @@ const MandalaCircle: React.FC<
|
||||||
numSymbols: number;
|
numSymbols: number;
|
||||||
} & SvgSymbolContext
|
} & SvgSymbolContext
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const center = getBoundingBoxCenter(props.data.bbox);
|
|
||||||
const degreesPerItem = 360 / props.numSymbols;
|
const degreesPerItem = 360 / props.numSymbols;
|
||||||
|
const { translation, rotation } = getAttachmentTransforms(
|
||||||
|
{
|
||||||
|
point: { x: 0, y: 0 },
|
||||||
|
normal: { x: 1, y: 0 },
|
||||||
|
},
|
||||||
|
getAnchorOrCenter(props.data)
|
||||||
|
);
|
||||||
|
|
||||||
const symbol = (
|
const symbol = (
|
||||||
<SvgTransform
|
<SvgTransform
|
||||||
transform={[
|
transform={[
|
||||||
svgTranslate({ x: props.radius, y: 0 }),
|
svgTranslate({ x: props.radius, y: 0 }),
|
||||||
svgTranslate(reversePoint(center)),
|
svgRotate(rotation),
|
||||||
|
svgTranslate(translation),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SvgSymbolContent {...props} />
|
<SvgSymbolContent {...props} />
|
||||||
|
@ -64,7 +89,7 @@ const RADIUS: NumericParams = {
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1000,
|
max: 1000,
|
||||||
step: 1,
|
step: 1,
|
||||||
default: 400,
|
default: 50,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NUM_SYMBOLS: NumericParams = {
|
const NUM_SYMBOLS: NumericParams = {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import { SVGProps } from "react";
|
import { SVGProps } from "react";
|
||||||
import { BBox } from "../vendor/bezier-js";
|
import { BBox } from "../vendor/bezier-js";
|
||||||
import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "./colors";
|
import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "./colors";
|
||||||
import { Specs } from "./specs";
|
import { AttachmentPointType, PointWithNormal, Specs } from "./specs";
|
||||||
import type { SvgSymbolMetadata } from "./svg-symbol-metadata";
|
import type { SvgSymbolMetadata } from "./svg-symbol-metadata";
|
||||||
import { VisibleSpecs } from "./visible-specs";
|
import { VisibleSpecs } from "./visible-specs";
|
||||||
|
|
||||||
|
@ -114,3 +114,44 @@ export const SvgSymbolContent: React.FC<
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class AttachmentPointError extends Error {}
|
||||||
|
|
||||||
|
export function getAttachmentPoint(
|
||||||
|
s: SvgSymbolData,
|
||||||
|
type: AttachmentPointType,
|
||||||
|
idx: number = 0
|
||||||
|
): PointWithNormal {
|
||||||
|
const { specs } = s;
|
||||||
|
if (!specs) {
|
||||||
|
throw new AttachmentPointError(`Symbol ${s.name} has no specs.`);
|
||||||
|
}
|
||||||
|
const points = specs[type];
|
||||||
|
if (!(points && points.length > idx)) {
|
||||||
|
throw new AttachmentPointError(
|
||||||
|
`Expected symbol ${s.name} to have at least ${
|
||||||
|
idx + 1
|
||||||
|
} ${type} attachment point(s).`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function safeGetAttachmentPoint(
|
||||||
|
s: SvgSymbolData,
|
||||||
|
type: AttachmentPointType,
|
||||||
|
idx: number = 0
|
||||||
|
): PointWithNormal | null {
|
||||||
|
try {
|
||||||
|
return getAttachmentPoint(s, type, idx);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof AttachmentPointError) {
|
||||||
|
console.log(e.message);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue