mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): align all tools to 1px grid
- Offset brush tool by 0.5px when width is odd, ensuring each stroke edge is exactly on a pixel boundary - Round the rect tool also
This commit is contained in:
parent
17f88cd5ad
commit
8e1a70b008
@ -5,6 +5,7 @@ import {
|
|||||||
BRUSH_BORDER_OUTER_COLOR,
|
BRUSH_BORDER_OUTER_COLOR,
|
||||||
BRUSH_ERASER_BORDER_WIDTH,
|
BRUSH_ERASER_BORDER_WIDTH,
|
||||||
} from 'features/controlLayers/konva/constants';
|
} from 'features/controlLayers/konva/constants';
|
||||||
|
import { alignCoordForTool } from 'features/controlLayers/konva/util';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
|
||||||
export class CanvasTool {
|
export class CanvasTool {
|
||||||
@ -183,12 +184,14 @@ export class CanvasTool {
|
|||||||
|
|
||||||
// No need to render the brush preview if the cursor position or color is missing
|
// No need to render the brush preview if the cursor position or color is missing
|
||||||
if (cursorPos && tool === 'brush') {
|
if (cursorPos && tool === 'brush') {
|
||||||
|
const alignedCursorPos = alignCoordForTool(cursorPos, toolState.brush.width);
|
||||||
const scale = stage.scaleX();
|
const scale = stage.scaleX();
|
||||||
// Update the fill circle
|
// Update the fill circle
|
||||||
const radius = toolState.brush.width / 2;
|
const radius = toolState.brush.width / 2;
|
||||||
|
|
||||||
this.konva.brush.fillCircle.setAttrs({
|
this.konva.brush.fillCircle.setAttrs({
|
||||||
x: cursorPos.x,
|
x: alignedCursorPos.x,
|
||||||
y: cursorPos.y,
|
y: alignedCursorPos.y,
|
||||||
radius,
|
radius,
|
||||||
fill: isDrawing ? '' : rgbaColorToString(currentFill),
|
fill: isDrawing ? '' : rgbaColorToString(currentFill),
|
||||||
});
|
});
|
||||||
@ -209,12 +212,14 @@ export class CanvasTool {
|
|||||||
this.konva.eraser.group.visible(false);
|
this.konva.eraser.group.visible(false);
|
||||||
// this.rect.group.visible(false);
|
// this.rect.group.visible(false);
|
||||||
} else if (cursorPos && tool === 'eraser') {
|
} else if (cursorPos && tool === 'eraser') {
|
||||||
|
const alignedCursorPos = alignCoordForTool(cursorPos, toolState.eraser.width);
|
||||||
|
|
||||||
const scale = stage.scaleX();
|
const scale = stage.scaleX();
|
||||||
// Update the fill circle
|
// Update the fill circle
|
||||||
const radius = toolState.eraser.width / 2;
|
const radius = toolState.eraser.width / 2;
|
||||||
this.konva.eraser.fillCircle.setAttrs({
|
this.konva.eraser.fillCircle.setAttrs({
|
||||||
x: cursorPos.x,
|
x: alignedCursorPos.x,
|
||||||
y: cursorPos.y,
|
y: alignedCursorPos.y,
|
||||||
radius,
|
radius,
|
||||||
fill: 'white',
|
fill: 'white',
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import { getObjectId, getScaledFlooredCursorPosition } from 'features/controlLayers/konva/util';
|
import {
|
||||||
|
alignCoordForTool,
|
||||||
|
getObjectId,
|
||||||
|
getScaledCursorPosition,
|
||||||
|
offsetCoord,
|
||||||
|
} from 'features/controlLayers/konva/util';
|
||||||
import type {
|
import type {
|
||||||
CanvasV2State,
|
CanvasV2State,
|
||||||
Coordinate,
|
Coordinate,
|
||||||
@ -22,7 +27,7 @@ import { BRUSH_SPACING_TARGET_SCALE, CANVAS_SCALE_BY, MAX_CANVAS_SCALE, MIN_CANV
|
|||||||
* @param setLastCursorPos The callback to store the cursor pos
|
* @param setLastCursorPos The callback to store the cursor pos
|
||||||
*/
|
*/
|
||||||
const updateLastCursorPos = (stage: Konva.Stage, setLastCursorPos: CanvasManager['stateApi']['setLastCursorPos']) => {
|
const updateLastCursorPos = (stage: Konva.Stage, setLastCursorPos: CanvasManager['stateApi']['setLastCursorPos']) => {
|
||||||
const pos = getScaledFlooredCursorPosition(stage);
|
const pos = getScaledCursorPosition(stage);
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -177,14 +182,17 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
getIsPrimaryMouseDown(e)
|
getIsPrimaryMouseDown(e)
|
||||||
) {
|
) {
|
||||||
setLastMouseDownPos(pos);
|
setLastMouseDownPos(pos);
|
||||||
|
const normalizedPoint = offsetCoord(pos, selectedEntity.position);
|
||||||
|
|
||||||
if (toolState.selected === 'brush') {
|
if (toolState.selected === 'brush') {
|
||||||
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity, toolState.selected);
|
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity, toolState.selected);
|
||||||
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||||
if (e.evt.shiftKey && lastLinePoint) {
|
if (e.evt.shiftKey && lastLinePoint) {
|
||||||
// Create a straight line from the last line point
|
// Create a straight line from the last line point
|
||||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
await selectedEntityAdapter.finalizeDrawingBuffer();
|
await selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getObjectId('brush_line', true),
|
id: getObjectId('brush_line', true),
|
||||||
type: 'brush_line',
|
type: 'brush_line',
|
||||||
@ -192,8 +200,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
// The last point of the last line is already normalized to the entity's coordinates
|
// The last point of the last line is already normalized to the entity's coordinates
|
||||||
lastLinePoint.x,
|
lastLinePoint.x,
|
||||||
lastLinePoint.y,
|
lastLinePoint.y,
|
||||||
pos.x - selectedEntity.position.x,
|
alignedPoint.x,
|
||||||
pos.y - selectedEntity.position.y,
|
alignedPoint.y,
|
||||||
],
|
],
|
||||||
strokeWidth: toolState.brush.width,
|
strokeWidth: toolState.brush.width,
|
||||||
color: getCurrentFill(),
|
color: getCurrentFill(),
|
||||||
@ -206,17 +214,18 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getObjectId('brush_line', true),
|
id: getObjectId('brush_line', true),
|
||||||
type: 'brush_line',
|
type: 'brush_line',
|
||||||
points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y],
|
points: [alignedPoint.x, alignedPoint.y],
|
||||||
strokeWidth: toolState.brush.width,
|
strokeWidth: toolState.brush.width,
|
||||||
color: getCurrentFill(),
|
color: getCurrentFill(),
|
||||||
clip: getClip(selectedEntity),
|
clip: getClip(selectedEntity),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(alignedPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'eraser') {
|
if (toolState.selected === 'eraser') {
|
||||||
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity, toolState.selected);
|
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity, toolState.selected);
|
||||||
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
||||||
if (e.evt.shiftKey && lastLinePoint) {
|
if (e.evt.shiftKey && lastLinePoint) {
|
||||||
// Create a straight line from the last line point
|
// Create a straight line from the last line point
|
||||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
@ -229,8 +238,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
// The last point of the last line is already normalized to the entity's coordinates
|
// The last point of the last line is already normalized to the entity's coordinates
|
||||||
lastLinePoint.x,
|
lastLinePoint.x,
|
||||||
lastLinePoint.y,
|
lastLinePoint.y,
|
||||||
pos.x - selectedEntity.position.x,
|
alignedPoint.x,
|
||||||
pos.y - selectedEntity.position.y,
|
alignedPoint.y,
|
||||||
],
|
],
|
||||||
strokeWidth: toolState.eraser.width,
|
strokeWidth: toolState.eraser.width,
|
||||||
clip: getClip(selectedEntity),
|
clip: getClip(selectedEntity),
|
||||||
@ -242,12 +251,12 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getObjectId('eraser_line', true),
|
id: getObjectId('eraser_line', true),
|
||||||
type: 'eraser_line',
|
type: 'eraser_line',
|
||||||
points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y],
|
points: [alignedPoint.x, alignedPoint.y],
|
||||||
strokeWidth: toolState.eraser.width,
|
strokeWidth: toolState.eraser.width,
|
||||||
clip: getClip(selectedEntity),
|
clip: getClip(selectedEntity),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(alignedPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'rect') {
|
if (toolState.selected === 'rect') {
|
||||||
@ -257,8 +266,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getObjectId('rect_shape', true),
|
id: getObjectId('rect_shape', true),
|
||||||
type: 'rect_shape',
|
type: 'rect_shape',
|
||||||
x: pos.x - selectedEntity.position.x,
|
x: Math.round(normalizedPoint.x),
|
||||||
y: pos.y - selectedEntity.position.y,
|
y: Math.round(normalizedPoint.y),
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
color: getCurrentFill(),
|
color: getCurrentFill(),
|
||||||
@ -340,12 +349,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
if (drawingBuffer?.type === 'brush_line') {
|
if (drawingBuffer?.type === 'brush_line') {
|
||||||
const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points));
|
const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points));
|
||||||
if (nextPoint) {
|
if (nextPoint) {
|
||||||
drawingBuffer.points.push(
|
const normalizedPoint = offsetCoord(nextPoint, selectedEntity.position);
|
||||||
nextPoint.x - selectedEntity.position.x,
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||||
nextPoint.y - selectedEntity.position.y
|
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
|
||||||
);
|
|
||||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
setLastAddedPoint(nextPoint);
|
setLastAddedPoint(alignedPoint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||||
@ -354,15 +362,17 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
await selectedEntityAdapter.finalizeDrawingBuffer();
|
await selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
}
|
}
|
||||||
|
const normalizedPoint = offsetCoord(pos, selectedEntity.position);
|
||||||
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getObjectId('brush_line', true),
|
id: getObjectId('brush_line', true),
|
||||||
type: 'brush_line',
|
type: 'brush_line',
|
||||||
points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y],
|
points: [alignedPoint.x, alignedPoint.y],
|
||||||
strokeWidth: toolState.brush.width,
|
strokeWidth: toolState.brush.width,
|
||||||
color: getCurrentFill(),
|
color: getCurrentFill(),
|
||||||
clip: getClip(selectedEntity),
|
clip: getClip(selectedEntity),
|
||||||
});
|
});
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(alignedPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,12 +382,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
if (drawingBuffer.type === 'eraser_line') {
|
if (drawingBuffer.type === 'eraser_line') {
|
||||||
const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points));
|
const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points));
|
||||||
if (nextPoint) {
|
if (nextPoint) {
|
||||||
drawingBuffer.points.push(
|
const normalizedPoint = offsetCoord(nextPoint, selectedEntity.position);
|
||||||
nextPoint.x - selectedEntity.position.x,
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
||||||
nextPoint.y - selectedEntity.position.y
|
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
|
||||||
);
|
|
||||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
setLastAddedPoint(nextPoint);
|
setLastAddedPoint(alignedPoint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||||
@ -386,14 +395,16 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
await selectedEntityAdapter.finalizeDrawingBuffer();
|
await selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
}
|
}
|
||||||
|
const normalizedPoint = offsetCoord(pos, selectedEntity.position);
|
||||||
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getObjectId('eraser_line', true),
|
id: getObjectId('eraser_line', true),
|
||||||
type: 'eraser_line',
|
type: 'eraser_line',
|
||||||
points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y],
|
points: [alignedPoint.x, alignedPoint.y],
|
||||||
strokeWidth: toolState.eraser.width,
|
strokeWidth: toolState.eraser.width,
|
||||||
clip: getClip(selectedEntity),
|
clip: getClip(selectedEntity),
|
||||||
});
|
});
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(alignedPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,8 +412,9 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||||
if (drawingBuffer) {
|
if (drawingBuffer) {
|
||||||
if (drawingBuffer.type === 'rect_shape') {
|
if (drawingBuffer.type === 'rect_shape') {
|
||||||
drawingBuffer.width = pos.x - selectedEntity.position.x - drawingBuffer.x;
|
const normalizedPoint = offsetCoord(pos, selectedEntity.position);
|
||||||
drawingBuffer.height = pos.y - selectedEntity.position.y - drawingBuffer.y;
|
drawingBuffer.width = Math.round(normalizedPoint.x - drawingBuffer.x);
|
||||||
|
drawingBuffer.height = Math.round(normalizedPoint.y - drawingBuffer.y);
|
||||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
} else {
|
} else {
|
||||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||||
@ -432,17 +444,20 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
getIsPrimaryMouseDown(e)
|
getIsPrimaryMouseDown(e)
|
||||||
) {
|
) {
|
||||||
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||||
|
const normalizedPoint = offsetCoord(pos, selectedEntity.position);
|
||||||
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') {
|
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') {
|
||||||
drawingBuffer.points.push(pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y);
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||||
|
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
|
||||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
await selectedEntityAdapter.finalizeDrawingBuffer();
|
await selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
} else if (toolState.selected === 'eraser' && drawingBuffer?.type === 'eraser_line') {
|
} else if (toolState.selected === 'eraser' && drawingBuffer?.type === 'eraser_line') {
|
||||||
drawingBuffer.points.push(pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y);
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
||||||
|
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
|
||||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
await selectedEntityAdapter.finalizeDrawingBuffer();
|
await selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
} else if (toolState.selected === 'rect' && drawingBuffer?.type === 'rect_shape') {
|
} else if (toolState.selected === 'rect' && drawingBuffer?.type === 'rect_shape') {
|
||||||
drawingBuffer.width = pos.x - selectedEntity.position.x - drawingBuffer.x;
|
drawingBuffer.width = Math.round(normalizedPoint.x - drawingBuffer.x);
|
||||||
drawingBuffer.height = pos.y - selectedEntity.position.y - drawingBuffer.y;
|
drawingBuffer.height = Math.round(normalizedPoint.y - drawingBuffer.y);
|
||||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
await selectedEntityAdapter.finalizeDrawingBuffer();
|
await selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { getImageDataTransparency } from 'common/util/arrayBuffer';
|
import { getImageDataTransparency } from 'common/util/arrayBuffer';
|
||||||
import { CanvasLayer } from 'features/controlLayers/konva/CanvasLayer';
|
import { CanvasLayer } from 'features/controlLayers/konva/CanvasLayer';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import type { GenerationMode, Rect, RenderableObject, RgbaColor } from 'features/controlLayers/store/types';
|
import type { Coordinate, GenerationMode, Rect, RenderableObject, RgbaColor } from 'features/controlLayers/store/types';
|
||||||
import { isValidLayer } from 'features/nodes/util/graph/generation/addLayers';
|
import { isValidLayer } from 'features/nodes/util/graph/generation/addLayers';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||||
@ -41,6 +41,39 @@ export const getScaledCursorPosition = (stage: Konva.Stage): Vector2d | null =>
|
|||||||
return stageTransform.invert().point(pointerPosition);
|
return stageTransform.invert().point(pointerPosition);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aligns a coordinate to the nearest integer. When the tool width is odd, an offset is added to align the edges
|
||||||
|
* of the tool to the grid. Without this alignment, the edges of the tool will be 0.5px off.
|
||||||
|
* @param coord The coordinate to align
|
||||||
|
* @param toolWidth The width of the tool
|
||||||
|
* @returns The aligned coordinate
|
||||||
|
*/
|
||||||
|
export const alignCoordForTool = (coord: Coordinate, toolWidth: number): Coordinate => {
|
||||||
|
const roundedX = Math.round(coord.x);
|
||||||
|
const roundedY = Math.round(coord.y);
|
||||||
|
const deltaX = coord.x - roundedX;
|
||||||
|
const deltaY = coord.y - roundedY;
|
||||||
|
const offset = (toolWidth / 2) % 1;
|
||||||
|
const point = {
|
||||||
|
x: roundedX + Math.sign(deltaX) * offset,
|
||||||
|
y: roundedY + Math.sign(deltaY) * offset,
|
||||||
|
};
|
||||||
|
return point;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offsets a point by the given offset. The offset is subtracted from the point.
|
||||||
|
* @param coord The coordinate to offset
|
||||||
|
* @param offset The offset to apply
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const offsetCoord = (coord: Coordinate, offset: Coordinate): Coordinate => {
|
||||||
|
return {
|
||||||
|
x: coord.x - offset.x,
|
||||||
|
y: coord.y - offset.y,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snaps a position to the edge of the stage if within a threshold of the edge
|
* Snaps a position to the edge of the stage if within a threshold of the edge
|
||||||
* @param pos The position to snap
|
* @param pos The position to snap
|
||||||
|
Loading…
x
Reference in New Issue
Block a user