Continues on drawing / rotation

This commit is contained in:
Steve Ruiz 2021-05-28 17:25:43 +01:00
parent 79c254a938
commit c95c54dae6
11 changed files with 311 additions and 324 deletions

View file

@ -7,7 +7,6 @@ import CenterHandle from './center-handle'
import CornerHandle from './corner-handle'
import EdgeHandle from './edge-handle'
import RotateHandle from './rotate-handle'
import Selected from '../selected'
export default function Bounds() {
const isBrushing = useSelector((s) => s.isIn('brushSelecting'))
@ -32,7 +31,6 @@ export default function Bounds() {
${(bounds.minY + bounds.maxY) / 2})
translate(${bounds.minX},${bounds.minY})`}
>
<Selected bounds={bounds} />
<CenterHandle bounds={bounds} />
<EdgeHandle size={size} bounds={bounds} edge={Edge.Top} />
<EdgeHandle size={size} bounds={bounds} edge={Edge.Right} />

View file

@ -1,5 +1,5 @@
import styled from 'styles'
import state from 'state'
import state, { useSelector } from 'state'
import inputs from 'state/inputs'
import React, { useCallback, useRef } from 'react'
import useZoomEvents from 'hooks/useZoomEvents'
@ -9,6 +9,7 @@ import Page from './page'
import Brush from './brush'
import Bounds from './bounds/bounding-box'
import BoundsBg from './bounds/bounds-bg'
import Selected from './selected'
export default function Canvas() {
const rCanvas = useRef<SVGSVGElement>(null)
@ -17,6 +18,8 @@ export default function Canvas() {
useCamera(rGroup)
const isReady = useSelector((s) => s.isIn('ready'))
const handlePointerDown = useCallback((e: React.PointerEvent) => {
rCanvas.current.setPointerCapture(e.pointerId)
state.send('POINTED_CANVAS', inputs.pointerDown(e, 'canvas'))
@ -40,12 +43,15 @@ export default function Canvas() {
onPointerUp={handlePointerUp}
>
<Defs />
<MainGroup ref={rGroup}>
<BoundsBg />
<Page />
<Bounds />
<Brush />
</MainGroup>
{isReady && (
<g ref={rGroup}>
<BoundsBg />
<Page />
<Bounds />
<Selected />
<Brush />
</g>
)}
</MainSVG>
)
}
@ -63,5 +69,3 @@ const MainSVG = styled('svg', {
userSelect: 'none',
},
})
const MainGroup = styled('g', {})

View file

@ -1,6 +1,6 @@
import { getShapeUtils } from "lib/shape-utils"
import { useSelector } from "state"
import { deepCompareArrays, getPage } from "utils/utils"
import { getShapeUtils } from 'lib/shape-utils'
import { useSelector } from 'state'
import { deepCompareArrays, getPage } from 'utils/utils'
export default function Defs() {
const currentPageShapeIds = useSelector(({ data }) => {
@ -20,6 +20,6 @@ export default function Defs() {
export function Def({ id }: { id: string }) {
const shape = useSelector(({ data }) => getPage(data).shapes[id])
if (!shape) return null
return getShapeUtils(shape).render(shape)
}

View file

@ -1,48 +1,48 @@
import styled from 'styles'
import { useSelector } from 'state'
import {
deepCompareArrays,
getBoundsCenter,
getPage,
getSelectedShapes,
} from 'utils/utils'
import * as vec from 'utils/vec'
import { deepCompareArrays, getPage } from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils'
import { Bounds } from 'types'
import useShapeEvents from 'hooks/useShapeEvents'
import { useRef } from 'react'
export default function Selected({ bounds }: { bounds: Bounds }) {
export default function Selected() {
const currentPageShapeIds = useSelector(({ data }) => {
return Array.from(data.selectedIds.values())
}, deepCompareArrays)
const isSelecting = useSelector((s) => s.isIn('selecting'))
if (!isSelecting) return null
return (
<g>
{currentPageShapeIds.map((id) => (
<ShapeOutline key={id} id={id} bounds={bounds} />
<ShapeOutline key={id} id={id} />
))}
</g>
)
}
export function ShapeOutline({ id, bounds }: { id: string; bounds: Bounds }) {
export function ShapeOutline({ id }: { id: string }) {
const rIndicator = useRef<SVGUseElement>(null)
const shape = useSelector(({ data }) => getPage(data).shapes[id])
const shapeBounds = getShapeUtils(shape).getBounds(shape)
const events = useShapeEvents(id, rIndicator)
if (!shape) return null
const transform = `
rotate(${shape.rotation * (180 / Math.PI)},
${getShapeUtils(shape).getCenter(shape)})
translate(${shape.point})`
return (
<Indicator
ref={rIndicator}
as="use"
href={'#' + id}
transform={`rotate(${shape.rotation * (180 / Math.PI)},${getBoundsCenter(
shapeBounds
)}) translate(${vec.sub(shape.point, [bounds.minX, bounds.minY])})`}
transform={transform}
{...events}
/>
)

View file

@ -1,10 +1,10 @@
import React, { useCallback, useRef, memo } from 'react'
import state, { useSelector } from 'state'
import inputs from 'state/inputs'
import React, { useRef, memo } from 'react'
import { useSelector } from 'state'
import styled from 'styles'
import { getShapeUtils } from 'lib/shape-utils'
import { getPage } from 'utils/utils'
import { ShapeStyles } from 'types'
import useShapeEvents from 'hooks/useShapeEvents'
function Shape({ id, isSelecting }: { id: string; isSelecting: boolean }) {
const isHovered = useSelector((state) => state.data.hoveredId === id)
@ -15,42 +15,7 @@ function Shape({ id, isSelecting }: { id: string; isSelecting: boolean }) {
const rGroup = useRef<SVGGElement>(null)
const handlePointerDown = useCallback(
(e: React.PointerEvent) => {
e.stopPropagation()
rGroup.current.setPointerCapture(e.pointerId)
state.send('POINTED_SHAPE', inputs.pointerDown(e, id))
},
[id]
)
const handlePointerUp = useCallback(
(e: React.PointerEvent) => {
e.stopPropagation()
rGroup.current.releasePointerCapture(e.pointerId)
state.send('STOPPED_POINTING', inputs.pointerUp(e))
},
[id]
)
const handlePointerEnter = useCallback(
(e: React.PointerEvent) => {
state.send('HOVERED_SHAPE', inputs.pointerEnter(e, id))
},
[id, shape]
)
const handlePointerMove = useCallback(
(e: React.PointerEvent) => {
state.send('MOVED_OVER_SHAPE', inputs.pointerEnter(e, id))
},
[id, shape]
)
const handlePointerLeave = useCallback(
() => state.send('UNHOVERED_SHAPE', { target: id }),
[id]
)
const events = useShapeEvents(id, rGroup)
// This is a problem with deleted shapes. The hooks in this component
// may sometimes run before the hook in the Page component, which means
@ -58,19 +23,18 @@ function Shape({ id, isSelecting }: { id: string; isSelecting: boolean }) {
// detects the change and pulls this component.
if (!shape) return null
const transform = `
rotate(${shape.rotation * (180 / Math.PI)},
${getShapeUtils(shape).getCenter(shape)})
translate(${shape.point})`
return (
<StyledGroup
ref={rGroup}
isHovered={isHovered}
isSelected={isSelected}
transform={`rotate(${shape.rotation * (180 / Math.PI)},${getShapeUtils(
shape
).getCenter(shape)}) translate(${shape.point})`}
onPointerDown={handlePointerDown}
onPointerUp={handlePointerUp}
onPointerEnter={handlePointerEnter}
onPointerLeave={handlePointerLeave}
onPointerMove={handlePointerMove}
transform={transform}
{...events}
>
{isSelecting && <HoverIndicator as="use" href={'#' + id} />}
<StyledShape id={id} style={shape.style} />

View file

@ -1,8 +1,8 @@
import React, { useEffect, useRef } from "react"
import state from "state"
import inputs from "state/inputs"
import * as vec from "utils/vec"
import { usePinch } from "react-use-gesture"
import React, { useEffect, useRef } from 'react'
import state from 'state'
import inputs from 'state/inputs'
import * as vec from 'utils/vec'
import { usePinch } from 'react-use-gesture'
/**
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
@ -21,16 +21,17 @@ export default function useZoomEvents(
function handleWheel(e: WheelEvent) {
e.preventDefault()
e.stopPropagation()
if (e.ctrlKey) {
state.send("ZOOMED_CAMERA", {
state.send('ZOOMED_CAMERA', {
delta: e.deltaY,
...inputs.wheel(e),
})
return
}
state.send("PANNED_CAMERA", {
state.send('PANNED_CAMERA', {
delta: [e.deltaX, e.deltaY],
...inputs.wheel(e),
})
@ -38,6 +39,7 @@ export default function useZoomEvents(
function handleTouchMove(e: TouchEvent) {
e.preventDefault()
e.stopPropagation()
if (e.touches.length === 2) {
const { clientX: x0, clientY: y0 } = e.touches[0]
@ -46,7 +48,7 @@ export default function useZoomEvents(
const dist = vec.dist([x0, y0], [x1, y1])
const point = vec.med([x0, y0], [x1, y1])
state.send("WHEELED", {
state.send('WHEELED', {
delta: dist - rTouchDist.current,
point,
})
@ -55,38 +57,37 @@ export default function useZoomEvents(
}
}
element.addEventListener("wheel", handleWheel)
element.addEventListener("touchstart", handleTouchMove)
element.addEventListener("touchmove", handleTouchMove)
element.addEventListener('wheel', handleWheel, { passive: false })
element.addEventListener('touchstart', handleTouchMove, { passive: false })
element.addEventListener('touchmove', handleTouchMove, { passive: false })
return () => {
element.removeEventListener("wheel", handleWheel)
element.removeEventListener("touchstart", handleTouchMove)
element.removeEventListener("touchmove", handleTouchMove)
element.removeEventListener('wheel', handleWheel)
element.removeEventListener('touchstart', handleTouchMove)
element.removeEventListener('touchmove', handleTouchMove)
}
}, [ref])
const rPinchDa = useRef<number[] | undefined>(undefined)
const rPinchAngle = useRef<number>(undefined)
const rPinchPoint = useRef<number[] | undefined>(undefined)
const bind = usePinch(({ pinching, da, origin }) => {
if (!pinching) {
state.send("STOPPED_PINCHING")
state.send('STOPPED_PINCHING')
rPinchDa.current = undefined
rPinchPoint.current = undefined
return
}
if (rPinchPoint.current === undefined) {
state.send("STARTED_PINCHING")
state.send('STARTED_PINCHING')
rPinchDa.current = da
rPinchPoint.current = origin
}
const [distanceDelta, angleDelta] = vec.sub(rPinchDa.current, da)
state.send("PINCHED", {
state.send('PINCHED', {
delta: vec.sub(rPinchPoint.current, origin),
point: origin,
distanceDelta,

View file

@ -7,11 +7,10 @@ import { boundsContainPolygon } from 'utils/bounds'
import getStroke from 'perfect-freehand'
import {
getBoundsFromPoints,
getRotatedCorners,
getSvgPathFromStroke,
translateBounds,
} from 'utils/utils'
import { DotCircle } from 'components/canvas/misc'
import { shades } from 'lib/colors'
const pathCache = new WeakMap<number[][], string>([])
@ -43,12 +42,17 @@ const draw = registerShapeUtils<DrawShape>({
render(shape) {
const { id, point, points } = shape
if (points.length < 2) {
return <DotCircle id={id} cx={point[0]} cy={point[1]} r={3} />
}
if (!pathCache.has(points)) {
pathCache.set(points, getSvgPathFromStroke(getStroke(points)))
if (points.length < 2) {
const left = vec.add(point, [6, 0])
let d: number[][] = []
for (let i = 0; i < 10; i++) {
d.push(vec.rotWith(left, point, i * ((Math.PI * 2) / 8)))
}
pathCache.set(points, getSvgPathFromStroke(d))
} else {
pathCache.set(points, getSvgPathFromStroke(getStroke(points)))
}
}
return <path id={id} d={pathCache.get(points)} />
@ -69,11 +73,13 @@ const draw = registerShapeUtils<DrawShape>({
},
getRotatedBounds(shape) {
return this.getBounds(shape)
return getBoundsFromPoints(
getRotatedCorners(this.getBounds(shape), shape.rotation)
)
},
getCenter(shape) {
const bounds = this.getBounds(shape)
const bounds = this.getRotatedBounds(shape)
return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2]
},
@ -114,6 +120,10 @@ const draw = registerShapeUtils<DrawShape>({
rotateTo(shape, rotation) {
shape.rotation = rotation
// console.log(shape.points.map(([x, y]) => [x, y]))
// const bounds = this.getBounds(shape)
// const center = [bounds.width / 2, bounds.height / 2]
// shape.points = shape.points.map((pt) => vec.rotWith(pt, center, rotation))
return this
},

View file

@ -16,7 +16,7 @@
"framer-motion": "^4.1.16",
"ismobilejs": "^1.1.1",
"next": "10.2.0",
"perfect-freehand": "^0.4.71",
"perfect-freehand": "^0.4.8",
"prettier": "^2.3.0",
"react": "17.0.2",
"react-dom": "17.0.2",

View file

@ -1,8 +1,8 @@
import { Data } from "types"
import * as vec from "utils/vec"
import BaseSession from "./base-session"
import commands from "state/commands"
import { current } from "immer"
import { Data } from 'types'
import * as vec from 'utils/vec'
import BaseSession from './base-session'
import commands from 'state/commands'
import { current } from 'immer'
import {
clampToRotationToSegments,
getBoundsCenter,
@ -10,8 +10,8 @@ import {
getPage,
getSelectedShapes,
getShapeBounds,
} from "utils/utils"
import { getShapeUtils } from "lib/shape-utils"
} from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils'
const PI2 = Math.PI * 2

View file

@ -1,13 +1,13 @@
import { createSelectorHook, createState } from "@state-designer/react"
import * as vec from "utils/vec"
import inputs from "./inputs"
import { defaultDocument } from "./data"
import { shades } from "lib/colors"
import { createShape, getShapeUtils } from "lib/shape-utils"
import history from "state/history"
import * as Sessions from "./sessions"
import commands from "./commands"
import { updateFromCode } from "lib/code/generate"
import { createSelectorHook, createState } from '@state-designer/react'
import * as vec from 'utils/vec'
import inputs from './inputs'
import { defaultDocument } from './data'
import { shades } from 'lib/colors'
import { createShape, getShapeUtils } from 'lib/shape-utils'
import history from 'state/history'
import * as Sessions from './sessions'
import commands from './commands'
import { updateFromCode } from 'lib/code/generate'
import {
clamp,
getChildren,
@ -18,7 +18,7 @@ import {
getShape,
screenToWorld,
setZoomCSS,
} from "utils/utils"
} from 'utils/utils'
import {
Data,
PointerInfo,
@ -32,7 +32,7 @@ import {
DistributeType,
AlignType,
StretchType,
} from "types"
} from 'types'
const initialData: Data = {
isReadOnly: false,
@ -55,8 +55,8 @@ const initialData: Data = {
pointedId: null,
hoveredId: null,
selectedIds: new Set([]),
currentPageId: "page0",
currentCodeFileId: "file0",
currentPageId: 'page0',
currentCodeFileId: 'file0',
codeControls: {},
document: defaultDocument,
}
@ -65,109 +65,116 @@ const state = createState({
data: initialData,
on: {
ZOOMED_CAMERA: {
do: "zoomCamera",
do: 'zoomCamera',
},
PANNED_CAMERA: {
do: "panCamera",
do: 'panCamera',
},
SELECTED_SELECT_TOOL: { to: "selecting" },
SELECTED_DRAW_TOOL: { unless: "isReadOnly", to: "draw" },
SELECTED_DOT_TOOL: { unless: "isReadOnly", to: "dot" },
SELECTED_CIRCLE_TOOL: { unless: "isReadOnly", to: "circle" },
SELECTED_ELLIPSE_TOOL: { unless: "isReadOnly", to: "ellipse" },
SELECTED_RAY_TOOL: { unless: "isReadOnly", to: "ray" },
SELECTED_LINE_TOOL: { unless: "isReadOnly", to: "line" },
SELECTED_POLYLINE_TOOL: { unless: "isReadOnly", to: "polyline" },
SELECTED_RECTANGLE_TOOL: { unless: "isReadOnly", to: "rectangle" },
TOGGLED_CODE_PANEL_OPEN: "toggleCodePanel",
TOGGLED_STYLE_PANEL_OPEN: "toggleStylePanel",
CHANGED_STYLE: ["updateStyles", "applyStylesToSelection"],
RESET_CAMERA: "resetCamera",
ZOOMED_TO_FIT: "zoomCameraToFit",
SELECTED_SELECT_TOOL: { to: 'selecting' },
SELECTED_DRAW_TOOL: { unless: 'isReadOnly', to: 'draw' },
SELECTED_DOT_TOOL: { unless: 'isReadOnly', to: 'dot' },
SELECTED_CIRCLE_TOOL: { unless: 'isReadOnly', to: 'circle' },
SELECTED_ELLIPSE_TOOL: { unless: 'isReadOnly', to: 'ellipse' },
SELECTED_RAY_TOOL: { unless: 'isReadOnly', to: 'ray' },
SELECTED_LINE_TOOL: { unless: 'isReadOnly', to: 'line' },
SELECTED_POLYLINE_TOOL: { unless: 'isReadOnly', to: 'polyline' },
SELECTED_RECTANGLE_TOOL: { unless: 'isReadOnly', to: 'rectangle' },
TOGGLED_CODE_PANEL_OPEN: 'toggleCodePanel',
TOGGLED_STYLE_PANEL_OPEN: 'toggleStylePanel',
CHANGED_STYLE: ['updateStyles', 'applyStylesToSelection'],
RESET_CAMERA: 'resetCamera',
ZOOMED_TO_FIT: 'zoomCameraToFit',
ZOOMED_TO_SELECTION: {
if: "hasSelection",
do: "zoomCameraToSelection",
if: 'hasSelection',
do: 'zoomCameraToSelection',
},
ZOOMED_TO_ACTUAL: {
if: "hasSelection",
do: "zoomCameraToSelectionActual",
else: "zoomCameraToActual",
if: 'hasSelection',
do: 'zoomCameraToSelectionActual',
else: 'zoomCameraToActual',
},
SELECTED_ALL: { to: "selecting", do: "selectAll" },
SELECTED_ALL: { to: 'selecting', do: 'selectAll' },
},
initial: "loading",
initial: 'loading',
states: {
loading: {
on: {
MOUNTED: {
do: ["restoreSavedData", "zoomCameraToFit"],
to: "ready",
},
MOUNTED: [
'restoreSavedData',
{
if: 'hasSelection',
do: 'zoomCameraToSelectionActual',
else: ['zoomCameraToFit', 'zoomCameraToActual'],
},
{
to: 'ready',
},
],
},
},
ready: {
on: {
UNMOUNTED: [
{ unless: "isReadOnly", do: "forceSave" },
{ to: "loading" },
{ unless: 'isReadOnly', do: 'forceSave' },
{ to: 'loading' },
],
},
initial: "selecting",
initial: 'selecting',
states: {
selecting: {
on: {
SAVED: "forceSave",
UNDO: { do: "undo" },
REDO: { do: "redo" },
CANCELLED: { do: "clearSelectedIds" },
DELETED: { do: "deleteSelectedIds" },
SAVED_CODE: "saveCode",
GENERATED_FROM_CODE: ["setCodeControls", "setGeneratedShapes"],
INCREASED_CODE_FONT_SIZE: "increaseCodeFontSize",
DECREASED_CODE_FONT_SIZE: "decreaseCodeFontSize",
CHANGED_CODE_CONTROL: "updateControls",
ALIGNED: "alignSelection",
STRETCHED: "stretchSelection",
DISTRIBUTED: "distributeSelection",
MOVED: "moveSelection",
STARTED_PINCHING: { to: "pinching" },
SAVED: 'forceSave',
UNDO: { do: 'undo' },
REDO: { do: 'redo' },
CANCELLED: { do: 'clearSelectedIds' },
DELETED: { do: 'deleteSelectedIds' },
SAVED_CODE: 'saveCode',
GENERATED_FROM_CODE: ['setCodeControls', 'setGeneratedShapes'],
INCREASED_CODE_FONT_SIZE: 'increaseCodeFontSize',
DECREASED_CODE_FONT_SIZE: 'decreaseCodeFontSize',
CHANGED_CODE_CONTROL: 'updateControls',
ALIGNED: 'alignSelection',
STRETCHED: 'stretchSelection',
DISTRIBUTED: 'distributeSelection',
MOVED: 'moveSelection',
STARTED_PINCHING: { to: 'pinching' },
},
initial: "notPointing",
initial: 'notPointing',
states: {
notPointing: {
on: {
POINTED_CANVAS: { to: "brushSelecting" },
POINTED_BOUNDS: { to: "pointingBounds" },
POINTED_CANVAS: { to: 'brushSelecting' },
POINTED_BOUNDS: { to: 'pointingBounds' },
POINTED_BOUNDS_HANDLE: {
if: "isPointingRotationHandle",
to: "rotatingSelection",
else: { to: "transformingSelection" },
if: 'isPointingRotationHandle',
to: 'rotatingSelection',
else: { to: 'transformingSelection' },
},
MOVED_OVER_SHAPE: {
if: "pointHitsShape",
if: 'pointHitsShape',
then: {
unless: "shapeIsHovered",
do: "setHoveredId",
unless: 'shapeIsHovered',
do: 'setHoveredId',
},
else: { if: "shapeIsHovered", do: "clearHoveredId" },
else: { if: 'shapeIsHovered', do: 'clearHoveredId' },
},
UNHOVERED_SHAPE: "clearHoveredId",
UNHOVERED_SHAPE: 'clearHoveredId',
POINTED_SHAPE: [
{
if: "isPressingMetaKey",
to: "brushSelecting",
if: 'isPressingMetaKey',
to: 'brushSelecting',
},
"setPointedId",
'setPointedId',
{
unless: "isPointedShapeSelected",
unless: 'isPointedShapeSelected',
then: {
if: "isPressingShiftKey",
do: ["pushPointedIdToSelectedIds", "clearPointedId"],
else: ["clearSelectedIds", "pushPointedIdToSelectedIds"],
if: 'isPressingShiftKey',
do: ['pushPointedIdToSelectedIds', 'clearPointedId'],
else: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
},
},
{
to: "pointingBounds",
to: 'pointingBounds',
},
],
},
@ -176,151 +183,153 @@ const state = createState({
on: {
STOPPED_POINTING: [
{
if: "isPressingShiftKey",
if: 'isPressingShiftKey',
then: {
if: "isPointedShapeSelected",
do: "pullPointedIdFromSelectedIds",
if: 'isPointedShapeSelected',
do: 'pullPointedIdFromSelectedIds',
},
else: {
unless: "isPointingBounds",
do: ["clearSelectedIds", "pushPointedIdToSelectedIds"],
unless: 'isPointingBounds',
do: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
},
},
{ to: "notPointing" },
{ to: 'notPointing' },
],
MOVED_POINTER: {
unless: "isReadOnly",
if: "distanceImpliesDrag",
to: "draggingSelection",
unless: 'isReadOnly',
if: 'distanceImpliesDrag',
to: 'draggingSelection',
},
},
},
rotatingSelection: {
onEnter: "startRotateSession",
onExit: "clearBoundsRotation",
onEnter: 'startRotateSession',
onExit: 'clearBoundsRotation',
on: {
MOVED_POINTER: "updateRotateSession",
PANNED_CAMERA: "updateRotateSession",
PRESSED_SHIFT_KEY: "keyUpdateRotateSession",
RELEASED_SHIFT_KEY: "keyUpdateRotateSession",
STOPPED_POINTING: { do: "completeSession", to: "selecting" },
CANCELLED: { do: "cancelSession", to: "selecting" },
MOVED_POINTER: 'updateRotateSession',
PANNED_CAMERA: 'updateRotateSession',
PRESSED_SHIFT_KEY: 'keyUpdateRotateSession',
RELEASED_SHIFT_KEY: 'keyUpdateRotateSession',
STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
CANCELLED: { do: 'cancelSession', to: 'selecting' },
},
},
transformingSelection: {
onEnter: "startTransformSession",
onEnter: 'startTransformSession',
on: {
MOVED_POINTER: "updateTransformSession",
PANNED_CAMERA: "updateTransformSession",
PRESSED_SHIFT_KEY: "keyUpdateTransformSession",
RELEASED_SHIFT_KEY: "keyUpdateTransformSession",
STOPPED_POINTING: { do: "completeSession", to: "selecting" },
CANCELLED: { do: "cancelSession", to: "selecting" },
MOVED_POINTER: 'updateTransformSession',
PANNED_CAMERA: 'updateTransformSession',
PRESSED_SHIFT_KEY: 'keyUpdateTransformSession',
RELEASED_SHIFT_KEY: 'keyUpdateTransformSession',
STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
CANCELLED: { do: 'cancelSession', to: 'selecting' },
},
},
draggingSelection: {
onEnter: "startTranslateSession",
onEnter: 'startTranslateSession',
on: {
MOVED_POINTER: "updateTranslateSession",
PANNED_CAMERA: "updateTranslateSession",
PRESSED_SHIFT_KEY: "keyUpdateTranslateSession",
RELEASED_SHIFT_KEY: "keyUpdateTranslateSession",
PRESSED_ALT_KEY: "keyUpdateTranslateSession",
RELEASED_ALT_KEY: "keyUpdateTranslateSession",
STOPPED_POINTING: { do: "completeSession", to: "selecting" },
CANCELLED: { do: "cancelSession", to: "selecting" },
MOVED_POINTER: 'updateTranslateSession',
PANNED_CAMERA: 'updateTranslateSession',
PRESSED_SHIFT_KEY: 'keyUpdateTranslateSession',
RELEASED_SHIFT_KEY: 'keyUpdateTranslateSession',
PRESSED_ALT_KEY: 'keyUpdateTranslateSession',
RELEASED_ALT_KEY: 'keyUpdateTranslateSession',
STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
CANCELLED: { do: 'cancelSession', to: 'selecting' },
},
},
brushSelecting: {
onEnter: [
{
unless: ["isPressingMetaKey", "isPressingShiftKey"],
do: "clearSelectedIds",
unless: ['isPressingMetaKey', 'isPressingShiftKey'],
do: 'clearSelectedIds',
},
"clearBoundsRotation",
"startBrushSession",
'clearBoundsRotation',
'startBrushSession',
],
on: {
MOVED_POINTER: "updateBrushSession",
PANNED_CAMERA: "updateBrushSession",
STOPPED_POINTING: { do: "completeSession", to: "selecting" },
CANCELLED: { do: "cancelSession", to: "selecting" },
MOVED_POINTER: 'updateBrushSession',
PANNED_CAMERA: 'updateBrushSession',
STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
CANCELLED: { do: 'cancelSession', to: 'selecting' },
},
},
},
},
pinching: {
on: {
STOPPED_PINCHING: { to: "selecting" },
PINCHED: { do: "pinchCamera" },
STOPPED_PINCHING: { to: 'selecting' },
PINCHED: { do: 'pinchCamera' },
},
},
draw: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
get: "newDraw",
do: "createShape",
to: "draw.editing",
get: 'newDraw',
do: 'createShape',
to: 'draw.editing',
},
UNDO: { do: "undo" },
REDO: { do: "redo" },
UNDO: { do: 'undo' },
REDO: { do: 'redo' },
},
},
editing: {
onEnter: "startDrawSession",
onEnter: 'startDrawSession',
on: {
STOPPED_POINTING: {
do: "completeSession",
to: "draw.creating",
do: 'completeSession',
to: 'draw.creating',
},
CANCELLED: {
do: ["cancelSession", "deleteSelectedIds"],
to: "selecting",
do: ['cancelSession', 'deleteSelectedIds'],
to: 'selecting',
},
MOVED_POINTER: "updateDrawSession",
PANNED_CAMERA: "updateDrawSession",
MOVED_POINTER: 'updateDrawSession',
PANNED_CAMERA: 'updateDrawSession',
},
},
},
},
dot: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
get: "newDot",
do: "createShape",
to: "dot.editing",
get: 'newDot',
do: 'createShape',
to: 'dot.editing',
},
},
},
editing: {
on: {
STOPPED_POINTING: { do: "completeSession", to: "selecting" },
STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
CANCELLED: {
do: ["cancelSession", "deleteSelectedIds"],
to: "selecting",
do: ['cancelSession', 'deleteSelectedIds'],
to: 'selecting',
},
},
initial: "inactive",
initial: 'inactive',
states: {
inactive: {
on: {
MOVED_POINTER: {
if: "distanceImpliesDrag",
to: "dot.editing.active",
if: 'distanceImpliesDrag',
to: 'dot.editing.active',
},
},
},
active: {
onEnter: "startTranslateSession",
onEnter: 'startTranslateSession',
on: {
MOVED_POINTER: "updateTranslateSession",
PANNED_CAMERA: "updateTranslateSession",
MOVED_POINTER: 'updateTranslateSession',
PANNED_CAMERA: 'updateTranslateSession',
},
},
},
@ -328,25 +337,26 @@ const state = createState({
},
},
circle: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
to: "circle.editing",
to: 'circle.editing',
},
},
},
editing: {
on: {
STOPPED_POINTING: { to: "selecting" },
CANCELLED: { to: "selecting" },
STOPPED_POINTING: { to: 'selecting' },
CANCELLED: { to: 'selecting' },
MOVED_POINTER: {
if: "distanceImpliesDrag",
if: 'distanceImpliesDrag',
then: {
get: "newCircle",
do: "createShape",
to: "drawingShape.bounds",
get: 'newCircle',
do: 'createShape',
to: 'drawingShape.bounds',
},
},
},
@ -354,26 +364,26 @@ const state = createState({
},
},
ellipse: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: "selecting" },
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
to: "ellipse.editing",
to: 'ellipse.editing',
},
},
},
editing: {
on: {
STOPPED_POINTING: { to: "selecting" },
CANCELLED: { to: "selecting" },
STOPPED_POINTING: { to: 'selecting' },
CANCELLED: { to: 'selecting' },
MOVED_POINTER: {
if: "distanceImpliesDrag",
if: 'distanceImpliesDrag',
then: {
get: "newEllipse",
do: "createShape",
to: "drawingShape.bounds",
get: 'newEllipse',
do: 'createShape',
to: 'drawingShape.bounds',
},
},
},
@ -381,26 +391,26 @@ const state = createState({
},
},
rectangle: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: "selecting" },
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
to: "rectangle.editing",
to: 'rectangle.editing',
},
},
},
editing: {
on: {
STOPPED_POINTING: { to: "selecting" },
CANCELLED: { to: "selecting" },
STOPPED_POINTING: { to: 'selecting' },
CANCELLED: { to: 'selecting' },
MOVED_POINTER: {
if: "distanceImpliesDrag",
if: 'distanceImpliesDrag',
then: {
get: "newRectangle",
do: "createShape",
to: "drawingShape.bounds",
get: 'newRectangle',
do: 'createShape',
to: 'drawingShape.bounds',
},
},
},
@ -408,50 +418,50 @@ const state = createState({
},
},
ray: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: "selecting" },
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
get: "newRay",
do: "createShape",
to: "ray.editing",
get: 'newRay',
do: 'createShape',
to: 'ray.editing',
},
},
},
editing: {
on: {
STOPPED_POINTING: { to: "selecting" },
CANCELLED: { to: "selecting" },
STOPPED_POINTING: { to: 'selecting' },
CANCELLED: { to: 'selecting' },
MOVED_POINTER: {
if: "distanceImpliesDrag",
to: "drawingShape.direction",
if: 'distanceImpliesDrag',
to: 'drawingShape.direction',
},
},
},
},
},
line: {
initial: "creating",
initial: 'creating',
states: {
creating: {
on: {
CANCELLED: { to: "selecting" },
CANCELLED: { to: 'selecting' },
POINTED_CANVAS: {
get: "newLine",
do: "createShape",
to: "line.editing",
get: 'newLine',
do: 'createShape',
to: 'line.editing',
},
},
},
editing: {
on: {
STOPPED_POINTING: { to: "selecting" },
CANCELLED: { to: "selecting" },
STOPPED_POINTING: { to: 'selecting' },
CANCELLED: { to: 'selecting' },
MOVED_POINTER: {
if: "distanceImpliesDrag",
to: "drawingShape.direction",
if: 'distanceImpliesDrag',
to: 'drawingShape.direction',
},
},
},
@ -463,28 +473,28 @@ const state = createState({
drawingShape: {
on: {
STOPPED_POINTING: {
do: "completeSession",
to: "selecting",
do: 'completeSession',
to: 'selecting',
},
CANCELLED: {
do: ["cancelSession", "deleteSelectedIds"],
to: "selecting",
do: ['cancelSession', 'deleteSelectedIds'],
to: 'selecting',
},
},
initial: "drawingShapeBounds",
initial: 'drawingShapeBounds',
states: {
bounds: {
onEnter: "startDrawTransformSession",
onEnter: 'startDrawTransformSession',
on: {
MOVED_POINTER: "updateTransformSession",
PANNED_CAMERA: "updateTransformSession",
MOVED_POINTER: 'updateTransformSession',
PANNED_CAMERA: 'updateTransformSession',
},
},
direction: {
onEnter: "startDirectionSession",
onEnter: 'startDirectionSession',
on: {
MOVED_POINTER: "updateDirectionSession",
PANNED_CAMERA: "updateDirectionSession",
MOVED_POINTER: 'updateDirectionSession',
PANNED_CAMERA: 'updateDirectionSession',
},
},
},
@ -515,7 +525,7 @@ const state = createState({
},
conditions: {
isPointingBounds(data, payload: PointerInfo) {
return payload.target === "bounds"
return payload.target === 'bounds'
},
isReadOnly(data) {
return data.isReadOnly
@ -545,9 +555,9 @@ const state = createState({
},
isPointingRotationHandle(
data,
payload: { target: Edge | Corner | "rotate" }
payload: { target: Edge | Corner | 'rotate' }
) {
return payload.target === "rotate"
return payload.target === 'rotate'
},
hasSelection(data) {
return data.selectedIds.size > 0
@ -753,7 +763,7 @@ const state = createState({
data.camera.zoom = 1
data.camera.point = [window.innerWidth / 2, window.innerHeight / 2]
document.documentElement.style.setProperty("--camera-zoom", "1")
document.documentElement.style.setProperty('--camera-zoom', '1')
},
zoomCameraToActual(data) {
const { camera } = data
@ -983,7 +993,7 @@ const state = createState({
if (selectedIds.size === 1) {
if (!shapes[0]) {
console.error("Could not find that shape! Clearing selected IDs.")
console.error('Could not find that shape! Clearing selected IDs.')
data.selectedIds.clear()
return null
}

View file

@ -6392,10 +6392,10 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
perfect-freehand@^0.4.71:
version "0.4.71"
resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.71.tgz#b98ffc3cbc4e3cd930528e8d74a8849ee77475fb"
integrity sha512-bJ3w2E6WcUfZJTXWPlS7DI6FIT9rRIYSCXgDYjvST8sAe/c+zNnJnlfJp3q8Hk1uPt9dH7bFuj8Sico6CKZw7A==
perfect-freehand@^0.4.8:
version "0.4.8"
resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.8.tgz#0054995322fdd9939c0c38c260d96a9d0f22eb4f"
integrity sha512-zU0hvTh0ctjb/h5+nwFhb+/5ZnqS8Z16mn7lY2tYy3NwTkjrEKZ8CJvQ2phlOV5kk+BnCDFGVz7tkKrl9+Mr9w==
performance-now@^2.1.0:
version "2.1.0"