function form_outline_d(form_n, x, y){ // square if (form_n == 0) { return "M" + (x) + "," + (y) + "l10,0 l0,10 l-10,0 z" } // triangle if (form_n == 1) { return "M" + (x) + "," + (y + 10) + "l5,-10 l5,10 z" } // circle if (form_n == 2) { return "M" + (x) + "," + (y + 5) + "a 5 5 0 1 0 0.01 0 z" } } function start_inventory_animation() { // vis config variables var svg = d3.select("#inventory-cycle") var speed = 0.00015 // ticks per millisecond var outer_r = 210 var inner_r = 160 var center_x = 289 var center_y_main = 380 var center_y_in = -30 // mapping functions var coord_x_in = function(r,a) { return center_x + r * Math.cos(a) } var coord_y_in = function(r,a) { return center_y_in+ r * Math.sin(a) } var coord_x_main = function(r,a) { return center_x + r * Math.cos(a) } var coord_y_main = function(r,a) { return center_y_main + r * Math.sin(a) } var angle_in = function(t, t1, t2) { var rel_t if (t2 == t1) { rel_t = 1 } else { rel_t = (t - t1) / (t2 - t1) } var a = (180.0 - 90.0 * rel_t) * (Math.PI/180.0) return a } var angle_main = function(t, t1, t2, e1, e2) { // compute angle in circle for a dot, // given its previous and next events, their timestamps, and the current time var rel_t if (t2 == t1) { rel_t = 1 } else { rel_t = (t - t1) / (t2 - t1) } if (e2 == 1) { return (90.0 + 180.0 * rel_t) * (Math.PI/180.0) } if (e2 == 2) { return (-90.0 + 135.0 * rel_t) * (Math.PI/180.0) } if (e2 == 3) { return (-90.0 + 135.0 + 45.0 * rel_t) * (Math.PI/180.0) } } // internal state variables var particle_count = 0 var current_events = [] d3.timer(function(t_ms) { var t = t_ms * speed // *** SIMULATION *** // check for expired events since last t // if previous event was #1, then move to #2 with random speed // if previous event was #2, then move to either #3 or #4 with some probability and random speed // if previous event was #3, then move to either #4 or #1 with some probability and random speed // if previous event was #4, then exit var removals = [] current_events.forEach(function(d,i){ if (d.t2 < t) { d.t1 = d.t2 d.e1 = d.e2 d.t2 = d.t1 + 0.3 + Math.random() * 0.4 if (d.e1 == 1) { d.e2 = 2 } else if (d.e1 == 2) { if (Math.random() < 0.5) { d.e2 = 4; d.t2 = d.t1 + 0.5 * ( 0.3 + Math.random() * 0.4 ) } else { d.e2 = 3 } } else if (d.e1 == 3) { if (Math.random() < 0.5) { d.e2 = 4; d.t2 = d.t1 + 0.5 * ( 0.3 + Math.random() * 0.4 ) } else { d.e2 = 1 } } else { removals.push(i) } } }) for (var i=removals.length - 1; i>-1; i--){ current_events.splice(removals[i],1) } // add some new particles, with some probability and random speed for (var i=0; i<2; i++) { if (Math.random() < 0.3) { particle_count += 1 current_events.push({ 'particle_id': particle_count, 'rad': inner_r + (outer_r - inner_r) * Math.random(), 'sil': Math.floor(Math.random() * 3), 't1': t, 't2': t + 0.5 * ( 0.3 + Math.random() * 0.4 ), 'e1': 0, 'e2': 1 }) } } // *** UPDATE SVG ANIMATION *** var inventory_items = svg.selectAll(".inventory-cycle-silhouette") .data(current_events, function(d) { return d.particle_id }) inventory_items.enter().append("path") .attr("class", "inventory-cycle-silhouette") .attr("fill", "#fff") .attr("stroke", "#000") .attr("stroke-width", 0.5) .attr("opacity", 1) .attr("d", function(d) { return form_outline_d(d.sil, 290, 0) }) inventory_items .attr("d", function(d){ var tx, ty if (d.e1 == 0) { var new_rad = inner_r + (outer_r - d.rad) if (coord_y_in(new_rad, angle_in(t, d.t1, d.t2, d.e1, d.e2)) > center_y_main - outer_r + new_rad) { tx = coord_x_in(new_rad, angle_in(t, d.t1, d.t2, d.e1, d.e2)) } else { tx = -90 + d.rad * 2 } ty = coord_y_in(new_rad, angle_in(t, d.t1, d.t2, d.e1, d.e2)) } else if (d.e2 == 4) { tx = 280 ty = 550 } else { tx = coord_x_main(d.rad, angle_main(t, d.t1, d.t2, d.e1, d.e2)) ty = coord_y_main(d.rad, angle_main(t, d.t1, d.t2, d.e1, d.e2)) } return form_outline_d(d.sil, tx, ty) }) inventory_items.exit().remove() }) }