2023-01-29 18:57:24 +01:00
|
|
|
import { TextOnPath } from './text.mjs'
|
|
|
|
import { getProps } from './utils.mjs'
|
|
|
|
import { round, formatMm } from 'shared/utils.mjs'
|
|
|
|
import { RawSpan } from 'shared/components/raw-span.mjs'
|
2022-01-25 11:22:09 +01:00
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
export const pointCoords = (point) =>
|
|
|
|
point ? `[ ${round(point.x, 2)}, ${round(point.y, 2)} ]` : null
|
2022-06-18 12:15:05 +02:00
|
|
|
|
2022-06-18 13:15:15 +02:00
|
|
|
export const Tr = ({ children }) => <tr className="border border-base-300">{children}</tr>
|
|
|
|
export const KeyTd = ({ children }) => <td className="p-3 text-right">{children}:</td>
|
|
|
|
export const ValTd = ({ children }) => <td className="p-3">{children}</td>
|
2022-06-18 12:15:05 +02:00
|
|
|
|
2023-02-05 19:58:25 +01:00
|
|
|
export const TextAlongPath = ({ id, size, txt }) => (
|
2022-06-18 12:15:05 +02:00
|
|
|
<text>
|
|
|
|
<textPath xlinkHref={`#${id}`} startOffset="50%">
|
|
|
|
<tspan
|
|
|
|
style={{ textAnchor: 'middle', fontSize: size }}
|
|
|
|
className="fill-neutral-content fill-opacity-50"
|
|
|
|
dy={size * -0.4}
|
|
|
|
>
|
|
|
|
{txt}
|
|
|
|
</tspan>
|
|
|
|
</textPath>
|
|
|
|
</text>
|
|
|
|
)
|
2022-10-09 17:32:32 +02:00
|
|
|
export const PointCircle = ({ point, size, className = 'stroke-neutral-content' }) => (
|
2022-06-18 12:15:05 +02:00
|
|
|
<circle
|
2022-10-09 17:32:32 +02:00
|
|
|
cx={point.x}
|
|
|
|
cy={point.y}
|
|
|
|
r={size / 50}
|
2022-06-18 12:15:05 +02:00
|
|
|
className={className}
|
2022-10-09 17:32:32 +02:00
|
|
|
fill="none"
|
|
|
|
strokeWidth={size / 150}
|
|
|
|
strokeOpacity="0.5"
|
2022-06-18 12:15:05 +02:00
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
const CpCircle = ({ point, className = 'fill-lining no-stroke' }) => (
|
|
|
|
<circle cx={point.x} cy={point.y} r={1.5} className={className} />
|
|
|
|
)
|
|
|
|
|
|
|
|
const EpCircle = ({ point, className = 'fill-note no-stroke' }) => (
|
|
|
|
<circle cx={point.x} cy={point.y} r={1.5} className={className} />
|
2022-06-18 12:49:49 +02:00
|
|
|
)
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
const pathDimensions = (from, to, cp1 = false, cp2 = false, path = false) => {
|
2022-06-18 12:15:05 +02:00
|
|
|
const topLeft = {
|
2022-10-09 17:32:32 +02:00
|
|
|
x: to.x < from.x ? to.x : from.x,
|
|
|
|
y: to.y < from.y ? to.y : from.y,
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
const bottomRight = {
|
2022-10-09 17:32:32 +02:00
|
|
|
x: to.x > from.x ? to.x : from.x,
|
|
|
|
y: to.y > from.y ? to.y : from.y,
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
let bbox = false
|
|
|
|
// Deal with curves
|
|
|
|
if (cp1 && cp2) {
|
|
|
|
if (cp1.x < topLeft.x) topLeft.x = cp1.x
|
|
|
|
if (cp2.x < topLeft.x) topLeft.x = cp2.x
|
|
|
|
if (cp1.x > bottomRight.x) bottomRight.x = cp1.x
|
|
|
|
if (cp2.x > bottomRight.x) bottomRight.x = cp2.x
|
|
|
|
if (cp1.y < topLeft.y) topLeft.y = cp1.y
|
|
|
|
if (cp2.y < topLeft.y) topLeft.y = cp2.y
|
|
|
|
if (cp1.y > bottomRight.y) bottomRight.y = cp1.y
|
|
|
|
if (cp2.y > bottomRight.y) bottomRight.y = cp2.y
|
|
|
|
// This undocumented core methods returns the curve's bounding box
|
2022-09-22 10:09:46 +02:00
|
|
|
bbox = path.bbox()
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
const w = bottomRight.x - topLeft.x
|
|
|
|
const h = bottomRight.y - topLeft.y
|
|
|
|
const size = w > h ? w : h
|
|
|
|
|
|
|
|
return {
|
|
|
|
topLeft,
|
|
|
|
bottomRight,
|
|
|
|
w,
|
|
|
|
h,
|
|
|
|
size,
|
2022-10-09 17:32:32 +02:00
|
|
|
bbox,
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-18 17:02:19 +02:00
|
|
|
export const Defs = () => (
|
2022-06-18 12:15:05 +02:00
|
|
|
<defs>
|
2022-10-09 17:32:32 +02:00
|
|
|
<marker orient="auto" refY="0.0" refX="0.0" id="arrowTo" style={{ overflow: 'visible' }}>
|
|
|
|
<path
|
|
|
|
className="fill-neutral-content"
|
|
|
|
d="M 0,0 L -12,-4 C -10,-2 -10,2 -12, 4 z"
|
|
|
|
fillOpacity="0.5"
|
|
|
|
></path>
|
2022-06-18 12:15:05 +02:00
|
|
|
</marker>
|
2022-10-09 17:32:32 +02:00
|
|
|
<marker orient="auto" refY="0.0" refX="0.0" id="arrowFrom" style={{ overflow: 'visible' }}>
|
|
|
|
<path
|
|
|
|
className="fill-neutral-content"
|
|
|
|
d="M 0,0 L 12,-4 C 10,-2 10,2 12, 4 z"
|
|
|
|
fillOpacity="0.5"
|
|
|
|
></path>
|
2022-06-18 12:15:05 +02:00
|
|
|
</marker>
|
|
|
|
</defs>
|
|
|
|
)
|
|
|
|
|
2022-06-18 17:02:19 +02:00
|
|
|
export const svgProps = {
|
2022-10-09 17:32:32 +02:00
|
|
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
|
|
xmlnsSvg: 'http://www.w3.org/2000/svg',
|
|
|
|
xmlnsXlink: 'http://www.w3.org/1999/xlink',
|
2022-06-18 12:15:05 +02:00
|
|
|
style: { maxHeight: 'inherit', strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
|
|
}
|
|
|
|
|
|
|
|
const Line = (props) => {
|
|
|
|
const ops = props.path.ops
|
|
|
|
const from = ops[0].to
|
|
|
|
const to = ops[1].to
|
|
|
|
const { topLeft, bottomRight, w, h, size } = pathDimensions(from, to)
|
|
|
|
const id = `${props.partName}_${props.pathName}_${props.i}`
|
|
|
|
|
|
|
|
const xyProps = {
|
2022-10-09 17:32:32 +02:00
|
|
|
className: 'stroke-neutral-content',
|
|
|
|
strokeOpacity: '0.5',
|
|
|
|
fill: 'none',
|
|
|
|
strokeWidth: size / 300,
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-10-09 17:32:32 +02:00
|
|
|
<svg
|
|
|
|
{...svgProps}
|
|
|
|
viewBox={`${topLeft.x - size / 10} ${topLeft.y - size / 10} ${w + size / 5} ${h + size / 5}`}
|
2022-06-18 12:15:05 +02:00
|
|
|
>
|
2022-10-09 17:32:32 +02:00
|
|
|
<path
|
|
|
|
id={`${id}_x`}
|
|
|
|
{...xyProps}
|
2022-06-18 12:15:05 +02:00
|
|
|
d={`M ${topLeft.x},${bottomRight.y} L ${bottomRight.x},${bottomRight.y}`}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<TextAlongPath
|
|
|
|
id={`${id}_x`}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(bottomRight.x - topLeft.x, props.gist.units, 'notags')}
|
|
|
|
/>
|
|
|
|
<path
|
|
|
|
id={`${id}_y`}
|
|
|
|
{...xyProps}
|
2022-06-18 12:15:05 +02:00
|
|
|
d={`M ${topLeft.x},${bottomRight.y} L ${topLeft.x},${topLeft.y}`}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<TextAlongPath
|
|
|
|
id={`${id}_y`}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(bottomRight.y - topLeft.y, props.gist.units, 'notags')}
|
|
|
|
/>
|
2022-06-18 12:15:05 +02:00
|
|
|
<path
|
|
|
|
id={id}
|
|
|
|
d={`M ${from.x},${from.y} L ${to.x},${to.y}`}
|
|
|
|
className="stroke-neutral-content"
|
|
|
|
strokeLinecap="round"
|
2022-10-09 17:32:32 +02:00
|
|
|
strokeWidth={size / 100}
|
|
|
|
/>
|
|
|
|
<TextAlongPath
|
|
|
|
id={id}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(props.path.length(), props.gist.units, 'notags')}
|
2022-06-18 12:15:05 +02:00
|
|
|
/>
|
|
|
|
<PointCircle point={from} size={size} />
|
|
|
|
<PointCircle point={to} size={size} />
|
|
|
|
</svg>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Curve = (props) => {
|
|
|
|
const ops = props.path.ops
|
|
|
|
const from = ops[0].to
|
|
|
|
const { to, cp1, cp2 } = ops[1]
|
2022-07-12 20:47:39 +02:00
|
|
|
const { topLeft, w, h, size, bbox } = pathDimensions(from, to, cp1, cp2, props.path)
|
2022-06-18 12:15:05 +02:00
|
|
|
const id = `${props.partName}_${props.pathName}_${props.i}`
|
|
|
|
|
|
|
|
const cpProps = {
|
2022-10-09 17:32:32 +02:00
|
|
|
className: 'stroke-success',
|
|
|
|
strokeOpacity: '0.85',
|
|
|
|
fill: 'none',
|
|
|
|
strokeWidth: size / 300,
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
const xyProps = {
|
|
|
|
...cpProps,
|
2022-10-09 17:32:32 +02:00
|
|
|
strokeOpacity: '0.5',
|
|
|
|
className: 'stroke-neutral-content',
|
|
|
|
markerEnd: 'url(#arrowTo)',
|
|
|
|
markerStart: 'url(#arrowFrom)',
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-10-09 17:32:32 +02:00
|
|
|
<svg
|
|
|
|
{...svgProps}
|
|
|
|
viewBox={`${topLeft.x - size / 10} ${topLeft.y - size / 10} ${w + size / 5} ${h + size / 5}`}
|
2022-06-18 12:15:05 +02:00
|
|
|
>
|
|
|
|
<Defs />
|
2022-10-09 17:32:32 +02:00
|
|
|
<path
|
|
|
|
id={`${id}_x`}
|
|
|
|
{...xyProps}
|
2022-06-18 12:15:05 +02:00
|
|
|
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.bottomRight.x},${bbox.bottomRight.y}`}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<TextAlongPath
|
|
|
|
id={`${id}_x`}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(bbox.bottomRight.x - bbox.topLeft.x, props.gist.units, 'notags')}
|
|
|
|
/>
|
|
|
|
<path
|
|
|
|
id={`${id}_y`}
|
|
|
|
{...xyProps}
|
2022-06-18 12:15:05 +02:00
|
|
|
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.topLeft.x},${bbox.topLeft.y}`}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<TextAlongPath
|
|
|
|
id={`${id}_y`}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(bbox.bottomRight.y - bbox.topLeft.y, props.gist.units, 'notags')}
|
|
|
|
/>
|
2022-06-18 12:15:05 +02:00
|
|
|
<path id={`${id}_cp1`} {...cpProps} d={`M ${from.x},${from.y} L ${cp1.x},${cp1.y}`} />
|
|
|
|
<PointCircle point={cp1} size={size} className="stroke-success" />
|
|
|
|
<path id={`${id}_cp2`} {...cpProps} d={`M ${to.x},${to.y} L ${cp2.x},${cp2.y}`} />
|
2022-10-09 17:32:32 +02:00
|
|
|
<PointCircle point={cp2} size={size} className="stroke-success" />
|
2022-06-18 12:15:05 +02:00
|
|
|
<path
|
|
|
|
id={id}
|
|
|
|
d={`M ${from.x},${from.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${to.x},${to.y}`}
|
|
|
|
className="stroke-neutral-content"
|
|
|
|
fill="none"
|
|
|
|
strokeLinecap="round"
|
2022-10-09 17:32:32 +02:00
|
|
|
strokeWidth={size / 100}
|
|
|
|
/>
|
|
|
|
<TextAlongPath
|
|
|
|
id={id}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(props.path.length(), props.gist.units, 'notags')}
|
2022-06-18 12:15:05 +02:00
|
|
|
/>
|
|
|
|
<PointCircle point={from} size={size} />
|
|
|
|
<PointCircle point={to} size={size} />
|
|
|
|
</svg>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
const MiniPath = (props) => {
|
2022-09-22 14:17:29 +02:00
|
|
|
const bbox = props.path.bbox()
|
2022-06-18 12:15:05 +02:00
|
|
|
const id = `${props.partName}_${props.pathName}_mini}`
|
|
|
|
const w = bbox.bottomRight.x - bbox.topLeft.x
|
|
|
|
const h = bbox.bottomRight.y - bbox.topLeft.y
|
|
|
|
const size = w > h ? w : h
|
|
|
|
|
|
|
|
const xyProps = {
|
2022-10-09 17:32:32 +02:00
|
|
|
fill: 'none',
|
|
|
|
strokeWidth: size / 300,
|
|
|
|
strokeOpacity: '0.5',
|
|
|
|
className: 'stroke-neutral-content',
|
|
|
|
markerEnd: 'url(#arrowTo)',
|
|
|
|
markerStart: 'url(#arrowFrom)',
|
2022-06-18 12:15:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-10-09 17:32:32 +02:00
|
|
|
<svg
|
|
|
|
{...svgProps}
|
|
|
|
viewBox={`${bbox.topLeft.x - size / 10} ${bbox.topLeft.y - size / 10} ${w + size / 5} ${
|
|
|
|
h + size / 5
|
|
|
|
}`}
|
2022-06-18 12:15:05 +02:00
|
|
|
className="freesewing pattern z-50"
|
|
|
|
>
|
|
|
|
<Defs />
|
2022-10-09 17:32:32 +02:00
|
|
|
<path
|
|
|
|
id={`${id}_x`}
|
|
|
|
{...xyProps}
|
2022-06-18 12:15:05 +02:00
|
|
|
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.bottomRight.x},${bbox.bottomRight.y}`}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<TextAlongPath
|
|
|
|
id={`${id}_x`}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(bbox.bottomRight.x - bbox.topLeft.x, props.gist.units, 'notags')}
|
|
|
|
/>
|
|
|
|
<path
|
|
|
|
id={`${id}_y`}
|
|
|
|
{...xyProps}
|
2022-06-18 12:15:05 +02:00
|
|
|
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.topLeft.x},${bbox.topLeft.y}`}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<TextAlongPath
|
|
|
|
id={`${id}_y`}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(bbox.bottomRight.y - bbox.topLeft.y, props.gist.units, 'notags')}
|
|
|
|
/>
|
2022-06-18 12:15:05 +02:00
|
|
|
<path
|
|
|
|
id={id}
|
|
|
|
d={props.path.asPathstring()}
|
|
|
|
className="stroke-neutral-content"
|
|
|
|
fill="none"
|
|
|
|
strokeLinecap="round"
|
2022-10-09 17:32:32 +02:00
|
|
|
strokeWidth={size / 100}
|
|
|
|
/>
|
|
|
|
<TextAlongPath
|
|
|
|
id={id}
|
|
|
|
size={size / 18}
|
|
|
|
txt={formatMm(props.path.length(), props.gist.units, 'notags')}
|
2022-06-18 12:15:05 +02:00
|
|
|
/>
|
|
|
|
<XrayPath {...props} />)
|
|
|
|
</svg>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const lineInfo = (props) => (
|
|
|
|
<div className="p-4 border bg-neutral bg-opacity-60 shadow rounded-lg">
|
|
|
|
<h5 className="text-neutral-content text-center pb-4">Line info</h5>
|
|
|
|
<div className="flex flex-row flex-wrap">
|
|
|
|
<table className="border-collapse h-fit">
|
|
|
|
<tbody>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>From</KeyTd>
|
2022-06-18 13:15:15 +02:00
|
|
|
<ValTd>{pointCoords(props.path.ops[0].to)}</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>To</KeyTd>
|
2022-06-18 13:15:15 +02:00
|
|
|
<ValTd>{pointCoords(props.path.ops[1].to)}</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Length</KeyTd>
|
2022-06-24 11:34:22 +02:00
|
|
|
<ValTd>{formatMm(props.path.length(), props.gist.units, 'notags')}</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Width</KeyTd>
|
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan
|
|
|
|
html={formatMm(
|
|
|
|
Math.abs(props.path.ops[0].to.dx(props.path.ops[1].to)),
|
|
|
|
props.gist.units
|
|
|
|
)}
|
|
|
|
/>
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Height</KeyTd>
|
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan
|
|
|
|
html={formatMm(
|
|
|
|
Math.abs(props.path.ops[0].to.dy(props.path.ops[1].to)),
|
|
|
|
props.gist.units
|
|
|
|
)}
|
|
|
|
/>
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Part</KeyTd>
|
|
|
|
<ValTd>{props.partName}</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Draw Op</KeyTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<ValTd>
|
|
|
|
{props.i}/{props.ops.length}
|
|
|
|
</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<div className="max-w-md" style={{ maxHeight: '80vh' }}>
|
|
|
|
<Line {...props} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
const XrayLine = (props) => (
|
2022-06-18 12:15:05 +02:00
|
|
|
<>
|
2022-01-29 10:50:02 +01:00
|
|
|
<path
|
|
|
|
d={props.path.asPathstring()}
|
|
|
|
{...getProps(props.path)}
|
2022-06-18 12:15:05 +02:00
|
|
|
className="opacity-0 stroke-4xl stroke-note hover:opacity-25 hover:cursor-pointer"
|
2022-10-09 17:32:32 +02:00
|
|
|
onClick={(evt) => {
|
|
|
|
evt.stopPropagation()
|
|
|
|
props.showInfo(lineInfo(props))
|
|
|
|
}}
|
2022-01-29 10:50:02 +01:00
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<EpCircle point={props.path.ops[0].to} />
|
|
|
|
<EpCircle point={props.path.ops[1].to} />
|
2022-06-18 12:15:05 +02:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
|
|
|
|
const curveInfo = (props) => (
|
|
|
|
<div className="p-4 border bg-neutral bg-opacity-40 shadow rounded-lg">
|
|
|
|
<h5 className="text-neutral-content text-center pb-4">Curve info</h5>
|
|
|
|
<div className="flex flex-row flex-wrap">
|
|
|
|
<table className="border-collapse h-fit">
|
|
|
|
<tbody>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>From</KeyTd>
|
2022-06-18 13:15:15 +02:00
|
|
|
<ValTd>{pointCoords(props.path.ops[0].to)}</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Cp1</KeyTd>
|
2022-06-18 13:15:15 +02:00
|
|
|
<ValTd>{pointCoords(props.path.ops[1].cp1)}</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Cp2</KeyTd>
|
2022-06-18 13:15:15 +02:00
|
|
|
<ValTd>{pointCoords(props.path.ops[1].cp2)}</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>To</KeyTd>
|
2022-06-18 13:15:15 +02:00
|
|
|
<ValTd>{pointCoords(props.path.ops[1].to)}</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Length</KeyTd>
|
2022-06-24 11:34:22 +02:00
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan html={formatMm(props.path.length(), props.gist.units)} />
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Width</KeyTd>
|
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan
|
|
|
|
html={formatMm(
|
|
|
|
Math.abs(props.path.ops[0].to.dx(props.path.ops[1].to)),
|
|
|
|
props.gist.units
|
|
|
|
)}
|
|
|
|
/>
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Height</KeyTd>
|
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan
|
|
|
|
html={formatMm(
|
|
|
|
Math.abs(props.path.ops[0].to.dy(props.path.ops[1].to)),
|
|
|
|
props.gist.units
|
|
|
|
)}
|
|
|
|
/>
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Part</KeyTd>
|
|
|
|
<ValTd>{props.partName}</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Draw Op</KeyTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<ValTd>
|
|
|
|
{props.i}/{props.ops.length}
|
|
|
|
</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<div className="max-w-md" style={{ maxHeight: '80vh' }}>
|
|
|
|
<Curve {...props} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-01-29 10:50:02 +01:00
|
|
|
)
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
export const Attributes = ({ list }) =>
|
|
|
|
list ? (
|
2022-06-18 12:15:05 +02:00
|
|
|
<ul>
|
2022-10-09 17:32:32 +02:00
|
|
|
{Object.keys(list).map((key) => (
|
|
|
|
<li key={key}>
|
|
|
|
<strong>{key}</strong>: {list[key]}
|
|
|
|
</li>
|
2022-06-18 12:15:05 +02:00
|
|
|
))}
|
|
|
|
</ul>
|
|
|
|
) : null
|
|
|
|
|
2022-06-18 18:15:17 +02:00
|
|
|
export const pathInfo = (props) => {
|
2022-09-22 14:17:29 +02:00
|
|
|
const bbox = props.path.bbox()
|
2022-06-18 12:15:05 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="p-4 border bg-neutral bg-opacity-40 shadow rounded-lg">
|
|
|
|
<h5 className="text-neutral-content text-center pb-4">Path info</h5>
|
|
|
|
<div className="flex flex-row flex-wrap overflow-scroll" style={{ maxHeight: '80vh' }}>
|
2022-06-18 18:15:17 +02:00
|
|
|
<div>
|
|
|
|
<table className="border-collapse h-fit">
|
|
|
|
<tbody>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Name</KeyTd>
|
2023-04-04 15:44:36 +00:00
|
|
|
<ValTd>{props.pathName}</ValTd>
|
2022-06-18 18:15:17 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Length</KeyTd>
|
2022-06-24 11:34:22 +02:00
|
|
|
<ValTd>
|
|
|
|
<RawSpan html={formatMm(props.path.length(), props.gist.units)} />
|
|
|
|
</ValTd>
|
2022-06-18 18:15:17 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Width</KeyTd>
|
2022-06-24 11:34:22 +02:00
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan
|
|
|
|
html={formatMm(Math.abs(bbox.bottomRight.x - bbox.topLeft.x), props.gist.units)}
|
|
|
|
/>
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
2022-06-18 18:15:17 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Height</KeyTd>
|
2022-06-24 11:34:22 +02:00
|
|
|
<ValTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<RawSpan
|
|
|
|
html={formatMm(Math.abs(bbox.bottomRight.y - bbox.topLeft.y), props.gist.units)}
|
|
|
|
/>
|
2022-06-24 11:34:22 +02:00
|
|
|
</ValTd>
|
2022-06-18 18:15:17 +02:00
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Top Left</KeyTd>
|
|
|
|
<ValTd>{pointCoords(bbox.topLeft)}</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Bottom Right</KeyTd>
|
|
|
|
<ValTd>{pointCoords(bbox.bottomRight)}</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Part</KeyTd>
|
|
|
|
<ValTd>{props.partName}</ValTd>
|
|
|
|
</Tr>
|
|
|
|
<Tr>
|
|
|
|
<KeyTd>Attributes</KeyTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<ValTd>
|
2023-02-05 17:10:32 +00:00
|
|
|
<Attributes list={props.path.attributes.list} />
|
2022-10-09 17:32:32 +02:00
|
|
|
</ValTd>
|
2022-06-18 18:15:17 +02:00
|
|
|
</Tr>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<div className="flex flex-row flex-wrap gap-2 mt-4">
|
2022-10-09 17:32:32 +02:00
|
|
|
<button className="btn btn-success" onClick={() => console.log(props.path)}>
|
|
|
|
console.log(path)
|
|
|
|
</button>
|
|
|
|
<button className="btn btn-success" onClick={() => console.table(props.path)}>
|
|
|
|
console.table(path)
|
|
|
|
</button>
|
2022-06-18 18:15:17 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-06-18 12:15:05 +02:00
|
|
|
<table className="border-collapse h-fit">
|
|
|
|
<tbody>
|
|
|
|
{props.path.ops.map((op, i) => (
|
|
|
|
<Tr key={i}>
|
|
|
|
<KeyTd>{i}</KeyTd>
|
2022-10-09 17:32:32 +02:00
|
|
|
<ValTd>
|
|
|
|
<PathOp op={op} />
|
|
|
|
</ValTd>
|
2022-06-18 12:15:05 +02:00
|
|
|
</Tr>
|
|
|
|
))}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<div className="max-w-md">
|
|
|
|
<MiniPath {...props} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const PathOp = ({ op }) => {
|
2022-10-09 17:32:32 +02:00
|
|
|
if (op.type === 'move')
|
|
|
|
return (
|
|
|
|
<span>
|
|
|
|
<strong>Move</strong> to {pointCoords(op.to)}
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
else if (op.type === 'line')
|
|
|
|
return (
|
|
|
|
<span>
|
|
|
|
<strong>Line</strong> to {pointCoords(op.to)}
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
else if (op.type === 'curve')
|
|
|
|
return (
|
|
|
|
<span>
|
|
|
|
<strong>Curve</strong> to {pointCoords(op.to)}
|
|
|
|
<br />
|
|
|
|
Cp1: {pointCoords(op.cp1)}
|
|
|
|
<br />
|
|
|
|
Cp2: {pointCoords(op.cp2)}
|
|
|
|
</span>
|
|
|
|
)
|
2022-06-18 12:15:05 +02:00
|
|
|
else if (op.type === 'noop') return <strong>NOOP</strong>
|
|
|
|
else if (op.type === 'close') return <strong>Close</strong>
|
|
|
|
else return <strong>FIXME: unknown path operation type: {op.type}</strong>
|
|
|
|
}
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
const XrayCurve = (props) => {
|
2022-06-18 12:49:49 +02:00
|
|
|
const from = props.path.ops[0].to
|
|
|
|
const { cp1, cp2, to } = props.path.ops[1]
|
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
return (
|
2022-06-18 12:49:49 +02:00
|
|
|
<>
|
|
|
|
<path
|
|
|
|
d={props.path.asPathstring()}
|
|
|
|
{...getProps(props.path)}
|
|
|
|
className="opacity-0 stroke-4xl stroke-lining hover:opacity-25 hover:cursor-pointer"
|
2022-10-09 17:32:32 +02:00
|
|
|
onClick={(evt) => {
|
|
|
|
evt.stopPropagation()
|
|
|
|
props.showInfo(curveInfo(props))
|
|
|
|
}}
|
2022-06-18 12:49:49 +02:00
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
<path d={`M ${from.x},${from.y} L ${cp1.x},${cp1.y}`} className="lining dotted" />
|
|
|
|
<path d={`M ${to.x},${to.y} L ${cp2.x},${cp2.y}`} className="lining dotted" />
|
|
|
|
<CpCircle point={cp1} />
|
|
|
|
<CpCircle point={cp2} />
|
|
|
|
<EpCircle point={from} />
|
|
|
|
<EpCircle point={to} />
|
2022-06-18 12:49:49 +02:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
2022-06-18 12:15:05 +02:00
|
|
|
|
2022-10-09 17:32:32 +02:00
|
|
|
const XrayPath = (props) => {
|
2022-09-22 14:17:29 +02:00
|
|
|
const classes = props.path.attributes.get('class')
|
|
|
|
if (typeof classes === 'string' && classes.includes('noxray')) return null
|
2022-06-18 12:15:05 +02:00
|
|
|
const ops = props.path.divide()
|
|
|
|
|
|
|
|
return (
|
|
|
|
<g>
|
|
|
|
<path
|
|
|
|
d={props.path.asPathstring()}
|
|
|
|
{...getProps(props.path)}
|
|
|
|
className="opacity-0 stroke-7xl stroke-contrast hover:opacity-25 hover:cursor-pointer"
|
2022-10-09 17:32:32 +02:00
|
|
|
onClick={(evt) => {
|
|
|
|
evt.preventDefault()
|
|
|
|
props.showInfo(pathInfo(props))
|
|
|
|
}}
|
|
|
|
markerStart="none"
|
|
|
|
markerEnd="none"
|
2022-06-18 12:15:05 +02:00
|
|
|
/>
|
2022-06-18 12:49:49 +02:00
|
|
|
{ops.length > 0
|
2022-10-09 17:32:32 +02:00
|
|
|
? ops.map((op, i) =>
|
|
|
|
op.ops[1].type === 'curve' ? (
|
2023-02-05 19:58:25 +01:00
|
|
|
<XrayCurve
|
|
|
|
{...props}
|
|
|
|
path={op}
|
|
|
|
ops={ops}
|
|
|
|
i={i}
|
|
|
|
pathName={`${props.pathName}_test`}
|
|
|
|
key={i}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
) : (
|
2023-02-05 19:58:25 +01:00
|
|
|
<XrayLine
|
|
|
|
{...props}
|
|
|
|
path={op}
|
|
|
|
ops={ops}
|
|
|
|
i={i}
|
|
|
|
pathName={`${props.pathName}_test`}
|
|
|
|
key={i}
|
|
|
|
/>
|
2022-10-09 17:32:32 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
: null}
|
2022-06-18 12:15:05 +02:00
|
|
|
</g>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-29 18:57:24 +01:00
|
|
|
export const Path = (props) => {
|
2022-01-29 10:50:02 +01:00
|
|
|
const { path, partName, pathName } = props
|
2022-09-18 22:58:03 +02:00
|
|
|
if (path.hidden) return null
|
2022-01-25 11:22:09 +01:00
|
|
|
const output = []
|
2022-01-29 10:50:02 +01:00
|
|
|
const pathId = 'path-' + partName + '-' + pathName
|
2022-03-18 19:11:00 +01:00
|
|
|
let d = ''
|
2022-10-09 17:32:32 +02:00
|
|
|
try {
|
|
|
|
d = path.asPathstring()
|
|
|
|
} catch (err) {
|
2022-03-18 19:11:00 +01:00
|
|
|
// Bail out
|
|
|
|
console.log(`Failed to generate pathstring for path ${pathId} in part ${partName}`, err)
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
output.push(<path id={pathId} key={pathId} d={d} {...getProps(path)} />)
|
2022-01-28 19:55:32 +01:00
|
|
|
if (path.attributes.get('data-text'))
|
|
|
|
output.push(<TextOnPath key={'text-on-path-' + name} pathId={pathId} {...props} />)
|
2022-10-09 17:32:32 +02:00
|
|
|
if (props.gist._state?.xray?.enabled) output.push(<XrayPath {...props} key={'xpath' + pathId} />)
|
2022-01-25 11:22:09 +01:00
|
|
|
|
|
|
|
return output
|
|
|
|
}
|