tldraw
Version:
A tiny little drawing editor.
349 lines (348 loc) • 12.2 kB
JavaScript
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import {
Group2d,
Vec,
canonicalizeRotation,
getPerfectDashProps,
useEditor
} from "@tldraw/editor";
import { ShapeFill } from "../../shared/ShapeFill.mjs";
import { STROKE_SIZES } from "../../shared/default-shape-constants.mjs";
import { useDefaultColorTheme } from "../../shared/useDefaultColorTheme.mjs";
import {
getCloudArcs,
getCloudPath,
getDrawHeartPath,
getHeartParts,
getHeartPath,
getRoundedInkyPolygonPath,
getRoundedPolygonPoints,
inkyCloudSvgPath
} from "../geo-shape-helpers.mjs";
import { getLines } from "../getLines.mjs";
function GeoShapeBody({
shape,
shouldScale,
forceSolid
}) {
const scaleToUse = shouldScale ? shape.props.scale : 1;
const editor = useEditor();
const theme = useDefaultColorTheme();
const { id, props } = shape;
const { w, color, fill, dash, growY, size, scale } = props;
const strokeWidth = STROKE_SIZES[size] * scaleToUse;
const h = props.h + growY;
switch (props.geo) {
case "cloud": {
if (dash === "solid") {
const d = getCloudPath(w, h, id, size, scale);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx("path", { d, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
} else if (dash === "draw") {
const d = inkyCloudSvgPath(w, h, id, size, scale);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx("path", { d, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
} else {
const d = getCloudPath(w, h, id, size, scale);
const arcs = getCloudArcs(w, h, id, size, scale);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx(
"g",
{
strokeWidth,
stroke: theme[color].solid,
fill: "none",
pointerEvents: "all",
children: arcs.map(({ leftPoint, rightPoint, center, radius }, i) => {
const arcLength = center ? radius * canonicalizeRotation(
canonicalizeRotation(Vec.Angle(center, rightPoint)) - canonicalizeRotation(Vec.Angle(center, leftPoint))
) : Vec.Dist(leftPoint, rightPoint);
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
arcLength,
strokeWidth,
{
style: dash,
start: "outset",
end: "outset",
forceSolid
}
);
return /* @__PURE__ */ jsx(
"path",
{
d: center ? `M${leftPoint.x},${leftPoint.y}A${radius},${radius},0,0,1,${rightPoint.x},${rightPoint.y}` : `M${leftPoint.x},${leftPoint.y}L${rightPoint.x},${rightPoint.y}`,
strokeDasharray,
strokeDashoffset
},
i
);
})
}
)
] });
}
}
case "ellipse": {
const geometry = shouldScale ? (
// cached
(editor.getShapeGeometry(shape))
) : (
// not cached
(editor.getShapeUtil(shape).getGeometry(shape))
);
const d = geometry.getSvgPathData(true);
if (dash === "dashed" || dash === "dotted") {
const perimeter = geometry.length;
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
perimeter < 64 ? perimeter * 2 : perimeter,
strokeWidth,
{
style: dash,
snap: 4,
closed: true,
forceSolid
}
);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx(
"path",
{
d,
strokeWidth,
fill: "none",
stroke: theme[color].solid,
strokeDasharray,
strokeDashoffset
}
)
] });
} else {
const geometry2 = shouldScale ? (
// cached
(editor.getShapeGeometry(shape))
) : (
// not cached
(editor.getShapeUtil(shape).getGeometry(shape))
);
const d2 = geometry2.getSvgPathData(true);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d: d2, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx("path", { d: d2, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
}
}
case "oval": {
const geometry = shouldScale ? (
// cached
(editor.getShapeGeometry(shape))
) : (
// not cached
(editor.getShapeUtil(shape).getGeometry(shape))
);
const d = geometry.getSvgPathData(true);
if (dash === "dashed" || dash === "dotted") {
const perimeter = geometry.getLength();
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
perimeter < 64 ? perimeter * 2 : perimeter,
strokeWidth,
{
style: dash,
snap: 4,
start: "outset",
end: "outset",
closed: true,
forceSolid
}
);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx(
"path",
{
d,
strokeWidth,
fill: "none",
stroke: theme[color].solid,
strokeDasharray,
strokeDashoffset
}
)
] });
} else {
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx("path", { d, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
}
}
case "heart": {
if (dash === "dashed" || dash === "dotted" || dash === "solid") {
const d = getHeartPath(w, h);
const curves = getHeartParts(w, h);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
curves.map((c, i) => {
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
c.length,
strokeWidth,
{
style: dash,
snap: 1,
start: "outset",
end: "outset",
closed: true,
forceSolid
}
);
return /* @__PURE__ */ jsx(
"path",
{
d: c.getSvgPathData(),
strokeWidth,
fill: "none",
stroke: theme[color].solid,
strokeDasharray,
strokeDashoffset,
pointerEvents: "all"
},
`curve_${i}`
);
})
] });
} else {
const d = getDrawHeartPath(w, h, strokeWidth, shape.id);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx("path", { d, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
}
}
default: {
const geometry = shouldScale ? (
// cached
(editor.getShapeGeometry(shape))
) : (
// not cached
(editor.getShapeUtil(shape).getGeometry(shape))
);
const outline = geometry instanceof Group2d ? geometry.children[0].vertices : geometry.vertices;
const lines = getLines(shape.props, strokeWidth);
if (dash === "solid") {
let d = "M" + outline[0] + "L" + outline.slice(1) + "Z";
if (lines) {
for (const [A, B] of lines) {
d += `M${A.x},${A.y}L${B.x},${B.y}`;
}
}
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsx("path", { d, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
} else if (dash === "dashed" || dash === "dotted") {
const d = "M" + outline[0] + "L" + outline.slice(1) + "Z";
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(ShapeFill, { theme, d, color, fill, scale: scaleToUse }),
/* @__PURE__ */ jsxs(
"g",
{
strokeWidth,
stroke: theme[color].solid,
fill: "none",
pointerEvents: "all",
children: [
Array.from(Array(outline.length)).map((_, i) => {
const A = Vec.ToFixed(outline[i]);
const B = Vec.ToFixed(outline[(i + 1) % outline.length]);
const dist = Vec.Dist(A, B);
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
dist,
strokeWidth,
{
style: dash,
start: "outset",
end: "outset",
forceSolid
}
);
return /* @__PURE__ */ jsx(
"line",
{
x1: A.x,
y1: A.y,
x2: B.x,
y2: B.y,
strokeDasharray,
strokeDashoffset
},
i
);
}),
lines && lines.map(([A, B], i) => {
const dist = Vec.Dist(A, B);
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
dist,
strokeWidth,
{
style: dash,
start: "skip",
end: "skip",
snap: dash === "dotted" ? 4 : void 0,
forceSolid
}
);
return /* @__PURE__ */ jsx(
"path",
{
d: `M${A.x},${A.y}L${B.x},${B.y}`,
stroke: theme[color].solid,
strokeWidth,
fill: "none",
strokeDasharray,
strokeDashoffset
},
`line_fg_${i}`
);
})
]
}
)
] });
} else if (dash === "draw") {
let d = getRoundedInkyPolygonPath(
getRoundedPolygonPoints(id, outline, strokeWidth / 3, strokeWidth * 2, 2)
);
if (lines) {
for (const [A, B] of lines) {
d += `M${A.toFixed()}L${B.toFixed()}`;
}
}
const innerPathData = getRoundedInkyPolygonPath(
getRoundedPolygonPoints(id, outline, 0, strokeWidth * 2, 1)
);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
ShapeFill,
{
theme,
d: innerPathData,
color,
fill,
scale: scaleToUse
}
),
/* @__PURE__ */ jsx("path", { d, stroke: theme[color].solid, strokeWidth, fill: "none" })
] });
}
}
}
}
export {
GeoShapeBody
};
//# sourceMappingURL=GeoShapeBody.mjs.map