UNPKG

tldraw

Version:

A tiny little drawing editor.

349 lines (348 loc) • 12.2 kB
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