Skip to content

Instantly share code, notes, and snippets.

@emepyc
Last active May 23, 2016 08:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emepyc/02931ac7797e1435d7b2fb035ba777f1 to your computer and use it in GitHub Desktop.
Save emepyc/02931ac7797e1435d7b2fb035ba777f1 to your computer and use it in GitHub Desktop.
Non overlapping elements

Example of track with overlapping elements using TnT Board

tnt.board.track.feature.non_overlapping_block = function () {
var block_feature = tnt.board.track.feature.block();
block_feature.create (function (blocks) {
var track = this;
var xScale = block_feature.scale();
blocks
.append("rect")
.attr("x", function (d, i) {
return xScale(block_feature.from()(d, i));
})
.attr("y", function (d) {
var slots = block_feature.layout().slots();
var height = track.height();
pos = (height / slots) * d.slot;
return pos + 5;
})
.attr("width", function (d, i) {
return (xScale(block_feature.to()(d, i)) - xScale(block_feature.from()(d, i)));
})
.attr("height", function (d) {
var slots = block_feature.layout().slots();
var height = track.height();
return (height / slots) - 5;
})
.attr("fill", track.color())
.transition()
.duration(500)
.attr("fill", function (d) {
return block_feature.color()(d);
});
});
block_feature.distribute (function (blocks) {
var track = this;
var xScale = block_feature.scale();
blocks
.select("rect")
.attr("width", function (d) {
return (xScale(d.end) - xScale(d.start));
})
.transition()
.duration(500)
.attr("y", function (d) {
var slots = block_feature.layout().slots();
var height = track.height();
pos = (height / slots) * d.slot;
return pos + 5;
});
});
return block_feature;
};
<!DOCTYPE html>
<head>
<link rel="stylesheet" href="https://tntvis.github.io/tnt.board/build/tnt.board.css" type="text/css" />
<style>
#mydiv {
margin-top: 200px;
}
</style>
<script src="https://d3js.org/d3.v3.min.js"></script>
<!-- es6 promise only needed to "fake" async load of data -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/3.2.2/es6-promise.min.js"></script>
<script src="http://tntvis.github.io/tnt.board/build/tnt.board.min.js"></script>
<script src="overlaps.js"></script>
<!-- New layout and new display that takes into account the vertical position of elements -->
<script src="layout.js"></script>
<script src="display.js"></script>
</head>
<body>
<div id="mydiv"></div>
<script>
var myBoard = tnt.board().from(20).to(500).max(500).width(950);
render (myBoard, document.getElementById("mydiv"));
</script>
</body>
var layout = function () {
var needed_slots;
var scale;
var feature_height = 20;
var callback = function (){};
var feature_layout = function (new_features) {
var track = this;
scale = track.display().scale();
needed_slots = collition_detector(new_features);
track.height(needed_slots * feature_height);
callback();
return new_features;
};
function collition_detector (features) {
var allocated = [];
var remaining = features;
var needed_slots = 0;
for (var i=0; i<remaining.length; i++) {
var features_by_slot = sort_features_by_slot(allocated);
var current = remaining[i];
var slot = 0;
OUTER: while (true) {
if (slot_has_space(current, features_by_slot[slot])) {
current.slot = slot;
allocated.push(current);
if (slot > needed_slots) {
needed_slots = slot;
}
break;
}
slot++;
}
}
return needed_slots + 1;
}
function slot_has_space (feature, features_in_slot) {
if (!features_in_slot) {
return true;
}
for (var i=0; i<features_in_slot.length; i++) {
var subject = features_in_slot[i];
var y1 = scale(subject.start);
var y2 = scale(subject.end);
var x1 = scale(feature.start);
var x2 = scale(feature.end);
if ( ((x1 <= y1) && (x2 >= y1)) ||
((x1 >= y1) && (x1 <= y2)) ) {
return false;
}
}
return true;
}
function sort_features_by_slot (features) {
var slots = [];
for (var i=0; i<features.length; i++) {
var feature = features[i];
if (!slots[feature.slot]) {
slots[feature.slot] = [];
}
slots[feature.slot].push(feature);
}
return slots;
}
feature_layout.elements = function () {};
feature_layout.slots = function () {
return needed_slots;
};
feature_layout.on_run = function(cbak) {
callback = cbak;
return this;
};
return feature_layout;
};
// Change the data and the algorithm will detect the overlapping elements
var data = [
{start:10, end:50, id:1, val:0.3},
{start:30, end:40, id:2, val:0.9},
{start:50, end:60, id:3, val:1.0},
{start:70, end:80, id:4, val:0.1},
{start:35, end:80, id:5, val:0.4},
{start:65, end:80, id:6, val:0.7},
{start:75, end:90, id:7, val:0.1},
{start:90, end:95, id:8, val:0.8},
{start:5, end:15, id:9, val:0.6},
{start:10, end:50, id:10, val:0.5}
];
function render (board, div) {
var current_height = 200;
// The color gradient
var col_grad = d3.scale.linear()
.domain([0,1])
.range(["#eff3ff","#08519c"]);
var axis_track = tnt.board.track()
.height(0)
.color("white")
.display(tnt.board.track.feature.axis()
.orientation("top")
);
var feature_track = tnt.board.track()
.height(50)
.color('white')
// The new defined block with no overlaps
.display(tnt.board.track.feature.non_overlapping_block()
.color(function (d) {
return col_grad(d.val);
})
.index(function (d) {
return d.id;
})
)
.data(tnt.board.track.data.sync()
.retriever(function (where) {
return filterData(data, where.from, where.to);
})
);
feature_track
.display(feature_track.display()
.layout(layout()
.on_run(function () {
board.tracks(board.tracks());
}))
);
function filterData (recs, from, to) {
var filtered = [];
for (var i=0; i<recs.length; i++) {
var data = recs[i];
if (((data.start >= from) && (data.start <= to)) ||
((data.end >= from) && (data.end <= to)) ||
((data.start <= from) && (data.end >= to)) ||
((data.start >= from) && (data.end <= to))) {
filtered.push(data);
}
}
return filtered;
}
board.from(0).to(100).max(120).min(0).zoom_out(120).zoom_in(20);
board.add_track([axis_track, feature_track]);
board(div);
board.start();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment