drawDB/src/context/CanvasContext.jsx

168 lines
3.7 KiB
React
Raw Normal View History

fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
import { useTransform } from "../hooks";
import { createContext, useCallback, useMemo, useRef, useState } from "react";
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
import { useEventListener, useResizeObserver } from "usehooks-ts";
export const CanvasContext = createContext({
canvas: {
screenSize: {
x: 0,
y: 0,
},
viewBox: new DOMRect(),
},
coords: {
toDiagramSpace(coords) {
return coords;
},
toScreenSpace(coords) {
return coords;
},
},
pointer: {
spaces: {
screen: {
x: 0,
y: 0,
},
diagram: {
x: 0,
y: 0,
},
},
style: "default",
setStyle() {},
},
});
export function CanvasContextProvider({ children, ...attrs }) {
const canvasWrapRef = useRef(null);
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
const { transform } = useTransform();
const canvasSize = useResizeObserver({
ref: canvasWrapRef,
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
box: "content-box",
});
const screenSize = useMemo(
() => ({
x: canvasSize.width ?? 0,
y: canvasSize.height ?? 0,
}),
[canvasSize.height, canvasSize.width],
);
const viewBoxSize = useMemo(
() => ({
x: screenSize.x / transform.zoom,
y: screenSize.y / transform.zoom,
}),
[screenSize.x, screenSize.y, transform.zoom],
);
const viewBox = useMemo(
() =>
new DOMRect(
transform.pan.x - viewBoxSize.x / 2,
transform.pan.y - viewBoxSize.y / 2,
viewBoxSize.x,
viewBoxSize.y,
),
[transform.pan.x, transform.pan.y, viewBoxSize.x, viewBoxSize.y],
);
const toDiagramSpace = useCallback(
(coord) => ({
x:
typeof coord.x === "number"
? (coord.x / screenSize.x) * viewBox.width + viewBox.left
: undefined,
y:
typeof coord.y === "number"
? (coord.y / screenSize.y) * viewBox.height + viewBox.top
: undefined,
}),
[
screenSize.x,
screenSize.y,
viewBox.height,
viewBox.left,
viewBox.top,
viewBox.width,
],
);
const toScreenSpace = useCallback(
(coord) => ({
x:
typeof coord.x === "number"
? ((coord.x - viewBox.left) / viewBox.width) * screenSize.x
: undefined,
y:
typeof coord.y === "number"
? ((coord.y - viewBox.top) / viewBox.height) * screenSize.y
: undefined,
}),
[
screenSize.x,
screenSize.y,
viewBox.height,
viewBox.left,
viewBox.top,
viewBox.width,
],
);
const [pointerScreenCoords, setPointerScreenCoords] = useState({
x: 0,
y: 0,
});
const pointerDiagramCoords = useMemo(
() => toDiagramSpace(pointerScreenCoords),
[pointerScreenCoords, toDiagramSpace],
);
const [pointerStyle, setPointerStyle] = useState("default");
/**
* @param {PointerEvent} e
*/
function detectPointerMovement(e) {
const targetElm = /** @type {HTMLElement | null} */ (e.currentTarget);
if (!e.isPrimary || !targetElm) return;
const canvasBounds = targetElm.getBoundingClientRect();
setPointerScreenCoords({
x: e.clientX - canvasBounds.left,
y: e.clientY - canvasBounds.top,
});
}
// Important for touch screen devices!
useEventListener("pointerdown", detectPointerMovement, canvasWrapRef);
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
useEventListener("pointermove", detectPointerMovement, canvasWrapRef);
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
const contextValue = {
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
canvas: {
screenSize,
viewBox,
},
coords: {
toDiagramSpace,
toScreenSpace,
},
pointer: {
spaces: {
screen: pointerScreenCoords,
diagram: pointerDiagramCoords,
},
style: pointerStyle,
setStyle: setPointerStyle,
},
};
return (
<CanvasContext.Provider value={contextValue}>
<div {...attrs} ref={canvasWrapRef}>
{children}
</div>
</CanvasContext.Provider>
)
fix: rewrite coordinate management After some initial smaller fixes, it turned out that I had broken the red line used when linking fields. Fixing this was not trivial as I found myself battling a lot of small bugs relating to scale and translation in the existing code. This was made extra difficult as a lot of coordinates were calculated when necessary in Canvas.jsx. This commit attempts to simplify the coordinate management in a few different ways: * There are now two distinct coordinate systems in use, typically referred to as "spaces". Screen space and diagram space. * Diagram space is no longer measured in pixels (though the dimension-less measure used instead still maps to pixels at 100% zoom). * The canvas now exposes helper methods for transforming between spaces. * Zoom and translation is now managed via the svg viewBox property. * This makes moving items in diagram space much easier as the coordinates remain constant regardless of zoom level. * The canvas now wraps the current mouse position in a context object, making mouse movement much easier to work with. * The transform.pan property now refers to the center of the screen. A new feature in this commit is that scroll wheel zoom is now based on the current cursor location, making the diagram more convenient to move around in. I have tried to focus on Canvas.jsx and avoid changes that might be desctructive on existing save files. I also believe more refactors and abstractions could be introduced based on these changes to make the diagram even easier to work with. However, I deem that out of scope for now.
2024-07-01 06:53:53 +08:00
}