Skip to content

Commit

Permalink
feat: replace x/y/rotation with transform
Browse files Browse the repository at this point in the history
  • Loading branch information
F-star committed Apr 6, 2024
1 parent 9a084ef commit 950ea4f
Show file tree
Hide file tree
Showing 42 changed files with 1,382 additions and 1,046 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"IRGBA",
"isequal",
"outfile",
"pixi",
"segs",
"suika",
"xigua"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"typescript": "^5.2.2"
},
"dependencies": {
"pixi.js": "^8.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
20 changes: 10 additions & 10 deletions packages/core/src/commands/align.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class AlignCmd implements ICommand {
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
this.dx[i] = mixedBBox.minX - bBoxes[i].minX;
el.updateAttrs({ x: el.attrs.x + this.dx[i] });
el.updateAttrs({ x: el.getX() + this.dx[i] });
}
break;
}
Expand All @@ -54,23 +54,23 @@ export class AlignCmd implements ICommand {
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
this.dx[i] = centerX - (bBoxes[i].minX / 2 + bBoxes[i].maxX / 2);
el.updateAttrs({ x: el.attrs.x + this.dx[i] });
el.updateAttrs({ x: el.getX() + this.dx[i] });
}
break;
}
case AlignType.Right: {
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
this.dx[i] = mixedBBox.maxX - bBoxes[i].maxX;
el.updateAttrs({ x: el.attrs.x + this.dx[i] });
el.updateAttrs({ x: el.getX() + this.dx[i] });
}
break;
}
case AlignType.Top: {
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
this.dy[i] = mixedBBox.minY - bBoxes[i].minY;
el.updateAttrs({ y: el.attrs.y + this.dy[i] });
el.updateAttrs({ y: el.getY() + this.dy[i] });
}
break;
}
Expand All @@ -79,15 +79,15 @@ export class AlignCmd implements ICommand {
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
this.dy[i] = centerY - (bBoxes[i].minY / 2 + bBoxes[i].maxY / 2);
el.updateAttrs({ y: el.attrs.y + this.dy[i] });
el.updateAttrs({ y: el.getY() + this.dy[i] });
}
break;
}
case AlignType.Bottom: {
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
this.dy[i] = mixedBBox.maxY - bBoxes[i].maxY;
el.updateAttrs({ y: el.attrs.y + this.dy[i] });
el.updateAttrs({ y: el.getY() + this.dy[i] });
}
break;
}
Expand All @@ -100,17 +100,17 @@ export class AlignCmd implements ICommand {
for (let i = 0; i < this.elements.length; i++) {
const el = this.elements[i];
el.updateAttrs({
x: el.attrs.x + (this.dx[i] ?? 0),
y: el.attrs.y + (this.dy[i] ?? 0),
x: el.getX() + (this.dx[i] ?? 0),
y: el.getY() + (this.dy[i] ?? 0),
});
}
}
undo() {
for (let i = 0; i < this.elements.length; i++) {
const el = this.elements[i];
el.updateAttrs({
x: el.attrs.x - (this.dx[i] ?? 0),
y: el.attrs.y - (this.dy[i] ?? 0),
x: el.getX() - (this.dx[i] ?? 0),
y: el.getY() - (this.dy[i] ?? 0),
});
}
}
Expand Down
14 changes: 3 additions & 11 deletions packages/core/src/commands/move_graphs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Graph } from '../graphs';
import { Graph } from '../graphs';
import { type ICommand } from './type';

export class MoveGraphsCommand implements ICommand {
Expand All @@ -9,17 +9,9 @@ export class MoveGraphsCommand implements ICommand {
private dy: number,
) {}
redo() {
const { dx, dy } = this;
for (let i = 0, len = this.graphs.length; i < len; i++) {
const element = this.graphs[i];
element.updateAttrs({ x: element.attrs.x + dx, y: element.attrs.y + dy });
}
Graph.dMove(this.graphs, this.dx, this.dy);
}
undo() {
const { dx, dy } = this;
for (let i = 0, len = this.graphs.length; i < len; i++) {
const element = this.graphs[i];
element.updateAttrs({ x: element.attrs.x - dx, y: element.attrs.y - dy });
}
Graph.dMove(this.graphs, -this.dx, -this.dy);
}
}
2 changes: 2 additions & 0 deletions packages/core/src/commands/set_elements_attrs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { cloneDeep } from '@suika/common';
import { type IMatrixArr } from '@suika/geo';

import { type Graph, type IPathItem } from '../graphs';
import { type IPaint } from '../paint';
Expand All @@ -17,6 +18,7 @@ export type ISetElementsAttrsType = Partial<{
objectName: string;
visible: boolean;
lock: boolean;
transform: IMatrixArr;
pathData: IPathItem[];
}>;

Expand Down
15 changes: 12 additions & 3 deletions packages/core/src/control_handle_manager/control_handle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type IRectWithRotation } from '@suika/geo';
import { type IMatrixArr, type ITransformRect } from '@suika/geo';

import { type ICursor } from '../cursor_manager';
import { type Graph } from '../graphs';
Expand All @@ -7,21 +7,24 @@ type HitTest = (
x: number,
y: number,
tol: number,
rect: IRectWithRotation | null,
rect: ITransformRect | null,
) => boolean;

type GetCursorFn = (
type: string,
selectedBox: IRectWithRotation | null,
selectedBox: ITransformRect | null,
) => ICursor;

export class ControlHandle {
cx: number;
cy: number;
rotation?: number;
transform?: IMatrixArr;
type: string;
graph: Graph;
padding: number;
/** rotation will follow rotated bbox */
isTransformHandle: boolean;
hitTest?: HitTest;
getCursor: GetCursorFn;

Expand All @@ -30,22 +33,28 @@ export class ControlHandle {
cy?: number;
type: string;
rotation?: number;
transform?: IMatrixArr;
padding?: number;
graph: Graph;
hitTest?: HitTest;
getCursor: GetCursorFn;
isTransformHandle?: boolean;
}) {
this.cx = attrs.cx ?? 0;
this.cy = attrs.cy ?? 0;
if (attrs.rotation !== undefined) {
this.rotation = attrs.rotation;
}
if (attrs.transform) {
this.transform = attrs.transform;
}
this.type = attrs.type;
this.padding = attrs.padding ?? 0;
this.graph = attrs.graph;
this.getCursor = attrs.getCursor;
if (attrs.hitTest) {
this.hitTest = attrs.hitTest;
}
this.isTransformHandle = attrs.isTransformHandle ?? false;
}
}
69 changes: 50 additions & 19 deletions packages/core/src/control_handle_manager/control_handle_manager.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {
getTransformAngle,
type IPoint,
type IRect,
type IRectWithRotation,
type ITransformRect,
offsetRect,
rectToMidPoints,
rectToPoints,
rectToVertices,
} from '@suika/geo';
import { Matrix } from 'pixi.js';

import { type ICursor } from '../cursor_manager';
import { type Editor } from '../editor';
Expand Down Expand Up @@ -85,7 +87,7 @@ export class ControlHandleManager {
this.editor.commandManager.off('change', this.onHoverItemChange);
}

private updateTransformHandles(rect: IRectWithRotation | null) {
private updateTransformHandles(rect: ITransformRect | null) {
if (!rect || this.editor.pathEditor.isActive()) {
this.transformHandlesVisible = false;
return;
Expand All @@ -98,11 +100,20 @@ export class ControlHandleManager {
const neswHandleWidth = this.editor.setting.get('neswHandleWidth');

// calculate handle position
const _rect = {
x: 0,
y: 0,
width: rect.width,
height: rect.height,
};
const handlePoints = (() => {
const cornerPoints = rectToPoints(rect);
const cornerPoints = rectToVertices(_rect, rect.transform);

const offset = handleSize / 2 / zoom;
const cornerRotation = rectToPoints(offsetRect(rect, offset));
const cornerRotates = rectToVertices(
offsetRect(_rect, offset),
rect.transform,
);

// when rect size < 40(viewport), nwse handle should outside the selectedBox
const MIN_SIZE = 40;
Expand All @@ -113,16 +124,29 @@ export class ControlHandleManager {
if (rect.height * zoom < MIN_SIZE) {
offsets[0] = offsets[2] = neswHandleWidth / 2 / zoom;
}
const neswRect = offsetRect(rect, offsets);
const midPoints = rectToMidPoints(neswRect);
const neswRect = offsetRect(_rect, offsets);

const tf = new Matrix(...rect.transform);
const midPoints = rectToMidPoints(neswRect).map((point) => {
const { x, y } = tf.apply(point);
return { x, y };
});

return {
...cornerPoints,
...midPoints,
nwRotation: cornerRotation.nw,
neRotation: cornerRotation.ne,
seRotation: cornerRotation.se,
swRotation: cornerRotation.sw,
nw: cornerPoints[0],
ne: cornerPoints[1],
se: cornerPoints[2],
sw: cornerPoints[3],

n: midPoints[0],
e: midPoints[1],
s: midPoints[2],
w: midPoints[3],

nwRotation: cornerRotates[0],
neRotation: cornerRotates[1],
seRotation: cornerRotates[2],
swRotation: cornerRotates[3],
};
})();

Expand Down Expand Up @@ -154,7 +178,7 @@ export class ControlHandleManager {
neswHandleWidth;
}

draw(rect: IRectWithRotation | null) {
draw(rect: ITransformRect | null) {
this.updateTransformHandles(rect);
const handles: ControlHandle[] = [];
if (this.transformHandlesVisible) {
Expand All @@ -165,6 +189,7 @@ export class ControlHandleManager {
}

const ctx = this.editor.ctx;
const rotate = rect ? getTransformAngle(rect.transform) : 0;
handles.forEach((handle) => {
const graph = handle.graph;
if (graph.type === GraphType.Path) {
Expand All @@ -175,15 +200,21 @@ export class ControlHandleManager {
handle.cy,
);
graph.updateAttrs({
x: x - graph.attrs.width / 2,
y: y - graph.attrs.height / 2,
transform: [
1,
0,
0,
1,
x - graph.attrs.width / 2,
y - graph.attrs.height / 2,
],
});
}
if (rect) {
graph.attrs.rotation = rect.rotation;
if (rect && handle.isTransformHandle) {
graph.setRotate(rotate);
}
if (handle.rotation !== undefined) {
graph.attrs.rotation = handle.rotation;
graph.setRotate(handle.rotation);
}

if (!graph.getVisible()) {
Expand Down
Loading

0 comments on commit 950ea4f

Please sign in to comment.