Skip to content

Commit 41fdb14

Browse files
author
hdngr
committed
Merge branch 'MDCox-optimization' into release-0.4.1
2 parents a0844ae + 8c2546f commit 41fdb14

15 files changed

Lines changed: 148 additions & 294 deletions

app/scripts/alchemy/API/create.coffee.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# You should have received a copy of the GNU Affero General Public License
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17-
class Alchemy::create
17+
class Alchemy::Create
1818
constructor: (instance)->
1919
@a = instance
2020
nodes: (nodeMap, nodeMaps...) ->

app/scripts/alchemy/API/get.coffee.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# make js array method called ._state
1919
# @a.set. nest set inside of get
2020
21-
Alchemy::get = (instance) ->
21+
Alchemy::Get = (instance) ->
2222
a: instance
2323
_el: []
2424
_elType: null
@@ -60,8 +60,8 @@
6060
elType = @_elType
6161
@_el = do (elType)->
6262
switch elType
63-
when "node" then return _.values a._nodes
64-
when "edge" then return _.flatten _.map a._edges, (e)-> e
63+
when "node" then return a.elements.nodes.val
64+
when "edge" then return a.elements.edges.flat
6565
@_makeChain @_el
6666
6767
elState: (state) ->
@@ -100,14 +100,13 @@
100100
clusterColoursObject
101101
102102
###### ALL METHODS BELOW THIS POINT WILL BE DEPRECATED UPON 1.0 ######
103-
allEdges: ->
104-
_.flatten _.map(@a._edges, (edgeArray) -> e for e in edgeArray)
103+
allEdges: -> @a.elements.nodes.flat
105104
106105
allNodes: (type) ->
107106
if type?
108107
_.filter @a._nodes, (n) -> n if n._nodeType is type
109108
else
110-
_.map @a._nodes, (n) -> n
109+
@a.elements.nodes.val
111110
112111
getNodes: (id, ids...)->
113112
a = @a
@@ -120,4 +119,4 @@
120119
edge_id = "#{id}-#{target}"
121120
@a._edges[edge_id]
122121
else if id? and not target?
123-
@a._nodes[id]._adjacentEdges
122+
@a._nodes[id]._adjacentEdges

app/scripts/alchemy/API/remove.coffee.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# You should have received a copy of the GNU Affero General Public License
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17-
class Alchemy::remove
17+
class Alchemy::Remove
1818
constructor: (instance) ->
1919
@a = instance
2020

app/scripts/alchemy/API/set.coffee.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
# You should have received a copy of the GNU Affero General Public License
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17-
Alchemy::set =
18-
state: (key, value) -> alchemy.state.key = value
17+
Alchemy::Set = (instance)->
18+
a : instance
19+
state: (key, value) -> @a.state.key = value

app/scripts/alchemy/Alchemy.coffee.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ title: Anotated Source
3030
3131
@version = "#VERSION#"
3232
# give access to default conf
33-
@get = new @get @
34-
@remove = new @remove @
35-
@create = new @create @
33+
@get = new @Get @
34+
@remove = new @Remove @
35+
@create = new @Create @
36+
@set = new @Set @
37+
3638
@drawing =
3739
DrawEdge : DrawEdge @
3840
DrawEdges: DrawEdges @
@@ -82,7 +84,7 @@ title: Anotated Source
8284
# The value is an array of edge 'packets', where the length of the array
8385
# is typically 1.
8486
@_edges = {}
85-
87+
8688
# Bind legacy API methods to earlier location
8789
# These will be deprecated on release-1.0
8890
@getNodes = @get.getNodes

app/scripts/alchemy/clustering/clustering.coffee.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
gravity: (k) -> _gravity(k)
5353
5454
identifyClusters: (a)->
55-
nodes = a.get.allNodes()
56-
clusters = _.uniq _.map(_.values(nodes), (node)-> node.getProperties()[a.conf.clusterKey])
55+
nodes = a.elements.nodes.val
56+
clusters = _.uniq _.map(nodes, (node)-> node.getProperties()[a.conf.clusterKey])
5757
@clusterMap = _.zipObject clusters, [0..clusters.length]
5858
5959
getClusterColour: (clusterValue) ->
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
Alchemy::Index = (instance, all)->
3+
a = instance
4+
5+
# Index maintains an index of common mappings/selections of
6+
# nodes and edges to reduce iteration during non-creative/destructive
7+
# operation of alchemy.
8+
9+
# a.elements is set with base iterations that other iterations are made
10+
# from. These are seperated out so that other indexes can use them
11+
# during creation instead of recreating them during index.
12+
13+
# ORDERING IS IMPORTANT!
14+
# Before reordering or adding a new index, see if you can
15+
# use a previous index in it's creation.
16+
17+
elements =
18+
nodes:
19+
val: do -> _.values a._nodes
20+
edges:
21+
val: do -> _.values a._edges
22+
23+
nodes = elements.nodes
24+
edges = elements.edges
25+
26+
elements.edges.flat = do -> _.flatten edges.val
27+
28+
elements.nodes.d3 = do -> _.map nodes.val, (n)-> n._d3
29+
elements.edges.d3 = do -> _.map edges.flat, (e)-> e._d3
30+
31+
a.elements = elements
32+
33+
() ->
34+
35+
# Auxiliary indexes.
36+
a.elements.nodes.svg = do -> a.vis.selectAll 'g.node'
37+
38+
a.elements.edges.svg = do -> a.vis.selectAll 'g.edge'

app/scripts/alchemy/core/interactions.coffee.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
a = instance
1919
2020
edgeClick: (d) ->
21-
a = d.self.a
22-
21+
# Don't consider drag a click
22+
return if d3.event.defaultPrevented
23+
# Don't tell alchemy about the click
2324
d3.event.stopPropagation()
25+
# Convert d3.edge to alchemy.edge
2426
edge = d.self
27+
28+
if typeof a.conf.edgeClick is 'function'
29+
a.conf.edgeClick(edge)
2530
if edge._state != "hidden"
2631
edge._state = do ->
2732
return "active" if edge._state is "selected"
2833
"selected"
2934
edge.setStyles()
30-
if typeof a.conf.edgeClick? is 'function'
31-
a.conf.edgeClick()
3235
3336
edgeMouseOver: (d) ->
3437
edge = d.self
@@ -70,18 +73,19 @@
7073
nodeClick: (n) ->
7174
# Don't consider drag a click
7275
return if d3.event.defaultPrevented
73-
76+
# Don't tell alchemy about the click
7477
d3.event.stopPropagation()
78+
# Convert d3.node to alchemy.node
7579
node = n.self
7680
81+
if typeof a.conf.nodeClick is 'function'
82+
a.conf.nodeClick(node)
83+
7784
if node._state != "hidden"
7885
node._state = do ->
7986
return "active" if node._state is "selected"
8087
"selected"
8188
node.setStyles()
82-
83-
if typeof a.conf.nodeClick is 'function'
84-
a.conf.nodeClick(n)
8589
8690
zoom: (extent) ->
8791
if not @_zoomBehavior?

app/scripts/alchemy/core/layout.coffee.md

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
1717
class Layout
1818
constructor: (instance)->
19-
@a = instance
19+
@a = a = instance
2020
2121
conf = @a.conf
2222
nodes = @a._nodes
2323
2424
@k = Math.sqrt Math.log(_.size(@a._nodes)) / (conf.graphWidth() * conf.graphHeight())
2525
@_clustering = new @a.clustering @a
26-
@d3NodeInternals = _.map @a._nodes, (v,k)-> v._d3
26+
@d3NodeInternals = a.elements.nodes.d3
2727
2828
if conf.cluster
2929
@_charge = () -> @_clustering.layout.charge
@@ -46,8 +46,6 @@
4646
else if typeof conf.linkDistancefn is 'function'
4747
@_linkDistancefn = (edge) -> conf.linkDistancefn(edge)
4848
49-
50-
5149
gravity: () =>
5250
if @a.conf.cluster
5351
@_clustering.layout.gravity @k
@@ -83,17 +81,19 @@
8381
y1 > ny2 or
8482
y2 < ny1
8583
86-
tick: () =>
87-
if @a.conf.collisionDetection
84+
tick: (draw) =>
85+
a = @a
86+
nodes = a.elements.nodes.svg
87+
edges = a.elements.edges.svg
88+
89+
if a.conf.collisionDetection
8890
q = d3.geom.quadtree @d3NodeInternals
8991
for node in @d3NodeInternals
9092
q.visit @collide(node)
91-
@a.vis
92-
.selectAll "g.node"
93-
.attr "transform", (d) -> "translate(#{d.x},#{d.y})"
9493
95-
edges = @a.vis.selectAll "g.edge"
96-
@drawEdge = @a.drawing.DrawEdge
94+
nodes.attr "transform", (d) -> "translate(#{d.x},#{d.y})"
95+
96+
@drawEdge = a.drawing.DrawEdge
9797
@drawEdge.styleText edges
9898
@drawEdge.styleLink edges
9999
@@ -103,7 +103,7 @@
103103
width: conf.graphWidth()
104104
height: conf.graphHeight()
105105
106-
rootNodes = _.filter @a.get.allNodes(), (node) -> node.getProperties('root')
106+
rootNodes = _.filter @a.elements.nodes.val, (node) -> node.getProperties('root')
107107
# if there is one root node, position it in the center
108108
if rootNodes.length is 1
109109
n = rootNodes[0]
@@ -119,31 +119,27 @@
119119
n._d3.y = container.height / 2
120120
n._d3.fixed = true
121121
122-
chargeDistance: () ->
123-
500
122+
chargeDistance: () -> 500
124123
125-
linkDistancefn: (edge) ->
126-
@_linkDistancefn edge
124+
linkDistancefn: (edge) -> @_linkDistancefn edge
127125
128-
charge: () ->
129-
@_charge()
126+
charge: () -> @_charge()
130127
131128
Alchemy::generateLayout = (instance)->
132129
a = instance
133130
(start=false)->
134131
conf = a.conf
135-
136132
a.layout = new Layout a
137133
a.force = d3.layout.force()
138134
.size [conf.graphWidth(), conf.graphHeight()]
139135
.theta 1.0
140136
.gravity a.layout.gravity()
141137
.friction a.layout.friction()
142138
143-
.nodes _.map(a._nodes, (node) -> node._d3)
144-
.links _.flatten _.map(a._edges, (edgeArray) -> e._d3 for e in edgeArray)
139+
.nodes a.elements.nodes.d3
140+
.links a.elements.edges.d3
145141
.linkDistance (link) -> a.layout.linkDistancefn link
146142
.linkStrength (link) -> a.layout.linkStrength link
147143
148144
.charge a.layout.charge()
149-
.chargeDistance a.layout.chargeDistance()
145+
.chargeDistance a.layout.chargeDistance()

app/scripts/alchemy/core/startGraph.coffee.md

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
if d3.select(conf.divSelector).empty()
2424
console.warn a.utils.warnings.divWarning()
2525
26-
# see if data is ok
26+
# Verify passed in data
2727
if not data
28+
data =
29+
nodes:[]
30+
edges:[]
2831
a.utils.warnings.dataWarning()
32+
if not data.edges? then data.edges = []
2933
3034
# create nodes map and update links
3135
a.create.nodes data.nodes
@@ -40,7 +44,7 @@
4044
.attr "xlink", "http://www.w3.org/1999/xlink"
4145
.attr "pointer-events", "all"
4246
.attr "style", "background:#{conf.backgroundColour};"
43-
.attr "alchInst", Alchemy::instances.length
47+
.attr "alchInst", (Alchemy::instances.length - 1)
4448
.on 'click', a.interactions.deselectAll
4549
.call a.interactions.zoom(conf.scaleExtent)
4650
.on "dblclick.zoom", null
@@ -51,34 +55,37 @@
5155
a.interactions.zoom().scale conf.initialScale
5256
a.interactions.zoom().translate conf.initialTranslate
5357
58+
a.index = Alchemy::Index a
59+
5460
a.generateLayout()
5561
a.controlDash.init()
5662
5763
#enter/exit nodes/edges
58-
d3Edges = _.flatten _.map(a._edges, (edgeArray) -> e._d3 for e in edgeArray)
59-
d3Nodes = _.map a._nodes, (n) -> n._d3
64+
d3Edges = a.elements.edges.d3
65+
d3Nodes = a.elements.nodes.d3
6066
6167
# if start
6268
a.layout.positionRootNodes()
6369
a.force.start()
64-
while a.force.alpha() > 0.005
65-
a.force.tick()
70+
if conf.forceLocked
71+
while a.force.alpha() > 0.005
72+
a.force.tick()
6673
6774
a._drawEdges = a.drawing.DrawEdges
68-
a._drawEdges.createEdge d3Edges
6975
a._drawNodes = a.drawing.DrawNodes
76+
77+
a._drawEdges.createEdge d3Edges
7078
a._drawNodes.createNode d3Nodes
7179
72-
initialComputationDone = true
73-
console.log Date() + ' completed initial computation'
80+
a.index()
7481
75-
nodes = a.vis.selectAll 'g.node'
76-
.attr 'transform', (id, i) -> "translate(#{id.x}, #{id.y})"
82+
a.elements.nodes.svg
83+
.attr "transform", (id,i)-> "translate(#{id.x}, #{id.y})"
7784
78-
# configuration for forceLocked
85+
console.log Date() + ' completed initial computation'
86+
7987
if !conf.forceLocked
80-
a.force
81-
.on "tick", a.layout.tick
88+
a.force.on "tick", a.layout.tick
8289
.start()
8390
8491
# call user-specified functions after load function if specified
@@ -89,25 +96,9 @@
8996
else if typeof conf.afterLoad is 'string'
9097
a[conf.afterLoad] = true
9198
92-
if conf.cluster or conf.directedEdges
99+
if conf.cluster
93100
defs = d3.select("#{a.conf.divSelector} svg").append "svg:defs"
94101
95-
if conf.directedEdges
96-
arrowSize = conf.edgeArrowSize + (conf.edgeWidth() * 2)
97-
marker = defs.append "svg:marker"
98-
.attr "id", "arrow"
99-
.attr "viewBox", "0 -#{arrowSize * 0.4} #{arrowSize} #{arrowSize}"
100-
.attr 'markerUnits', 'userSpaceOnUse'
101-
.attr "markerWidth", arrowSize
102-
.attr "markerHeight", arrowSize
103-
.attr "orient", "auto"
104-
marker.append "svg:path"
105-
.attr "d", "M #{arrowSize},0 L 0,#{arrowSize * 0.4} L 0,-#{arrowSize * 0.4}"
106-
if conf.curvedEdges
107-
marker.attr "refX", arrowSize + 1
108-
else
109-
marker.attr 'refX', 1
110-
111102
if conf.nodeStats
112103
a.stats.nodeStats()
113104

0 commit comments

Comments
 (0)