Skip to content
Snippets Groups Projects
Select Git revision
  • 6dbce0905765fca2c343beedc6cf42f269d9dc57
  • master default protected
  • leo
  • dex
  • pendulum
  • apfelstruder
  • littlerascal
7 results

example-path-small.js

Blame
  • floopscr.js 7.79 KiB
    // view force layout,
    
    /* ---------------------------    ---------------------------- */
    /* ---------------------- FORCE LAYOUT ----------------------- */
    /* ---------------------------    ---------------------------- */
    
    // ok, my thoughts on this
    /*
    
    when you're up with big programs, spend a day / a handful, just making the UI sing
    - https://bl.ocks.org/mbostock/3750558
    
    at the moment this is kind of 'fine'
     - starting condition is mostly random (and elsewhere) - maybe some graph analysis
      - for who-is-generally-downstream-of-whomst
      - this is nice code golf for boring times when you have lots of graphs
     - still not looking at links for layout force: do that first
     - want to connect this notion with the 'design patterns' ides ...
      - (links) find (comm/*) connected, arrange in a stack
      - (view) finds (link) connected, also stackup ...
       - the links / split through views -> this is actually a lot of the work,
       - and it's not unimportant
    
    */
    
    let blocks = new Array()
    let flsimrun = false
    let flsim = {}
    let flnodes = []
    
    let finAlpha = 0.1
    let sizemultiple = 0.5
    
    // happens when items added, deleted (changing topology)
    let updateForceLoop = () => {
      // init and/or update
      if (!flsimrun && blocks.length > 3) {
        // Case for starting sim
        msgbox.write('starting force sim')
        flsimrun = true
        // start with two nodes
        let positions = this.getAllHunkPositions()
        let sizes = this.getAllHunkSizes()
        for (let i in positions) {
          let nd = {
            index: i,
            x: positions[i].x,
            y: positions[i].y,
            vx: 0,
            vy: 0,
            r: sizes[i].width * sizemultiple
          }
          flnodes.push(nd)
        }
        flsim = d3.forceSimulation(flnodes)
          .force('charge', d3.forceManyBody().strength(250))
          .force('center', d3.forceCenter(600, 600))
          .force('collide', d3.forceCollide((node, i, nodes) => {
            return node.r
          }))
          .alphaMin(finAlpha)
          .on('tick', flTick)
          .on('end', flEnd)
      } else if (blocks.length <= 3) {
        // donot
      } else {
        // case for adding / rming from sim
        msgbox.write('UPD8 Force Sim')
        let positions = this.getAllHunkPositions()
        let sizes = this.getAllHunkSizes()
        if (positions.length > flnodes.length) {
          let last = positions.length - 1
          //console.log('to add new node like', positions[last])
          let nd = {
            index: last,
            x: positions[last].x,
            y: positions[last].y,
            vx: 0,
            vy: 0,
            r: sizes[last].width * sizemultiple
          }
          flnodes.push(nd)
          // console.log('SIM adds now this', newNode.x, newNode.y)
        } else {
          //msgbox.write("SIM DELETE CASE NOT WRITTEN")
        }
        flsim.nodes(flnodes)
        flsim.alpha(1)
          .alphaMin(finAlpha)
          .restart()
      }
    }
    
    // happens when things perterbed in existing state (i.e. drags)
    let kickForceLoop = () => {
      // hmm... but fix the one you're dragging, say?
      flsim.alpha(1).restart()
    }
    
    let flTick = () => {
      // called on sim update
      let blks = $(this.plane).children('.block').not('#NROL39_0').not('#TLView')
      if (blks.length !== flnodes.length) {
        console.log('FLOOP NODES MISMATCH', blks.length, flnodes.length)
      } else {
        for (let i = 0; i < blks.length; i++) {
          blks[i].style.left = flnodes[i].x + 'px'
          blks[i].style.top = flnodes[i].y + 'px'
        }
      }
      this.drawLinks()
      if ($(msgbox.zeCheckbox).prop('checked')) {
        this.zoomExtents()
      }
    }
    
    let flEnd = () => {
      console.log('FIN DU SIM')
    }
    
    this.zoomExtents = () => {
      // to zoom-extends
      let psns = this.getAllHunkPositions()
      let sizes = this.getAllHunkSizes()
      // bless up, these are all in 0,0 relative space
      let minxy = {
        x: 0,
        y: 0
      }
      let maxxy = {
        x: 500,
        y: 500
      }
      let maxx, minx, maxy, miny
      for (let ind in psns) {
        maxx = psns[ind].x + sizes[ind].width
        minx = psns[ind].x
        maxy = psns[ind].y + sizes[ind].height
        miny = psns[ind].y
        // max cases
        if (maxx > maxxy.x) {
          maxxy.x = maxx
        }
        if (maxy > maxxy.y) {
          maxxy.y = maxy
        }
        // min cases
        if (minx < minxy.x) {
          minxy.x = minx
        }
        if (miny < minxy.y) {
          minxy.y = miny
        }
      }
      // margin
      let margin = 100
      minxy.x -= margin
      minxy.y -= margin
      maxxy.x += margin
      maxxy.y += margin
      // ok, compare bounding box to current frustrum ?
      let ct = dt.readTransform(this.plane)
      let wd = this.dom.clientWidth
      let ht = this.dom.clientHeight
      // to find scale, do
      let pfsx = (wd) / (maxxy.x - minxy.x)
      let pfsy = (ht) / (maxxy.y - minxy.y)
      let pfs = Math.min(pfsx, pfsy)
      // write em
      ct.s = pfs
      ct.x = -minxy.x * pfs
      ct.y = -minxy.y * pfs
      dt.writeTransform(this.plane, ct)
      dt.writeBackgroundTransform(this.dom, ct)
    }
    
    this.getAllHunkPositions = () => {
      // returns positions as numbers,
      let nds = $(this.plane).children('.block')
      let positions = new Array()
      for (let nd of nds) {
        if ($(nd).attr('id') === "NROL39_0" || $(nd).attr('id') === "TLView") {
          //console.log('skip')
        } else {
          let pos = dt.readXY(nd)
          pos.id = nd.id
          positions.push(pos)
        }
      }
      return positions
      // should do transform here ?
    }
    
    this.getAllHunkSizes = () => {
      let nds = $(this.plane).children('.block')
      let sizes = new Array()
      for (let nd of nds) {
        if ($(nd).attr('id') === "NROL39_0" || $(nd).attr('id') === "TLView") {
          //console.log('skip')
        } else {
          let sz = dt.readSize(nd)
          sz.id = nd.id
          sizes.push(sz)
        }
      }
      return sizes
    }
    
    // here is where you rm'd drawing & moving
    
    
    // from http://bl.ocks.org/natebates/273b99ddf86e2e2e58ff
    
    var width = 960,
      height = 500;
    
    var nodes = d3.range(100).map(function(d, i) {
        return {
          width: ~~(Math.random() * 40 + 15),
          height: ~~(Math.random() * 40 + 15),
        };
      }),
      root = nodes[0],
      color = d3.scale.category10();
    
    var svg = d3.select('body').append('svg')
      .attr('width', width)
      .attr('height', height);
    
    svg.selectAll('.rect')
      .data(nodes.slice(1))
      .enter().append('rect')
      .attr('width', function(d) {
        return d.width;
      })
      .attr('height', function(d) {
        return d.height;
      })
      .style('fill', function(d, i) {
        return color(i % 3);
      })
      .attr('transform', function(d) {
        return 'translate(' + (-d.width / 2) + ',' + (-d.height / 2) + ')';
      });
    
    svg.on('mousemove', function() {
      var p1 = d3.mouse(this);
    
      root.px = p1[0];
      root.py = p1[1];
      force.resume();
    });
    
    // mouse node, position off screen initially
    root.x = 2000;
    root.y = 2000;
    root.width = 0;
    root.height = 0;
    root.fixed = true;
    
    var force = d3.layout.force()
      .gravity(0.05)
      .charge(function(d, i) {
        return i ? -30 : -2000;
      })
      .nodes(nodes)
      .size([width, height]);
    
    force.on('tick', function(e) {
      var q = d3.geom.quadtree(nodes),
        i = 0,
        n = nodes.length;
    
      while (++i < n) {
        q.visit(collide(nodes[i]));
      }
    
      svg.selectAll('rect')
        .attr('x', function(d) {
          return d.x;
        })
        .attr('y', function(d) {
          return d.y;
        });
    });
    
    force.start();
    
    function collide(node) {
      return function(quad, x1, y1, x2, y2) {
        var updated = false;
        if (quad.point && (quad.point !== node)) {
    
          var x = node.x - quad.point.x,
            y = node.y - quad.point.y,
            xSpacing = (quad.point.width + node.width) / 2,
            ySpacing = (quad.point.height + node.height) / 2,
            absX = Math.abs(x),
            absY = Math.abs(y),
            l,
            lx,
            ly;
    
          if (absX < xSpacing && absY < ySpacing) {
            l = Math.sqrt(x * x + y * y);
    
            lx = (absX - xSpacing) / l;
            ly = (absY - ySpacing) / l;
    
            // the one that's barely within the bounds probably triggered the collision
            if (Math.abs(lx) > Math.abs(ly)) {
              lx = 0;
            } else {
              ly = 0;
            }
    
            node.x -= x *= lx;
            node.y -= y *= ly;
            quad.point.x += x;
            quad.point.y += y;
    
            updated = true;
          }
        }
        return updated;
      };
    }