Interfacing the law project proposal Zalan Szakacs: Difference between revisions

From XPUB & Lens-Based wiki
 
(28 intermediate revisions by the same user not shown)
Line 41: Line 41:
<br>
<br>


=Project proposal=
=Project proposal XPPL Volumetric Catalogue=
<onlyinclude>
<onlyinclude>
[[File:Screen Shot 2018-05-22 at 14.38.06.png|thumbnail|left|First sketch of 3D stack]]
[[File:Screen Shot 2018-05-22 at 14.38.06.png|thumbnail|left|First sketch of 3D stack]]
''How would it be possible to explore an exisiting book collection in a serendipitous way?''
''How would it be possible to explore an exisiting book collection in a serendipitous way?''
<br>
<br>
An addion to the XPPL would be a 3D web interface, which creates customized stacks in forms of 3D shapes. The morphology of the shapes will be generated based on the ASCII numeric values of the book titles (x, y, x values.) ASCII, abbreviated from American Standard Code for Information Interchange, is a character encoding standard for electronic communication. ASCII codes represent text in computers, telecommunications equipment, and other devices. Either round, spicky, robust, geometric or organic–each book has it's own character, which will be mirrored in the interface. By clicking on the specific shape the user will be linked to the API of the specific book. This project explores new ways of spreading information in the multidimensional environment and alternative ways of representing books in digital libraries. It aims to break the rigidness of square presentation formats and plays with syntaxes of abuising file formats. This interface could run locally or online.
An addition to the XPPL (space for potential pirate librarianship of the Piet Zwart Institute) would be a 3D web interface, which translates the metadata of the books into volumetric geometries. The morphology of the shapes is  generated based on the string values of the book titles. Either round, spicy, robust, geometric or organic–each book has it's own character, which will be mirrored in the interface. By clicking on the specific shape the user will be linked to the API of the specific book. This project explores new ways of spreading information in the multidimensional environment, datavisualisation and alternative ways of representing books in digital libraries. It aims to break the rigidness of square presentation formats and plays with syntaxes of abusing file formats.  
<br>
<br>
The interface will be written in JavaScript. WebGL is an JavaScript API for rendering interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. WebGL does so by introducing an API that closely conforms to OpenGL ES 2.0 that can be used in HTML5 <canvas> elements.
The interface will be written in JavaScript. WebGL is an JavaScript API for rendering interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. WebGL does so by introducing an API that closely conforms to OpenGL ES 2.0 that can be used in HTML5 <canvas> elements.
<br>
This project approaches the interfaces in a serendipitous and subjective manner by using steganography, which is encoding and decoding information on 2D and 3D levels.
<br>
<br>
This project uses the similar way of thinking, but the outcome differentiats from the other students. The fascination came from a long time research about encoding and decoding information on 2D and 3D levels, steganography (hiding information in other file formats).
This project uses the similar way of thinking, but the outcome differentiats from the other students. The fascination came from a long time research about encoding and decoding information on 2D and 3D levels, steganography (hiding information in other file formats).
Line 68: Line 66:


=Steps to take=
=Steps to take=
→ Loading all the books in the .json file
 
<del>Loading all the books in the .json file</del>
<br>
→ <del>scenery creation (light, shadow)</del>
<br>
<br>
scenery creation (light, shadow)
<del>materiality of the shapes (material or color)</del>
<br>
<br>
materiality of the shapes (material or color)
<del>rotation add to the shapes </del>
<br>
<br>
rotation add to the shapes
<del>hover over mouse effect</del>
<br>
<br>
more diversity in the shapes
<del>link the shapes with the books</del>
<br>
<br>
hover over mouse effect
<del>hosting online or offline?</del>
<br>
<br>
link the shapes with the books
<del>fine-tuning!!!</del>
<br>
<br>
→ hosting online or offline?
 
=Javascript process=
 
'''Transforming the book titles into ASCII numeric values'''
 
<gallery class="center" widths=692px heights=405px>
Screen Shot 2018-05-31 at 23.33.05.png|The book titles are translated into ASCII numeric values
</gallery>
<br>
<br>
→ fine-tuning!!!
 
<gallery class="center" widths=692px heights=405px>
1600px-USASCII code chart.png|ASCII code chart
</gallery>
<br>
<br>


=Javascript process=
''HTML''
<onlyinclude>
<pre>
[[File:Screen Shot 2018-05-31 at 23.33.05.png|thumbnail|left|The book titles are translated into ASCII numeric values]]
<syntaxhighlight lang="lang="html4strict">...</source>
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<html>
Line 173: Line 182:
</html>
</html>


</syntaxhighlight>
</pre>
 
'''Using ASCII numeric values for x, y, z values for generating 3D shapes (spheres and cubes)'''
 
 
 
<gallery class="center" widths=692px heights=405px>
Threejs.gif|Using ASCII numeric values for x, y, z values for generating 3D shapes (spheres and cubes)
</gallery>
<br>
 
 
''HTML''
 
<pre>
<!DOCTYPE html>
<html>
 
<head>
<title></title>
</head>
<body>
<canvas id="viewer"></canvas>
<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
<script src="setup.js"></script>
 
</body>
</html>
 
</pre>
 
''JS''
 
<pre>
 
const MAX_DISTANCE = 100;
const MAX_SPHERE_RADIUS = 50;
 
let camera, scene, controls, renderer;
 
console.clear();
let bookTitles = [{
        title: "Mac OS X Leopard Edition",
        id: "1",
        type: 'sphere'
    },
    {
        title: "The Qmail Handbook",
        id: "2",
        type: 'sphere'
    },
    {
        title: "Hardening Network Infrastructure: Bulletproof Your Systems Before You Are Hacked!",
        id: "3",
        type: 'sphere'
    },
    {
        title: "Cocoa Programming for Mac OS X Second Edition",
        id: "4",
        type: 'cube'
    },
    {
        title: "LDAP System Administration",
        id: "5",
        type: 'cube'
    }
 
];
 
function setupBooks() {
    bookTitles.forEach(book => {
        let geometry;
        let radius = Math.floor(Math.random() * MAX_SPHERE_RADIUS);
        switch (book.type) {
            case 'sphere':
                geometry = new THREE.SphereGeometry(radius, 50, 50);
                break;
            case 'cube':
                geometry = new THREE.BoxGeometry(radius, radius, radius, 10, 10);
                break;
        }
 
        // Add missing material
        let mesh = new THREE.Mesh(geometry);
 
 
        mesh.position.copy(getRandomVector());
       
        scene.add(mesh);
    });
}
 
function getRandomVector() {
    return new THREE.Vector3(getRandom(), getRandom(), getRandom());
}
 
function getRandom() {
    return Math.floor(Math.random() * MAX_DISTANCE);
}
 
function animate() {
    requestAnimationFrame(animate);
    controls.update();
    render();
}
 
function render() {
    renderer.render(scene, camera);
}
 
window.onload = function () {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xeeeeee);
 
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10000);
    camera.position.z = 500;
 
    camera.updateProjectionMatrix();
 
    scene.add(camera);
 
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
 
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
 
    let viewer = document.querySelector('#viewer');
   
    viewer.parentNode.replaceChild(renderer.domElement, viewer);
 
    controls = new THREE.OrbitControls(camera, renderer.domElement);
 
    animate();
    setupBooks();
}
 
window.onresize = function () {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
};
 
</pre>
 
'''Final working script'''
 
<gallery class="center" widths=692px heights=405px>
Xppl.gif
</gallery>
<br>
 
 
string value = radius
 
 
colours = categories of the library
 
 
category2color["Technical"] = "#003d5d";
<br>
category2color["Science/History"] = "#f23d5d";
<br>
category2color["Media studies"] = "#8c3d5d";
<br>
category2color["Philosophy"] = "#723d5d";
<br>
category2color["Computer culture"] = "#423d5d";
<br>
category2color["Design / writing / publishing"] = "#ff0000";
<br>
category2color["Desk"] = "#09edff";
<br>
category2color["Art"] = "#ffedff";
<br>
category2color["Digital (Steve Trim 2 reading)"] = "#ffe1cc";
<br>
category2color["Media Studies (Femke Trim 3)"] = "#00e1cc";
<br>
category2color["Literature, Culture, Theory"] = "#00a1d4";
 
 
x,y,z = title, category, file format
 
 
sphere.position.x = get_x(book.title)
<br>
sphere.position.z = get_z(book.category)
<br>
sphere.rotation.y =get_y(book.fileformat)
<br>
 
''The string values of the titles and the metadata from the JSON file is generating the radius and the position of the shapes''
 
<gallery class="center" widths=692px heights=405px>
Screen Shot 2018-06-13 at 19.35.47.png
</gallery>
<br>
 
 
 
<pre>
 
<!DOCTYPE html>
 
<html>
<head>
    <title>XPPL VOLUMETRIC CATALOGUE </title>
    <meta charset="utf-8">
    <link href="https://fonts.googleapis.com/css?family=Karla" rel="stylesheet">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body{
           
                font-family: 'Karla', sans-serif;
                font-size:15px;
 
                text-align:left;
                background: #140b33;
                margin: 0px;
                overflow: hidden;
            }
            #info {
                position: absolute;
                top: 0px; width: 10%;
                padding: 10px;
            }
            a {
                color: #444b7c;
            }
 
            .swal-button {
                padding: 7px 19px;
 
                background-color: #aca4c1;
                font-size: 12px;
 
 
}
 
            .swal-title {
                margin: 0px;
                font-size: 16px;
                box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.21);
                margin-bottom: 28px;
}
 
            .swal-overlay {
                background-color: rgba(20, 13, 50, 0.6);
}
            .swal-modal {
                background-color: rgba(172, 164, 193, 1);
 
}
 
 
 
    </style>
</head>
<body>
    <div id="info"><a  target="_blank" rel="noopener"> <b> <font size="5"> XPPL VOLUMETRIC CATALOGUE </font> </b> <br>  <br> Feel free to move around and explore the content of the library by clicking on the volumetric shapes</div>
    <div id="container"></div>
    <canvas id="viewer"></canvas>
    <script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js"></script>
    <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
    <script>
 
 
var category2color = new Object();
category2color["Technical"] = "#003d5d";
category2color["Science/History"] = "#f23d5d";
category2color["Media studies"] = "#8c3d5d";
category2color["Philosophy"] = "#723d5d";
category2color["Computer culture"] = "#423d5d";
category2color["Design / writing / publishing"] = "#ff0000";
category2color["Desk"] = "#09edff";
category2color["Art"] = "#ffedff";
category2color["Digital (Steve Trim 2 reading)"] = "#ffe1cc";
category2color["Media Studies (Femke Trim 3)"] = "#00e1cc";
category2color["Literature, Culture, Theory"] = "#00a1d4";
 
category2color["default"] = "#cdc1ec";
 
 
 
 
        console.clear();
 
const MAX_DISTANCE = 1000;
//
const MIN_DISTANCE = 200;
 
const MAX_SPHERE_RADIUS = 700;
 
var bookTitles;
var request = new XMLHttpRequest();
request.open('GET', 'export.json', true);
 
request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    var data = JSON.parse(request.responseText);
    //bookTitles = data;
    bookTitles = data.books[0];
 
    bookTitles.forEach(function(book) {
      book.type = "sphere";
      console.log('BOOK:', book);
 
 
 
    });
    console.log("got books", bookTitles)
    // loaded data, start
    init();
    animate();
 
  } else {
    // We reached our target server, but it returned an error
 
  }
};
 
request.onerror = function() {
  // There was a connection error of some sort
};
 
request.send();
 
 
// --- threeJS --- //
var renderer, scene, camera, distance, raycaster, projector;
 
var container = document.getElementById('container');
var raycaster = new THREE.Raycaster(),INTERSECTED;
var mouse = new THREE.Vector2();
var distance = 600;
 
// -- basic initialization -- //
function init() {
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
 
 
    renderer.setClearColor(0x140b33, 1);
    //renderer.setClearColor(0xFF00FF, 1.0);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);
    renderer.clear();
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 300000);
  //  controls = new THREE.OrbitControls(camera);
    //camera.position.set(1, 1, -5);
    camera.position.set(MAX_DISTANCE/2, MAX_DISTANCE/20, MAX_DISTANCE/20);
 
    camera.updateProjectionMatrix();
 
    controls = new THREE.OrbitControls(camera, renderer.domElement);
  //  controls.maxDistance = MAX_DISTANCE * 3;
    controls.target = new THREE.Vector3(0, 0, 0);
    controls.rotateSpeed = .2;
    controls.enableDamping = true;
    console.log(controls.enableDamping);
    controls.dampingFactor = .15;
    controls.panSpeed = .15;
    controls.screenSpacePanning = true;
    // to disable zoom
    controls.enableZoom = true;
    // to disable rotation
    controls.enableRotate = true;
    // to disable pan
    controls.enablePan = true;
    scene.add(camera);
 
    //camera.position.set(-150, 10, 1);
    //controls.update();
  // scene.add(camera);
 
    light = new THREE.PointLight(0x404040);
    light.position.set(50, 0, 0);
    scene.add(light);
  /* light_two = new THREE.PointLight(0xffffff, 1, 4000);
    light_two.position.set(-100, 800, 800);
    lightAmbient = new THREE.AmbientLight(0x404040);
    scene.add(light, light_two, lightAmbient);*/
 
    // createSpheres();
    scene.add(new THREE.DirectionalLight(0xffffff));
    scene.add(new THREE.AmbientLight(0x222222));
 
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.clear();
 
    let viewer = document.querySelector('#viewer');
    viewer.parentNode.replaceChild(renderer.domElement, viewer);
 
   
 
 
window.onload = function () {
 
    createDiamond();
    createSpace();
   
 
    document.addEventListener('mousemove', onMouseMove, false);
    document.addEventListener('mousedown', onDocumentMouseDown, false);
    window.addEventListener('resize', onWindowResize, false);
};
 
 
}
 
// -- diamonds -- //
 
 
function catInobject(obj, category){ //checks if category is in object, if so return corresponding color
    if (obj.hasOwnProperty(category)) {
    var color = obj[category];
    } else { var color = obj["default"];
}
    return color;
}
 
function createDiamond() {
 
    diamondsGroup = new THREE.Object3D();
 
 
        console.log();
 
        bookTitles.forEach(function (book) {
            var chosencolor = catInobject(category2color, book.category);
            var sphere = new THREE.SphereGeometry(1.3, Math.random() * 50, Math.random() * 50);
            var material = new THREE.MeshPhongMaterial({
                color: chosencolor,//Math.random() * 0xff00000 - 0xff00000,
                shading: THREE.FlatShading
            });
            var sphere = new THREE.Mesh(sphere, material);
            sphere.position.x = get_x(book.title) * Math.random() * 100 - 1000;
            sphere.position.z = get_z(book.category) * Math.random() *  100 - 1000;
            sphere.rotation.y =get_y(book.fileformat) * Math.random() * 100 - 1000;
            //sphere.scale.x = sphere.scale.y = sphere.scale.z = Math.random() * 50 + 10;
            sphere.scale.x = sphere.scale.y = sphere.scale.z = get_size(book.title);
 
            sphere.userData = book;
            diamondsGroup.add(sphere);
 
        })
        diamondsGroup.position.x = 500;
        scene.add(diamondsGroup);
 
};
 
function get_size(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}
 
function get_x(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}
function get_y(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}
function get_z(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}
 
// -- dots on the back -- //
function createSpace() {
 
    let dots = new THREE.Object3D();
 
    for (let i = 0; i < 1000; i++) {
        let circleGeometry = new THREE.SphereGeometry(2, Math.random() * 5, Math.random() * 5);
        let color;
        if (Math.round(Math.random()) === 0)
            color = new THREE.Color('#003d5d');
        else
            //color = new THREE.Color('#f23d5d');
            color = new THREE.Color('#f23d5d');
 
        let material = new THREE.MeshPhongMaterial({
            color: color
            //side: THREE.DoubleSide
        });
 
        material.flatShading = true;
 
        //var material = new THREE.MeshBasicMaterial({
            //color : new THREE.Color(Math.floor(Math.random() * 1) === 1),
            //color : new THREE.Color(Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 0),
            //shading: THREE.FlatShading,
        //})
 
       
      var circle = new THREE.Mesh(circleGeometry, material);
        material.side = THREE.DoubleSide;
 
        circle.position.x = Math.random() * -distance * 100;
        circle.position.y = Math.random() * -distance * 8;
        circle.position.z = Math.random() * distance * 5;
        circle.rotation.y = Math.random() * 2 * Math.PI;
        circle.scale.x = circle.scale.y = circle.scale.z = Math.random() * 8 + 5;
        dots.add(circle);
    }
 
    dots.position.x = 14000;
    dots.position.y = 1500;
    dots.position.z = 4000;
    dots.rotation.y = Math.PI * 600;
    dots.rotation.z = Math.PI * 500;
 
    scene.add(dots);
};
/*
 
 
  let circle = new THREE.Mesh(circleGeometry, material);
 
    let direction = getRandomVector().normalize();
    let minDistance = MAX_DISTANCE * 8;
    let maxDistance = minDistance + 0.5;
    direction.multiplyScalar(Math.random() * (maxDistance - minDistance) + minDistance);
 
    circle.position.add(direction);
 
    dots.add(circle);
    }
    scene.add(dots);
}
*/
 
 
function getRandomVector(max_distance) {
    if (max_distance === undefined)
        max_distance = 1;
    return new THREE.Vector3(getRandom(max_distance), getRandom(max_distance), getRandom(max_distance));
}
 
function getRandom(max_distance) {
    let random = Math.random() * max_distance;
    random *= Math.floor(Math.random() * 2) === 1 ? 1 : -1;
    return random;
}
 
 
 
// -- events -- //
function onMouseMove(event) {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
    mouseX = event.clientX - window.innerWidth / 2 ;
    mouseY = event.clientY - window.innerHeight / 2 ;
    //camera.position.x += (mouseX - camera.position.x) * 0.01;
    //camera.position.y += (mouseY - camera.position.y) * 0.01;
    // camera.lookAt(scene.position);
};
 
function onDocumentMouseDown(event) {
 
    event.preventDefault();
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
 
    raycaster.setFromCamera(mouse, camera);
 
    var intersects = raycaster.intersectObjects(diamondsGroup.children);
    if (intersects.length > 0) {
        var book = intersects[0].object.userData;
        console.log("click", book);
        //alert("book" + book.title);
 
        //swal(book.title + book.authors);
 
 
        swal(book.title + " \n " + " " + book.authors.map(function(x){return x.author_name}).join(", ") +  " \n" + book.category, {
            buttons: ["return", "open" ],
        }).then (function(button){
            console.log("button", button)
            if (button) {
              window.location = "/books/"+book.id
            }
           
        });
 
 
 
    }
 
};
 
function onDocumentMouseWheel( event ) {
 
    fov -= event.wheelDeltaY * 0.05;
    camera.projectionMatrix = THREE.Matrix4.makePerspective( fov, window.innerWidth / window.innerHeight, 1, 1100 );
 
}
 
 
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.updateProjectionMatrix();
    controls.update();
};
 
// ---- //
function animate() {
    requestAnimationFrame(animate);
    controls.update();
    render();
};
 
// -- render all -- //
function render() {
 
    var timer = 0.00001 * Date.now();
 
    for (var i = 0, l = diamondsGroup.children.length; i < l; i++) {
        var object = diamondsGroup.children[i];
        object.position.y = 500 * Math.cos(timer + i);
        object.rotation.y += Math.PI / 500;
    }
 
    // update the picking ray with the camera and mouse position
    raycaster.setFromCamera(mouse, camera);
 
    // calculate objects intersecting the picking ray
    var intersects = raycaster.intersectObjects(diamondsGroup.children);
 
    if (intersects.length > 0) {
        if (INTERSECTED != intersects[0].object) {
            if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
            INTERSECTED = intersects[0].object;
            INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
            INTERSECTED.material.emissive.setHex(Math.random() * 0xff00000 - 0xff00000);
        }
    } else {
        if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
        INTERSECTED = null;
    }
 
    renderer.render(scene, camera);
};
 
// -- run functions -- //
//init();
//animate();
 
    </script>
 
</body>
</html>
</pre>


=Resources=
=Resources=

Latest revision as of 20:07, 13 June 2018



Research about 3D Steganography

Redesigns of the 3D printed gun
3D Steganography talk by Dennis de Bel
3D Steganography 3D printing project by Dennis de Bel
Pattern based 3D image Steganography
Embedding data in 3D models
A High Capacity 3D Steganography Algorithm
A High-Capacity Data Hiding Method for Polygonal Meshes?
Object Steganography, Noah Feehan (2012)
Disarming Corruptor
pirating 3d prints by listening to the printer

Questions to think about

  • Which part of the collection should be hidden? Why?
  • How to link with the X-Lib?
  • Based on which metadata variables are the shapes created?
  • Which feature would transform the 3D generated shapes into 3D printable files?
  • Who is using it? Only PZ?
  • What is the most important features?
  • Where would be situated? local? networked? physical space?
  • When would it be available? Temporary? Always?


Project proposal XPPL Volumetric Catalogue

First sketch of 3D stack

How would it be possible to explore an exisiting book collection in a serendipitous way?
An addition to the XPPL (space for potential pirate librarianship of the Piet Zwart Institute) would be a 3D web interface, which translates the metadata of the books into volumetric geometries. The morphology of the shapes is  generated based on the string values of the book titles. Either round, spicy, robust, geometric or organic–each book has it's own character, which will be mirrored in the interface. By clicking on the specific shape the user will be linked to the API of the specific book. This project explores new ways of spreading information in the multidimensional environment, datavisualisation and alternative ways of representing books in digital libraries. It aims to break the rigidness of square presentation formats and plays with syntaxes of abusing file formats.
The interface will be written in JavaScript. WebGL is an JavaScript API for rendering interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. WebGL does so by introducing an API that closely conforms to OpenGL ES 2.0 that can be used in HTML5 <canvas> elements.
This project uses the similar way of thinking, but the outcome differentiats from the other students. The fascination came from a long time research about encoding and decoding information on 2D and 3D levels, steganography (hiding information in other file formats).
During my previous project ACCP I started to explore different encoding and decoding systems for informations. This fascination became very intriguing for me, so therefor I decided to invest more time about it for a new project, which will explore the potential of hiding informations in 3D shapes. An other learning goal with this project to get familiar with JavaScript and WebGL.

The issues could arise on the technical level, since there is a limited time (4 weeks) to realise the whole project without JavaScript knowledge beforehand. An other issue could arise about the connection way of the original interface and 3D interface.


Bibliography

  • Golden, K. (2015). The Nest Interface Is No Interface: The simple path to brilliant technology. New Riders.
  • Steyerl, H. (2012). The Wretched of the Screen. SternbergPress.
  • Fuller, M. (2013). Behind The Blip: Essays on the culture of software. Autonomedia.


Steps to take

Loading all the books in the .json file
scenery creation (light, shadow)
materiality of the shapes (material or color)
rotation add to the shapes
hover over mouse effect
link the shapes with the books
hosting online or offline?
fine-tuning!!!

Javascript process

Transforming the book titles into ASCII numeric values



HTML

<!DOCTYPE html>
<html>
<head>
    <title>Collection</title>
    <script>
        var booktitles = [
                        {
                            title: "Mac OS X Leopard Edition",
                            id: "1",
 
                        },
                        {
                            title: "The Qmail Handbook",
                            id: "2",
 
                        },
                        {
                            title: "Hardening Network Infrastructure: Bulletproof Your Systems Before You Are Hacked!",
                            id: "3",
 
                        },
                        {
                            title: "Cocoa Programming for Mac OS X Second Edition",
                            id: "4",
 
                        },
                        {
                            title: "LDAP System Administration",
                            id: "5",
 
                        }
 
                        ];
 
 
    </script>
 
</head>
<body>
<ul>
    <script>
        for(i =0; i < booktitles.length;i++){
            document.write('<h4>'+booktitles[i].title+'</h4>');
 
            var myCoords = xyzGen(booktitles[i].title)
            console.log("Coordinates of " + booktitles[i].title)
            console.log("x : " + myCoords.x)
            console.log("y : " + myCoords.y)
            console.log("z : " + myCoords.z)
 
        }
    //  console.log(booktitles);
 
        var x,y,z
 
        function xyzGen(string) {
            let coords = {
                x: 0,
                y: 0,
                z: 0,
            }
            string = string.split(' ')
 
            for (var i = 0; i < 3; i++) {
                for (c in string[i]) {
                    if (i == 0) {
                        coords.x += string[i].charCodeAt(c)
                    } else if (i == 1) {
                        coords.y += string[i].charCodeAt(c)
                    } else if (i == 2) {
                        coords.z += string[i].charCodeAt(c)
                    }
                }
            }
            return coords
        }
    </script>
</ul>
 
</body>
</html>

Using ASCII numeric values for x, y, z values for generating 3D shapes (spheres and cubes)




HTML

<!DOCTYPE html>
<html>

<head>
	<title></title>
</head>
<body>
	<canvas id="viewer"></canvas> 
	<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
	<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
	<script src="setup.js"></script>
		

</body>
</html>

JS


const MAX_DISTANCE = 100;
const MAX_SPHERE_RADIUS = 50;

let camera, scene, controls, renderer;

console.clear();
let bookTitles = [{
        title: "Mac OS X Leopard Edition",
        id: "1",
        type: 'sphere'
    },
    {
        title: "The Qmail Handbook",
        id: "2",
        type: 'sphere'
    },
    {
        title: "Hardening Network Infrastructure: Bulletproof Your Systems Before You Are Hacked!",
        id: "3",
        type: 'sphere'
    },
    {
        title: "Cocoa Programming for Mac OS X Second Edition",
        id: "4",
        type: 'cube'
    },
    {
        title: "LDAP System Administration",
        id: "5",
        type: 'cube'
    }

];

function setupBooks() {
    bookTitles.forEach(book => {
        let geometry;
        let radius = Math.floor(Math.random() * MAX_SPHERE_RADIUS);
        switch (book.type) {
            case 'sphere':
                geometry = new THREE.SphereGeometry(radius, 50, 50);
                break;
            case 'cube':
                geometry = new THREE.BoxGeometry(radius, radius, radius, 10, 10);
                break;
        }

        // Add missing material
        let mesh = new THREE.Mesh(geometry);


        mesh.position.copy(getRandomVector());
        
        scene.add(mesh);
    });
}

function getRandomVector() {
    return new THREE.Vector3(getRandom(), getRandom(), getRandom());
}

function getRandom() {
    return Math.floor(Math.random() * MAX_DISTANCE);
}

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    render();
}

function render() {
    renderer.render(scene, camera);
}

window.onload = function () {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xeeeeee);

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10000);
    camera.position.z = 500;

    camera.updateProjectionMatrix();

    scene.add(camera);

    renderer = new THREE.WebGLRenderer({
        antialias: true
    });

    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);

    let viewer = document.querySelector('#viewer');
    
    viewer.parentNode.replaceChild(renderer.domElement, viewer);

    controls = new THREE.OrbitControls(camera, renderer.domElement);

    animate();
    setupBooks();
}

window.onresize = function () {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
};

Final working script



string value = radius


colours = categories of the library


category2color["Technical"] = "#003d5d";
category2color["Science/History"] = "#f23d5d";
category2color["Media studies"] = "#8c3d5d";
category2color["Philosophy"] = "#723d5d";
category2color["Computer culture"] = "#423d5d";
category2color["Design / writing / publishing"] = "#ff0000";
category2color["Desk"] = "#09edff";
category2color["Art"] = "#ffedff";
category2color["Digital (Steve Trim 2 reading)"] = "#ffe1cc";
category2color["Media Studies (Femke Trim 3)"] = "#00e1cc";
category2color["Literature, Culture, Theory"] = "#00a1d4";


x,y,z = title, category, file format


sphere.position.x = get_x(book.title)
sphere.position.z = get_z(book.category)
sphere.rotation.y =get_y(book.fileformat)

The string values of the titles and the metadata from the JSON file is generating the radius and the position of the shapes




<!DOCTYPE html>

<html>
<head>
    <title>XPPL VOLUMETRIC CATALOGUE </title>
    <meta charset="utf-8">
    <link href="https://fonts.googleapis.com/css?family=Karla" rel="stylesheet">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body{
            
                font-family: 'Karla', sans-serif;
                font-size:15px;

                text-align:left;
                background: #140b33;
                margin: 0px;
                overflow: hidden;
            }
            #info {
                position: absolute;
                top: 0px; width: 10%;
                padding: 10px;
            }
            a {
                color: #444b7c;
            }

            .swal-button {
                padding: 7px 19px;

                background-color: #aca4c1;
                font-size: 12px;


}

            .swal-title {
                margin: 0px;
                font-size: 16px;
                box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.21);
                margin-bottom: 28px;
}

            .swal-overlay {
                background-color: rgba(20, 13, 50, 0.6);
}
            .swal-modal {
                background-color: rgba(172, 164, 193, 1);

}



    </style>
</head>
<body>
    <div id="info"><a  target="_blank" rel="noopener"> <b> <font size="5"> XPPL VOLUMETRIC CATALOGUE </font> </b> <br>  <br> Feel free to move around and explore the content of the library by clicking on the volumetric shapes</div>
    <div id="container"></div>
    <canvas id="viewer"></canvas>
    <script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js"></script>
    <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
    <script>


var category2color = new Object();
category2color["Technical"] = "#003d5d";
category2color["Science/History"] = "#f23d5d";
category2color["Media studies"] = "#8c3d5d";
category2color["Philosophy"] = "#723d5d";
category2color["Computer culture"] = "#423d5d";
category2color["Design / writing / publishing"] = "#ff0000";
category2color["Desk"] = "#09edff";
category2color["Art"] = "#ffedff";
category2color["Digital (Steve Trim 2 reading)"] = "#ffe1cc";
category2color["Media Studies (Femke Trim 3)"] = "#00e1cc";
category2color["Literature, Culture, Theory"] = "#00a1d4";

category2color["default"] = "#cdc1ec";




        console.clear();

const MAX_DISTANCE = 1000;
//
const MIN_DISTANCE = 200;

const MAX_SPHERE_RADIUS = 700;

var bookTitles;
var request = new XMLHttpRequest();
request.open('GET', 'export.json', true);

request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    var data = JSON.parse(request.responseText);
    //bookTitles = data;
    bookTitles = data.books[0];

    bookTitles.forEach(function(book) {
      book.type = "sphere";
      console.log('BOOK:', book);



    });
    console.log("got books", bookTitles)
    // loaded data, start
    init();
    animate();

  } else {
    // We reached our target server, but it returned an error

  }
};

request.onerror = function() {
  // There was a connection error of some sort
};

request.send();


// --- threeJS --- //
var renderer, scene, camera, distance, raycaster, projector;

var container = document.getElementById('container');
var raycaster = new THREE.Raycaster(),INTERSECTED;
var mouse = new THREE.Vector2();
var distance = 600;

// -- basic initialization -- //
function init() {
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });


    renderer.setClearColor(0x140b33, 1);
    //renderer.setClearColor(0xFF00FF, 1.0);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);
    renderer.clear();
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 300000);
  //  controls = new THREE.OrbitControls(camera);
    //camera.position.set(1, 1, -5);
    camera.position.set(MAX_DISTANCE/2, MAX_DISTANCE/20, MAX_DISTANCE/20);

    camera.updateProjectionMatrix();

    controls = new THREE.OrbitControls(camera, renderer.domElement);
  //  controls.maxDistance = MAX_DISTANCE * 3;
    controls.target = new THREE.Vector3(0, 0, 0);
    controls.rotateSpeed = .2;
    controls.enableDamping = true;
    console.log(controls.enableDamping);
    controls.dampingFactor = .15;
    controls.panSpeed = .15;
    controls.screenSpacePanning = true;
    // to disable zoom
    controls.enableZoom = true;
    // to disable rotation
    controls.enableRotate = true;
    // to disable pan
    controls.enablePan = true;
    scene.add(camera);

    //camera.position.set(-150, 10, 1);
    //controls.update();
   // scene.add(camera);

    light = new THREE.PointLight(0x404040);
    light.position.set(50, 0, 0);
    scene.add(light);
   /* light_two = new THREE.PointLight(0xffffff, 1, 4000);
    light_two.position.set(-100, 800, 800);
    lightAmbient = new THREE.AmbientLight(0x404040);
    scene.add(light, light_two, lightAmbient);*/

    // createSpheres();
    scene.add(new THREE.DirectionalLight(0xffffff));
    scene.add(new THREE.AmbientLight(0x222222));

    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.clear();

    let viewer = document.querySelector('#viewer');
    viewer.parentNode.replaceChild(renderer.domElement, viewer);

    


window.onload = function () {

    createDiamond();
    createSpace();
    

    document.addEventListener('mousemove', onMouseMove, false);
    document.addEventListener('mousedown', onDocumentMouseDown, false);
    window.addEventListener('resize', onWindowResize, false);
};


}

// -- diamonds -- //


function catInobject(obj, category){ //checks if category is in object, if so return corresponding color
    if (obj.hasOwnProperty(category)) {
    var color = obj[category];
    } else { var color = obj["default"];
 }
    return color;
}

function createDiamond() {

    diamondsGroup = new THREE.Object3D();


        console.log();

        bookTitles.forEach(function (book) {
            var chosencolor = catInobject(category2color, book.category);
            var sphere = new THREE.SphereGeometry(1.3, Math.random() * 50, Math.random() * 50);
            var material = new THREE.MeshPhongMaterial({
                color: chosencolor,//Math.random() * 0xff00000 - 0xff00000,
                shading: THREE.FlatShading
            });
            var sphere = new THREE.Mesh(sphere, material);
            sphere.position.x = get_x(book.title) * Math.random() * 100 - 1000;
            sphere.position.z = get_z(book.category) * Math.random() *  100 - 1000;
            sphere.rotation.y =get_y(book.fileformat) * Math.random() * 100 - 1000;
            //sphere.scale.x = sphere.scale.y = sphere.scale.z = Math.random() * 50 + 10;
            sphere.scale.x = sphere.scale.y = sphere.scale.z = get_size(book.title);

            sphere.userData = book;
            diamondsGroup.add(sphere);

        })
        diamondsGroup.position.x = 500;
        scene.add(diamondsGroup);

};

function get_size(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}

function get_x(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}
function get_y(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}
function get_z(title){
    radius = 0;
    for (var i = 0; i < title.length; i++) {
      radius = radius + title.charCodeAt(i)
    }
    console.log(radius)
    return radius/100;
}

// -- dots on the back -- //
function createSpace() {

    let dots = new THREE.Object3D();

    for (let i = 0; i < 1000; i++) {
        let circleGeometry = new THREE.SphereGeometry(2, Math.random() * 5, Math.random() * 5);
        let color;
        if (Math.round(Math.random()) === 0)
            color = new THREE.Color('#003d5d');
        else
            //color = new THREE.Color('#f23d5d');
            color = new THREE.Color('#f23d5d');

        let material = new THREE.MeshPhongMaterial({
            color: color
            //side: THREE.DoubleSide
        });

        material.flatShading = true;

        //var material = new THREE.MeshBasicMaterial({
            //color : new THREE.Color(Math.floor(Math.random() * 1) === 1),
            //color : new THREE.Color(Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 0),
            //shading: THREE.FlatShading,
        //})

         
       var circle = new THREE.Mesh(circleGeometry, material);
        material.side = THREE.DoubleSide;

        circle.position.x = Math.random() * -distance * 100;
        circle.position.y = Math.random() * -distance * 8;
        circle.position.z = Math.random() * distance * 5;
        circle.rotation.y = Math.random() * 2 * Math.PI;
        circle.scale.x = circle.scale.y = circle.scale.z = Math.random() * 8 + 5;
        dots.add(circle);
    }

    dots.position.x = 14000;
    dots.position.y = 1500;
    dots.position.z = 4000;
    dots.rotation.y = Math.PI * 600;
    dots.rotation.z = Math.PI * 500;

    scene.add(dots);
};
/*


   let circle = new THREE.Mesh(circleGeometry, material);

    let direction = getRandomVector().normalize();
    let minDistance = MAX_DISTANCE * 8;
    let maxDistance = minDistance + 0.5;
    direction.multiplyScalar(Math.random() * (maxDistance - minDistance) + minDistance);

    circle.position.add(direction);

    dots.add(circle);
    }
    scene.add(dots);
} 
*/


function getRandomVector(max_distance) {
    if (max_distance === undefined)
        max_distance = 1;
    return new THREE.Vector3(getRandom(max_distance), getRandom(max_distance), getRandom(max_distance));
}

function getRandom(max_distance) {
    let random = Math.random() * max_distance;
    random *= Math.floor(Math.random() * 2) === 1 ? 1 : -1;
    return random;
}



// -- events -- //
function onMouseMove(event) {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
     mouseX = event.clientX - window.innerWidth / 2 ;
     mouseY = event.clientY - window.innerHeight / 2 ;
    //camera.position.x += (mouseX - camera.position.x) * 0.01;
    //camera.position.y += (mouseY - camera.position.y) * 0.01;
    // camera.lookAt(scene.position);
};

function onDocumentMouseDown(event) {

    event.preventDefault();
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);

    var intersects = raycaster.intersectObjects(diamondsGroup.children);
    if (intersects.length > 0) {
        var book = intersects[0].object.userData;
        console.log("click", book);
        //alert("book" + book.title);

        //swal(book.title + book.authors);


        swal(book.title + " \n " + " " + book.authors.map(function(x){return x.author_name}).join(", ") +   " \n" + book.category, {
            buttons: ["return", "open" ],
        }).then (function(button){
            console.log("button", button)
            if (button) {
               window.location = "/books/"+book.id 
            }
            
        });



    }

};

function onDocumentMouseWheel( event ) {

    fov -= event.wheelDeltaY * 0.05;
    camera.projectionMatrix = THREE.Matrix4.makePerspective( fov, window.innerWidth / window.innerHeight, 1, 1100 );

}


function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.updateProjectionMatrix();
    controls.update();
};

// ---- //
function animate() {
    requestAnimationFrame(animate);
    controls.update();
    render();
};

// -- render all -- //
function render() {

    var timer = 0.00001 * Date.now();

    for (var i = 0, l = diamondsGroup.children.length; i < l; i++) {
        var object = diamondsGroup.children[i];
        object.position.y = 500 * Math.cos(timer + i);
        object.rotation.y += Math.PI / 500;
    }

    // update the picking ray with the camera and mouse position
    raycaster.setFromCamera(mouse, camera);

    // calculate objects intersecting the picking ray
    var intersects = raycaster.intersectObjects(diamondsGroup.children);

    if (intersects.length > 0) {
        if (INTERSECTED != intersects[0].object) {
            if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
            INTERSECTED = intersects[0].object;
            INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
            INTERSECTED.material.emissive.setHex(Math.random() * 0xff00000 - 0xff00000);
        }
    } else {
        if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
        INTERSECTED = null;
    }

    renderer.render(scene, camera);
};

// -- run functions -- //
//init();
//animate();

    </script>

</body>
</html>

Resources

A High-Capacity 3D Steganography Algorithm With Adjustable Distortion
3D image watermarking
Watermarking of 3D Meshes using Matlab
3D printing
Matlab 3D printing
Pure Javascript demo code for parsing and rendering STL (ascii and binary) files