Skip to content

Commit

Permalink
feat: support close path
Browse files Browse the repository at this point in the history
  • Loading branch information
F-star committed Mar 4, 2024
1 parent 0ca8d07 commit dc54248
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 134 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",
"segs",
"suika",
"xigua"
]
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/commands/set_elements_attrs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Graph, ISegment } from '../graphs';
import { Graph, IPathItem } from '../graphs';
import { ITexture } from '../texture';
import { ICommand } from './type';

Expand All @@ -15,7 +15,7 @@ export type ISetElementsAttrsType = Partial<{
objectName: string;
visible: boolean;
lock: boolean;
pathData: ISegment[][];
pathData: IPathItem[];
}>;

export class SetGraphsAttrsCmd implements ICommand {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/core/src/cursor_manager/cursor.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions packages/core/src/cursor_manager/cursor_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ export type ICursor =
| 'pointer'
| 'crosshair'
| 'text'
| 'pen';
| 'pen'
| 'pen-close';

export class CursorManger {
private cursor!: ICursor;
// the cursors with custom style, need to add class to canvas element
private customClassCursor = new Set<ICursor>(['default', 'move', 'pen']);
private customClassCursor = new Set<ICursor>([
'default',
'move',
'pen',
'pen-close',
]);

constructor(private editor: Editor) {
this.setCursor('default');
Expand Down
60 changes: 33 additions & 27 deletions packages/core/src/graphs/path/path.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
import { parseRGBAStr } from '@suika/common';
import { addPoint, IPoint, IRect, IRectWithRotation } from '@suika/geo';
import { addPoint, IRect, IRectWithRotation } from '@suika/geo';
import { Bezier } from 'bezier-js';

import { ImgManager } from '../../Img_manager';
import { TextureType } from '../../texture';
import { GraphType } from '../../type';
import { rotateInCanvas } from '../../utils';
import { Graph, GraphAttrs } from '../graph';

export interface ISegment {
point: IPoint;
/** the coordinates relative to point */
handleIn: IPoint;
/** the coordinates relative to point */
handleOut: IPoint;
}
import { IPathItem, ISegment } from './type';

export interface PathAttrs extends GraphAttrs {
pathData: ISegment[][];
pathData: IPathItem[];
}

export class Path extends Graph<PathAttrs> {
Expand All @@ -34,10 +27,14 @@ export class Path extends Graph<PathAttrs> {
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (const path of pathData) {
for (let i = 1; i < path.length; i++) {
const seg = path[i];
const prevSeg = path[i - 1];
for (const pathItem of pathData) {
const segs = pathItem.segs;
for (let i = 1; i <= segs.length; i++) {
if (i === segs.length && !pathItem.closed) {
continue;
}
const seg = segs[i % segs.length];
const prevSeg = segs[i - 1];
const bbox = new Bezier(
prevSeg.point,
Path.getHandleOut(prevSeg),
Expand Down Expand Up @@ -75,7 +72,7 @@ export class Path extends Graph<PathAttrs> {
const dx = attrs.x - originX;
const pathData = this.attrs.pathData;
for (const pathItem of pathData) {
for (const seg of pathItem) {
for (const seg of pathItem.segs) {
seg.point.x += dx;
}
}
Expand All @@ -85,7 +82,7 @@ export class Path extends Graph<PathAttrs> {
const dy = attrs.y - originY;
const pathData = this.attrs.pathData;
for (const pathItem of pathData) {
for (const seg of pathItem) {
for (const seg of pathItem.segs) {
seg.point.y += dy;
}
}
Expand All @@ -110,12 +107,18 @@ export class Path extends Graph<PathAttrs> {
}

ctx.beginPath();
for (const path of pathData) {
const first = path[0];
for (const pathItem of pathData) {
const first = pathItem.segs[0];
if (!first) continue;
ctx.moveTo(first.point.x, first.point.y);
for (let i = 1; i < path.length; i++) {
const currSeg = path[i];
const prevSeg = path[i - 1];

const segs = pathItem.segs;
for (let i = 1; i <= segs.length; i++) {
if (i === segs.length && !pathItem.closed) {
continue;
}
const currSeg = segs[i % segs.length];
const prevSeg = segs[i - 1];
const pointX = currSeg.point.x;
const pointY = currSeg.point.y;
const handle1 = Path.getHandleOut(prevSeg);
Expand All @@ -133,6 +136,9 @@ export class Path extends Graph<PathAttrs> {
);
}
}
if (pathItem.closed) {
ctx.closePath();
}
}

for (const texture of fill ?? []) {
Expand Down Expand Up @@ -178,32 +184,32 @@ export class Path extends Graph<PathAttrs> {
}

static getHandleIn(seg: ISegment) {
return addPoint(seg.point, seg.handleIn);
return addPoint(seg.point, seg.in);
}
static getHandleOut(seg: ISegment) {
return addPoint(seg.point, seg.handleOut);
return addPoint(seg.point, seg.out);
}

getSeg(pathIdx: number, segIdx: number) {
return Path.getSeg(this.attrs.pathData, pathIdx, segIdx);
}
static getSeg(pathData: ISegment[][], pathIdx: number, segIdx: number) {
static getSeg(pathData: IPathItem[], pathIdx: number, segIdx: number) {
const pathDataItem = pathData[pathIdx];
if (!pathDataItem) {
return null;
}
return pathDataItem[segIdx] ?? null;
return pathDataItem.segs[segIdx] ?? null;
}
setSeg(pathIdx: number, segIdx: number, seg: ISegment) {
const pathData = this.attrs.pathData;
const pathDataItem = pathData[pathIdx];
if (!pathDataItem) {
throw new Error(`pathIdx ${pathIdx} is out of range`);
}
if (segIdx >= pathDataItem.length) {
if (segIdx >= pathDataItem.segs.length) {
throw new Error(`segIdx ${segIdx} is out of range`);
}
pathDataItem[segIdx] = seg;
pathDataItem.segs[segIdx] = seg;
this.updateAttrs({ pathData });
}
}
15 changes: 15 additions & 0 deletions packages/core/src/graphs/path/type.ts
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
import { IPoint } from '@suika/geo';

export type PathSegPointType = 'in' | 'out' | 'anchor';

export interface ISegment {
point: IPoint;
/** the coordinates relative to point */
in: IPoint;
/** the coordinates relative to point */
out: IPoint;
}

export interface IPathItem {
segs: ISegment[];
closed: boolean;
}
28 changes: 18 additions & 10 deletions packages/core/src/path_editor/path_editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ export class PathEditor {
const path = this.path;
if (!path) return;
const pathData = path.attrs.pathData;
if (pathData.length === 0 || pathData.every((item) => item.length <= 1)) {
if (
pathData.length === 0 ||
pathData.every((item) => item.segs.length <= 1)
) {
this.editor.commandManager.pushCommand(
new RemoveGraphsCmd('remove empty path', this.editor, [path]),
);
Expand Down Expand Up @@ -161,7 +164,9 @@ export class PathEditor {
if (pathIdx < 0 || pathIdx >= path.attrs.pathData.length) {
continue;
}
const segCount = path.attrs.pathData[pathIdx].length;
const pathItem = path.attrs.pathData[pathIdx];
const segCount = pathItem.segs.length;
const closed = pathItem.closed;
if (segIdx < 0 || segIdx >= segCount) {
continue;
}
Expand All @@ -175,8 +180,11 @@ export class PathEditor {
segIdxSet.add(segIdx);

if (type === 'anchor') {
if (segIdx - 1 >= 0) {
segIdxSet.add(segIdx - 1);
const leftSegIdx = segIdx - 1;
if (leftSegIdx < 0 && closed) {
segIdxSet.add(segCount - 1);
} else if (leftSegIdx >= 0 && !closed) {
segIdxSet.add(leftSegIdx);
}
if (segIdx + 1 < segCount) {
segIdxSet.add(segIdx + 1);
Expand Down Expand Up @@ -215,9 +223,9 @@ export class PathEditor {
const handleLinesAndPoints: ControlHandle[] = [];

for (let i = 0; i < pathData.length; i++) {
const pathDataItem = pathData[i];
for (let j = 0; j < pathDataItem.length; j++) {
const seg = pathDataItem[j];
const pathItem = pathData[i];
for (let j = 0; j < pathItem.segs.length; j++) {
const seg = pathItem.segs[j];
const anchor = seg.point;

// 1. draw anchor
Expand All @@ -235,7 +243,7 @@ export class PathEditor {
cy: anchor.y,
type: ['anchor', i, j].join('-'),
graph: new Ellipse({
objectName: '',
objectName: 'anchor',
x: anchor.x,
y: anchor.y,
width: anchorSize,
Expand Down Expand Up @@ -279,7 +287,7 @@ export class PathEditor {
type: 'handleLine',
rotation: rect.rotation,
graph: new Line({
objectName: '',
objectName: 'handleLine',
...rect,
width: rect.width * zoom,
stroke: [
Expand All @@ -300,7 +308,7 @@ export class PathEditor {
rotation: QUARTER_PI,
type: [handleIdx === 0 ? 'in' : 'out', i, j].join('-'),
graph: new Rect({
objectName: '',
objectName: 'pathHandle',
x: handle.x,
y: handle.y,
width: handleInOutSize,
Expand Down
Loading

0 comments on commit dc54248

Please sign in to comment.