@cosmos.gl/graph 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,6 +21,7 @@ export declare class Points extends CoreModule {
21
21
  private updatePositionCommand;
22
22
  private dragPointCommand;
23
23
  private findPointsOnAreaSelectionCommand;
24
+ private findPointsOnLassoSelectionCommand;
24
25
  private findHoveredPointCommand;
25
26
  private clearHoveredFboCommand;
26
27
  private clearSampledPointsFboCommand;
@@ -31,6 +32,9 @@ export declare class Points extends CoreModule {
31
32
  private greyoutStatusTexture;
32
33
  private sizeTexture;
33
34
  private trackedIndicesTexture;
35
+ private lassoPathTexture;
36
+ private lassoPathFbo;
37
+ private lassoPathLength;
34
38
  private drawPointIndices;
35
39
  private hoveredPointIndices;
36
40
  private sampledPointIndices;
@@ -45,6 +49,8 @@ export declare class Points extends CoreModule {
45
49
  updatePosition(): void;
46
50
  drag(): void;
47
51
  findPointsOnAreaSelection(): void;
52
+ findPointsOnLassoSelection(): void;
53
+ updateLassoPath(lassoPath: [number, number][]): void;
48
54
  findHoveredPoint(): void;
49
55
  trackPointsByIndices(indices?: number[] | undefined): void;
50
56
  getTrackedPositionsMap(): Map<number, [number, number]>;
@@ -0,0 +1,6 @@
1
+ import { Graph } from '../../..';
2
+ export declare const lassoSelection: () => {
3
+ div: HTMLDivElement;
4
+ graph: Graph;
5
+ destroy: () => void;
6
+ };
@@ -0,0 +1,20 @@
1
+ export declare class LassoSelection {
2
+ private canvas;
3
+ private ctx;
4
+ private isDrawing;
5
+ private points;
6
+ private graphDiv;
7
+ private onLassoComplete?;
8
+ private boundStartDrawing;
9
+ private boundDraw;
10
+ private boundStopDrawing;
11
+ private resizeObserver;
12
+ constructor(graphDiv: HTMLElement, onLassoComplete?: (points: [number, number][]) => void);
13
+ enableLassoMode(): void;
14
+ disableLassoMode(): void;
15
+ destroy(): void;
16
+ private resizeCanvas;
17
+ private startDrawing;
18
+ private draw;
19
+ private stopDrawing;
20
+ }
@@ -5,4 +5,5 @@ declare const meta: Meta<CosmosStoryProps>;
5
5
  export declare const Worm: Story;
6
6
  export declare const Radial: Story;
7
7
  export declare const WithLabels: Story;
8
+ export declare const LassoSelection: Story;
8
9
  export default meta;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmos.gl/graph",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "GPU-based force graph layout and rendering",
5
5
  "jsdelivr": "dist/index.min.js",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -663,6 +663,31 @@ export class Graph {
663
663
  .filter(d => d !== -1)
664
664
  }
665
665
 
666
+ /**
667
+ * Get points indices inside a lasso (polygon) area.
668
+ * @param lassoPath - Array of points `[[x1, y1], [x2, y2], ..., [xn, yn]]` that defines the lasso polygon.
669
+ * The coordinates should be from 0 to the width/height of the canvas.
670
+ * @returns A Float32Array containing the indices of points inside the lasso area.
671
+ */
672
+ public getPointsInLasso (lassoPath: [number, number][]): Float32Array {
673
+ if (this._isDestroyed || !this.reglInstance || !this.points) return new Float32Array()
674
+ if (lassoPath.length < 3) return new Float32Array() // Need at least 3 points for a polygon
675
+
676
+ const h = this.store.screenSize[1]
677
+ // Convert coordinates to WebGL coordinate system (flip Y)
678
+ const convertedPath = lassoPath.map(([x, y]) => [x, h - y] as [number, number])
679
+ this.points.updateLassoPath(convertedPath)
680
+ this.points.findPointsOnLassoSelection()
681
+ const pixels = readPixels(this.reglInstance, this.points.selectedFbo as regl.Framebuffer2D)
682
+
683
+ return pixels
684
+ .map((pixel, i) => {
685
+ if (i % 4 === 0 && pixel !== 0) return i / 4
686
+ else return -1
687
+ })
688
+ .filter(d => d !== -1)
689
+ }
690
+
666
691
  /** Select points inside a rectangular area.
667
692
  * @param selection - Array of two corner points `[[left, top], [right, bottom]]`.
668
693
  * The `left` and `right` coordinates should be from 0 to the width of the canvas.
@@ -686,6 +711,36 @@ export class Graph {
686
711
  this.points.updateGreyoutStatus()
687
712
  }
688
713
 
714
+ /** Select points inside a lasso (polygon) area.
715
+ * @param lassoPath - Array of points `[[x1, y1], [x2, y2], ..., [xn, yn]]` that defines the lasso polygon.
716
+ * The coordinates should be from 0 to the width/height of the canvas.
717
+ * Set to null to clear selection. */
718
+ public selectPointsInLasso (lassoPath: [number, number][] | null): void {
719
+ if (this._isDestroyed || !this.reglInstance || !this.points) return
720
+ if (lassoPath) {
721
+ if (lassoPath.length < 3) {
722
+ console.warn('Lasso path requires at least 3 points to form a polygon.')
723
+ return
724
+ }
725
+
726
+ const h = this.store.screenSize[1]
727
+ // Convert coordinates to WebGL coordinate system (flip Y)
728
+ const convertedPath = lassoPath.map(([x, y]) => [x, h - y] as [number, number])
729
+ this.points.updateLassoPath(convertedPath)
730
+ this.points.findPointsOnLassoSelection()
731
+ const pixels = readPixels(this.reglInstance, this.points.selectedFbo as regl.Framebuffer2D)
732
+ this.store.selectedIndices = pixels
733
+ .map((pixel, i) => {
734
+ if (i % 4 === 0 && pixel !== 0) return i / 4
735
+ else return -1
736
+ })
737
+ .filter(d => d !== -1)
738
+ } else {
739
+ this.store.selectedIndices = null
740
+ }
741
+ this.points.updateGreyoutStatus()
742
+ }
743
+
689
744
  /**
690
745
  * Select a point by index. If you want the adjacent points to get selected too, provide `true` as the second argument.
691
746
  * @param index The index of the point in the array of points.
@@ -0,0 +1,65 @@
1
+ #ifdef GL_ES
2
+ precision highp float;
3
+ #endif
4
+
5
+ uniform sampler2D positionsTexture;
6
+ uniform sampler2D lassoPathTexture; // Texture containing lasso path points
7
+ uniform int lassoPathLength;
8
+ uniform float spaceSize;
9
+ uniform vec2 screenSize;
10
+ uniform mat3 transformationMatrix;
11
+
12
+ varying vec2 textureCoords;
13
+
14
+ // Get a point from the lasso path texture at a specific index
15
+ vec2 getLassoPoint(sampler2D pathTexture, int index, int pathLength) {
16
+ if (index >= pathLength) return vec2(0.0);
17
+
18
+ // Calculate texture coordinates for the index
19
+ int textureSize = int(ceil(sqrt(float(pathLength))));
20
+ int x = index - (index / textureSize) * textureSize;
21
+ int y = index / textureSize;
22
+
23
+ vec2 texCoord = (vec2(float(x), float(y)) + 0.5) / float(textureSize);
24
+ vec4 pathData = texture2D(pathTexture, texCoord);
25
+
26
+ return pathData.xy;
27
+ }
28
+
29
+ // Point-in-polygon algorithm using ray casting
30
+ bool pointInPolygon(vec2 point, sampler2D pathTexture, int pathLength) {
31
+ bool inside = false;
32
+
33
+ for (int i = 0; i < 2048; i++) {
34
+ if (i >= pathLength) break;
35
+
36
+ int j = int(mod(float(i + 1), float(pathLength)));
37
+
38
+ vec2 pi = getLassoPoint(pathTexture, i, pathLength);
39
+ vec2 pj = getLassoPoint(pathTexture, j, pathLength);
40
+
41
+ if (((pi.y > point.y) != (pj.y > point.y)) &&
42
+ (point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y) + pi.x)) {
43
+ inside = !inside;
44
+ }
45
+ }
46
+
47
+ return inside;
48
+ }
49
+
50
+ void main() {
51
+ vec4 pointPosition = texture2D(positionsTexture, textureCoords);
52
+ vec2 p = 2.0 * pointPosition.rg / spaceSize - 1.0;
53
+ p *= spaceSize / screenSize;
54
+ vec3 final = transformationMatrix * vec3(p, 1);
55
+
56
+ // Convert to screen coordinates for polygon check
57
+ vec2 screenPos = (final.xy + 1.0) * screenSize / 2.0;
58
+
59
+ gl_FragColor = vec4(0.0, 0.0, pointPosition.rg);
60
+
61
+ // Check if point center is inside the lasso polygon
62
+ if (pointInPolygon(screenPos, lassoPathTexture, lassoPathLength)) {
63
+ gl_FragColor.r = 1.0;
64
+ }
65
+ }
@@ -6,6 +6,7 @@ import { defaultConfigValues } from '@/graph/variables'
6
6
  import drawPointsFrag from '@/graph/modules/Points/draw-points.frag'
7
7
  import drawPointsVert from '@/graph/modules/Points/draw-points.vert'
8
8
  import findPointsOnAreaSelectionFrag from '@/graph/modules/Points/find-points-on-area-selection.frag'
9
+ import findPointsOnLassoSelectionFrag from '@/graph/modules/Points/find-points-on-lasso-selection.frag'
9
10
  import drawHighlightedFrag from '@/graph/modules/Points/draw-highlighted.frag'
10
11
  import drawHighlightedVert from '@/graph/modules/Points/draw-highlighted.vert'
11
12
  import findHoveredPointFrag from '@/graph/modules/Points/find-hovered-point.frag'
@@ -41,6 +42,7 @@ export class Points extends CoreModule {
41
42
  private updatePositionCommand: regl.DrawCommand | undefined
42
43
  private dragPointCommand: regl.DrawCommand | undefined
43
44
  private findPointsOnAreaSelectionCommand: regl.DrawCommand | undefined
45
+ private findPointsOnLassoSelectionCommand: regl.DrawCommand | undefined
44
46
  private findHoveredPointCommand: regl.DrawCommand | undefined
45
47
  private clearHoveredFboCommand: regl.DrawCommand | undefined
46
48
  private clearSampledPointsFboCommand: regl.DrawCommand | undefined
@@ -51,6 +53,9 @@ export class Points extends CoreModule {
51
53
  private greyoutStatusTexture: regl.Texture2D | undefined
52
54
  private sizeTexture: regl.Texture2D | undefined
53
55
  private trackedIndicesTexture: regl.Texture2D | undefined
56
+ private lassoPathTexture: regl.Texture2D | undefined
57
+ private lassoPathFbo: regl.Framebuffer2D | undefined
58
+ private lassoPathLength = 0
54
59
  private drawPointIndices: regl.Buffer | undefined
55
60
  private hoveredPointIndices: regl.Buffer | undefined
56
61
  private sampledPointIndices: regl.Buffer | undefined
@@ -280,6 +285,27 @@ export class Points extends CoreModule {
280
285
  })
281
286
  }
282
287
 
288
+ if (!this.findPointsOnLassoSelectionCommand) {
289
+ this.findPointsOnLassoSelectionCommand = reglInstance({
290
+ frag: findPointsOnLassoSelectionFrag,
291
+ vert: updateVert,
292
+ framebuffer: () => this.selectedFbo as regl.Framebuffer2D,
293
+ primitive: 'triangle strip',
294
+ count: 4,
295
+ attributes: {
296
+ vertexCoord: createQuadBuffer(reglInstance),
297
+ },
298
+ uniforms: {
299
+ positionsTexture: () => this.currentPositionFbo,
300
+ spaceSize: () => store.adjustedSpaceSize,
301
+ screenSize: () => store.screenSize,
302
+ transformationMatrix: () => store.transform,
303
+ lassoPathTexture: () => this.lassoPathTexture,
304
+ lassoPathLength: () => this.lassoPathLength,
305
+ },
306
+ })
307
+ }
308
+
283
309
  if (!this.clearHoveredFboCommand) {
284
310
  this.clearHoveredFboCommand = reglInstance({
285
311
  frag: clearFrag,
@@ -547,6 +573,49 @@ export class Points extends CoreModule {
547
573
  this.findPointsOnAreaSelectionCommand?.()
548
574
  }
549
575
 
576
+ public findPointsOnLassoSelection (): void {
577
+ this.findPointsOnLassoSelectionCommand?.()
578
+ }
579
+
580
+ public updateLassoPath (lassoPath: [number, number][]): void {
581
+ const { reglInstance } = this
582
+ this.lassoPathLength = lassoPath.length
583
+
584
+ if (lassoPath.length === 0) {
585
+ this.lassoPathTexture = undefined
586
+ this.lassoPathFbo = undefined
587
+ return
588
+ }
589
+
590
+ // Calculate texture size (square texture)
591
+ const textureSize = Math.ceil(Math.sqrt(lassoPath.length))
592
+ const textureData = new Float32Array(textureSize * textureSize * 4)
593
+
594
+ // Fill texture with lasso path points
595
+ for (const [i, point] of lassoPath.entries()) {
596
+ const [x, y] = point
597
+ textureData[i * 4] = x
598
+ textureData[i * 4 + 1] = y
599
+ textureData[i * 4 + 2] = 0 // unused
600
+ textureData[i * 4 + 3] = 0 // unused
601
+ }
602
+
603
+ if (!this.lassoPathTexture) this.lassoPathTexture = reglInstance.texture()
604
+ this.lassoPathTexture({
605
+ data: textureData,
606
+ width: textureSize,
607
+ height: textureSize,
608
+ type: 'float',
609
+ })
610
+
611
+ if (!this.lassoPathFbo) this.lassoPathFbo = reglInstance.framebuffer()
612
+ this.lassoPathFbo({
613
+ color: this.lassoPathTexture,
614
+ depth: false,
615
+ stencil: false,
616
+ })
617
+ }
618
+
550
619
  public findHoveredPoint (): void {
551
620
  this.clearHoveredFboCommand?.()
552
621
  this.findHoveredPointCommand?.()
@@ -249,6 +249,25 @@ Select points within a rectangular area defined by two corner points `[[left, to
249
249
 
250
250
  The `top` and `bottom` values represent the vertical position in pixels, relative to the top edge of the canvas, with `0` being the topmost position and the height of the canvas being the bottommost position.
251
251
 
252
+ ### <a name="get_points_in_lasso" href="#get_points_in_lasso">#</a> graph.<b>getPointsInLasso</b>(<i>lassoPath</i>)
253
+
254
+ Get points as a Float32Array within a lasso (polygon) area defined by an array of coordinate points.
255
+
256
+ * **`lassoPath`** (Array): An array of coordinate points in the format `[[x1, y1], [x2, y2], ..., [xN, yN]]` that defines the lasso polygon. The coordinates should be in pixels relative to the canvas, where:
257
+ - **`x`**: Horizontal position from 0 to the width of the canvas
258
+ - **`y`**: Vertical position from 0 to the height of the canvas
259
+ - The polygon requires at least 3 points to form a valid selection area
260
+
261
+ **Returns:** A Float32Array containing the indices of points inside the lasso area.
262
+
263
+ ### <a name="select_points_in_lasso" href="#select_points_in_lasso">#</a> graph.<b>selectPointsInLasso</b>(<i>lassoPath</i>)
264
+
265
+ Select points within a lasso (polygon) area defined by an array of coordinate points. This method combines the functionality of `getPointsInLasso` with point selection, making the identified points visually selected in the graph.
266
+
267
+ * **`lassoPath`** (Array | null): An array of coordinate points in the format `[[x1, y1], [x2, y2], ..., [xN, yN]]` that defines the lasso polygon, or `null` to clear the current selection. The coordinates should be in pixels relative to the canvas, where:
268
+ - **`x`**: Horizontal position from 0 to the width of the canvas
269
+ - **`y`**: Vertical position from 0 to the height of the canvas
270
+ - The polygon requires at least 3 points to form a valid selection area
252
271
 
253
272
  ### <a name="select_point_by_index" href="#select_point_by_index">#</a> graph.<b>selectPointByIndex</b>(<i>index</i>, [<i>selectAdjacentPoints</i>])
254
273
 
@@ -0,0 +1,53 @@
1
+ import { Graph } from '@cosmos.gl/graph'
2
+ import { createCosmos } from '../../create-cosmos'
3
+ import { generateMeshData } from '../../generate-mesh-data'
4
+ import { LassoSelection } from './lasso'
5
+
6
+ export const lassoSelection = (): {div: HTMLDivElement; graph: Graph; destroy: () => void } => {
7
+ const nClusters = 25
8
+ const { pointPositions, pointColors, pointClusters } = generateMeshData(150, 150, nClusters, 1.0)
9
+
10
+ const { div, graph } = createCosmos({
11
+ pointPositions,
12
+ pointColors,
13
+ pointClusters,
14
+ simulationGravity: 1.5,
15
+ simulationCluster: 0.3,
16
+ simulationRepulsion: 8,
17
+ pointSize: 8,
18
+ backgroundColor: '#1a1a2e',
19
+ pointGreyoutOpacity: 0.2,
20
+ onClick: (index: number | undefined): void => {
21
+ if (index === undefined) {
22
+ graph.unselectPoints()
23
+ }
24
+ },
25
+ })
26
+
27
+ graph.setZoomLevel(0.4)
28
+
29
+ const lassoSelection = new LassoSelection(div, (lassoPoints) => {
30
+ graph.selectPointsInLasso(lassoPoints)
31
+ })
32
+
33
+ const actionsDiv = document.createElement('div')
34
+ actionsDiv.className = 'actions'
35
+ div.appendChild(actionsDiv)
36
+
37
+ const lassoButton = document.createElement('div')
38
+ lassoButton.className = 'action'
39
+ lassoButton.textContent = 'Enable Lasso Selection'
40
+ lassoButton.addEventListener('click', () => {
41
+ lassoSelection.enableLassoMode()
42
+ })
43
+ actionsDiv.appendChild(lassoButton)
44
+
45
+ const destroy = (): void => {
46
+ lassoSelection.destroy()
47
+ if (actionsDiv.parentNode) {
48
+ actionsDiv.parentNode.removeChild(actionsDiv)
49
+ }
50
+ }
51
+
52
+ return { div, graph, destroy }
53
+ }
@@ -0,0 +1,143 @@
1
+ import './style.css'
2
+
3
+ export class LassoSelection {
4
+ private canvas: HTMLCanvasElement
5
+ private ctx: CanvasRenderingContext2D
6
+ private isDrawing = false
7
+ private points: Array<{ x: number; y: number }> = []
8
+ private graphDiv: HTMLElement
9
+ private onLassoComplete?: (points: [number, number][]) => void
10
+ private boundStartDrawing: (e: MouseEvent) => void
11
+ private boundDraw: (e: MouseEvent) => void
12
+ private boundStopDrawing: () => void
13
+ private resizeObserver: ResizeObserver
14
+
15
+ public constructor (graphDiv: HTMLElement, onLassoComplete?: (points: [number, number][]) => void) {
16
+ this.graphDiv = graphDiv
17
+ this.onLassoComplete = onLassoComplete
18
+
19
+ // Bind event handlers
20
+ this.boundStartDrawing = this.startDrawing.bind(this)
21
+ this.boundDraw = this.draw.bind(this)
22
+ this.boundStopDrawing = this.stopDrawing.bind(this)
23
+
24
+ // Create canvas
25
+ this.canvas = document.createElement('canvas')
26
+ this.canvas.className = 'lasso-canvas'
27
+
28
+ const ctx = this.canvas.getContext('2d')
29
+ if (!ctx) throw new Error('Could not get canvas context')
30
+ this.ctx = ctx
31
+
32
+ this.graphDiv.appendChild(this.canvas)
33
+
34
+ this.resizeObserver = new ResizeObserver(() => {
35
+ this.resizeCanvas()
36
+ })
37
+ this.resizeObserver.observe(this.graphDiv)
38
+ }
39
+
40
+ public enableLassoMode (): void {
41
+ this.canvas.style.pointerEvents = 'auto'
42
+ this.canvas.style.cursor = 'crosshair'
43
+
44
+ // Add event listeners
45
+ this.canvas.addEventListener('mousedown', this.boundStartDrawing)
46
+ this.canvas.addEventListener('mousemove', this.boundDraw)
47
+ this.canvas.addEventListener('mouseup', this.boundStopDrawing)
48
+ }
49
+
50
+ public disableLassoMode (): void {
51
+ this.canvas.style.pointerEvents = 'none'
52
+ this.canvas.style.cursor = 'default'
53
+
54
+ // Remove event listeners
55
+ this.canvas.removeEventListener('mousedown', this.boundStartDrawing)
56
+ this.canvas.removeEventListener('mousemove', this.boundDraw)
57
+ this.canvas.removeEventListener('mouseup', this.boundStopDrawing)
58
+
59
+ // Clear canvas
60
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
61
+ }
62
+
63
+ public destroy (): void {
64
+ this.disableLassoMode()
65
+ this.resizeObserver.disconnect()
66
+ if (this.canvas.parentNode) {
67
+ this.canvas.parentNode.removeChild(this.canvas)
68
+ }
69
+ }
70
+
71
+ private resizeCanvas (): void {
72
+ const rect = this.graphDiv.getBoundingClientRect()
73
+
74
+ // Apply pixel ratio for crisp rendering
75
+ const pixelRatio = window.devicePixelRatio || 1
76
+ this.canvas.width = rect.width * pixelRatio
77
+ this.canvas.height = rect.height * pixelRatio
78
+
79
+ // Reset transform and scale the context to match the pixel ratio
80
+ this.ctx.resetTransform()
81
+ this.ctx.scale(pixelRatio, pixelRatio)
82
+ }
83
+
84
+ private startDrawing (e: MouseEvent): void {
85
+ this.isDrawing = true
86
+ this.points = []
87
+ const rect = this.canvas.getBoundingClientRect()
88
+ this.points.push({ x: e.clientX - rect.left, y: e.clientY - rect.top })
89
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
90
+ }
91
+
92
+ private draw (e: MouseEvent): void {
93
+ if (!this.isDrawing) return
94
+
95
+ const rect = this.canvas.getBoundingClientRect()
96
+ this.points.push({ x: e.clientX - rect.left, y: e.clientY - rect.top })
97
+
98
+ // Clear the entire canvas accounting for pixel ratio
99
+ const pixelRatio = window.devicePixelRatio || 1
100
+ this.ctx.clearRect(0, 0, this.canvas.width / pixelRatio, this.canvas.height / pixelRatio)
101
+
102
+ this.ctx.beginPath()
103
+ if (this.points.length > 0 && this.points[0]) {
104
+ this.ctx.moveTo(this.points[0].x, this.points[0].y)
105
+ }
106
+
107
+ for (let i = 1; i < this.points.length; i++) {
108
+ const point = this.points[i]
109
+ if (point) {
110
+ this.ctx.lineTo(point.x, point.y)
111
+ }
112
+ }
113
+
114
+ this.ctx.strokeStyle = '#ffffff'
115
+ this.ctx.lineWidth = 2
116
+ this.ctx.stroke()
117
+ }
118
+
119
+ private stopDrawing (): void {
120
+ if (!this.isDrawing) return
121
+ this.isDrawing = false
122
+
123
+ if (this.points.length > 2) {
124
+ this.ctx.closePath()
125
+ this.ctx.stroke()
126
+
127
+ const lassoPoints: [number, number][] = this.points.map(p => [p.x, p.y])
128
+ const firstLassoPoint = lassoPoints[0]
129
+ const lastLassoPoint = lassoPoints[lassoPoints.length - 1]
130
+ if (firstLassoPoint && lastLassoPoint && (firstLassoPoint[0] !== lastLassoPoint[0] || firstLassoPoint[1] !== lastLassoPoint[1])) {
131
+ lassoPoints.push(firstLassoPoint)
132
+ }
133
+
134
+ if (this.onLassoComplete) {
135
+ this.onLassoComplete(lassoPoints)
136
+ }
137
+ }
138
+
139
+ const pixelRatio = window.devicePixelRatio || 1
140
+ this.ctx.clearRect(0, 0, this.canvas.width / pixelRatio, this.canvas.height / pixelRatio)
141
+ this.disableLassoMode()
142
+ }
143
+ }
@@ -0,0 +1,8 @@
1
+ .lasso-canvas {
2
+ position: absolute;
3
+ top: 0;
4
+ left: 0;
5
+ pointer-events: none;
6
+ width: 100%;
7
+ height: 100%;
8
+ }
@@ -4,6 +4,7 @@ import { createStory, Story } from '@/graph/stories/create-story'
4
4
  import { withLabels } from './clusters/with-labels'
5
5
  import { worm } from './clusters/worm'
6
6
  import { radial } from './clusters/radial'
7
+ import { lassoSelection } from './clusters/lasso-selection'
7
8
 
8
9
  import createCosmosRaw from './create-cosmos?raw'
9
10
  import generateMeshDataRaw from './generate-mesh-data?raw'
@@ -11,6 +12,9 @@ import withLabelsStoryRaw from './clusters/with-labels?raw'
11
12
  import createClusterLabelsRaw from './create-cluster-labels?raw'
12
13
  import wormStory from './clusters/worm?raw'
13
14
  import radialStory from './clusters/radial?raw'
15
+ import lassoSelectionStory from './clusters/lasso-selection?raw'
16
+ import lassoSelectionStyleRaw from './clusters/lasso-selection/style.css?raw'
17
+ import lassoSelectionLassoRaw from './clusters/lasso-selection/lasso.ts?raw'
14
18
 
15
19
  const meta: Meta<CosmosStoryProps> = {
16
20
  title: 'Examples/Clusters',
@@ -57,5 +61,17 @@ export const WithLabels: Story = {
57
61
  },
58
62
  }
59
63
 
64
+ export const LassoSelection: Story = {
65
+ ...createStory(lassoSelection),
66
+ parameters: {
67
+ sourceCode: [
68
+ { name: 'Story', code: lassoSelectionStory },
69
+ { name: 'lasso.ts', code: lassoSelectionLassoRaw },
70
+ ...sourceCodeAddonParams,
71
+ { name: 'style.css', code: lassoSelectionStyleRaw },
72
+ ],
73
+ },
74
+ }
75
+
60
76
  // eslint-disable-next-line import/no-default-export
61
77
  export default meta