1
1
import * as d3 from 'd3' ;
2
- import {
3
- getNodeIdWithLevel ,
4
- parseNodeIdWidthLevel ,
5
- } from 'FederLayout/visDataHandler/hnsw/utils' ;
6
- import clearCanvas from 'FederView/clearCanvas' ;
7
- import InfoPanel , { TInfoPanelContentItem } from 'FederView/InfoPanel' ;
2
+ import InfoPanel from 'FederView/InfoPanel' ;
3
+ import initCanvas from 'FederView/initCanvas' ;
4
+ import initEventListener from 'FederView/initEventListener' ;
8
5
import TViewHandler from 'FederView/types' ;
9
- import { EMediaType , TCoord , TD3Link , TId } from 'Types' ;
6
+ import { TCoord , TId } from 'Types' ;
10
7
import {
11
8
TViewParamsHnsw ,
12
9
TVisDataHnswGraph ,
13
10
TVisDataHnswGraphNode ,
14
11
TVisDataHnswOverview ,
15
12
} from 'Types/visData' ;
16
- import { getDisL2Square , vecAdd , vecMultiply } from 'Utils/distFunc' ;
13
+ import { getDisL2Square } from 'Utils/distFunc' ;
17
14
import defaultViewParamsHnsw from '../defaultViewParamsHnsw' ;
18
- import renderLayer from '../HnswSearchView/renderLayer' ;
19
15
import initPanels from '../initPanels' ;
20
- import renderTipLine from '../renderTipLine ' ;
21
- import renderLinks from './renderLinks ' ;
22
- import renderNodes from './renderNodes ' ;
16
+ import updateClickedPanel from './updateClickedPanel ' ;
17
+ import updateStaticPanel from './updateStaticPanel ' ;
18
+ import renderView from './renderView ' ;
23
19
export default class HnswOverview implements TViewHandler {
24
20
node : HTMLElement ;
25
21
staticPanel : InfoPanel ;
@@ -59,8 +55,8 @@ export default class HnswOverview implements TViewHandler {
59
55
}
60
56
init ( ) : void {
61
57
this . initIdWithLevel2node ( ) ;
62
- this . initCanvas ( ) ;
63
- this . initEventListener ( ) ;
58
+ initCanvas . call ( this ) ;
59
+ initEventListener . call ( this ) ;
64
60
initPanels . call ( this ) ;
65
61
}
66
62
initIdWithLevel2node ( ) {
@@ -72,126 +68,14 @@ export default class HnswOverview implements TViewHandler {
72
68
) ;
73
69
this . idWithLevel2node = idWithLevel2node ;
74
70
}
75
- initCanvas ( ) {
76
- const { width, height, canvasScale } = this . viewParams ;
77
- const divD3 = d3
78
- . create ( 'div' )
79
- . style ( 'width' , `${ width } px` )
80
- . style ( 'height' , `${ height } px` )
81
- . style ( 'position' , 'relative' ) ;
82
- this . node = divD3 . node ( ) ;
83
- const canvasD3 = divD3
84
- . append ( 'canvas' )
85
- . attr ( 'width' , width )
86
- . attr ( 'height' , height ) ;
87
- this . ctx = canvasD3 . node ( ) . getContext ( '2d' ) ;
88
- this . ctx . scale ( 1 / canvasScale , 1 / canvasScale ) ;
89
- }
90
71
render ( ) : void {
91
72
this . initView ( ) ;
92
73
}
93
- async updateStaticPanel ( ) {
94
- this . staticPanel . setContent ( {
95
- themeColor : '#FFFFFF' ,
96
- hasBorder : true ,
97
- content : [
98
- {
99
- title : 'HNSW' ,
100
- } ,
101
- { text : `M = ${ this . M } , ef_construction = ${ this . efConstruction } ` } ,
102
- {
103
- text : `${ this . ntotal } vectors, ${ this . nlevels } -layer hierarchical graph (only visual the top-${ this . overviewNodesLevels . length } layers).` ,
104
- } ,
105
- ...this . nodesCount
106
- . map ( ( c , level ) => {
107
- return {
108
- title : `Level ${ level } ` ,
109
- text : `${ c } vectors, ${ this . linksCount [ level ] } links` ,
110
- } ;
111
- } )
112
- . reverse ( ) ,
113
- ] ,
114
- } ) ;
115
- }
116
- async updateClickedPanel ( ) {
117
- const node = this . clickedNode ;
118
- if ( ! node ) {
119
- this . clickedPanel . setContent ( { content : [ ] } ) ;
120
- return ;
121
- }
122
-
123
- const mediaContent = { } as TInfoPanelContentItem ;
124
- if ( this . viewParams . mediaType === EMediaType . image )
125
- mediaContent . image = this . viewParams . mediaContent ( node . id ) ;
126
- else if ( this . viewParams . mediaType === EMediaType . text )
127
- mediaContent . text = this . viewParams . mediaContent ( node . id ) ;
128
-
129
- const pathFromEntryTexts = this . overviewNodesLevels
130
- . filter ( ( _ , level ) => {
131
- return level >= this . clickedLevel ;
132
- } )
133
- . map (
134
- ( { level } ) =>
135
- `level ${ level } : ` +
136
- node . pathFromEntry
137
- . filter (
138
- ( idWithLevel ) => parseNodeIdWidthLevel ( idWithLevel ) [ 0 ] === level
139
- )
140
- . map ( ( idWithLevel ) => parseNodeIdWidthLevel ( idWithLevel ) [ 1 ] )
141
- . join ( ' => ' )
142
- )
143
- . reverse ( ) ;
144
-
145
- const linkedNodeText = node . links . join ( ', ' ) ;
146
- this . clickedPanel . setContent ( {
147
- themeColor : '#FFFC85' ,
148
- hasBorder : true ,
149
- content : [
150
- { title : `Level ${ node . level } ` } ,
151
- { title : `Row No. ${ node . id } ` } ,
152
- mediaContent ,
153
- { title : `Shortest path from the entry:` } ,
154
- ...pathFromEntryTexts . map ( ( text ) => ( { text } ) ) ,
155
- { title : `Linked vectors:` } ,
156
- { text : linkedNodeText } ,
157
- ] ,
158
- } ) ;
159
- }
160
- async updateHoveredPanel ( hoveredPanelPos : TCoord , reverse = false ) {
161
- if ( ! hoveredPanelPos ) {
162
- this . hoveredPanel . setContent ( { content : [ ] } ) ;
163
- return ;
164
- }
165
- if ( reverse )
166
- this . hoveredPanel . setPosition ( {
167
- left : null ,
168
- right : `${ this . viewParams . width - hoveredPanelPos [ 0 ] } px` ,
169
- top : `${ hoveredPanelPos [ 1 ] - 4 } px` ,
170
- } ) ;
171
- else
172
- this . hoveredPanel . setPosition ( {
173
- left : `${ hoveredPanelPos [ 0 ] } px` ,
174
- top : `${ hoveredPanelPos [ 1 ] - 4 } px` ,
175
- } ) ;
176
-
177
- const mediaContent = { } as TInfoPanelContentItem ;
178
- if ( this . viewParams . mediaType === EMediaType . image )
179
- mediaContent . image = this . viewParams . mediaContent ( this . hoveredNode . id ) ;
180
- else if ( this . viewParams . mediaType === EMediaType . text )
181
- mediaContent . text = this . viewParams . mediaContent ( this . hoveredNode . id ) ;
182
74
183
- this . hoveredPanel . setContent ( {
184
- themeColor : '#FFFC85' ,
185
- hasBorder : false ,
186
- flex : true ,
187
- flexDirection : reverse ? 'row-reverse' : 'row' ,
188
- content : [ { title : `No. ${ this . hoveredNode . id } ` } , mediaContent ] ,
189
- } ) ;
190
- }
191
75
initView ( ) {
192
76
// event;
193
- this . renderView ( ) ;
194
- this . updateStaticPanel ( ) ;
77
+ renderView . call ( this ) ;
78
+ updateStaticPanel . call ( this ) ;
195
79
196
80
const mouse2level = ( x : number , y : number ) =>
197
81
this . overviewLayerPosLevels . findIndex ( ( points ) =>
@@ -214,13 +98,13 @@ export default class HnswOverview implements TViewHandler {
214
98
const clickedNode = mouse2node ( x , y , this . clickedLevel ) ;
215
99
if ( clickedNode != this . clickedNode ) {
216
100
this . clickedNode = clickedNode ;
217
- this . renderView ( ) ;
218
- this . updateClickedPanel ( ) ;
101
+ renderView . call ( this ) ;
102
+ updateClickedPanel . call ( this ) ;
219
103
}
220
104
} else {
221
105
this . clickedNode = null ;
222
- this . renderView ( ) ;
223
- this . updateClickedPanel ( ) ;
106
+ renderView . call ( this ) ;
107
+ updateClickedPanel . call ( this ) ;
224
108
}
225
109
} ;
226
110
this . mouseMoveHandler = ( { x, y } : { x : number ; y : number } ) => {
@@ -229,104 +113,18 @@ export default class HnswOverview implements TViewHandler {
229
113
const hoveredNode = mouse2node ( x , y , this . hoveredLevel ) ;
230
114
if ( hoveredNode != this . hoveredNode ) {
231
115
this . hoveredNode = hoveredNode ;
232
- this . renderView ( ) ;
116
+ renderView . call ( this ) ;
233
117
}
234
118
} else {
235
119
this . hoveredLevel = - 1 ;
236
120
this . hoveredNode = null ;
237
- this . renderView ( ) ;
121
+ renderView . call ( this ) ;
238
122
}
239
123
} ;
240
124
this . mouseLeaveHandler = ( ) => {
241
125
this . hoveredLevel = - 1 ;
242
126
this . hoveredNode = null ;
243
- this . renderView ( ) ;
127
+ renderView . call ( this ) ;
244
128
} ;
245
129
}
246
- initEventListener ( ) {
247
- const { canvasScale } = this . viewParams ;
248
- this . node . addEventListener ( 'mousemove' , ( e ) => {
249
- const { offsetX, offsetY } = e ;
250
- const x = offsetX * canvasScale ;
251
- const y = offsetY * canvasScale ;
252
- this . mouseMoveHandler && this . mouseMoveHandler ( { x, y } ) ;
253
- } ) ;
254
- this . node . addEventListener ( 'click' , ( e ) => {
255
- const { offsetX, offsetY } = e ;
256
- const x = offsetX * canvasScale ;
257
- const y = offsetY * canvasScale ;
258
- this . mouseClickHandler && this . mouseClickHandler ( { x, y } ) ;
259
- } ) ;
260
- this . node . addEventListener ( 'mouseleave' , ( ) => {
261
- this . mouseLeaveHandler && this . mouseLeaveHandler ( ) ;
262
- } ) ;
263
- }
264
- renderView ( ) {
265
- clearCanvas . call ( this ) ;
266
-
267
- const highlightNode = this . clickedNode || this . hoveredNode ;
268
-
269
- for ( let i = 0 ; i < this . overviewNodesLevels . length ; i ++ ) {
270
- const { nodes, level } = this . overviewNodesLevels [ i ] ;
271
-
272
- const baseLinks =
273
- i > 1
274
- ? nodes . reduce (
275
- ( acc , node ) =>
276
- acc . concat (
277
- node . links . map ( ( targetId ) => ( {
278
- source : getNodeIdWithLevel ( node . id , level ) ,
279
- target : getNodeIdWithLevel ( targetId , level ) ,
280
- } ) )
281
- ) ,
282
- [ ] as TD3Link [ ]
283
- )
284
- : [ ] ;
285
- const pathFromEntryLinks =
286
- ( highlightNode ?. pathFromEntry
287
- . map ( ( idWithLevel , k ) => {
288
- const [ _level , id ] = parseNodeIdWidthLevel ( idWithLevel ) ;
289
- if ( k > 0 && _level === level ) {
290
- return {
291
- source : highlightNode . pathFromEntry [ k - 1 ] ,
292
- target : idWithLevel ,
293
- } ;
294
- }
295
- return null ;
296
- } )
297
- . filter ( ( a ) => a ) as TD3Link [ ] ) || [ ] ;
298
- const path2NeighborLinks =
299
- highlightNode && level === highlightNode . level
300
- ? highlightNode . links . map (
301
- ( neighborId ) =>
302
- ( {
303
- source : highlightNode . idWithLevel ,
304
- target : getNodeIdWithLevel ( neighborId , highlightNode . level ) ,
305
- } as TD3Link )
306
- )
307
- : [ ] ;
308
-
309
- renderLayer . call ( this , this . overviewLayerPosLevels [ i ] ) ;
310
- renderLinks . call ( this , baseLinks , pathFromEntryLinks , path2NeighborLinks ) ;
311
- }
312
-
313
- renderNodes . call ( this ) ;
314
-
315
- if ( ! ! this . hoveredNode ) {
316
- const nodePos = this . hoveredNode . overviewPos ;
317
- const origin = vecMultiply (
318
- vecAdd (
319
- this . overviewLayerPosLevels [ 0 ] [ 0 ] ,
320
- this . overviewLayerPosLevels [ 0 ] [ 2 ]
321
- ) ,
322
- 0.5
323
- ) ;
324
- const reverse = this . hoveredNode . overviewPos [ 0 ] < origin [ 0 ] ;
325
- const tooltipPos = renderTipLine . call ( this , nodePos , reverse ) ;
326
- this . updateHoveredPanel (
327
- vecMultiply ( tooltipPos , 1 / this . viewParams . canvasScale ) as TCoord ,
328
- reverse
329
- ) ;
330
- } else this . updateHoveredPanel ( null ) ;
331
- }
332
130
}
0 commit comments