
This PR adds a user preference for "dynamic size mode" where the scale of shapes (text size, stroke width) is relative to the current zoom level. This means that the stroke width in screen pixels (or text size in screen pixels) is identical regardless of zoom level.  - [x] Draw shape - [x] Text shape - [x] Highlighter shape - [x] Geo shape - [x] Arrow shape - [x] Note shape - [x] Line shape Embed shape? ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `feature` — New feature ### Test Plan 1. Use the tools. 2. Change zoom - [ ] Unit Tests ### Release Notes - Adds a dynamic size user preferences. - Removes double click to reset scale on text shapes. - Removes double click to reset autosize on text shapes. --------- Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com> Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
126 lines
3.2 KiB
TypeScript
126 lines
3.2 KiB
TypeScript
import { track } from '@tldraw/state'
|
|
import { modulate } from '@tldraw/utils'
|
|
import { useEffect, useState } from 'react'
|
|
import { useEditor } from '../hooks/useEditor'
|
|
import { Geometry2d } from '../primitives/geometry/Geometry2d'
|
|
import { Group2d } from '../primitives/geometry/Group2d'
|
|
|
|
function useTick(isEnabled = true) {
|
|
const [_, setTick] = useState(0)
|
|
const editor = useEditor()
|
|
useEffect(() => {
|
|
if (!isEnabled) return
|
|
const update = () => setTick((tick) => tick + 1)
|
|
editor.on('tick', update)
|
|
return () => {
|
|
editor.off('tick', update)
|
|
}
|
|
}, [editor, isEnabled])
|
|
}
|
|
|
|
export const GeometryDebuggingView = track(function GeometryDebuggingView({
|
|
showStroke = true,
|
|
showVertices = true,
|
|
showClosestPointOnOutline = true,
|
|
}: {
|
|
showStroke?: boolean
|
|
showVertices?: boolean
|
|
showClosestPointOnOutline?: boolean
|
|
}) {
|
|
const editor = useEditor()
|
|
|
|
useTick(showClosestPointOnOutline)
|
|
|
|
const zoomLevel = editor.getZoomLevel()
|
|
const renderingShapes = editor.getRenderingShapes()
|
|
const {
|
|
inputs: { currentPagePoint },
|
|
} = editor
|
|
|
|
return (
|
|
<svg
|
|
style={{
|
|
position: 'absolute',
|
|
pointerEvents: 'none',
|
|
zIndex: 999999999,
|
|
top: 0,
|
|
left: 0,
|
|
overflow: 'visible',
|
|
}}
|
|
>
|
|
{renderingShapes.map((result) => {
|
|
const shape = editor.getShape(result.id)!
|
|
|
|
if (shape.type === 'group') return null
|
|
|
|
const geometry = editor.getShapeGeometry(shape)
|
|
const pageTransform = editor.getShapePageTransform(shape)!
|
|
|
|
const pointInShapeSpace = editor.getPointInShapeSpace(shape, currentPagePoint)
|
|
const nearestPointOnShape = geometry.nearestPoint(pointInShapeSpace)
|
|
const distanceToPoint = geometry.distanceToPoint(pointInShapeSpace, true)
|
|
const dist = Math.abs(distanceToPoint) * zoomLevel
|
|
const hitInside = distanceToPoint < 0
|
|
|
|
const { vertices } = geometry
|
|
|
|
return (
|
|
<g
|
|
key={result.id + '_outline'}
|
|
transform={pageTransform.toCssString()}
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
{showStroke && (
|
|
<g
|
|
stroke={geometry.debugColor ?? 'red'}
|
|
opacity="1"
|
|
strokeWidth={2 / zoomLevel}
|
|
fill="none"
|
|
>
|
|
<GeometryStroke geometry={geometry} />
|
|
</g>
|
|
)}
|
|
{showVertices &&
|
|
vertices.map((v, i) => (
|
|
<circle
|
|
key={`v${i}`}
|
|
cx={v.x}
|
|
cy={v.y}
|
|
r={2 / zoomLevel}
|
|
fill={`hsl(${modulate(i, [0, vertices.length - 1], [120, 200])}, 100%, 50%)`}
|
|
stroke="black"
|
|
strokeWidth={1 / zoomLevel}
|
|
/>
|
|
))}
|
|
{showClosestPointOnOutline && dist < 150 && (
|
|
<line
|
|
x1={nearestPointOnShape.x}
|
|
y1={nearestPointOnShape.y}
|
|
x2={pointInShapeSpace.x}
|
|
y2={pointInShapeSpace.y}
|
|
opacity={1 - dist / 150}
|
|
stroke={hitInside ? 'goldenrod' : 'dodgerblue'}
|
|
strokeWidth={2 / zoomLevel}
|
|
/>
|
|
)}
|
|
</g>
|
|
)
|
|
})}
|
|
</svg>
|
|
)
|
|
})
|
|
|
|
function GeometryStroke({ geometry }: { geometry: Geometry2d }) {
|
|
if (geometry instanceof Group2d) {
|
|
return (
|
|
<>
|
|
{[...geometry.children, ...geometry.ignoredChildren].map((child, i) => (
|
|
<GeometryStroke geometry={child} key={i} />
|
|
))}
|
|
</>
|
|
)
|
|
}
|
|
|
|
return <path d={geometry.toSimpleSvgPath()} />
|
|
}
|