block by fil 6037e056202bb3be9f172a6ab3361b53

d3-cluster

Full Screen

d3-cluster compiled from @stormpython and @emeeks’ https://github.com/stormpython/d3-cluster

Built with blockbuilder.org

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="d3-cluster.min.js"></script>
  <style>
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
  svg {
    height: 1000px;
    width: 1000px;
    border: 1px solid lightgray;
  }
</style>
</head>

<body>

<div id="viz">
  <svg class="main">
  </svg>
</div>
</body>
  <footer>
<script>
const colors = [
    "#00a2ce",
    "#4d430c",
    "#b3331d",
    "#b6a756"
]
const testData = []
for (let x=1;x<500;x++) {
    testData.push({ x: 10 + Math.random() * 940, y: 10 + Math.random() * 480, r: 1 + Math.random() * 20, color: colors[x%4] })
}
d3.select("svg")
  .selectAll("circle")
  .data(testData)
  .enter()
  .append("circle")
  .attr("r", d => d.r)
  .attr("cx", d => d.x)
  .attr("cy", d => d.y)
  .style("fill", d => d.color)
  .style("fill-opacity", 0.5)

setTimeout(() => {
  const cluster = d3.cluster()
  .x(d => d.x)
  .y(d => d.y)
  .radius(d => d.r);
d3.select("svg")
  .selectAll("circle.collision")
  .data(cluster(testData))
  .enter()
  .append("circle")
  .attr("r", d => d.radius)
  .attr("cx", d => d.x)
  .attr("cy", d => d.y)
  .style("fill", "#FCBC34")
  .style("fill-opacity", d => d.overlap.length === 0  ? 1 : 0)
  .style("stroke", d => d.overlap.length === 0 ? "none" : "#FE9922")
  .style("stroke-width", d => d.overlap.length + 1)

}, 1000)
</script>
  </footer>

d3-cluster.js

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-scale')) :
  typeof define === 'function' && define.amd ? define(['exports', 'd3-scale'], factory) :
  (factory((global.d3 = global.d3 || {}),global.d3));
}(this, (function (exports,d3Scale) { 'use strict';

function isArray(o) {
  return Array.isArray(o);
}

function isPlainObject(o) {
  return !isArray(o) && typeof o === 'object';
}

function isEqualArray(target, source) {
  return target.every(function (o, i) {
    if (isPlainObject(o)) {
      return isEqualObject(o, source[i]);
    }
    if (isArray(o)) {
      return isEqualArray(o, source[i]);
    }
    return o === source[i];
  });
}

function isEqualObject(target, source) {
  return Object.keys(target).every(function (key) {
    return target[key] === source[key];
  });
}

function isEqual(target, source) {
  if (isArray(target) && isArray(source)) {
    return isEqualArray(target, source);
  }

  if (isPlainObject(target) && isPlainObject(source)) {
    return isEqualObject(target, source);
  }

  return target === source;
}

function cloneArray(source) {
  var target = [];

  source.forEach(function (o) {
    if (typeof o === 'object') {
      if (isArray(o)) {
        target.push(cloneArray(o));
      } else {
        target.push(Object.assign({}, o));
      }
    } else {
      target.push(o);
    }
  });

  return target;
}

function cloneDeep(source) {
  if (isArray(source)) {
    return cloneArray(source);
  }

  if (isPlainObject(source)) {
    return Object.assign({}, source);
  }

  return source;
}

// import quadtree from 'd3-quadtree';
function isMissing(arr, obj) {
  return arr.every(function (d) {
    return !isEqual(d, obj);
  });
}

function cluster () {
  var x = function (d) { return d[0]; };
  var y = function (d) { return d[1]; };
  var radius = function (d) { return d[2]; };
  var xScale = d3Scale.scaleLinear();
  var yScale = d3Scale.scaleLinear();
  var centroid = function (p0, p1) { return (p1 + p0) / 2; };

  function X(d, i) {
    return xScale(x.call(this, d, i));
  }

  function Y(d, i) {
    return yScale(y.call(this, d, i));
  }

  function clusterize(data) {
    var clusteredPoints = [];
    var overlappingPoints = []; // What is the point of this?

    var modifiedData = data
      .map(function (d, i) {
        return {
          x: X.call(this, d, i),
          y: Y.call(this, d, i),
          radius: radius.call(this, d, i),
          point: cloneDeep(d) // copy of original data point
        };
      })
      .sort(function (a, b) {
        return b.radius - a.radius;
      });

//    var targetPoints = cloneDeep(modifiedData); // copy of data
    var targetPoints = modifiedData; // copy of data

    modifiedData.forEach(function (p) {
      if (isMissing(overlappingPoints, p)) {
        p.overlap = [];

        clusteredPoints.push(p);

        targetPoints.forEach(function (t) {
          if (t !== p) {
            var distance = Math.sqrt(Math.pow(Math.abs(t.x - p.x), 2) +
              Math.pow(Math.abs(t.y - p.y), 2));

            if (distance < (t.radius + p.radius)) {
              t.clustered = true;
              p.overlap.push(t);
              overlappingPoints.push(t);
            }
          }
        });

        targetPoints = targetPoints.filter(function (d) {
          return isMissing(p.overlap, d);
        });
      }
    });

    return clusteredPoints;
  }

  function layout(data) {
    return clusterize(data);
  }

  // Public API
  layout.x = function (_) {
    if (!arguments.length) { return x; }

    x = typeof _ === 'function' ? _ : x;
    return layout;
  };

  layout.y = function (_) {
    if (!arguments.length) { return y; }

    y = typeof _  === 'function' ? _ : y;
    return layout;
  };

  layout.radius = function (_) {
    if (!arguments.length) { return radius; }
    if (typeof _ === 'function') { radius = _; }
    if (typeof _ === 'number') { radius = function () { return _; }; }

    return layout;
  };

  layout.centroid = function (_) {
    if (!arguments.length) { return centroid; }

    centroid = typeof _ === 'function' ? _ : centroid;
    return layout;
  };

  layout.xScale = function (_) {
    if (!arguments.length) { return xScale; }

    xScale = typeof _ === 'function' ? _ : xScale;
    return layout;
  };

  layout.yScale = function (_) {
    if (!arguments.length) { return yScale; }

    yScale = typeof _ === 'function' ? _ : yScale;
    return layout;
  };

  return layout;
}

exports.cluster = cluster;

Object.defineProperty(exports, '__esModule', { value: true });

})));

d3-cluster.min.js

!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-scale")):"function"==typeof define&&define.amd?define(["exports","d3-scale"],t):t(n.d3=n.d3||{},n.d3)}(this,function(n,t){"use strict";function e(n){return Array.isArray(n)}function r(n){return!e(n)&&"object"==typeof n}function u(n,t){return n.every(function(n,i){return r(n)?o(n,t[i]):e(n)?u(n,t[i]):n===t[i]})}function o(n,t){return Object.keys(n).every(function(e){return n[e]===t[e]})}function i(n,t){return e(n)&&e(t)?u(n,t):r(n)&&r(t)?o(n,t):n===t}function c(n){var t=[];return n.forEach(function(n){"object"==typeof n?e(n)?t.push(c(n)):t.push(Object.assign({},n)):t.push(n)}),t}function f(n){return e(n)?c(n):r(n)?Object.assign({},n):n}function a(n,t){return n.every(function(n){return!i(n,t)})}function s(){function n(n,t){return s(o.call(this,n,t))}function e(n,t){return l(i.call(this,n,t))}function r(t){var r=[],u=[],o=t.map(function(t,r){return{x:n.call(this,t,r),y:e.call(this,t,r),radius:c.call(this,t,r),point:f(t)}}).sort(function(n,t){return t.radius-n.radius}),i=o;return o.forEach(function(n){a(u,n)&&(n.overlap=[],r.push(n),i.forEach(function(t){if(t!==n){Math.sqrt(Math.pow(Math.abs(t.x-n.x),2)+Math.pow(Math.abs(t.y-n.y),2))<t.radius+n.radius&&(t.clustered=!0,n.overlap.push(t),u.push(t))}}),i=i.filter(function(t){return a(n.overlap,t)}))}),r}function u(n){return r(n)}var o=function(n){return n[0]},i=function(n){return n[1]},c=function(n){return n[2]},s=t.scaleLinear(),l=t.scaleLinear(),p=function(n,t){return(t+n)/2};return u.x=function(n){return arguments.length?(o="function"==typeof n?n:o,u):o},u.y=function(n){return arguments.length?(i="function"==typeof n?n:i,u):i},u.radius=function(n){return arguments.length?("function"==typeof n&&(c=n),"number"==typeof n&&(c=function(){return n}),u):c},u.centroid=function(n){return arguments.length?(p="function"==typeof n?n:p,u):p},u.xScale=function(n){return arguments.length?(s="function"==typeof n?n:s,u):s},u.yScale=function(n){return arguments.length?(l="function"==typeof n?n:l,u):l},u}n.cluster=s,Object.defineProperty(n,"__esModule",{value:!0})});