UNPKG

fieldkit

Version:

Basic building blocks for computational design projects. Written in CoffeeScript for browser and server environments.

180 lines (131 loc) 4.17 kB
### Force Directed Graph ### class GNode children: [] particle: null constructor: (@name) -> @children = [] addNode: (name) -> node = new GNode @name + "/" + name @children.push node node getWeight: -> weight = @children.length for child in @children weight += child.getWeight() weight init: (graph, center, radius, parentSize = 0) -> physics = graph.physics weight = @getWeight() @particle = physics.emitter.create() @particle.size = Math.min weight / 2 + 3, 30 @particle.position.set(center) alpha = Math.random() * Math.PI * 2 jitter = (parentSize + @particle.size) * 2 + Math.random() * 10 @particle.position.x += Math.sin(alpha) * jitter @particle.position.y += Math.cos(alpha) * jitter @particle.clearVelocity() @particle.color = new fk.Color(1, 1, 0) @particle.color.randomize() for child in @children radius = weight * 0.5 + @particle.size * 4 child.init graph, @particle.position, radius, @particle.size spring = graph.addLink @particle, child.particle class Graph root: null constructor: (springIterations) -> @physics = new fk.physics.Physics() # use 2D particles @physics.emitter.type = fk.physics.Particle2 @physics.emitter.rate = 0 @physics.springIterations = springIterations # Behaviours # @physics.add new fk.physics.Force new fk.math.Vec2(0, 1), 0.01 # Constraints collision = new fk.physics.Collision(@physics) @physics.add collision @root = new GNode this, "root" addLink: (parent, child) -> spring = new fk.physics.Spring parent, child @physics.addSpring spring spring # call this after all nodes are created init: (center) -> @root.init this, center, 0 @root.particle.isLocked = true update: -> @physics.update() getBounds: -> rect = new fk.math.Rect(Number.MAX_VALUE, Number.MAX_VALUE, Number.MIN_VALUE, Number.MIN_VALUE) for particle in @physics.particles p = particle.position rect.x1 = p.x if p.x < rect.x1 rect.y1 = p.y if p.y < rect.y1 rect.x2 = p.x if p.x > rect.x2 rect.y2 = p.y if p.y > rect.y2 rect centerGraph: (screenCenter) -> bounds = @getBounds() offset = bounds.center().sub(screenCenter).scale(1) # offset = screenCenter.sub().div(2) console.log "screenCenter: #{screenCenter} bounds: #{bounds.center()} offset: #{offset}" for particle in @physics.particles particle.position.sub offset particle.clearVelocity() class Example extends fk.client.Sketch State = fk.physics.State rng = new fk.math.Random() # Settings maxDepth = 3 springIterations = maxDepth * 16 warmupIterations = 16 setup: -> # create graph structure @graph = new Graph(springIterations) createNodes = (parent, depth=0) -> numChildren = rng.randi 0, (depth * 5) for i in [0..numChildren] child = parent.addNode "c#{i}" if depth < maxDepth createNodes child, depth + 1 createNodes @graph.root # create drawing structure center = new fk.math.Vec2(@width / 2, @height / 2) @graph.init center # run simulation for i in [0..warmupIterations] @graph.update() # find min / max @graph.centerGraph center console.log "created graph with #{@graph.root.getWeight()} nodes" draw: -> # update @graph.update() # draw physics = @graph.physics @background(0) # draw bounds r = @graph.getBounds() @noFill() @stroke(64) @rect r.x1, r.y1, r.width(), r.height() @fill(64) @rect r.center().x, r.center().y, 6, 6 # draw springs @noFill() @stroke 128 @lineWidth 1 for spring in physics.springs @line spring.a.position, spring.b.position # draw particles @noStroke() @lineWidth 2 for particle in physics.particles if particle.state == State.ALIVE if particle == @graph.root.particle @stroke 255 @fill 255,0,0 else @noStroke() @fill 255 @circle particle.position.x, particle.position.y, particle.size * 0.8