1-
2- import { knoxClass , getSBOLImage , splitElementID } from "./knox.js" ;
1+ import { knoxClass , getSBOLImage , splitElementID , condenseVisualization } from "./knox.js" ;
32
43// The target class observes an SVG element on the page, and
54// provides methods for setting and clearing graph data. A variable
@@ -34,45 +33,39 @@ export default class Target{
3433 }
3534
3635 setGraph ( graph ) {
36+ condenseVisualization ( graph ) ;
37+
3738 var zoom = d3 . behavior . zoom ( )
3839 . scaleExtent ( [ 1 , 10 ] )
3940 . on ( "zoom" , ( ) => {
4041 svg . attr ( "transform" , "translate(" + d3 . event . translate + ")scale(" + d3 . event . scale + ")" ) ;
4142 } ) ;
4243
43- var svg = d3 . select ( this . id ) . call ( zoom ) . append ( "svg:g" ) ;
44- svg . append ( "defs" ) . append ( "marker" )
45- . attr ( "id" , "endArrow" )
46- . attr ( "viewBox" , "0 -5 10 10" )
47- . attr ( "refX" , 6 )
48- . attr ( "markerWidth" , 6 )
49- . attr ( "markerHeight" , 6 )
50- . attr ( "orient" , "auto" )
51- . append ( "path" )
52- . attr ( "d" , "M0,-5L10,0L0,5" )
53- . attr ( "fill" , "#999" )
54- . attr ( "opacity" , "0.5" ) ;
55- var force = ( this . layout = d3 . layout . force ( ) ) ;
56- force . drag ( ) . on ( "dragstart" , ( ) => {
57- d3 . event . sourceEvent . stopPropagation ( ) ;
58- } ) ;
59- force . charge ( - 400 ) . linkDistance ( 100 ) ;
60- force . nodes ( graph . nodes ) . links ( graph . links ) . size ( [
61- $ ( this . id ) . parent ( ) . width ( ) , $ ( this . id ) . parent ( ) . height ( )
62- ] ) . start ( ) ;
63-
64- var linksEnter = svg . selectAll ( ".link" )
65- . data ( graph . links )
66- . enter ( ) ;
67-
68- var links = linksEnter . append ( "path" )
69- . attr ( "class" , "link" ) ;
44+ //add SVG container
45+ let svg = d3 . select ( this . id )
46+ . call ( zoom )
47+ . append ( "svg:g" ) ;
48+
49+ //def objects are not displayed until referenced
50+ let defs = svg . append ( "svg:defs" ) ;
51+
52+ let force = ( this . layout = d3 . layout . force ( ) )
53+ . charge ( - 400 )
54+ . linkDistance ( 100 )
55+ . nodes ( graph . nodes )
56+ . links ( graph . links )
57+ . size ( [ $ ( this . id ) . parent ( ) . width ( ) , $ ( this . id ) . parent ( ) . height ( ) ] )
58+ . start ( ) ;
59+ force . drag ( ) . on ( "dragstart" , ( ) => {
60+ d3 . event . sourceEvent . stopPropagation ( ) ;
61+ } ) ;
7062
71- var nodesEnter = svg . selectAll ( ".node" )
63+ // add nodes (circles)
64+ let nodesEnter = svg . selectAll ( ".node" )
7265 . data ( graph . nodes )
7366 . enter ( ) ;
7467
75- var circles = nodesEnter . append ( "circle" )
68+ let circles = nodesEnter . append ( "circle" )
7669 . attr ( "class" , function ( d ) {
7770 if ( d . nodeTypes . length === 0 ) {
7871 return "node" ;
@@ -82,14 +75,60 @@ export default class Target{
8275 return "accept-node" ;
8376 }
8477 } )
85- . attr ( "r" , 7 ) . call ( force . drag ) ;
78+ . attr ( "r" , 7 ) //radius
79+ . call ( force . drag ) ;
8680
87- const sbolImgSize = 30 ;
81+ // Filter out links if the "show" flag is false
82+ let linksEnter = svg . selectAll ( ".link" )
83+ . data ( graph . links . filter ( link => link . show ) )
84+ . enter ( ) ;
85+
86+ function marker ( isBlank ) {
87+
88+ let fill = isBlank ? "none" : "#999" ;
89+ let id = "arrow" + fill . replace ( "#" , "" ) ;
90+
91+ defs . append ( "svg:marker" )
92+ . attr ( "id" , id )
93+ . attr ( "viewBox" , "0 -5 10 10" )
94+ . attr ( "refX" , 6 )
95+ . attr ( "markerWidth" , 6 )
96+ . attr ( "markerHeight" , 6 )
97+ . attr ( "orient" , "auto" )
98+ . append ( "svg:path" )
99+ . attr ( "d" , "M0,-5L10,0L0,5" )
100+ . attr ( "stroke" , "#999" )
101+ . attr ( "fill" , fill ) ;
102+
103+ return "url(#" + id + ")" ;
104+ }
105+
106+ // Optional links will be rendered as dashed lines
107+ // Blank edges will be rendered with an unfilled arrow
108+ let links = linksEnter . append ( "path" )
109+ . attr ( "class" , ( l ) => {
110+ if ( l . optional ) {
111+ return "link dashed-link" ;
112+ }
113+ return "link"
114+ } )
115+ . attr ( "d" , "M0,-5L10,0L0,5" )
116+ . attr ( "marker-end" , ( l ) => {
117+ let isBlank = l [ "componentRoles" ] . length === 0 && l [ "componentIDs" ] . length === 0 ;
118+ return marker ( isBlank ) ;
119+ } ) ;
88120
121+ //place SBOL svg on links
122+ const sbolImgSize = 30 ;
89123 let images = linksEnter . append ( "svg:image" )
90124 . attr ( "height" , sbolImgSize )
91125 . attr ( "width" , sbolImgSize )
92- . attr ( "class" , "sboltip" )
126+ . attr ( "class" , ( d ) => {
127+ if ( d . hasOwnProperty ( "componentRoles" ) && d [ "componentRoles" ] . length > 0 ) {
128+ return "sboltip" ;
129+ }
130+ return null ;
131+ } )
93132 . attr ( "title" , ( d ) => {
94133 if ( d . hasOwnProperty ( "componentIDs" ) ) {
95134 let titleStr = "" ;
@@ -104,21 +143,34 @@ export default class Target{
104143 return titleStr ;
105144 }
106145 } )
107- . attr ( "xlink:href" , ( d ) => {
146+ . attr ( "href" , ( d ) => {
147+ if ( d . hasOwnProperty ( "componentRoles" ) && d [ "componentRoles" ] . length > 0 ) {
148+ return getSBOLImage ( d [ "componentRoles" ] [ 0 ] ) ;
149+ }
150+ return null ;
151+ } ) ;
152+
153+ let reverseImgs = linksEnter . append ( "svg:image" )
154+ . attr ( "height" , sbolImgSize )
155+ . attr ( "width" , sbolImgSize )
156+ . attr ( "href" , ( d ) => {
108157 if ( d . hasOwnProperty ( "componentRoles" ) ) {
109- if ( d [ "componentRoles" ] . length > 0 ) {
110- let role = d [ "componentRoles" ] [ 0 ] ;
111- return getSBOLImage ( role ) ;
158+ if ( d [ "componentRoles" ] . length > 0 && d . hasReverseOrient ) {
159+ return getSBOLImage ( d [ "componentRoles" ] [ 0 ] ) ;
112160 }
113161 }
114- return "" ;
115- } ) ;
162+ return null ;
163+ } ) ;
116164
165+ //place tooltip on the SVG images
117166 $ ( '.sboltip' ) . tooltipster ( {
118167 theme : 'tooltipster-shadow'
119168 } ) ;
120169
170+ // Handles positioning when moved
121171 force . on ( "tick" , function ( ) {
172+
173+ // Position links
122174 links . attr ( 'd' , function ( d ) {
123175 var deltaX = d . target . x - d . source . x ,
124176 deltaY = d . target . y - d . source . y ,
@@ -134,18 +186,43 @@ export default class Target{
134186 return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY ;
135187 } ) ;
136188
189+ // Position circles
137190 circles . attr ( "cx" , function ( d ) {
138191 return d . x ;
139192 } )
140193 . attr ( "cy" , function ( d ) {
141194 return d . y ;
142195 } ) ;
143196
197+ // Position SBOL images
144198 images . attr ( "x" , function ( d ) {
145- return ( d . source . x + d . target . x ) / 2 - sbolImgSize / 2 ;
199+ if ( d . hasReverseOrient ) {
200+ return ( d . source . x + d . target . x ) / 2 - sbolImgSize ;
201+ }
202+ return ( d . source . x + d . target . x ) / 2 - sbolImgSize / 2 ;
203+ } )
204+ . attr ( "y" , function ( d ) {
205+ return ( d . source . y + d . target . y ) / 2 - sbolImgSize / 2 ;
206+ } )
207+ . attr ( 'transform' , function ( d ) {
208+ //transform 180 if the orientation is REVERSE_COMPLEMENT
209+ if ( d . orientation === "REVERSE_COMPLEMENT" && ! d . hasReverseOrient ) {
210+ let x1 = ( d . source . x + d . target . x ) / 2 ; //the center x about which you want to rotate
211+ let y1 = ( d . source . y + d . target . y ) / 2 ; //the center y about which you want to rotate
212+ return `rotate(180, ${ x1 } , ${ y1 } )` ;
213+ }
214+ } ) ;
215+
216+ reverseImgs . attr ( "x" , function ( d ) {
217+ return ( d . source . x + d . target . x ) / 2 - sbolImgSize ;
146218 } )
147219 . attr ( "y" , function ( d ) {
148220 return ( d . source . y + d . target . y ) / 2 - sbolImgSize / 2 ;
221+ } )
222+ . attr ( 'transform' , function ( d ) {
223+ let x1 = ( d . source . x + d . target . x ) / 2 ; //the center x about which you want to rotate
224+ let y1 = ( d . source . y + d . target . y ) / 2 ; //the center y about which you want to rotate
225+ return `rotate(180, ${ x1 } , ${ y1 } )` ;
149226 } ) ;
150227 } ) ;
151228 }
0 commit comments