Fix paint bucket coordinates inside objects

This commit is contained in:
Skyler Lehmkuhl 2025-01-10 18:26:13 -05:00
parent 9205205ecd
commit 112de7ed1a
1 changed files with 187 additions and 162 deletions

View File

@ -40,6 +40,7 @@ import {
rotateAroundPointIncremental, rotateAroundPointIncremental,
rgbToHsv, rgbToHsv,
multiplyMatrices, multiplyMatrices,
growBoundingBox,
} from "./utils.js"; } from "./utils.js";
import { import {
backgroundColor, backgroundColor,
@ -1976,13 +1977,6 @@ function deriveControlPoints(S, A, E, e1, e2, t) {
return { v1, v2, C1, C2 }; return { v1, v2, C1, C2 };
} }
function growBoundingBox(bboxa, bboxb) {
bboxa.x.min = Math.min(bboxa.x.min, bboxb.x.min);
bboxa.y.min = Math.min(bboxa.y.min, bboxb.y.min);
bboxa.x.max = Math.max(bboxa.x.max, bboxb.x.max);
bboxa.y.max = Math.max(bboxa.y.max, bboxb.y.max);
}
function regionToBbox(region) { function regionToBbox(region) {
return { return {
x: { x: {
@ -2633,176 +2627,182 @@ class Layer extends Widget {
} }
mousedown(x, y) { mousedown(x, y) {
const mouse = {x: x, y: y} const mouse = {x: x, y: y}
switch(mode) { if (this==context.activeLayer) {
case "rectangle": switch(mode) {
case "ellipse": case "rectangle":
case "draw": case "ellipse":
this.clicked = true case "draw":
this.activeShape = new Shape(x, y, context, uuidv4()) this.clicked = true
this.lastMouse = mouse; this.activeShape = new Shape(x, y, context, uuidv4())
break; this.lastMouse = mouse;
case "select":
case "transform":
break;
case "paint_bucket":
debugCurves = [];
debugPoints = [];
let epsilon = context.fillGaps;
let regionPoints;
// First, see if there's an existing shape to change the color of
// TODO: get this from self
let pointShape = getShapeAtPoint(
mouse,
context.activeObject.currentFrame.shapes,
);
if (pointShape) {
actions.colorShape.create(pointShape, context.fillStyle);
break; break;
} case "select":
case "transform":
break;
case "paint_bucket":
debugCurves = [];
debugPoints = [];
let epsilon = context.fillGaps;
let regionPoints;
// We didn't find an existing region to paintbucket, see if we can make one // First, see if there's an existing shape to change the color of
try { // TODO: get this from self
regionPoints = floodFillRegion( let pointShape = getShapeAtPoint(
mouse, mouse,
epsilon, context.activeObject.currentFrame.shapes,
config.fileWidth,
config.fileHeight,
context,
debugPoints,
debugPaintbucket,
); );
} catch (e) {
updateUI(); if (pointShape) {
throw e; actions.colorShape.create(pointShape, context.fillStyle);
} break;
if (regionPoints.length > 0 && regionPoints.length < 10) { }
// probably a very small area, rerun with minimum epsilon
regionPoints = floodFillRegion( // We didn't find an existing region to paintbucket, see if we can make one
mouse, try {
1, regionPoints = floodFillRegion(
config.fileWidth, mouse,
config.fileHeight, epsilon,
context, config.fileWidth,
debugPoints, config.fileHeight,
); context,
} debugPoints,
let points = []; debugPaintbucket,
for (let point of regionPoints) { );
points.push([point.x, point.y]); } catch (e) {
} updateUI();
let cxt = { throw e;
...context, }
fillShape: true, if (regionPoints.length > 0 && regionPoints.length < 10) {
strokeShape: false, // probably a very small area, rerun with minimum epsilon
sendToBack: true, regionPoints = floodFillRegion(
}; mouse,
let shape = new Shape(regionPoints[0].x, regionPoints[0].y, cxt); 1,
shape.fromPoints(points, 1); config.fileWidth,
actions.addShape.create(context.activeObject, shape, cxt); config.fileHeight,
break; context,
debugPoints,
);
}
let points = [];
for (let point of regionPoints) {
points.push([point.x, point.y]);
}
let cxt = {
...context,
fillShape: true,
strokeShape: false,
sendToBack: true,
};
let shape = new Shape(regionPoints[0].x, regionPoints[0].y, cxt);
shape.fromPoints(points, 1);
actions.addShape.create(context.activeObject, shape, cxt);
break;
}
} }
} }
mousemove(x, y) { mousemove(x, y) {
const mouse = {x: x, y: y} const mouse = {x: x, y: y}
switch (mode) { if (this==context.activeLayer) {
case "draw": switch (mode) {
if (this.activeShape) { case "draw":
if (vectorDist(mouse, context.lastMouse) > minSegmentSize) { if (this.activeShape) {
this.activeShape.addLine(x, y); if (vectorDist(mouse, context.lastMouse) > minSegmentSize) {
this.lastMouse = mouse; this.activeShape.addLine(x, y);
this.lastMouse = mouse;
}
} }
} break;
break; case "rectangle":
case "rectangle": if (this.activeShape) {
if (this.activeShape) { this.activeShape.clear();
this.activeShape.clear(); this.activeShape.addLine(x, this.activeShape.starty);
this.activeShape.addLine(x, this.activeShape.starty); this.activeShape.addLine(x, y);
this.activeShape.addLine(x, y); this.activeShape.addLine(this.activeShape.startx, y);
this.activeShape.addLine(this.activeShape.startx, y); this.activeShape.addLine(
this.activeShape.addLine(
this.activeShape.startx,
this.activeShape.starty,
);
this.activeShape.update();
}
break;
case "ellipse":
if (this.activeShape) {
let midX = (mouse.x + this.activeShape.startx) / 2;
let midY = (mouse.y + this.activeShape.starty) / 2;
let xDiff = (mouse.x - this.activeShape.startx) / 2;
let yDiff = (mouse.y - this.activeShape.starty) / 2;
let ellipseConst = 0.552284749831; // (4/3)*tan(pi/(2n)) where n=4
this.activeShape.clear();
this.activeShape.addCurve(
new Bezier(
midX,
this.activeShape.starty,
midX + ellipseConst * xDiff,
this.activeShape.starty,
mouse.x,
midY - ellipseConst * yDiff,
mouse.x,
midY,
),
);
this.activeShape.addCurve(
new Bezier(
mouse.x,
midY,
mouse.x,
midY + ellipseConst * yDiff,
midX + ellipseConst * xDiff,
mouse.y,
midX,
mouse.y,
),
);
this.activeShape.addCurve(
new Bezier(
midX,
mouse.y,
midX - ellipseConst * xDiff,
mouse.y,
this.activeShape.startx, this.activeShape.startx,
midY + ellipseConst * yDiff,
this.activeShape.startx,
midY,
),
);
this.activeShape.addCurve(
new Bezier(
this.activeShape.startx,
midY,
this.activeShape.startx,
midY - ellipseConst * yDiff,
midX - ellipseConst * xDiff,
this.activeShape.starty, this.activeShape.starty,
midX, );
this.activeShape.starty, this.activeShape.update();
), }
); break;
} case "ellipse":
break; if (this.activeShape) {
let midX = (mouse.x + this.activeShape.startx) / 2;
let midY = (mouse.y + this.activeShape.starty) / 2;
let xDiff = (mouse.x - this.activeShape.startx) / 2;
let yDiff = (mouse.y - this.activeShape.starty) / 2;
let ellipseConst = 0.552284749831; // (4/3)*tan(pi/(2n)) where n=4
this.activeShape.clear();
this.activeShape.addCurve(
new Bezier(
midX,
this.activeShape.starty,
midX + ellipseConst * xDiff,
this.activeShape.starty,
mouse.x,
midY - ellipseConst * yDiff,
mouse.x,
midY,
),
);
this.activeShape.addCurve(
new Bezier(
mouse.x,
midY,
mouse.x,
midY + ellipseConst * yDiff,
midX + ellipseConst * xDiff,
mouse.y,
midX,
mouse.y,
),
);
this.activeShape.addCurve(
new Bezier(
midX,
mouse.y,
midX - ellipseConst * xDiff,
mouse.y,
this.activeShape.startx,
midY + ellipseConst * yDiff,
this.activeShape.startx,
midY,
),
);
this.activeShape.addCurve(
new Bezier(
this.activeShape.startx,
midY,
this.activeShape.startx,
midY - ellipseConst * yDiff,
midX - ellipseConst * xDiff,
this.activeShape.starty,
midX,
this.activeShape.starty,
),
);
}
break;
}
} }
} }
mouseup(x, y) { mouseup(x, y) {
this.clicked = false this.clicked = false
switch (mode) { if (this==context.activeLayer) {
case "draw": switch (mode) {
if (this.activeShape) { case "draw":
this.activeShape.addLine(x, y); if (this.activeShape) {
this.activeShape.simplify(context.simplifyMode); this.activeShape.addLine(x, y);
} this.activeShape.simplify(context.simplifyMode);
case "rectangle": }
case "ellipse": case "rectangle":
if (this.activeShape) { case "ellipse":
actions.addShape.create(context.activeObject, this.activeShape); if (this.activeShape) {
this.activeShape = undefined; actions.addShape.create(context.activeObject, this.activeShape);
} this.activeShape = undefined;
break; }
break;
}
} }
} }
} }
@ -3579,6 +3579,10 @@ class GraphicsObject extends Widget {
// this.children = [] // this.children = []
this.shapes = []; this.shapes = [];
this._globalEvents.add("mousedown")
this._globalEvents.add("mousemove")
this._globalEvents.add("mouseup")
} }
static fromJSON(json) { static fromJSON(json) {
const graphicsObject = new GraphicsObject(json.idx); const graphicsObject = new GraphicsObject(json.idx);
@ -4059,6 +4063,11 @@ Object.defineProperty(context, "activeObject", {
return this.objectStack.at(-1); return this.objectStack.at(-1);
}, },
}); });
Object.defineProperty(context, "activeLayer", {
get: function () {
return this.objectStack.at(-1).activeLayer
}
})
context.objectStack = [root]; context.objectStack = [root];
async function greet() { async function greet() {
@ -6875,6 +6884,22 @@ function renderLayers() {
2 * Math.PI, 2 * Math.PI,
); );
ctx.fill(); ctx.fill();
if (frameInfo.valueAtN.keyTypes.has("motion")) {
ctx.strokeStyle = "#7a00b3";
ctx.lineWidth = 2;
ctx.beginPath()
ctx.moveTo(j*frameWidth, layerHeight*0.25)
ctx.lineTo((j+1)*frameWidth, layerHeight*0.25)
ctx.stroke()
}
if (frameInfo.valueAtN.keyTypes.has("shape")) {
ctx.strokeStyle = "#9bff9b";
ctx.lineWidth = 2;
ctx.beginPath()
ctx.moveTo(j*frameWidth, layerHeight*0.35)
ctx.lineTo((j+1)*frameWidth, layerHeight*0.35)
ctx.stroke()
}
} else if (frameInfo.prev && frameInfo.next) { } else if (frameInfo.prev && frameInfo.next) {
ctx.fillStyle = foregroundColor; ctx.fillStyle = foregroundColor;
drawBorderedRect( drawBorderedRect(