Skip to content

Commit

Permalink
feat: Implement simultaneous corner radius moving for bubble shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
miyanokomiya committed Dec 15, 2024
1 parent b5395c4 commit 8bbd1f0
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 21 deletions.
40 changes: 31 additions & 9 deletions src/composables/shapeHandlers/bubbleHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { applyFillStyle } from "../../utils/fillStyle";
import { TAU } from "../../utils/geometry";
import { defineShapeHandler } from "./core";
import { BubbleShape, getBeakControls, getBeakSize } from "../../shapes/polygons/bubble";
import { applyLocalSpace, renderOutlinedCircle, scaleGlobalAlpha } from "../../utils/renderer";
import { applyLocalSpace, renderOutlinedCircle, renderValueLabel, scaleGlobalAlpha } from "../../utils/renderer";
import { getShapeDetransform } from "../../shapes/rectPolygon";
import { getLocalAbsolutePoint } from "../../shapes/simplePolygon";
import { applyStrokeStyle } from "../../utils/strokeStyle";
Expand Down Expand Up @@ -88,7 +88,7 @@ export const newBubbleHandler = defineShapeHandler<BubbleHitResult, Option>((opt
});

if (hitResult?.type === "cornerXC" || hitResult?.type === "cornerYC") {
renderCornerGuidlines(ctx, shape, style, scale);
renderCornerGuidlines(ctx, style, scale, shape);
}
}

Expand Down Expand Up @@ -185,19 +185,41 @@ function renderRootGuid(

export function renderCornerGuidlines(
renderCtx: CanvasRenderingContext2D,
shape: BubbleShape,
style: StyleScheme,
scale: number,
shape: BubbleShape,
showLabel = false,
) {
applyStrokeStyle(renderCtx, {
color: style.selectionSecondaly,
width: 2 * scale,
dash: "short",
});

const shapeRect = { x: shape.p.x, y: shape.p.y, width: shape.width, height: shape.height };
applyLocalSpace(renderCtx, shapeRect, shape.rotation, () => {
const [cornerXC, cornerYC] = getLocalCornerControl(shape, scale);

if (showLabel) {
const margin = 20 * scale;
renderValueLabel(
renderCtx,
`${Math.round(shape.cornerC.x * 200)}%`,
{ x: 0, y: -margin },
-shape.rotation,
scale,
true,
);
renderValueLabel(
renderCtx,
`${Math.round(shape.cornerC.y * 200)}%`,
{ x: -margin, y: 0 },
-shape.rotation,
scale,
true,
);
}

applyStrokeStyle(renderCtx, {
color: style.selectionSecondaly,
width: 2 * scale,
dash: "short",
});

renderCtx.beginPath();
renderCtx.rect(0, 0, cornerXC.x, cornerYC.y);
renderCtx.stroke();
Expand Down
54 changes: 42 additions & 12 deletions src/composables/states/appCanvas/bubble/bubbleSelectedState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { getLocalAbsolutePoint, getLocalRelativeRate } from "../../../../shapes/
import { IVec2, applyAffine, clamp, getDistance, getInner, getPedal, sub } from "okageo";
import { BubbleShape, getBeakControls, getMaxBeakSize } from "../../../../shapes/polygons/bubble";
import { defineSingleSelectedHandlerState } from "../singleSelectedHandlerState";
import { COMMAND_EXAM_SRC } from "../commandExams";
import { snapNumber } from "../../../../utils/geometry";

export const newBubbleSelectedState = defineSingleSelectedHandlerState(
(getters) => {
Expand Down Expand Up @@ -59,46 +61,74 @@ export const newBubbleSelectedState = defineSingleSelectedHandlerState(
renderFn: renderBeakControls,
snapType: "disabled",
});
case "cornerXC":
case "cornerXC": {
let showLabel = !event.data.options.ctrl;
return () =>
movingShapeControlState<BubbleShape>({
targetId: targetShape.id,
patchFn: (s, p) => {
const rate = getLocalRelativeRate(s, applyAffine(getShapeDetransform(s), p));
return { cornerC: { x: clamp(0, 0.5, rate.x), y: s.cornerC.y } };
snapType: "custom",
extraCommands: [COMMAND_EXAM_SRC.RESIZE_PROPORTIONALLY],
patchFn: (s, p, movement) => {
let nextRate = clamp(
0,
0.5,
getLocalRelativeRate(s, applyAffine(getShapeDetransform(s), p)).x,
);
if (movement.ctrl) {
showLabel = false;
} else {
nextRate = snapNumber(nextRate, 0.01);
showLabel = true;
}
return { cornerC: { x: nextRate, y: movement.shift ? nextRate : s.cornerC.y } };
},
getControlFn: (s, scale) =>
applyAffine(getShapeTransform(s), getLocalCornerControl(s, scale)[0]),
renderFn: (stateCtx, renderCtx, latestShape) => {
renderCornerGuidlines(
renderCtx,
latestShape,
stateCtx.getStyleScheme(),
stateCtx.getScale(),
latestShape,
showLabel,
);
},
snapType: "disabled",
});
case "cornerYC":
}
case "cornerYC": {
let showLabel = !event.data.options.ctrl;
return () =>
movingShapeControlState<BubbleShape>({
targetId: targetShape.id,
patchFn: (s, p) => {
const rate = getLocalRelativeRate(s, applyAffine(getShapeDetransform(s), p));
return { cornerC: { x: s.cornerC.x, y: clamp(0, 0.5, rate.y) } };
snapType: "custom",
extraCommands: [COMMAND_EXAM_SRC.RESIZE_PROPORTIONALLY],
patchFn: (s, p, movement) => {
let nextRate = clamp(
0,
0.5,
getLocalRelativeRate(s, applyAffine(getShapeDetransform(s), p)).y,
);
if (movement.ctrl) {
showLabel = false;
} else {
nextRate = snapNumber(nextRate, 0.01);
showLabel = true;
}
return { cornerC: { x: movement.shift ? nextRate : s.cornerC.x, y: nextRate } };
},
getControlFn: (s, scale) =>
applyAffine(getShapeTransform(s), getLocalCornerControl(s, scale)[1]),
renderFn: (stateCtx, renderCtx, latestShape) => {
renderCornerGuidlines(
renderCtx,
latestShape,
stateCtx.getStyleScheme(),
stateCtx.getScale(),
latestShape,
showLabel,
);
},
snapType: "disabled",
});
}
default:
return;
}
Expand Down

0 comments on commit 8bbd1f0

Please sign in to comment.