Skip to content

Commit 506a621

Browse files
committed
Start working on pattern preview
1 parent 9e38fe4 commit 506a621

3 files changed

Lines changed: 120 additions & 41 deletions

File tree

src/App.tsx

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
getRelativePatternPosition,
99
} from './functions'
1010
import { useStableFunction } from './hooks'
11+
import { Preview } from './preview'
1112
import { AbsolutePattern, AbsolutePoint, NumberPair, RelativePattern, State } from './types'
1213

1314
const getDraftState = (state: State, draftClick: DraftClick, mousePosition: AbsolutePoint): State => {
@@ -59,7 +60,23 @@ const BASE_STATE = {
5960
patterns: [],
6061
}
6162

63+
const PreviewButton: React.FC<{ onClick: () => void; children: React.ReactNode }> = ({
64+
onClick,
65+
children,
66+
}) => {
67+
return (
68+
<button
69+
className='text-amber-300 hover:text-black cursor-pointer p-2 hover:bg-amber-300'
70+
type='button'
71+
onClick={onClick}
72+
>
73+
{children}
74+
</button>
75+
)
76+
}
77+
6278
function App() {
79+
const [previewOpen, setPreviewOpen] = useState(false)
6380
const [canvasEl, setCanvasEl] = useState<HTMLCanvasElement | null>(null)
6481
const mousePositionRef = useRef<AbsolutePoint>(BASE_MOUSE_POSITION)
6582

@@ -145,45 +162,75 @@ function App() {
145162
}, [draftClick, state, resizeCount, ctx])
146163

147164
return (
148-
<canvas
149-
className='w-full h-full'
150-
ref={setCanvasEl}
151-
onPointerDown={e => {
152-
if (!ctx) return
153-
154-
const mousePoint = getMousePoint(ctx, e)
155-
156-
// create draft screen based on current cursor position
157-
setDraftClick({
158-
anchor: mousePoint,
159-
clickedPath: findClickedScreenOrPattern(state, mousePoint),
160-
})
161-
}}
162-
onPointerUp={e => {
163-
if (!ctx) return
164-
if (!draftClick) return
165-
166-
// reset origin. note: this is async so we can still use the value below.
167-
setDraftClick(undefined)
168-
169-
const mousePoint = getMousePoint(ctx, e)
170-
171-
const [x1, y1] = draftClick.anchor
172-
const [x2, y2] = mousePoint
173-
174-
// validate size, ignore drawings that are too small (arbitrary)
175-
// todo: convert to viewport size and check pixels
176-
if (Math.abs(x2 - x1) < 0.01) return
177-
if (Math.abs(y2 - y1) < 0.01) return
178-
179-
setState(prevState => getDraftState(prevState, draftClick, mousePoint))
180-
}}
181-
onPointerMove={e => {
182-
if (!ctx) return
183-
184-
mousePositionRef.current = getMousePoint(ctx, e)
185-
}}
186-
/>
165+
<div className='size-full flex'>
166+
{previewOpen ? (
167+
<div className='flex-1/3 border-r-amber-300 border-r-1'>
168+
<div className='flex gap-2 items-center justify-between'>
169+
<h2 className='text-amber-300'>preview (wip)</h2>
170+
<PreviewButton onClick={() => setPreviewOpen(false)}>x</PreviewButton>
171+
</div>
172+
<Preview state={state} />
173+
</div>
174+
) : (
175+
<div className=' border-r-amber-300 border-r-1'>
176+
<PreviewButton onClick={() => setPreviewOpen(true)}>&gt;</PreviewButton>
177+
</div>
178+
)}
179+
<div className='flex-2/3'>
180+
<canvas
181+
className='size-full'
182+
ref={setCanvasEl}
183+
onPointerDown={e => {
184+
if (!ctx) return
185+
186+
const mousePoint = getMousePoint(ctx, e)
187+
188+
console.log(
189+
'pointerdown',
190+
e.clientX,
191+
e.width,
192+
e.pageX,
193+
e.screenX,
194+
e.movementX,
195+
ctx.canvas.width,
196+
ctx.canvas.clientWidth,
197+
e,
198+
getMousePoint(ctx, e)
199+
)
200+
201+
// create draft screen based on current cursor position
202+
setDraftClick({
203+
anchor: mousePoint,
204+
clickedPath: findClickedScreenOrPattern(state, mousePoint),
205+
})
206+
}}
207+
onPointerUp={e => {
208+
if (!ctx) return
209+
if (!draftClick) return
210+
211+
// reset origin. note: this is async so we can still use the value below.
212+
setDraftClick(undefined)
213+
214+
const mousePoint = getMousePoint(ctx, e)
215+
216+
const [x1, y1] = draftClick.anchor
217+
const [x2, y2] = mousePoint
218+
219+
// validate size, ignore drawings that are too small (arbitrary)
220+
// todo: convert to viewport size and check pixels
221+
if (Math.abs(x2 - x1) < 0.01) return
222+
if (Math.abs(y2 - y1) < 0.01) return
223+
224+
setState(prevState => getDraftState(prevState, draftClick, mousePoint))
225+
}}
226+
onPointerMove={e => {
227+
if (!ctx) return
228+
229+
mousePositionRef.current = getMousePoint(ctx, e)
230+
}}
231+
/>
232+
</div>
233+
</div>
187234
)
188235
}
189236

src/functions.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const mutateBoundariesFromPattern = (() => {
4040
}
4141
})()
4242

43-
const getBoundariesFromPattern = <N extends PatternNumber>(pattern: Pattern<N>): Boundaries<N> => {
43+
export const getBoundariesFromPattern = <N extends PatternNumber>(pattern: Pattern<N>): Boundaries<N> => {
4444
const obj: Boundaries<N> = { xMin: 0 as N, xMax: 0 as N, yMin: 0 as N, yMax: 0 as N }
4545
mutateBoundariesFromPattern(pattern, obj)
4646
return obj
@@ -161,7 +161,11 @@ export const getMousePoint = (
161161
ctx: CanvasRenderingContext2D,
162162
mouseEvent: React.MouseEvent<HTMLCanvasElement, MouseEvent>
163163
): AbsolutePoint => {
164-
const mousePosition = [mouseEvent.clientX, mouseEvent.clientY] satisfies NumberPair as ViewportPoint
164+
const canvasRect = ctx.canvas.getBoundingClientRect()
165+
const mousePosition = [
166+
mouseEvent.clientX - canvasRect.x,
167+
mouseEvent.clientY - canvasRect.y,
168+
] satisfies NumberPair as ViewportPoint
165169

166170
return mapPointFromViewportSpace(mousePosition, getScreenSize(ctx))
167171
}

src/preview.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { getBoundariesFromPattern } from './functions'
2+
import { State } from './types'
3+
4+
export const Preview: React.FC<{ state: State }> = ({ state }) => {
5+
return (
6+
<div className='border-1 border-amber-300 aspect-square'>
7+
<svg viewBox='0 0 1 1' xmlns='http://www.w3.org/2000/svg' className='w-full aspect-square'>
8+
{state.patterns.map((p, i) => {
9+
const { xMin, xMax, yMin, yMax } = getBoundariesFromPattern(p)
10+
11+
return (
12+
<rect
13+
key={i}
14+
className='stroke-amber-100'
15+
vectorEffect='non-scaling-stroke'
16+
fill='none'
17+
strokeWidth='1px'
18+
x={xMin.toFixed(3)}
19+
y={yMin.toFixed(3)}
20+
width={(xMax - xMin).toFixed(3)}
21+
height={(yMax - yMin).toFixed(3)}
22+
/>
23+
)
24+
})}
25+
</svg>
26+
</div>
27+
)
28+
}

0 commit comments

Comments
 (0)