Interfacing the law project proposal Zalan Szakacs: Difference between revisions

From XPUB & Lens-Based wiki
 
(95 intermediate revisions by 2 users not shown)
Line 1: Line 1:
__NOTOC__
__NOTOC__


='''Research about 3D Steganography'''=
<gallery class="center" widths=692px heights=405px>
[[File:Screen Shot 2018-05-13 at 23.14.14.png|692 x 405px|framed|Embedded Process and Extracting Process]]
Screen Shot 2018-05-13 at 23.14.14.png|(a) Embedding and (b) Extracting Process
</gallery>
<br>
 
=Research about 3D Steganography=
 


[https://www.forbes.com/forbes/welcome/?toURL=https://www.forbes.com/sites/andygreenberg/2013/08/30/here-are-five-ridiculous-redesigns-of-the-3d-printed-gun/&refURL=&referrer=#3eb889105965 Redesigns of the 3D printed gun]
[https://www.forbes.com/forbes/welcome/?toURL=https://www.forbes.com/sites/andygreenberg/2013/08/30/here-are-five-ridiculous-redesigns-of-the-3d-printed-gun/&refURL=&referrer=#3eb889105965 Redesigns of the 3D printed gun]
Line 11: Line 16:
<br>
<br>
[https://www.researchgate.net/publication/256104122_Pattern_based_3D_image_Steganography Pattern based 3D image Steganography]
[https://www.researchgate.net/publication/256104122_Pattern_based_3D_image_Steganography Pattern based 3D image Steganography]
<br>
[https://www.researchgate.net/publication/225126087_Embedding_data_in_3D_models?enrichId=rgreq-f6b8b648a1fdffc4c699d364cdfc8a2b-XXX&enrichSource=Y292ZXJQYWdlOzIyNTEyNjA4NztBUzoxMDI5NDY0OTg5NDA5MzNAMTQwMTU1NTc2NzM0Mw%3D%3D&el=1_x_3&_esc=publicationCoverPdf Embedding data in 3D models]
<br>
<br>
[http://graphics.csie.ncku.edu.tw/Paper_Video/TVCG_Data_Hinding/TVCG_Data_Hinding_accepted_2208_06.pdf A High Capacity 3D Steganography Algorithm]
[http://graphics.csie.ncku.edu.tw/Paper_Video/TVCG_Data_Hinding/TVCG_Data_Hinding_accepted_2208_06.pdf A High Capacity 3D Steganography Algorithm]
Line 20: Line 27:
[http://www.plummerfernandez.com/Disarming-Corruptor Disarming Corruptor]
[http://www.plummerfernandez.com/Disarming-Corruptor Disarming Corruptor]
<br>
<br>
[https://gizmodo.com/a-smartphone-can-copy-3d-a-model-by-just-recording-the-1762851946 pirating 3d prints by listening to the printer]
<br>
=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?
<br>
=Project proposal XPPL Volumetric Catalogue=
<onlyinclude>
[[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?''
<br>
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>
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 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).
<br>
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.
<br>
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.
<br>
</onlyinclude>
== 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.
<br>
=Steps to take=
→ <del>Loading all the books in the .json file</del>
<br>
→ <del>scenery creation (light, shadow)</del>
<br>
→ <del>materiality of the shapes (material or color)</del>
<br>
→ <del>rotation add to the shapes </del>
<br>
→  <del>hover over mouse effect</del>
<br>
→  <del>link the shapes with the books</del>
<br>
→  <del>hosting online or offline?</del>
<br>
→  <del>fine-tuning!!!</del>
<br>
=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>
<gallery class="center" widths=692px heights=405px>
1600px-USASCII code chart.png|ASCII code chart
</gallery>
<br>
''HTML''
<pre>
<!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>
</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
  }
};


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


*3D steganography version of the library
request.send();
*3D shapes printed with the 3D printer from the school
*circumvention of censorship
*simhash process MD5 Hash - showing the visability in the 3D shape (shows the evolution of the collection)
*with a 3D scanner it will be possible to access the collection of the library again
*mirroring and visualising the collection
*alternative way of looking at the collection
*compression
*time capture in 3D file


='''Steps to take'''=


='''Script research'''=
// --- 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=
[https://ieeexplore.ieee.org/document/8089350/authors A High-Capacity 3D Steganography Algorithm With Adjustable Distortion]
<br>
[https://nl.mathworks.com/matlabcentral/fileexchange/52494-3d-image-watermarking-using-dwt--dct--dct+dwt?focused=3892556&tab=function 3D image watermarking]
<br>
[http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.228.220 Watermarking of 3D Meshes using Matlab]
<br>
[https://www.embodi3d.com/blogs/entry/193-how-to-create-3d-printable-models-from-medical-scans-in-30-minutes-using-free-software-osirix-blender-and-meshmixer 3D printing]
<br>
[https://ef.engr.utk.edu/ef230-2016-01//modules/3dprinting/matlab3d.php Matlab 3D printing]
<br>
[https://github.com/tonylukasavage/jsstl Pure Javascript demo code for parsing and rendering STL (ascii and binary) files]

Latest revision as of 21: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