86 wiersze
2.4 KiB
TypeScript
86 wiersze
2.4 KiB
TypeScript
import React from "react";
|
|
|
|
import { getAttachmentTransforms } from "./attach";
|
|
import { getBoundingBoxCenter } from "./bounding-box";
|
|
import { PointWithNormal } from "./specs";
|
|
import {
|
|
safeGetAttachmentPoint,
|
|
SvgSymbolContent,
|
|
SvgSymbolContext,
|
|
SvgSymbolData,
|
|
swapColors,
|
|
} from "./svg-symbol";
|
|
import { svgRotate, SvgTransform, svgTranslate } from "./svg-transform";
|
|
import { isEvenNumber, range } from "./util";
|
|
|
|
/**
|
|
* 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 negative
|
|
* y-axis (i.e., up).
|
|
*/
|
|
function getAnchorOrCenter(symbol: SvgSymbolData): PointWithNormal {
|
|
return (
|
|
safeGetAttachmentPoint(symbol, "anchor") || {
|
|
point: getBoundingBoxCenter(symbol.bbox),
|
|
normal: { x: 0, y: -1 },
|
|
}
|
|
);
|
|
}
|
|
|
|
export type MandalaCircleParams = {
|
|
data: SvgSymbolData;
|
|
radius: number;
|
|
numSymbols: number;
|
|
symbolTransforms?: SvgTransform[];
|
|
invertEveryOtherSymbol: boolean;
|
|
};
|
|
|
|
export type MandalaCircleProps = MandalaCircleParams & SvgSymbolContext;
|
|
|
|
export const MandalaCircle: React.FC<MandalaCircleProps> = (props) => {
|
|
const degreesPerItem = 360 / props.numSymbols;
|
|
const { translation, rotation } = getAttachmentTransforms(
|
|
{
|
|
point: { x: 0, y: 0 },
|
|
normal: { x: 0, y: -1 },
|
|
},
|
|
getAnchorOrCenter(props.data)
|
|
);
|
|
const transform: SvgTransform[] = [
|
|
// Remember that transforms are applied in reverse order,
|
|
// so read the following from the end first!
|
|
|
|
// Finally, move the symbol out along the radius of the circle.
|
|
svgTranslate({ x: 0, y: -props.radius }),
|
|
|
|
// Then apply any individual symbol transformations.
|
|
...(props.symbolTransforms || []),
|
|
|
|
// First, re-orient the symbol so its anchor point is at
|
|
// the origin and facing the proper direction.
|
|
svgRotate(rotation),
|
|
svgTranslate(translation),
|
|
];
|
|
|
|
const invertEveryOtherSymbol =
|
|
isEvenNumber(props.numSymbols) && props.invertEveryOtherSymbol;
|
|
|
|
const symbols = range(props.numSymbols)
|
|
.reverse()
|
|
.map((i) => (
|
|
<SvgTransform
|
|
key={i}
|
|
transform={[svgRotate(degreesPerItem * i), ...transform]}
|
|
>
|
|
{invertEveryOtherSymbol && isEvenNumber(i) ? (
|
|
<SvgSymbolContent {...swapColors(props)} />
|
|
) : (
|
|
<SvgSymbolContent {...props} />
|
|
)}
|
|
</SvgTransform>
|
|
));
|
|
|
|
return <>{symbols}</>;
|
|
};
|