@@ -21,12 +21,14 @@ type Node = {
2121 vx : number ;
2222 vy : number ;
2323 className : string | null ;
24+ longestLinkLength : number ;
2425} ;
2526
2627type Link = {
2728 source : string ;
2829 target : string ;
2930 data : N3 . Quad ;
31+ name : string ;
3032} ;
3133
3234type GraphRef = ForceGraphMethods <
@@ -60,7 +62,8 @@ function mkNode(data: N3.Quad_Object, prefixes: Record<string, string>): Node {
6062 data,
6163 vx : Math . random ( ) * 2.0 - 1.0 ,
6264 vy : Math . random ( ) * 2.0 - 1.0 ,
63- className : null
65+ className : null ,
66+ longestLinkLength : 0
6467 } ;
6568}
6669
@@ -113,6 +116,84 @@ function pointerAreaPaint(
113116 ctx . fillRect ( n . x ! - width * 0.5 , n . y ! - height * 0.5 , width , height ) ;
114117}
115118
119+ export function boxCollision (
120+ w : number ,
121+ h : number ,
122+ dx : number ,
123+ dy : number
124+ ) : number {
125+ const ax = Math . abs ( dx ) ;
126+ const ay = Math . abs ( dy ) ;
127+
128+ return Math . min ( ( w * 0.5 ) / ax , ( h * 0.5 ) / ay ) ;
129+ }
130+
131+ function renderLink (
132+ l : LinkObject < Node , Link > ,
133+ ctx : CanvasRenderingContext2D
134+ ) : void {
135+ const s = l . source as Node ;
136+ const t = l . target as Node ;
137+
138+ if ( typeof s !== "object" || typeof t !== "object" ) {
139+ return ;
140+ }
141+
142+ ctx . strokeStyle = "rgba(255, 255, 255, 0.25)" ;
143+ ctx . lineWidth = 2 ;
144+ ctx . lineJoin = "bevel" ;
145+
146+ const [ sw , sh ] = getNodeSize ( s ) ;
147+ const [ tw , th ] = getNodeSize ( t ) ;
148+ let dx = t . x ! - s . x ! ;
149+ let dy = t . y ! - s . y ! ;
150+
151+ const len = Math . sqrt ( dx * dx + dy * dy ) ;
152+ dx /= len ;
153+ dy /= len ;
154+
155+ const o1 = boxCollision ( sw , sh , dx , dy ) ;
156+ const o2 = boxCollision ( tw , th , - dx , - dy ) ;
157+
158+ const tx = t . x ! - dx * o2 ;
159+ const ty = t . y ! - dy * o2 ;
160+ const arrowBack = 5 ;
161+ const arrowSide = 5 ;
162+
163+ ctx . beginPath ( ) ;
164+ ctx . moveTo ( s . x ! + dx * o1 , s . y ! + dy * o1 ) ;
165+ ctx . lineTo ( tx , ty ) ;
166+ ctx . lineTo (
167+ tx - dx * arrowBack + dy * arrowSide ,
168+ ty - dy * arrowBack - dx * arrowSide
169+ ) ;
170+ ctx . lineTo ( tx , ty ) ;
171+ ctx . lineTo (
172+ tx - dx * arrowBack - dy * arrowSide ,
173+ ty - dy * arrowBack + dx * arrowSide
174+ ) ;
175+ ctx . stroke ( ) ;
176+
177+ const origTransform = ctx . getTransform ( ) ;
178+ const angle = Math . atan2 ( dy , dx ) ;
179+
180+ ctx . translate ( s . x ! , s . y ! ) ;
181+ ctx . rotate ( Math . atan2 ( dy , dx ) ) ;
182+ ctx . translate ( len * 0.5 , 0 ) ;
183+
184+ if ( angle < - Math . PI * 0.5 || angle > Math . PI * 0.5 ) {
185+ ctx . rotate ( Math . PI ) ;
186+ }
187+
188+ ctx . font = "8px monospace" ;
189+ ctx . textAlign = "center" ;
190+ ctx . textBaseline = "middle" ;
191+ ctx . fillStyle = "rgba(255, 255, 255, 0.5)" ;
192+ ctx . fillText ( l . name , 0 , 8 ) ;
193+
194+ ctx . setTransform ( origTransform ) ;
195+ }
196+
116197const GraphView = React . memo ( function GraphView ( {
117198 data
118199} : GraphViewProps ) : React . JSX . Element {
@@ -148,11 +229,24 @@ const GraphView = React.memo(function GraphView({
148229 nodeMap . set ( object . id , object ) ;
149230 }
150231
232+ const linkName = stripPrefix ( q . predicate . value , prefixes ) ;
233+
151234 links . push ( {
152235 source : subject . id ,
153236 target : object . id ,
154- data : q
237+ data : q ,
238+ name : linkName
155239 } ) ;
240+
241+ subject . longestLinkLength = Math . max (
242+ subject . longestLinkLength ,
243+ linkName . length
244+ ) ;
245+
246+ object . longestLinkLength = Math . max (
247+ object . longestLinkLength ,
248+ linkName . length
249+ ) ;
156250 }
157251
158252 for ( const q of types ) {
@@ -180,7 +274,9 @@ const GraphView = React.memo(function GraphView({
180274 "collide" ,
181275 forceCollide ( n => {
182276 const [ w , h ] = getNodeSize ( n as Node ) ;
183- return Math . max ( w , h ) * 0.5 + 5 ;
277+ const linkSz = ( n as Node ) . longestLinkLength * 6 ;
278+
279+ return Math . max ( w , h , linkSz ) * 0.5 + 5 ;
184280 } )
185281 ) ;
186282
@@ -193,11 +289,7 @@ const GraphView = React.memo(function GraphView({
193289 graphData = { graphData }
194290 nodeCanvasObject = { renderNode }
195291 nodePointerAreaPaint = { pointerAreaPaint }
196- linkColor = { ( ) => "rgba(255, 255, 255, 0.25)" }
197- linkWidth = { 2 }
198- linkDirectionalArrowLength = { 10 }
199- linkDirectionalArrowRelPos = { 1.0 }
200- linkCurvature = { 0.1 }
292+ linkCanvasObject = { renderLink }
201293 ref = { graphRef as RefObject < GraphRef > }
202294 cooldownTime = { Number . POSITIVE_INFINITY }
203295 />
0 commit comments