From 602bd67a743a401989a06db5dcd72a2fa7b08a7a Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Wed, 12 May 2021 10:48:35 +0100 Subject: [PATCH] Improves shape props types, improves circle --- components/canvas/shapes/circle.tsx | 19 +++------ components/canvas/shapes/dot.tsx | 12 ++---- components/canvas/shapes/polyline.tsx | 34 +++++++-------- components/canvas/shapes/rectangle.tsx | 13 ++---- components/canvas/shapes/shape-g.tsx | 57 +++++++++++++++++--------- state/sessions/brush-session.ts | 7 +++- state/state.ts | 32 ++++++++++++++- types.ts | 16 ++++++++ utils/vec.ts | 18 ++++++++ 9 files changed, 137 insertions(+), 71 deletions(-) diff --git a/components/canvas/shapes/circle.tsx b/components/canvas/shapes/circle.tsx index cb114d6a7..20517d9e1 100644 --- a/components/canvas/shapes/circle.tsx +++ b/components/canvas/shapes/circle.tsx @@ -1,25 +1,18 @@ import { useSelector } from "state" -import { CircleShape } from "types" +import { CircleShape, ShapeProps } from "types" import ShapeGroup from "./shape-g" -interface BaseCircleProps extends Pick { - radius: number - fill?: string - stroke?: string - strokeWidth?: number -} - function BaseCircle({ radius, - fill = "#ccc", + fill = "#999", stroke = "none", strokeWidth = 0, -}: BaseCircleProps) { +}: ShapeProps) { return ( ) { return ( <> { - fill?: string - stroke?: string - strokeWidth?: number -} - function BasePolyline({ points, fill = "none", - stroke = "#ccc", - strokeWidth = 2, -}: BasePolylineProps) { + stroke = "#999", + strokeWidth = 1, +}: ShapeProps) { return ( - + <> + + + ) } diff --git a/components/canvas/shapes/rectangle.tsx b/components/canvas/shapes/rectangle.tsx index 18134a89c..d0a2e6016 100644 --- a/components/canvas/shapes/rectangle.tsx +++ b/components/canvas/shapes/rectangle.tsx @@ -1,20 +1,13 @@ import { useSelector } from "state" -import { RectangleShape } from "types" +import { RectangleShape, ShapeProps } from "types" import ShapeGroup from "./shape-g" -interface BaseRectangleProps extends Pick { - size: number[] - fill?: string - stroke?: string - strokeWidth?: number -} - function BaseRectangle({ size, - fill = "#ccc", + fill = "#999", stroke = "none", strokeWidth = 0, -}: BaseRectangleProps) { +}: ShapeProps) { return ( (null) + + const handlePointerDown = useCallback( + (e: React.PointerEvent) => { + e.stopPropagation() + rGroup.current.setPointerCapture(e.pointerId) + state.send("POINTED_SHAPE", { id, ...getPointerEventInfo(e) }) + }, + [id] + ) + + const handlePointerUp = useCallback( + (e: React.PointerEvent) => { + e.stopPropagation() + rGroup.current.releasePointerCapture(e.pointerId) + state.send("STOPPED_POINTING_SHAPE", { id, ...getPointerEventInfo(e) }) + }, + [id] + ) + + const handlePointerEnter = useCallback( + (e: React.PointerEvent) => + state.send("HOVERED_SHAPE", { id, ...getPointerEventInfo(e) }), + [id] + ) + + const handlePointerLeave = useCallback( + (e: React.PointerEvent) => + state.send("UNHOVERED_SHAPE", { id, ...getPointerEventInfo(e) }), + [id] + ) + return ( - state.send("POINTED_SHAPE", { id, ...getPointerEventInfo(e) }) - } - onPointerUp={(e) => - state.send("STOPPED_POINTING_SHAPE", { - id, - ...getPointerEventInfo(e), - }) - } - onPointerEnter={(e) => - state.send("HOVERED_SHAPE", { id, ...getPointerEventInfo(e) }) - } - onPointerLeave={(e) => - state.send("UNHOVERED_SHAPE", { - id, - ...getPointerEventInfo(e), - }) - } + onPointerDown={handlePointerDown} + onPointerUp={handlePointerUp} + onPointerEnter={handlePointerEnter} + onPointerLeave={handlePointerLeave} > {children} diff --git a/state/sessions/brush-session.ts b/state/sessions/brush-session.ts index 6b65dc5a1..a66f03d2f 100644 --- a/state/sessions/brush-session.ts +++ b/state/sessions/brush-session.ts @@ -82,8 +82,11 @@ export default class BrushSession extends BaseSession { shape, test: (brushBounds: Bounds) => boundsContained(bounds, brushBounds) || - intersectCircleBounds(shape.point, shape.radius, brushBounds) - .length > 0, + intersectCircleBounds( + vec.addScalar(shape.point, shape.radius), + shape.radius, + brushBounds + ).length > 0, } } case ShapeType.Rectangle: { diff --git a/state/state.ts b/state/state.ts index afba700e3..d7880bc2b 100644 --- a/state/state.ts +++ b/state/state.ts @@ -32,11 +32,23 @@ const state = createState({ selecting: { on: { POINTED_CANVAS: { to: "brushSelecting" }, + POINTED_SHAPE: [ + "setPointedId", + { + if: "isPressingShiftKey", + then: { + if: "isPointedShapeSelected", + do: "pullPointedIdFromSelectedIds", + else: "pushPointedIdToSelectedIds", + }, + else: ["clearSelectedIds", "pushPointedIdToSelectedIds"], + }, + ], }, }, brushSelecting: { onEnter: [ - { unless: "isPressingShiftKey", do: "clearSelection" }, + { unless: "isPressingShiftKey", do: "clearSelectedIds" }, "startBrushSession", ], on: { @@ -48,6 +60,9 @@ const state = createState({ }, }, conditions: { + isPointedShapeSelected(data) { + return data.selectedIds.includes(data.pointedId) + }, isPressingShiftKey(data, payload: { shiftKey: boolean }) { return payload.shiftKey }, @@ -71,9 +86,22 @@ const state = createState({ session.update(data, screenToWorld(payload.point, data)) }, // Selection - clearSelection(data) { + setPointedId(data, payload: { id: string }) { + data.pointedId = payload.id + }, + clearPointedId(data) { + data.pointedId = undefined + }, + clearSelectedIds(data) { data.selectedIds = [] }, + pullPointedIdFromSelectedIds(data) { + const { selectedIds, pointedId } = data + selectedIds.splice(selectedIds.indexOf(pointedId, 1)) + }, + pushPointedIdToSelectedIds(data) { + data.selectedIds.push(data.pointedId) + }, // Camera zoomCamera(data, payload: { delta: number; point: number[] }) { const { camera } = data diff --git a/types.ts b/types.ts index e753d72c6..6f0dad4e2 100644 --- a/types.ts +++ b/types.ts @@ -106,3 +106,19 @@ export interface Shapes extends Record { [ShapeType.Polyline]: PolylineShape [ShapeType.Rectangle]: RectangleShape } + +export interface BaseShapeStyles { + fill: string + stroke: string + strokeWidth: number +} + +export type Difference = A extends B ? never : A + +export type ShapeSpecificProps = Pick< + T, + Difference +> + +export type ShapeProps = Partial & + ShapeSpecificProps diff --git a/utils/vec.ts b/utils/vec.ts index 97850c1b5..3ec15fc66 100644 --- a/utils/vec.ts +++ b/utils/vec.ts @@ -26,6 +26,15 @@ export function add(A: number[], B: number[]) { return [A[0] + B[0], A[1] + B[1]] } +/** + * Add scalar to vector. + * @param A + * @param B + */ +export function addScalar(A: number[], n: number) { + return [A[0] + n, A[1] + n] +} + /** * Subtract vectors. * @param A @@ -35,6 +44,15 @@ export function sub(A: number[], B: number[]) { return [A[0] - B[0], A[1] - B[1]] } +/** + * Subtract scalar from vector. + * @param A + * @param B + */ +export function subScalar(A: number[], n: number) { + return [A[0] - n, A[1] - n] +} + /** * Get the vector from vectors A to B. * @param A