Skip to content

Commit 0da1dc7

Browse files
committed
feat: split Path2DGroup to chunks
feat: add isPathVisible method to BlockConnection and ConnectionArrow, improve path visibility handling in BatchPath2D
1 parent ec8a02f commit 0da1dc7

File tree

4 files changed

+83
-17
lines changed

4 files changed

+83
-17
lines changed

src/components/canvas/GraphComponent/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ export class GraphComponent<
5656
event.stopPropagation();
5757
dragListener(this.context.ownerDocument)
5858
.on(EVENTS.DRAG_START, (event: MouseEvent) => {
59-
this.context.graph.getGraphLayer().captureEvents(this);
6059
if (onDragStart?.(event) === false) {
6160
return;
6261
}
62+
this.context.graph.getGraphLayer().captureEvents(this);
6363
const xy = getXY(this.context.canvas, event);
6464
startDragCoords = this.context.camera.applyToPoint(xy[0], xy[1]);
6565
})

src/components/canvas/connections/BatchPath2D/index.tsx

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import debounce from "lodash/debounce";
2+
13
import { cache } from "../../../../lib/utils";
24

35
export type Path2DRenderStyleResult =
@@ -9,40 +11,56 @@ export interface Path2DRenderInstance {
911
getPath(): Path2D | undefined | null;
1012
style(ctx: CanvasRenderingContext2D): Path2DRenderStyleResult | undefined;
1113
afterRender?(ctx: CanvasRenderingContext2D): void;
14+
isPathVisible?(): boolean;
1215
}
1316

14-
class Path2DGroup {
17+
const CHUNK_SIZE = 100;
18+
19+
class Path2DChunk {
1520
protected items: Set<Path2DRenderInstance> = new Set();
1621

22+
protected visibleItems = cache(() => {
23+
return Array.from(this.items).filter((item) => item.isPathVisible?.() ?? true);
24+
});
25+
1726
protected path = cache(() => {
1827
const path = new Path2D();
1928
path.moveTo(0, 0);
20-
return Array.from(this.items).reduce((path, item) => {
21-
const subPath = item.getPath();
22-
if (subPath) {
23-
path.addPath(subPath);
29+
return this.visibleItems.get().reduce((path, item) => {
30+
if (item.isPathVisible?.() ?? true) {
31+
const subPath = item.getPath();
32+
if (subPath) {
33+
path.addPath(subPath);
34+
}
2435
}
2536
return path;
2637
}, path);
2738
});
2839

2940
protected applyStyles(ctx) {
30-
const val = Array.from(this.items)[0];
31-
return val.style(ctx);
41+
const val = Array.from(this.items).find((item) => item.isPathVisible?.() ?? true);
42+
return val?.style(ctx);
3243
}
3344

3445
public add(item: Path2DRenderInstance) {
3546
this.items.add(item);
36-
this.path.reset();
47+
this.reset();
3748
}
3849

39-
public delete(item) {
50+
public delete(item: Path2DRenderInstance) {
4051
this.items.delete(item);
52+
this.reset();
53+
}
54+
55+
public reset() {
4156
this.path.reset();
57+
this.visibleItems.reset();
4258
}
4359

4460
public render(ctx: CanvasRenderingContext2D) {
45-
if (this.items.size) {
61+
const visibleItems = this.visibleItems.get();
62+
63+
if (visibleItems.length) {
4664
ctx.save();
4765

4866
const result = this.applyStyles(ctx);
@@ -63,11 +81,55 @@ class Path2DGroup {
6381
}
6482
}
6583
ctx.restore();
66-
for (const item of this.items) {
84+
for (const item of visibleItems) {
6785
item.afterRender?.(ctx);
6886
}
6987
}
7088
}
89+
90+
public get size() {
91+
return this.items.size;
92+
}
93+
}
94+
95+
class Path2DGroup {
96+
protected chunks: Path2DChunk[] = [new Path2DChunk()];
97+
protected itemToChunk: Map<Path2DRenderInstance, Path2DChunk> = new Map();
98+
99+
public add(item: Path2DRenderInstance) {
100+
let lastChunk = this.chunks[this.chunks.length - 1];
101+
if (lastChunk.size >= CHUNK_SIZE) {
102+
lastChunk = new Path2DChunk();
103+
this.chunks.push(lastChunk);
104+
}
105+
lastChunk.add(item);
106+
this.itemToChunk.set(item, lastChunk);
107+
}
108+
109+
public delete(item: Path2DRenderInstance) {
110+
const chunk = this.itemToChunk.get(item);
111+
if (chunk) {
112+
chunk.delete(item);
113+
this.itemToChunk.delete(item);
114+
if (chunk.size === 0 && this.chunks.length > 1) {
115+
const index = this.chunks.indexOf(chunk);
116+
if (index > -1) {
117+
this.chunks.splice(index, 1);
118+
}
119+
}
120+
}
121+
}
122+
123+
public resetItem(item: Path2DRenderInstance) {
124+
const chunk = this.itemToChunk.get(item);
125+
chunk?.reset();
126+
}
127+
128+
public render(ctx: CanvasRenderingContext2D) {
129+
for (const chunk of this.chunks) {
130+
chunk.render(ctx);
131+
}
132+
}
71133
}
72134

73135
export class BatchPath2DRenderer {
@@ -126,4 +188,13 @@ export class BatchPath2DRenderer {
126188
this.orderedPaths.reset();
127189
this.onChange?.();
128190
}
191+
192+
public markDirty(item: Path2DRenderInstance) {
193+
const params = this.itemParams.get(item);
194+
if (params) {
195+
const group = this.getGroup(params.zIndex, params.group);
196+
group.resetItem(item);
197+
this.onChange?.();
198+
}
199+
}
129200
}

src/graph.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ export class Graph {
162162

163163
public zoomTo(target: TGraphZoomTarget, config?: ZoomConfig) {
164164
this.scheduleTask(() => {
165-
console.log("zoomTo", target, config);
166165
if (target === "center") {
167166
this.api.zoomToViewPort(config);
168167
return;

src/react-components/BlocksList.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ export type TBlockProps = {
2727
export const Block: React.FC<TBlockProps> = memo((props: TBlockProps) => {
2828
const block = useSignal(props.blockState.$state);
2929

30-
useLayoutEffect(() => {
31-
console.log("block-update", props.blockState.id);
32-
}, [props.blockState]);
33-
3430
if (!block) return;
3531

3632
return props.renderBlock(props.graphObject, block, props.blockState);

0 commit comments

Comments
 (0)