d3.hasChanged

Modifying particles with Redux

by IPWright83

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.js"></script>
<svg width="960" height="500"></svg>
https://jsfiddle.net/ak5p0y0L/33/#run

JavaScript

const nodes = 250;
const width = 960;
const height = 500;

const locals = {};
d3.selection.prototype.hasChanged = function(...keys) {
  for (let key of keys) {
    if (!locals[key]) {
      locals[key] = d3.local();
    }
  }

  return this.filter(function(d) {
    for (let key of keys) {
      const local = locals[key];
      const oldValue = local.get(this);
      let newValue = d[key];
      if (oldValue !== newValue) {
        local.set(this, newValue);
        return true;
      }
    }

    return false;
  })
};

const initState = {};
d3.range(nodes).forEach((d, i) => initState[i] = {
  xloc: 0,
  yloc: 0,
  xvel: 0,
  yvel: 0
});

const x = d3.scaleLinear().domain([-5, 5]).range([0, width]);
const y = d3.scaleLinear().domain([-5, 5]).range([0, height]);

const circle = d3.select("svg")
  .selectAll("circle")
  .data(Object.values(initState)).enter()
  .append("circle")
  .attr("cx", 10)
  .attr("cy", 10)
  .attr("r", 1);

const reducer = (state = initState, action) => {
  switch (action.type) {
    case "VELOCITY":
      {
        const payload = action.payload;
        const index = payload.i;
        const datum = state[index];
        const newDatum = {
          xloc: datum.xloc + datum.xvel,
          yloc: datum.yloc + datum.yvel,
          xvel: datum.xvel + payload.vx,
          yvel: datum.yvel + payload.vy,
        };

        return {
          ...state, [index]: newDatum
        };
      }
    default:
      return state;
  }
};

const store = Redux.createStore(reducer);
store.subscribe(() => {
  const data = Object.values(store.getState());

  d3.select("svg")
    .selectAll("circle")
    .data(data)
    //.hasChanged("xloc", "yloc", "xvel", "yvel")
    .attr("transform", function(d) {
      return "translate(" + x(d.xloc) + "," + y(d.yloc) + ")";
    })
    .attr("r", function(d) {
      return Math.min(1 + 1000 * Math.abs(d.xvel * d.yvel), 10);
    })
});

d3.timer(function() {
  const data = Object.values(store.getState());
 ...