@@ -29,9 +29,11 @@ export class DotLayout extends go.Layout {
2929    if  ( ! graphviz )  throw  new  Error ( `no Graphviz instance was provided to DotLayout` ) ; 
3030    const  proto  =  Object . getPrototypeOf ( graphviz ) ; 
3131    if  ( ! ( proto  &&  typeof  proto . dot  ===  'function' ) )  throw  new  Error ( `provided Graphviz instance must include dot layout function` ) ; 
32+ 
3233    super ( ) ; 
3334    this . isRouting  =  true ; 
34-     this . graphviz  =  graphviz ; 
35+ 
36+     this . _graphviz  =  graphviz ; 
3537
3638    this . _direction  =  'TB' ; 
3739    this . _layerSpacing  =  25 ; 
@@ -45,7 +47,7 @@ export class DotLayout extends go.Layout {
4547   *  
4648   * Valid values are "LR", "TB", "RL", "BT". Defaults to "TB". 
4749   *  
48-    * This corresponds with  the dot layout rankdir attribute: https://graphviz.org/docs/attrs/rankdir/. 
50+    * This corresponds to  the dot layout rankdir attribute: https://graphviz.org/docs/attrs/rankdir/. 
4951   */ 
5052  get  direction ( )  {  return  this . _direction ;  } 
5153  set  direction ( value )  { 
@@ -61,7 +63,7 @@ export class DotLayout extends go.Layout {
6163   *  
6264   * This value must be greater than 1.92 (to correspond to dot's minimum). Defaults to 25. 
6365   *  
64-    * This corresponds with  the dot layout ranksep attribute: https://graphviz.org/docs/attrs/ranksep/. 
66+    * This corresponds to  the dot layout ranksep attribute: https://graphviz.org/docs/attrs/ranksep/. 
6567   */ 
6668  get  layerSpacing ( )  {  return  this . _layerSpacing ;  } 
6769  set  layerSpacing ( value )  { 
@@ -79,7 +81,7 @@ export class DotLayout extends go.Layout {
7981   *  
8082   * This value must be greater than 1.92 (to correspond to dot's minimum). Defaults to 25. 
8183   *  
82-    * This corresponds with  the dot layout nodesep attribute: https://graphviz.org/docs/attrs/nodesep/. 
84+    * This corresponds to  the dot layout nodesep attribute: https://graphviz.org/docs/attrs/nodesep/. 
8385   */ 
8486  get  nodeSpacing ( )  {  return  this . _nodeSpacing ;  } 
8587  set  nodeSpacing ( value )  { 
@@ -92,11 +94,11 @@ export class DotLayout extends go.Layout {
9294
9395  /** 
9496   * Gets or sets whether the fromSpot and toSpot of each link should be used by the layout when routing links. 
95-    * Link routes is  typically more readable when this is left as false. 
97+    * Link routes are  typically more readable when this is left as false. 
9698   *  
9799   * Defaults to false. 
98100   *  
99-    * This corresponds with  the Graphviz edge headport/tailport attributes: 
101+    * This corresponds to  the Graphviz edge headport/tailport attributes: 
100102   * https://graphviz.org/docs/attrs/headport/ 
101103   * https://graphviz.org/docs/attrs/tailport/ 
102104   */ 
@@ -114,7 +116,7 @@ export class DotLayout extends go.Layout {
114116   *  
115117   * This value must be positive. Defaults to NaN, meaning no limit. 
116118   *  
117-    * This corresponds with  the dot layout nslimit attribute: https://graphviz.org/docs/attrs/nslimit/. 
119+    * This corresponds to  the dot layout nslimit attribute: https://graphviz.org/docs/attrs/nslimit/. 
118120   */ 
119121  get  iterations ( )  {  return  this . _iterations ;  } 
120122  set  iterations ( value )  { 
@@ -125,11 +127,35 @@ export class DotLayout extends go.Layout {
125127    } 
126128  } 
127129
130+   /** 
131+    * Creates a copy of this Layout and returns it. 
132+    * @returns  A copied DotLayout 
133+    */ 
134+   copy ( )  { 
135+     const  copy  =  new  ( this . constructor ) ( this . _graphviz ) ; 
136+     this . cloneProtected ( copy ) ; 
137+     return  copy ; 
138+   } 
139+ 
140+   /** 
141+    * Copies properties to a cloned Layout. 
142+    */ 
143+   cloneProtected ( copy )  { 
144+     super . cloneProtected ( copy ) ; 
145+     // don't copy .root 
146+     copy . _direction  =  this . _direction ; 
147+     copy . _layerSpacing  =  this . _layerSpacing ; 
148+     copy . _nodeSpacing  =  this . _nodeSpacing ; 
149+     copy . _usesLinkSpots  =  this . _usesLinkSpots ; 
150+     copy . _iterations  =  this . _iterations ; 
151+   } 
152+ 
128153  /** 
129154   * Use a LayoutNetwork that always creates DotEdges. 
130155   */ 
131156  createNetwork ( )  { 
132157    const  net  =  new  go . LayoutNetwork ( this ) ; 
158+     net . createVertex  =  ( )  =>  new  DotVertex ( net ) ; 
133159    net . createEdge  =  ( )  =>  new  DotEdge ( net ) ; 
134160    return  net ; 
135161  } 
@@ -156,7 +182,7 @@ export class DotLayout extends go.Layout {
156182    const  dot  =  this . generateDot ( ) ; 
157183
158184    // perform Graphviz dot layout via Graphviz WASM 
159-     const  jsonStr  =  this . graphviz . dot ( dot ,  'json0' ) ; 
185+     const  jsonStr  =  this . _graphviz . dot ( dot ,  'json0' ) ; 
160186    const  json  =  JSON . parse ( jsonStr ) ; 
161187
162188    const  h  =  parseFloat ( json . bb . substring ( json . bb . lastIndexOf ( ',' )  +  1 ) ) ;   // save height so we can convert y values 
@@ -179,7 +205,7 @@ export class DotLayout extends go.Layout {
179205          const  { x,  y}  =  this . parsePos ( ptStr ,  h ) ; 
180206          ptList . add ( new  go . Point ( x ,  y ) ) ; 
181207        } 
182-         edge . pts  =  ptList ; 
208+         edge . _pts  =  ptList ; 
183209      } 
184210    } 
185211
@@ -206,16 +232,21 @@ export class DotLayout extends go.Layout {
206232  ranksep=${ ranksep }  
207233  nodesep=${ nodesep }  
208234  ${ nslimit }  
209-   node [shape="box" fixedsize=true fontname="arial" ] 
235+   node [shape="box" fixedsize=true] 
210236  edge [arrowhead="none"]\n` ; 
211237    const  vit  =  this . network . vertexes . iterator ; 
212238    while  ( vit . next ( ) )  { 
213239        const  v  =  vit . value ; 
214240        const  node  =  v . node ; 
215241        if  ( ! node )  continue ; 
216242        if  ( node  instanceof  go . Group )  throw  new  Error ( 'DotLayout does not currently support Groups.' ) ; 
243+         this . assignVertexProperties ( v ) ; 
244+         // prepare DOT language attributes for non-default vertex properties 
245+         const  width  =  ! isNaN ( v . width )  ? `width=${ v . width  /  96 }   : '' ; 
246+         const  height  =  ! isNaN ( v . height )  ? ` height=${ v . height  /  96 }   : '' ; 
247+         const  shape  =  v . shape  !==  'box'  ? ` shape="${ v . shape }   : '' ; 
217248        // divide size by 96 since Graphviz expects inches 
218-         dot  +=  `  ${ node . key } width= ${ node . actualBounds . width   /   96 }  height= ${ node . actualBounds . height   /   96 }  ; 
249+         dot  +=  `  ${ node . key } ${ width } ${ height } ${ shape }  ; 
219250    } 
220251    const  eit  =  this . network . edges . iterator ; 
221252    while  ( eit . next ( ) )  { 
@@ -225,31 +256,38 @@ export class DotLayout extends go.Layout {
225256        const  fromNode  =  link . fromNode ; 
226257        const  toNode  =  link . toNode ; 
227258        if  ( ! ( fromNode  &&  toNode ) )  continue ; 
259+         this . assignEdgeProperties ( e ) ; 
260+         // prepare DOT language attributes for non-default edge properties 
228261        const  constraint  =  ! e . isConstraint  ? ' constraint=false'  : '' ; 
229-         let  tailport  =  '' ; 
230-         let  headport  =  '' ; 
231-         if  ( this . usesLinkSpots )  { 
232-           tailport  =  this . getPortAttr ( link . fromSpot ,  true ) ; 
233-           headport  =  this . getPortAttr ( link . toSpot ,  false ) ; 
234-         } 
262+         const  tailport  =  e . tailport  !==  'c'  ? ` tailport=${ e . tailport }   : '' ; 
263+         const  headport  =  e . headport  !==  'c'  ? ` headport=${ e . headport }   : '' ; 
264+         const  weight  =  e . weight  !==  1  ? ` weight=${ e . weight }   : '' ; 
235265        // include the Link key as an ID for quick lookup from the output 
236-         dot  +=  `  ${ fromNode . key } ${ toNode . key } ${ link . key } ${ constraint } ${ tailport } ${ headport }  ; 
266+         dot  +=  `  ${ fromNode . key } ${ toNode . key } ${ link . key } ${ constraint } ${ tailport } ${ headport } ${ weight }  ; 
237267    } 
238268    dot  +=  '}' ; 
239269    return  dot ; 
240270  } 
241271
242272  /** 
243-    * Parse a pos string into an x-y pair, normalizing  to a top-left origin and pixels . 
244-    * @param  { * } str A Graphviz "pos" property string  
245-    * @param  { * } h The height, in points, of the Graphviz output graph  
246-    * @returns   An x, y pair representing a point  
273+    * Override this method in order to set vertex properties prior  to the dot layout being performed . 
274+    *  
275+    * By default, this method does nothing.  
276+    * @param   { * } v The vertex for which properties can be assigned  
247277   */ 
248-   parsePos ( str ,  h )  { 
249-     const  idx  =  str . indexOf ( ',' ) ; 
250-     const  x  =  parseFloat ( str . substring ( 0 ,  idx ) )  *  96  /  72 ; 
251-     const  y  =  ( h  -  parseFloat ( str . substring ( idx  +  1 ) )  +  1 )  *  96  /  72 ; 
252-     return  {  x,  y } ; 
278+   assignVertexProperties ( v )  {  } 
279+ 
280+   /** 
281+    * Override this method in order to set edge properties prior to the dot layout being performed. 
282+    *  
283+    * By default, this method sets the headport and tailport properties if usesLinkSpots is true. 
284+    * @param  {* } e The edge for which properties can be assigned 
285+    */ 
286+   assignEdgeProperties ( e )  { 
287+     if  ( this . usesLinkSpots )  { 
288+       e . tailport  =  this . getPortAttr ( e . link . fromSpot ,  true ) ; 
289+       e . headport  =  this . getPortAttr ( e . link . toSpot ,  false ) ; 
290+     } 
253291  } 
254292
255293  /** 
@@ -283,27 +321,78 @@ export class DotLayout extends go.Layout {
283321      case  go . Spot . Default : port  =  'c' ;  break ; 
284322      default : port  =  '_' ;  break ; 
285323    } 
324+     return  port ; 
325+   } 
326+ 
327+   /** 
328+    * Parse a pos string into an x-y pair, normalizing to a top-left origin and pixels. 
329+    * @param  {* } str A Graphviz "pos" property string 
330+    * @param  {* } h The height, in points, of the Graphviz output graph 
331+    * @returns  An x, y pair representing a point 
332+    */ 
333+   parsePos ( str ,  h )  { 
334+     const  idx  =  str . indexOf ( ',' ) ; 
335+     const  x  =  parseFloat ( str . substring ( 0 ,  idx ) )  *  96  /  72 ; 
336+     const  y  =  ( h  -  parseFloat ( str . substring ( idx  +  1 ) )  +  1 )  *  96  /  72 ; 
337+     return  {  x,  y } ; 
338+   } 
339+ } 
286340
287-     return  isfrom  ? ` tailport=${ port }   : ` headport=${ port }  ; 
341+ /** 
342+  * DotVertex, a LayoutVertex that holds additional info specific to dot layouts. 
343+  */ 
344+ class  DotVertex  extends  go . LayoutVertex  { 
345+   constructor ( network )  { 
346+     super ( network ) ; 
347+ 
348+     // default to NaN so we can ensure they're set 
349+     this . width  =  NaN ; 
350+     this . height  =  NaN ; 
351+ 
352+     this . _shape  =  'box' ; 
353+   } 
354+ 
355+   /** 
356+    * Gets or sets the shape of this vertex. 
357+    *  
358+    * Valid values can be found here: https://graphviz.org/doc/info/shapes.html#polygon. Defaults to "box". 
359+    *  
360+    * This property can be set during assignVertexProperties. 
361+    *  
362+    * This corresponds to the Graphviz node shape attribute: https://graphviz.org/docs/attrs/shape/. 
363+    */ 
364+   get  shape ( )  {  return  this . _shape ;  } 
365+   set  shape ( value )  { 
366+     if  ( this . _shape  !==  value )  { 
367+       this . _shape  =  value ; 
368+     } 
288369  } 
289370} 
290371
291372/** 
292-  * DotEdge, a LayoutEdge that holds additional info about link points . 
373+  * DotEdge, a LayoutEdge that holds additional info specific to dot layouts . 
293374 */ 
294375class  DotEdge  extends  go . LayoutEdge  { 
376+   static  validPorts  =  [ 'n' ,  'ne' ,  'e' ,  'se' ,  's' ,  'sw' ,  'w' ,  'nw' ,  'c' ,  '_' ] ; 
377+ 
295378  constructor ( network )  { 
296379    super ( network ) ; 
297-     this . pts  =  null ; 
380+     this . _pts  =  null ;   // internal 
381+ 
298382    this . _isConstraint  =  true ; 
383+     this . _headport  =  'c' ; 
384+     this . _tailport  =  'c' ; 
385+     this . _weight  =  1 ; 
299386  } 
300387
301388  /** 
302389   * Gets or sets whether this edge is used in determining node layers. 
303390   *  
304391   * Defaults to true. 
305392   *  
306-    * This corresponds with the Graphviz edge constraint attribute: https://www.graphviz.org/docs/attrs/constraint/. 
393+    * This property can be set during assignEdgeProperties. 
394+    *  
395+    * This corresponds to the Graphviz edge constraint attribute: https://www.graphviz.org/docs/attrs/constraint/. 
307396   */ 
308397  get  isConstraint ( )  {  return  this . _isConstraint ;  } 
309398  set  isConstraint ( value )  { 
@@ -313,7 +402,58 @@ class DotEdge extends go.LayoutEdge {
313402    } 
314403  } 
315404
405+   /** 
406+    * Gets or sets the "compass point" where this edge connects to its to vertex. 
407+    *  
408+    * Valid values are "n", "ne", "e", "se", "s", "sw", "w", "nw", "c", "_". Defaults to "c". 
409+    *  
410+    * This property can be set during assignEdgeProperties. 
411+    *  
412+    * This corresponds to the Graphviz edge headport attribute: https://graphviz.org/docs/attrs/headport/. 
413+    */ 
414+   get  headport ( )  {  return  this . _headport ;  } 
415+   set  headport ( value )  { 
416+     if  ( ! DotEdge . validPorts . includes ( value ) )  throw  new  Error ( `invalid value for DotEdge.headport: ${ value }  ) ; 
417+     if  ( this . _headport  !==  value )  { 
418+       this . _headport  =  value ; 
419+     } 
420+   } 
421+ 
422+   /** 
423+    * Gets or sets the "compass point" where this edge connects to its from vertex. 
424+    *  
425+    * Valid values are "n", "ne", "e", "se", "s", "sw", "w", "nw", "c", "_". Defaults to "c". 
426+    *  
427+    * This property can be set during assignEdgeProperties. 
428+    *  
429+    * This corresponds to the Graphviz edge tailport attribute: https://graphviz.org/docs/attrs/tailport/. 
430+    */ 
431+   get  tailport ( )  {  return  this . _tailport ;  } 
432+   set  tailport ( value )  { 
433+     if  ( ! DotEdge . validPorts . includes ( value ) )  throw  new  Error ( `invalid value for DotEdge.tailport: ${ value }  ) ; 
434+     if  ( this . _tailport  !==  value )  { 
435+       this . _tailport  =  value ; 
436+     } 
437+   } 
438+ 
439+   /** 
440+    * Gets or sets the weight of this edge. 
441+    *  
442+    * This value must be an integer >= 0. Defaults to 1. 
443+    *  
444+    * This property can be set during assignEdgeProperties. 
445+    *  
446+    * This corresponds to the Graphviz edge constraint attribute: https://graphviz.org/docs/attrs/weight/. 
447+    */ 
448+   get  weight ( )  {  return  this . _weight ;  } 
449+   set  weight ( value )  { 
450+     if  ( typeof  value  !==  'number'  ||  value  <  0  ||  ! Number . isInteger ( value ) )  throw  new  Error ( `new value for DotEdge.weight must be an integer >= 0, not: ${ value }  ) ; 
451+     if  ( this . _weight  !==  value )  { 
452+       this . _weight  =  value ; 
453+     } 
454+   } 
455+ 
316456  commit ( )  { 
317-     if  ( this . network . layout . isRouting )  this . link . points  =  this . pts ; 
457+     if  ( this . network . layout . isRouting )  this . link . points  =  this . _pts ; 
318458  } 
319459} 
0 commit comments