Skip to content
Snippets Groups Projects
Unverified Commit f0307298 authored by Christopher Pietsch's avatar Christopher Pietsch Committed by GitHub
Browse files

Merge pull request #4 from cpietsch/tsne-layout

added tsne layout option
parents 4475a7e4 cec8448b
Branches main
No related tags found
No related merge requests found
...@@ -81,7 +81,6 @@ ...@@ -81,7 +81,6 @@
height: 100%; height: 100%;
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
} }
.infobar .infobutton svg { .infobar .infobutton svg {
...@@ -99,12 +98,17 @@ ...@@ -99,12 +98,17 @@
/*stroke: #000;*/ /*stroke: #000;*/
} }
.navi.hide {
display: none;
}
.navi { .navi {
z-index: 300; z-index: 300;
top: 30%; top: 30%;
position: absolute; position: absolute;
left: 0; left: 0;
} }
.navi .button { .navi .button {
transition: all 0.3s; transition: all 0.3s;
text-transform: uppercase; text-transform: uppercase;
...@@ -131,6 +135,7 @@ ...@@ -131,6 +135,7 @@
padding-right: 10px; padding-right: 10px;
/* border-right: 4px solid rgba(247, 239, 205, 0.18); */ /* border-right: 4px solid rgba(247, 239, 205, 0.18); */
} }
.navi .button:hover:not(.active) { .navi .button:hover:not(.active) {
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);
} }
...@@ -180,6 +185,7 @@ ...@@ -180,6 +185,7 @@
direction: ltr; direction: ltr;
/*direction: ltr;*/ /*direction: ltr;*/
} }
.infobar .inner>div { .infobar .inner>div {
/* height: 100%; */ /* height: 100%; */
position: relative; position: relative;
...@@ -264,5 +270,3 @@ ...@@ -264,5 +270,3 @@
opacity: 0.7; opacity: 0.7;
padding-top: 30px; padding-top: 30px;
} }
\ No newline at end of file
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>VikusViewer</title> <title>VikusViewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui" /> <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui"
/>
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
...@@ -71,11 +73,14 @@ cpietsch@gmail.com ...@@ -71,11 +73,14 @@ cpietsch@gmail.com
--> -->
</head> </head>
<body> <body>
<div id="hiddenreload"></div> <div id="hiddenreload"></div>
<div class="browserInfo"> <div class="browserInfo">
<p>This visualization is not optimized for mobile phones and needs WebGL enabled.</p><p>Please come back on a Computer.</p><span>💡</span> <p>This visualization is not optimized for mobile phones and needs WebGL enabled.</p>
<p>Please come back on a Computer.</p>
<span>💡</span>
</div> </div>
<div class="search"></div> <div class="search"></div>
...@@ -91,37 +96,19 @@ cpietsch@gmail.com ...@@ -91,37 +96,19 @@ cpietsch@gmail.com
<div class="outer"> <div class="outer">
<div id="detail" class="inner"> <div id="detail" class="inner">
<div class="entries" v-if="item"> <div class="entries" v-if="item">
<div <div v-if="item._imagenum > 1" class="entry wide pages">
v-if="item._imagenum > 1"
class="entry wide pages"
>
<div class="label">Seite</div> <div class="label">Seite</div>
<div class="content"> <div class="content">
<span <span v-for="i in parseInt(item._imagenum)" v-bind:key="i" v-on:click="displayPage(i-1)" v-bind:class="{ active: i === page+1, keyword: true }">
v-for="i in parseInt(item._imagenum)"
v-bind:key="i"
v-on:click="displayPage(i-1)"
v-bind:class="{ active: i === page+1, keyword: true }"
>
{{ i }} {{ i }}
</span> </span>
</div> </div>
</div> </div>
<div <div v-for="entry in structure" v-bind:key="entry.name" v-bind:class="entry.display" class="entry" v-if="hasData(entry)">
v-for="entry in structure"
v-bind:key="entry.name"
v-bind:class="entry.display"
class="entry"
v-if="hasData(entry)"
>
<div class="label">{{ entry.name }}</div> <div class="label">{{ entry.name }}</div>
<div class="content"> <div class="content">
<span v-if="entry.type === 'keywords'"> <span v-if="entry.type === 'keywords'">
<span <span v-for="keyword in item[entry.source]" v-bind:key="keyword" class="keyword">
v-for="keyword in item[entry.source]"
v-bind:key="keyword"
class="keyword"
>
{{ keyword }} {{ keyword }}
</span> </span>
</span> </span>
...@@ -141,7 +128,12 @@ cpietsch@gmail.com ...@@ -141,7 +128,12 @@ cpietsch@gmail.com
</div> </div>
</div> </div>
<div class="infobar sneaks"> <div class="navi hide">
<div class="time button active">time</div>
<div class="tsne button">tsne</div>
</div>
<div class="infobar sneak">
<div class="infobutton"> <div class="infobutton">
<svg width="16px" height="24px" viewBox="0 0 16 24"> <svg width="16px" height="24px" viewBox="0 0 16 24">
<path d="M13.6824546,2 L3.7109392,11.9715154 L13.7394238,22" stroke="#FFF" stroke-width="5"></path> <path d="M13.6824546,2 L3.7109392,11.9715154 L13.7394238,22" stroke="#FFF" stroke-width="5"></path>
...@@ -152,7 +144,9 @@ cpietsch@gmail.com ...@@ -152,7 +144,9 @@ cpietsch@gmail.com
<div class="inner"> <div class="inner">
<div id="infobar" class="infosidebar"> <div id="infobar" class="infosidebar">
<span v-html="marked(info)"></span> <span v-html="marked(info)"></span>
<div class="credit">Powered by <a href="https://vikusviewer.fh-potsdam.de/" target="_blank">VIKUS Viewer</a></div> <div class="credit">Powered by
<a href="https://vikusviewer.fh-potsdam.de/" target="_blank">VIKUS Viewer</a>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -169,4 +163,5 @@ cpietsch@gmail.com ...@@ -169,4 +163,5 @@ cpietsch@gmail.com
<script src="js/viz.js"></script> <script src="js/viz.js"></script>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -100,15 +100,27 @@ function Canvas() { ...@@ -100,15 +100,27 @@ function Canvas() {
var timelineData; var timelineData;
var stage, stage1, stage2, stage3, stage4, stage5; var stage, stage1, stage2, stage3, stage4, stage5;
var timelineHover = false; var timelineHover = false;
var tsne = []
var tsneIndex = {}
function canvas() {} function canvas() {}
canvas.rangeBand = function() { return rangeBand } canvas.rangeBand = function () {
canvas.width = function() { return width } return rangeBand
canvas.height = function() { return height } }
canvas.rangeBandImage = function() { return rangeBandImage } canvas.width = function () {
return width
}
canvas.height = function () {
return height
}
canvas.rangeBandImage = function () {
return rangeBandImage
}
canvas.zoom = zoom canvas.zoom = zoom
canvas.selectedImage = function() { return selectedImage } canvas.selectedImage = function () {
return selectedImage
}
canvas.x = x canvas.x = x
canvas.resize = function () { canvas.resize = function () {
...@@ -193,15 +205,23 @@ function Canvas() { ...@@ -193,15 +205,23 @@ function Canvas() {
}); });
var canvasDomain = d3.nest() var canvasDomain = d3.nest()
.key(function(d) { return d.year; }) .key(function (d) {
return d.year;
})
.entries(_data.concat(_timeline)) .entries(_data.concat(_timeline))
.sort(function(a, b) { return a.key - b.key; }) .sort(function (a, b) {
.map(function(d) { return d.key; }) return a.key - b.key;
})
.map(function (d) {
return d.key;
})
timeDomain = canvasDomain.map(function (d) { timeDomain = canvasDomain.map(function (d) {
return { return {
key: d, key: d,
values: _timeline.filter(function(e) { return d == e.year; }) values: _timeline.filter(function (e) {
return d == e.year;
})
} }
}) })
...@@ -247,7 +267,10 @@ function Canvas() { ...@@ -247,7 +267,10 @@ function Canvas() {
}) })
.on("click", function () { .on("click", function () {
console.log("click"); console.log("click");
if (spriteClick) { spriteClick = false; return; } if (spriteClick) {
spriteClick = false;
return;
}
if (selectedImage && !selectedImage.id) return; if (selectedImage && !selectedImage.id) return;
if (drag) return; if (drag) return;
if (selectedImageDistance > 15) return; if (selectedImageDistance > 15) return;
...@@ -271,6 +294,35 @@ function Canvas() { ...@@ -271,6 +294,35 @@ function Canvas() {
state.init = true; state.init = true;
}; };
canvas.addTsneData = function (d) {
console.time("tsne")
var clean = d.map(function (d) {
return {
id: d.id,
x: parseFloat(d.x),
y: parseFloat(d.y)
}
})
var xExtent = d3.extent(clean, function (d) {
return d.x
})
var yExtent = d3.extent(clean, function (d) {
return d.y
})
var x = d3.scale.linear().range([0, 1]).domain(xExtent)
var y = d3.scale.linear().range([0, 1]).domain(yExtent)
d.forEach(function (d) {
tsneIndex[d.id] = [
x(d.x),
y(d.y)
]
})
console.timeEnd("tsne")
}
function mousemove(d) { function mousemove(d) {
if (timelineHover) return; if (timelineHover) return;
...@@ -399,7 +451,14 @@ function Canvas() { ...@@ -399,7 +451,14 @@ function Canvas() {
} }
canvas.wakeup = function() { sleep = false } canvas.wakeup = function () {
sleep = false
}
canvas.setMode = function (mode) {
state.mode = mode
canvas.project()
}
function animate(time) { function animate(time) {
requestAnimationFrame(animate); requestAnimationFrame(animate);
...@@ -590,16 +649,89 @@ function Canvas() { ...@@ -590,16 +649,89 @@ function Canvas() {
canvas.wakeup(); canvas.wakeup();
} }
// canvas.project = function () {
// sleep = false
// canvas.split();
// canvas.resetZoom();
// }
canvas.project = function () { canvas.project = function () {
sleep = false sleep = false
if (state.mode == "tsne") {
canvas.projectTSNE();
} else {
canvas.split(); canvas.split();
}
canvas.resetZoom(); canvas.resetZoom();
} }
canvas.projectTSNE = function () {
var marginBottom = -height / 2.5;
var inactive = data.filter(function (d) {
return !d.active;
});
var inactiveSize = inactive.length;
var active = data.filter(function (d) {
return d.active;
});
// inactive.sort(function (a, b) {
// return a.rTSNE - b.rTSNE
// });
var dimension = Math.min(width, height) * 0.8
inactive.forEach(function (d, i) {
var r = dimension / 1.9 + Math.random() * 40;
var a = -Math.PI / 2 + (i / inactiveSize) * 2 * Math.PI;
d.x = r * Math.cos(a) + width / 2 + margin.left;
d.y = r * Math.sin(a) + marginBottom;
});
active.forEach(function (d) {
var factor = height / 2;
var tsneEntry = tsneIndex[d.id]
// var tsneEntry = tsne.find(function (t) {
// return t.id == d.id
// })
d.x = (tsneEntry[0] * dimension) + width / 2 - dimension / 2 + margin.left;
d.y = (tsneEntry[1] * dimension) - dimension / 2 + marginBottom;
})
data.forEach(function (d) {
d.x1 = d.x * scale1 + imageSize / 2;
d.y1 = d.y * scale1 + imageSize / 2;
if (d.sprite.position.x == 0) {
d.sprite.position.x = d.x1;
d.sprite.position.y = d.y1;
}
if (d.sprite2) {
d.sprite2.position.x = d.x * scale2 + imageSize2 / 2;
d.sprite2.position.y = d.y * scale2 + imageSize2 / 2;
}
});
quadtree = Quadtree(data);
//chart.resetZoom();
}
canvas.resetZoom = function () { canvas.resetZoom = function () {
var duration = 1400; var duration = 1400;
console.log(translate)
extent = d3.extent(data, function(d) { return d.y; }); extent = d3.extent(data, function (d) {
return d.y;
});
var y = -extent[1] - bottomPadding; var y = -extent[1] - bottomPadding;
y = (extent[1] / -3) - bottomPadding y = (extent[1] / -3) - bottomPadding
...@@ -663,7 +795,8 @@ function Canvas() { ...@@ -663,7 +795,8 @@ function Canvas() {
} }
// console.log("load", d) // console.log("load", d)
var texture = new PIXI.Texture.fromImage(config.loader.textures.detail.url + d.id + '.jpg', true) var url = encodeURIComponent(config.loader.textures.detail.url + d.id + '.jpg')
var texture = new PIXI.Texture.fromImage(url, true)
var sprite = new PIXI.Sprite(texture); var sprite = new PIXI.Sprite(texture);
var update = function () { var update = function () {
...@@ -696,7 +829,7 @@ function Canvas() { ...@@ -696,7 +829,7 @@ function Canvas() {
state.lastZoomed = d.id; state.lastZoomed = d.id;
var page = d.page ? '_' + d.page : '' var page = d.page ? '_' + d.page : ''
var url = config.loader.textures.big.url + d.id + page + ".jpg"; var url = encodeURIComponent(config.loader.textures.big.url + d.id + page + ".jpg");
var texture = new PIXI.Texture.fromImage(url, true) var texture = new PIXI.Texture.fromImage(url, true)
var sprite = new PIXI.Sprite(texture); var sprite = new PIXI.Sprite(texture);
......
function Timeline() { function Timeline() {
var fontScaleYear = d3.scale.linear() var fontScaleYear = d3.scale.linear()
...@@ -19,6 +18,7 @@ function Timeline() { ...@@ -19,6 +18,7 @@ function Timeline() {
var container var container
var timeDomain var timeDomain
var fontSize = 1 var fontSize = 1
var disabled = false
function timeline() {} function timeline() {}
...@@ -35,7 +35,13 @@ function Timeline() { ...@@ -35,7 +35,13 @@ function Timeline() {
fontSize = timelineFontScale(scale) fontSize = timelineFontScale(scale)
} }
timeline.setDisabled = function (d) {
disabled = d
container.style("display", disabled ? "none" : "block")
}
timeline.update = function (x1, x2, scale, translate) { timeline.update = function (x1, x2, scale, translate) {
if (disabled) return
timeDomain.forEach(function (d) { timeDomain.forEach(function (d) {
d.pos = ((d.x - x1) * scale); d.pos = ((d.x - x1) * scale);
......
...@@ -44,7 +44,6 @@ if (Modernizr.webgl && !utils.isMobile()) { ...@@ -44,7 +44,6 @@ if (Modernizr.webgl && !utils.isMobile()) {
init(); init();
} }
function init() { function init() {
tags = Tags(); tags = Tags();
...@@ -57,7 +56,7 @@ function init() { ...@@ -57,7 +56,7 @@ function init() {
utils.initConfig(config) utils.initConfig(config)
d3.csv(config.loader.timeline, function(timeline) { Loader(config.loader.timeline).finished(function (timeline) {
Loader(config.loader.items).finished(function (data) { Loader(config.loader.items).finished(function (data) {
utils.clean(data); utils.clean(data);
...@@ -66,11 +65,21 @@ function init() { ...@@ -66,11 +65,21 @@ function init() {
search.init(); search.init();
canvas.init(data, timeline, config); canvas.init(data, timeline, config);
if (config.loader.tsne) {
d3.csv(config.loader.tsne, function (tsne) {
console.log(tsne)
d3.select(".navi").classed("hide", false)
canvas.addTsneData(tsne)
})
}
LoaderSprites() LoaderSprites()
.progress(function (textures) { .progress(function (textures) {
Object.keys(textures).forEach(function (id) { Object.keys(textures).forEach(function (id) {
data data
.filter(function(d) { return d.id === id }) .filter(function (d) {
return d.id === id
})
.forEach(function (d) { .forEach(function (d) {
d.sprite.texture = textures[id] d.sprite.texture = textures[id]
}) })
...@@ -110,6 +119,18 @@ function init() { ...@@ -110,6 +119,18 @@ function init() {
var s = !d3.select(".infobar").classed("sneak"); var s = !d3.select(".infobar").classed("sneak");
d3.select(".infobar").classed("sneak", s) d3.select(".infobar").classed("sneak", s)
}) })
d3.selectAll(".navi .button")
.on("click", function () {
var that = this
var mode = d3.select(this).text()
canvas.setMode(mode)
timeline.setDisabled(mode != "time")
d3.selectAll(".navi .button").classed("active", function () {
return that === this
});
})
} }
d3.select(".browserInfo").classed("show", utils.isMobile()); d3.select(".browserInfo").classed("show", utils.isMobile());
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment