From 7fe8047f233eb8678a8903b27b71a53217669c19 Mon Sep 17 00:00:00 2001 From: Michael Peters Date: Sat, 27 Jan 2024 11:52:37 -0800 Subject: [PATCH] drawing lines (shift to draw multiple) --- package-lock.json | 20 ++++-- package.json | 2 + src/components/grid/grid.tsx | 133 +++++++++++++++++++++++++++-------- 3 files changed, 120 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 551de15..c40c478 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "license": "ISC", "dependencies": { "@uidotdev/usehooks": "^2.4.1", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { + "@types/lodash": "^4.14.202", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "css-loader": "^6.7.3", @@ -212,6 +214,12 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -2331,8 +2339,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -4522,6 +4529,12 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -6150,8 +6163,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "loose-envify": { "version": "1.4.0", diff --git a/package.json b/package.json index e185bfe..2cc410f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "author": "", "license": "ISC", "devDependencies": { + "@types/lodash": "^4.14.202", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "css-loader": "^6.7.3", @@ -28,6 +29,7 @@ }, "dependencies": { "@uidotdev/usehooks": "^2.4.1", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/src/components/grid/grid.tsx b/src/components/grid/grid.tsx index cd3054c..c945ecb 100644 --- a/src/components/grid/grid.tsx +++ b/src/components/grid/grid.tsx @@ -1,8 +1,10 @@ -import { useWindowSize } from '@uidotdev/usehooks'; -import { FC, useMemo, useState } from 'react'; +import { useMouse, useWindowSize } from '@uidotdev/usehooks'; +import { FC, MouseEvent, useCallback, useMemo, useState } from 'react'; import './grid.scss'; +import * as lodash from 'lodash'; + interface Point { x: number; y: number; @@ -15,6 +17,18 @@ interface Range { y1: number; } +function pointsToRange(p0: Point, p1: Point): Range { + return { x0: p0.x, y0: p0.y, x1: p1.x, y1: p1.y }; +} + +function snapToGrid(p: Point, gap: number): Point { + // snap a point to a grid + return { + x: gap * Math.round(p.x / gap), + y: gap * Math.round(p.y / gap), + }; +} + const ORIGIN: Point = { x: 0, y: 0 }; function useWindowSizeCustom() { @@ -40,73 +54,130 @@ function useRange(offset: Point): Range { return range; } +function useMouseInRange(range: Range): Point { + const [mouse, _] = useMouse(); + return { x: mouse.x + range.x0, y: mouse.y + range.y0 }; +} + const Grid: FC = () => { const [offset, setOffset] = useState(ORIGIN); const range = useRange(offset); + const mousePoint = useMouseInRange(range); - const gap = 100; - - const nodes = useMemo(() => { - const nodes = []; - for (let x = range.x0; x <= range.x1; x += gap) { - for (let y = range.y0; y <= range.y1; y += gap) { - nodes.push({ x, y }); - } - } - return nodes; - }, [range, gap]); + const GAP = 20; const gridLines = useMemo(() => { const lines = []; - for (let x = range.x0; x <= range.x1; x += gap) { + for (let x = range.x0; x <= range.x1; x += GAP) { lines.push({ ...range, x0: x, x1: x }); } - for (let y = range.y0; y <= range.y1; y += gap) { + for (let y = range.y0; y <= range.y1; y += GAP) { lines.push({ ...range, y0: y, y1: y }); } return lines; - }, [range, gap]); + }, [range, GAP]); - const nodeElements = useMemo(() => { - return nodes.map((point) => ( - - )); - }, [nodes]); + const [userLines, setUserLines] = useState([]); + const [userStartPoint, setUserStartPoint] = useState(null); const gridLineElements = useMemo(() => { - return gridLines.map((line) => ( + return gridLines.map((line, idx) => ( )); }, [gridLines]); - const mid = useMemo(() => { - return { - x: (range.x1 - range.x0) / 2, - y: (range.y1 - range.y0) / 2, - }; - }, [range]); + const userLineIndicatorElement = useMemo(() => { + if (lodash.isNull(userStartPoint)) return null; + const point = snapToGrid(mousePoint, GAP); + return ( + + ); + }, [userStartPoint, mousePoint]); + const userPointIndicatorElement = useMemo(() => { + const point = snapToGrid(mousePoint, GAP); + return ( + + ); + }, [userStartPoint, mousePoint]); + + const userLineElements = useMemo(() => { + return userLines.map((line, idx) => ( + + )); + }, [userLines]); + + const onGridClick = useCallback( + (event: MouseEvent) => { + console.log('grid click', event); + const pointRaw = { x: event.clientX, y: event.clientY }; + const point = snapToGrid(pointRaw, GAP); + + if (lodash.isEqual(userStartPoint, point)) return; + if (lodash.isNull(userStartPoint)) { + setUserStartPoint(point); + return; + } + + const line = pointsToRange(userStartPoint, point); + setUserLines([...userLines, line]); + + if (event.shiftKey == false) { + setUserStartPoint(null); + } else { + setUserStartPoint(point); + } + }, + [userStartPoint, setUserStartPoint, setUserLines, GAP] + ); return ( - {nodeElements} {gridLineElements} + {userLineElements} + {userLineIndicatorElement} + {userPointIndicatorElement} GRID