diff --git a/event/static/js/calendar.js b/event/static/js/calendar.js
index a2019c9..d5deb70 100644
--- a/event/static/js/calendar.js
+++ b/event/static/js/calendar.js
@@ -70,7 +70,8 @@ class Calendar {
this.applyLocationStylesAsCSS();
this.updateEventLocationStyleID();
- this.sortEventNodesByEndTimeAndLocation();
+ //this.sortEventNodesByEndTimeAndLocation();
+ this.sortEventNodesByLocationAndIntervalGraphColoring();
this.updateCalendarNodeHeight();
this.initEventOverflowTooltips();
@@ -334,6 +335,68 @@ class Calendar {
// Event node sorting
+ // The following method requires the IntervalColoration class,
+ // which provides the algorithm (see interval_coloring.js)
+ sortEventNodesByLocationAndIntervalGraphColoring () {
+ // Group events by location
+ let locationsToEvents = new Map();
+ for (let event of this.events) {
+ let location = event.location;
+
+ if (! locationsToEvents.has(location)) {
+ locationsToEvents.set(location, [event]);
+ }
+ else {
+ locationsToEvents
+ .get(location)
+ .push(event);
+ }
+ }
+
+ // Assign a color to all events,
+ // by using the interval graph coloration algorithm
+ // on each subset of events with the same location
+ let allEventColors = [];
+ let nbAlreadyColoredEvents = 0;
+
+ for (let eventsAtSameLocation of locationsToEvents.values()) {
+ // Build intervals for each event
+ let intervals = [];
+ for (let event of eventsAtSameLocation) {
+ intervals.push([
+ event.startDate.getTime(),
+ event.endDate.getTime()
+ ]);
+ }
+
+ // Get the graph coloring, and assign each color to each event
+ let intervalGraphColors = new IntervalColoration(intervals).colors;
+ let eventsToColors = new Map();
+ for (let i = 0; i < eventsAtSameLocation; i++) {
+ eventsToColors.set(event, intervalGraphColors[i]);
+ }
+
+ // Sort the events by color
+ eventsAtSameLocation.sort((event1, event2) => {
+ return eventsToColors.get(event2) - eventsToColors.get(event1);
+ });
+ }
+
+ // Finally sort all event nodes,
+ // (1) by their location and (2) by their color
+
+ // Note: the container is detached from the DOM for better performances
+ this.eventContainerNode.detach();
+
+ for (let eventsAtSameLocation of locationsToEvents.values()) {
+ for (let event of eventsAtSameLocation) {
+ this.eventContainerNode.prepend(event.node);
+ }
+ }
+
+ this.eventContainerNode.appendTo(this.containerNode);
+ }
+
sortEventNodesByEndTimeAndLocation () {
this.events.sort((event1, event2) => {
return event2.endDate.getTime() - event1.endDate.getTime();
diff --git a/event/static/js/interval_coloration.js b/event/static/js/interval_coloration.js
new file mode 100644
index 0000000..082b9de
--- /dev/null
+++ b/event/static/js/interval_coloration.js
@@ -0,0 +1,84 @@
+// Interval graph coloring algorithm, by Twal
+
+class IntervalColoration {
+ constructor (intervals) {
+ this.intervals = intervals;
+ this.n = this.intervals.length;
+ this.computeInterferenceGraph();
+ this.computePEO();
+ this.computeColoration();
+ }
+
+ computeInterferenceGraph() {
+ this.adj = new Array(this.n);
+ for (let i = 0; i < this.n; ++i) {
+ this.adj[i] = [];
+ }
+ for (let i = 0; i < this.n; ++i) {
+ for (let j = 0; j < i; ++j) {
+ let inti = this.intervals[i];
+ let intj = this.intervals[j];
+ if (inti[0] <= intj[1] && intj[0] <= inti[1]) {
+ this.adj[i].push(j);
+ this.adj[j].push(i);
+ }
+ }
+ }
+ }
+
+ //Perfect elimination order using Maximum Cardinality Search
+ //Runs in O(n^2), could be optimized in O(n log n)
+ computePEO() {
+ let marked = new Array(this.n);
+ let nbMarkedNeighbor = new Array(this.n);
+ this.perm = new Array(this.n);
+ for (let i = 0; i < this.n; ++i) {
+ marked[i] = false;
+ nbMarkedNeighbor[i] = 0;
+ }
+ for (let k = this.n-1; k >= 0; --k) {
+ let maxi = -1;
+ for (let i = 0; i < this.n; ++i) {
+ if (!marked[i] && (maxi == -1 || nbMarkedNeighbor[i] >= nbMarkedNeighbor[maxi])) {
+ maxi = i;
+ }
+ }
+ for (let i = 0; i < this.adj[maxi].length; ++i) {
+ nbMarkedNeighbor[this.adj[maxi][i]] += 1;
+ }
+ this.perm[maxi] = k;
+ marked[maxi] = true;
+ }
+ // console.log(this.perm);
+ }
+
+ computeColoration() {
+ this.colors = new Array(this.n);
+ let isColorUsed = new Array(this.n);
+ for (let i = 0; i < this.n; ++i) {
+ this.colors[i] = -1;
+ isColorUsed[i] = false;
+ }
+ for (let i = 0; i < this.n; ++i) {
+ let ind = this.perm[i];
+ for (let j = 0; j < this.adj[ind].length; ++j) {
+ let neigh = this.adj[ind][j];
+ if (this.colors[neigh] >= 0) {
+ isColorUsed[this.colors[neigh]] = true;
+ }
+ }
+ for (let j = 0; j < this.n; ++j) {
+ if (!isColorUsed[j]) {
+ this.colors[ind] = j;
+ break;
+ }
+ }
+ for (let j = 0; j < this.adj[ind].length; ++j) {
+ let neigh = this.adj[ind][j];
+ if (this.colors[neigh] >= 0) {
+ isColorUsed[this.colors[neigh]] = false;
+ }
+ }
+ }
+ }
+}
diff --git a/event/templates/event/calendar.html b/event/templates/event/calendar.html
index 2e4a1dc..7aa8196 100644
--- a/event/templates/event/calendar.html
+++ b/event/templates/event/calendar.html
@@ -26,6 +26,7 @@
{% block extra_js %}
{{ block.super }}
+