Skip to content

Instantly share code, notes, and snippets.

@patrickthompson
Last active February 29, 2016 18:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save patrickthompson/4d508eb3b8feac90762e to your computer and use it in GitHub Desktop.
Save patrickthompson/4d508eb3b8feac90762e to your computer and use it in GitHub Desktop.
discrete-cubism.js

discrete-cubism.js

This is a version of cubism.js written by Patrick Thompson to use cubism tracking for discrete events that occur in real time.

Cubism is a library written by Mike Bostock.

For more information about cubism.js go here.

html {
min-width: 1040px;
}
body {
font-family:"Helvetica Neue", Helvetica, sans-serif;
margin: auto;
margin-top: 40px;
margin-bottom: 4em;
width: 800px;
position:relative;
}
#body {
position: left;
}
footer {
font-size: small;
margin-top: 8em;
}
aside {
font-size: small;
left: 780px;
position: absolute;
width: 180px;
}
#body > p, li > p {
line-height: 1.5em;
}
#body > p {
width: 720px;
}
#body > blockquote {
width: 640px;
}
li {
width: 680px;
}
a {
color: steelblue;
}
a:not(:hover) {
text-decoration: none;
}
pre, code, textarea {
font-family:"Menlo", monospace;
}
code {
line-height: 1em;
}
textarea {
font-size: 100%;
}
#body > pre {
border-left: solid 2px #ccc;
padding-left: 18px;
margin: 2em 0 2em -20px;
}
.html .value, .javascript .string, .javascript .regexp {
color: #756bb1;
}
.html .tag, .css .tag, .javascript .keyword {
color: #3182bd;
}
.comment {
color: #636363;
}
.html .doctype, .javascript .number {
color: #31a354;
}
.html .attribute, .css .attribute, .javascript .class, .javascript .special {
color: #e6550d;
}
svg {
font: 10px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
sup, sub {
line-height: 0;
}
q:before, blockquote:before {
content:"“";
}
q:after, blockquote:after {
content:"”";
}
blockquote:before {
position: absolute;
left: 2em;
}
blockquote:after {
position: absolute;
}
h1 {
font-size: 96px;
margin-top: .3em;
margin-bottom: 0;
}
h1 + h2 {
margin-top: 0;
}
h2 {
font-weight: 400;
font-size: 28px;
}
h1, h2 {
font-family:"Yanone Kaffeesatz";
text-rendering: optimizeLegibility;
}
#logo {
width: 122px;
height: 31px;
}
#fork {
position: absolute;
top: 0;
right: 0;
}
.axis {
font: 10px sans-serif;
}
.axis text {
-webkit-transition: fill-opacity 250ms linear;
}
.axis path {
display: none;
}
.axis line {
stroke: #000;
shape-rendering: crispEdges;
}
.horizon {
border-bottom: solid 1px #000;
overflow: hidden;
position: relative;
}
.horizon {
border-top: solid 0px #000;
border-bottom: solid 1px #000;
}
.horizon + .horizon {
border-top: none;
}
.horizon canvas {
display: block;
}
.horizon .title, .horizon .value {
bottom: 0;
line-height: 30px;
margin: 0 6px;
position: absolute;
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
white-space: nowrap;
}
.horizon .title {
left: 0;
}
.horizon .value {
right: 0;
}
.line {
background: #000;
opacity: .2;
z-index: 2;
}
//////////////////////////////////////////////////
//DISCRETE CUBISM.JS
//////////////////////////////////////////////////
//This is a version of cubism.js written by Patrick Thompson
//to use cubism tracking for discrete events
//that occur in real time
////////////////////////////////////////////////////
//Cubism is a library written by Mike Bostock
//For more information about cubism go to
//https://square.github.io/cubism/
//////////////////////////////////////////////////
//(Note:this is written in the user/event paradigm)
//but can be translated into any system that has discrete events
//////////////////////////////////////////////////
//TODO
//1. Add block colors (I do not fully understand how to add colors to cubism.js yet)
//2. Add a line above the first user (just haven't gotten around to it yet
//3. Add "delete user" and "delete event" input (not really necessary to demonstrate)
var AllUsers = []; //Create an array that will contain all users
var Users = [{ //Create the first template user
name: "User1",
event: 1,
newevent: false
}];
var events = [{ //Create the New event notification
name: "New Event!",
value: "20"
}]
events.push({ //Create the default event notification
name: "No Events",
value: "0.0"
});
events.push({ //Create a couple of events
name: "Home Page",
value: "5.0"
});
events.push({ //Create a couple of events
name: "Subject View",
value: "-5.0"
});
//////////////////////////////////////////////////
//BEGIN CUBISM FUNCTIONS /////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//Add the cubism context /////////////////////////
//Required for cubism to work ////////////////////
//////////////////////////////////////////////////
var context = cubism.context() // set the cubism context
.serverDelay(0) // No server delay
.clientDelay(0) // No client delay
.step(1e3) // step once ever second
.size(800); // and make the horizon div 960 px wide.
//////////////////////////////////////////////////
//Execute when AddNewEvent button pressed ////////
//Adds a new event to the event list /////////////
//////////////////////////////////////////////////
d3.select("#example1").call(function (div) {
AddAllUsers();
div.append("div")
.attr("class", "axis")
.call(context.axis().orient("top"));
RunData();
div.append("div")
.attr("class", "rule")
.call(context.rule());
RefreshUsers();
RefreshEvents();
RefreshRule();
});
//////////////////////////////////////////////////
//Execute when AddAllUsers button pressed ////////
//Adds a new user to the AllUsers list /////////////
//////////////////////////////////////////////////
function AddAllUsers() {
for (var i = 0; i < Users.length; i++) {
var myuser = random(Users[i].name);
AllUsers.push(myuser);
}
}
//////////////////////////////////////////////////
//Execute RunData function ////////
//Adds all horizons to cubism div /////////////
//////////////////////////////////////////////////
function RunData() {
d3.select("#example1").selectAll(".horizon")
.data(AllUsers)
.enter().append("div")
.attr("class", "horizon")
.call(context.horizon().extent([-20, 20]));
}
/////////////////////////////////////////////////////////////////////
//Execute the running process (heart of the code)////////////////////
//This is also executed each time a user is added////////////////////
/////////////////////////////////////////////////////////////////////
function random(name) {
var value = 0,
values = [],
area = [],
i = 0,
last;
return context.metric(function (start, stop, step, callback) {
start = +start, stop = +stop;
if (isNaN(last)) last = start;
while (last < stop) {
last += step;
for (var j = 0; j < Users.length; j++) { //For all users
if (Users[j].name == name) { //If the user name in the loop matches the current user name
if (Users[j].newevent) { //If the user has a new event
value = parseInt(events[0].value); //Show a "blip" in event zero
Users[j].newevent = false; //And switch newevent OFF
} else { //Otherwise
value = parseFloat(events[Users[j].event].value); //Write the value of the current user
}
values.push(value); //And add it to the values array
}
}
}
callback(null, values = values.slice((start - stop) / step)); //And execute the callback function
}, name);
}
//////////////////////////////////////////////////
//Execute when AddNewEvent button pressed ////////
//Adds a new event to the event list /////////////
//////////////////////////////////////////////////
function UpdateRule(i) {
d3.selectAll(".value").each(function (index, d, k) {
for (l = 0; l < events.length; l++) {
//console.log($(this).text().replace("−", "-"));
if ($(this).text().replace("−", "-") == events[l].value) {
$(this).text(events[l].name);
}
}
}).style("right", i == null ? null : context.size() - i + 10 + "px");
}
//////////////////////////////////////////////////
//BEGIN INPUT FUNCTIONS ////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//Execute when AddNewEvent button pressed ////////
//Adds a new event to the event list /////////////
//////////////////////////////////////////////////
function addNewEvent() {
var prefixstring = "";
var suffixstring = "";
if (events.length + 4 < 10) {
suffixstring = ".0"
}
if (events.length % 2 == 0) {
prefixstring = "-"
}
events.push({
name: $("#NewEventName").val(),
value: prefixstring + (events.length + 4).toString() + suffixstring
});
$(NewEventName).val('');
RefreshEvents();
};
/////////////////////////////////////////////////////////////////////
//Execute when AddEvent button pressed //////////////////////////////
//Adds event to the selected user //////////////////////////////////
/////////////////////////////////////////////////////////////////////
function addEvent() {
Users[$("#UserList").val()].newevent = true;
Users[$("#UserList").val()].event = $("#EventList").val();
}
//////////////////////////////////////////////////
//Execute when AddUser button pressed ////////
//Adds a new user to the user list /////////////
//////////////////////////////////////////////////
function addUser() { //Add a user function (executed by pressing add user button)
totalusers = Users.length + 1; //a variable for total users
Users.push({ //add a user to the users object
name: $("#UserName").val(), //name equals username text field value
event: 1, //default event of 1
newevent: false //the event for this user has not been created yet, so keep the newevent false
});
$("#UserName").val(''); //clear the username field
RefreshUsers(); //execute the refreshusers function
myuser = random(Users[totalusers - 1].name); //
AllUsers.push(myuser); //push the user onto the AllUsers array of users
RunData(); //Run the data with the new user
RefreshRule(); //Refresh the rule for all users (will not refresh new user rule if not invoked)
}
//////////////////////////////////////////////////
//BEGIN SELECT OPTION UPDATE FUNCTIONS ////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//Execute RefreshUsers function ////////
//Clears and populates the user select option list /////////////
//////////////////////////////////////////////////
function RefreshUsers() {
$('#UserList')
.find('option')
.remove();
for (i = 0; i < Users.length; i++) {
$('#UserList').append('<option value="' + i + '">' + Users[i].name + '</option>');
}
};
//////////////////////////////////////////////////
//Execute the RefreshEvents function ////////
//Clears and populates the event select option list /////////////
//////////////////////////////////////////////////
function RefreshEvents() {
$('#EventList')
.find('option')
.remove();
for (i = 2; i < events.length; i++) {
$('#EventList').append('<option value="' + i + '">' + events[i].name + '</option>');
}
};
//////////////////////////////////////////////////
//Execute the RefreshRule function ////////
//Refreshes the horizontal rule on mouse context focus
//////////////////////////////////////////////////
function RefreshRule() {
// On mousemove, reposition the chart values to match the rule.
context.on("focus", function (i) {
UpdateRule(i)
});
}
<HTML><HEAD></HEAD><BODY>
<link rel="stylesheet" type="text/css" href="discrete-cubism.css">
<div id="example1"></div>
<br/>Step 1: Add a user by the name of
<input id="UserName" />
<button id="AddUser" onClick="javascript:addUser();">Go!</button>
<br/>Step 2: Add a new event by the name of
<input id="NewEventName" />
<button id="AddNewEvent" onClick="javascript:addNewEvent();">Go!</button>
<br/>Step 3: Add event
<select id="EventList"></select>to user
<select id="UserList"></select>
<button id="AddEvent" onClick="javascript:addEvent();">Go!</button>
<br/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="http://square.github.io/cubism/d3.v2.js"></script>
<script src="http://square.github.io/cubism/cubism.v1.js"></script>
<script src="http://square.github.io/cubism/highlight.min.js"></script>
<script src="discrete-cubism.js"></script>
</BODY></HTML>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment