Index: js/standard_expdef_demo/README.txt
===================================================================
--- js/standard_expdef_demo/README.txt	(revision 880)
+++ js/standard_expdef_demo/README.txt	(revision 880)
@@ -0,0 +1,12 @@
+This is a simple demo/animation demonstrating the basic behavior of "standard.expdef" experiment definition in Framsticks.
+This demo uses Framsticks SDK and three.js.
+Published online at http://www.framsticks.com/common/std_expdef.html
+
+
+To compile/run, you need to download the following files: (TODO automate this)
+
+- prepare/ensure you have Framsticks SDK in ../sdk/frams-sdk.js
+- js/external/threejs/CSS3DRenderer.js 
+- js/external/threejs/OrbitControls.js 
+- js/external/threejs/Sky.js 
+- js/external/threejs/three.min.js 
Index: js/standard_expdef_demo/index.html
===================================================================
--- js/standard_expdef_demo/index.html	(revision 880)
+++ js/standard_expdef_demo/index.html	(revision 880)
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <head>
+		<meta charset=utf-8>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <link rel="stylesheet" type="text/css" href="static/styles.css">
+		<title>Standard.expdef Animation</title>
+	</head>
+	<body style="margin: 0; overflow: hidden;">
+    <!--External dependencies-->
+		<script src="js/external/threejs/three.min.js"></script>
+    <script src="js/external/threejs/CSS3DRenderer.js"></script>
+    <script src="js/external/threejs/OrbitControls.js"></script>
+    <script src="js/external/threejs/Sky.js"></script>
+    <script src="js/external/tweenjs/tween.min.js"></script>
+    <script src="../sdk/frams-sdk.js"></script>
+    <script src="js/external/framsjs/visualization/jointmeshfactory.js"></script>
+    <script src="js/external/framsjs/visualization/partmeshfactory.js"></script>
+    <script src="js/external/framsjs/visualization/transformations.js"></script>
+    <!--Project sources-->
+    <script src="js/config.js"></script>
+    <script src="js/world/core.js"></script>
+    <script src="js/world/object/framstick.js"></script>
+    <script src="js/world/object/table.js"></script>
+    <script src="js/world/object/text.js"></script>
+    <script src="js/world/object/arrow.js"></script>
+    <script src="js/simulation/core.js"></script>
+		<script src="js/app.js"></script>
+	</body>
+</html>
Index: js/standard_expdef_demo/js/app.js
===================================================================
--- js/standard_expdef_demo/js/app.js	(revision 880)
+++ js/standard_expdef_demo/js/app.js	(revision 880)
@@ -0,0 +1,116 @@
+/**
+ * @file Main Module
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+class App {
+  /**
+   * Main module responsible for the maintanance of the core animation components.
+   */
+
+  constructor() {
+    this.scene = this.Scene();
+    this.camera = this.Camera();
+    this.controls = this.Controls();
+    this.webGLRenderer = this.WebGLRenderer();
+    this.css3DRenderer = this.CSS3DRenderer();
+
+    this.simulation = new Simulation(new World(this.camera, this.scene));
+
+    document.body.appendChild(this.webGLRenderer.domElement);
+    document.body.appendChild(this.css3DRenderer.domElement);
+    window.addEventListener("resize", this.onWindowResize.bind(this), false);
+    return this;
+  }
+
+  Scene() {
+    /**
+     * Creates a new Scene object.
+     */
+    let scene = new THREE.Scene();
+    scene.fog = new THREE.Fog(Config.Fog.BACKGROUND, Config.Fog.NEAR, Config.Fog.FAR);
+    return scene;
+  }
+
+  Camera() {
+    /**
+     * Creates a new Camera object.
+     */
+    let camera = new THREE.PerspectiveCamera(Config.Camera.FOV, Config.Camera.ASPECT, Config.Camera.NEAR, Config.Camera.FAR);
+    camera.position.set(Config.Camera.X_POS, Config.Camera.Y_POS, Config.Camera.Z_POS);
+    camera.updateProjectionMatrix();
+    return camera;
+  }
+
+  Controls() {
+    /**
+     * Creates a new Control object alowing to steer the camera.
+     */
+    let controls = new THREE.OrbitControls(this.camera);
+    controls.autoRotate = Config.Controls.AUTO_ROTATE;
+    controls.autoRotateSpeed = Config.Controls.AUTO_ROTATE_SPEED;
+    controls.minDistance = Config.Controls.MIN_DISTANCE;
+    controls.maxDistance = Config.Controls.MAX_DISTANCE;
+    controls.maxPolarAngle = Config.Controls.MAX_POLAR_ANGLE;
+    controls.update();
+    return controls
+  }
+
+  WebGLRenderer() {
+    /**
+     * Creates a WebGL Renderer object used to visualize 3D components.
+     */
+    let renderer = new THREE.WebGLRenderer({antialias: Config.WebGLRenderer.ANTIALIAS});
+    renderer.setSize(Config.WebGLRenderer.WIDTH, Config.WebGLRenderer.HEIGHT);
+    renderer.setPixelRatio(Config.WebGLRenderer.PIXEL_RATIO);
+    renderer.shadowMap.enabled = Config.WebGLRenderer.ShadowMap.ENABLED;
+    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+    renderer.shadowMap.renderSingleSided = Config.WebGLRenderer.ShadowMap.SINGLE_SIDED;
+    renderer.gammaInput = Config.WebGLRenderer.GAMMA_INPUT;
+    renderer.gammaOutput = Config.WebGLRenderer.GAMMA_OUTPUT;
+    return renderer;
+  }
+
+  CSS3DRenderer() {
+    /**
+     * Creates a CSS3D Renderer object used to visualize HTML+CSS components in 3D scene.
+     */
+    let renderer = new THREE.CSS3DRenderer();
+    renderer.setSize(Config.CSS3DRenderer.WIDTH, Config.CSS3DRenderer.HEIGHT);
+    renderer.domElement.style.position = Config.CSS3DRenderer.POSITION;
+    renderer.domElement.style.top = Config.CSS3DRenderer.TOP;
+    return renderer
+  }
+
+  run() {
+    /**
+     * Starts the simulation and rendering process.
+     */
+    this.render();
+    this.simulation.run();
+  }
+
+  render() {
+    /**
+     * Main renderer loop, called on each frame.
+     */
+    requestAnimationFrame(this.render.bind(this));
+    TWEEN.update();
+    this.controls.update();
+    this.webGLRenderer.render(this.scene, this.camera);
+    this.css3DRenderer.render(this.scene, this.camera);
+  }
+
+  onWindowResize() {
+    /**
+     * Updates the dimmensions of renderers after window size changes.
+     */
+    this.camera.aspect = window.innerWidth / window.innerHeight;
+    this.camera.updateProjectionMatrix();
+    this.webGLRenderer.setSize(window.innerWidth, window.innerHeight);
+    this.css3DRenderer.setSize(window.innerWidth, window.innerHeight);
+  }
+}
+
+new App().run();
Index: js/standard_expdef_demo/js/config.js
===================================================================
--- js/standard_expdef_demo/js/config.js	(revision 880)
+++ js/standard_expdef_demo/js/config.js	(revision 880)
@@ -0,0 +1,324 @@
+/**
+ * @file Configuration File
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+var Config = {
+
+  Simulation: {
+    MUTATION_PROBABILITY: 0.5,
+    CROSSOVER_PROBABILITY: 0.3,
+    EVOLUTION_MAX_REPETITIONS: 100,
+    GENOTYPES: [
+      "/*4*/ #5,<<X><<X>X>X>>LLX",
+      "/*4*/ <X>l<X>l<<X>X>LLLX",
+      "/*4*/ <,<,,<,X,,>X>X>X",
+      "/*4*/ <X><X><<X>X><X>X",
+      "/*4*/ <<X>RR<<X>X>X>X",
+      "/*4*/ <<X><<X>X>X>X",
+    ],
+
+    Timing: {
+      INITIAL: 1000,
+      EVOLUTION_METHOD_SELECTION: 1000,
+      GENOTYPE_SELECTION: 3500,
+      EVOLUTION_RESULT: 100,
+      EVOLUTION_RETURN: 100,
+      SHOW_FRAMSTICK: 3500,
+      DESTROY_FRAMSTICK: 2500,
+      SHOW_RESULT: 2500
+    },
+
+    Element: {
+      Position: {
+        RESULT_TEXT: {x: 400, y: 305, z: -105},
+        MUTATION_TEXT: {x: 400, y: 460, z: -277},
+        CROSSOVER_TEXT: {x: 400, y: 540, z: -277},
+        FITNESS_TEXT: {x: 400, y: 300, z: 485},
+        EVOLVED_GENOTYPE_PLANK: {x: 400, y: 240, z: 0},
+        EVOLVED_FITNESS_PLANK: {x: 400, y: 240, z: 380},
+        MUTATION_GENOTYPE_PLANK: {x: 400, y: 400, z: 0},
+        MUTATION_FITNESS_PLANK: {x: 400, y: 400, z: 380},
+        CROSSOVER_GENOTYPE_PLANK1: {x: 400, y: 400, z: 0},
+        CROSSOVER_FITNESS_PLANK1: {x: 400, y: 400, z: 380},
+        CROSSOVER_GENOTYPE_PLANK2: {x: 400, y: 480, z: 0},
+        CROSSOVER_FITNESS_PLANK2: {x: 400, y: 480, z: 380}
+      },
+
+      Angle: {
+          SIDE_VIEW: {y: -Math.PI / 2},
+          NORMAL: {y: 0}
+      }
+    },
+
+    Camera: {
+      SPEED: 1800,
+      Position: {
+        TABLE: {x: 0, y: 100, z: 1200},
+        FRAMSTICK: {x: -723, y: 454, z: -4},
+        SIDE_VIEW: {x: -972, y: 206, z: -14}
+      }
+    }
+  },
+
+  Camera: {
+    FOV: 60,
+    ASPECT: window.innerWidth / window.innerHeight,
+    NEAR: 0.1,
+    FAR: 2000000,
+    X_POS: 0,
+    Y_POS: 100,
+    Z_POS: 1200
+  },
+
+  Controls: {
+    AUTO_ROTATE: false,
+    AUTO_ROTATE_SPEED: 1,
+    MIN_DISTANCE: 100,
+    MAX_DISTANCE: 1500,
+    MAX_POLAR_ANGLE: Math.PI / 2.2
+  },
+
+  Light: {
+    Ambient: {
+      COLOR: 0x8a8a8a
+    },
+
+    Directional: {
+      COLOR: 0xdfebff,
+      INTENSITY: 1,
+      X_POS: 50,
+      Y_POS: 200,
+      Z_POS: 100,
+      CAST_SHADOW: true,
+
+      Shadow: {
+        MapSize: {
+          WIDTH: 2048,
+          HEIGHT: 2048
+        },
+
+        Camera: {
+          LEFT: -2000,
+          RIGHT: 2000,
+          TOP: 2000,
+          BOTTOM: -2000,
+          FAR: 1000
+        }
+      }
+    }
+  },
+
+  WebGLRenderer: {
+    WIDTH: window.innerWidth,
+    HEIGHT: window.innerHeight,
+    PIXEL_RATIO: window.devicePixelRatio,
+    ANTIALIAS: true,
+    GAMMA_INPUT: true,
+    GAMMA_OUTPUT: true,
+
+    ShadowMap: {
+      ENABLED: true,
+      SINGLE_SIDED: false
+    }
+  },
+
+  CSS3DRenderer: {
+    WIDTH: window.innerWidth,
+    HEIGHT: window.innerHeight,
+    POSITION: "absolute",
+    TOP: 0
+  },
+
+  Fog: {
+    BACKGROUND: 0xb8edfc,
+    NEAR: 3000,
+    FAR: 15000
+  },
+
+  Sky: {
+    SCALE: 450000,
+    TURBIDITY: 3,
+    RAYLEIGH: 0.21,
+    LUMINANCE: 0.1,
+    MIE_COEFFICIENT: 0.005,
+    MIE_DIRECTIONAL_G: 1
+  },
+
+  Sun: {
+    RADIUS: 20000,
+    WIDTH_SEGMENTS: 16,
+    HEIGHT_SEGMENTS: 8,
+    COLOR: 0xffffff,
+    INCLINATION: 0.182,
+    AZIMUTH: 0.193,
+    Y_POS: -700000
+  },
+
+  Ground: {
+    TEXTURE: "https://raw.githubusercontent.com/mrdoob/three.js/master/examples/textures/terrain/grasslight-big.jpg",
+    REPEAT: 25,
+    ANISOTROPY: 16,
+    WIDTH: 25000,
+    Y_POS: -40,
+    X_ROT: -Math.PI / 2,
+    RECEIVE_SHADOW: true
+  },
+
+  Framstick: {
+    COLOR: 0xcacaca,
+    Y_POS: 100,
+    X_ROT: -Math.PI / 2,
+    CAST_SHADOW: true,
+    CAST_SHADOW: true,
+
+    Tween: {
+      Scale: {
+        Up: {
+          SIZE: 100,
+          EASING: TWEEN.Easing.Quadratic.InOut
+        },
+        Down: {
+          SIZE: 80,
+          EASING: TWEEN.Easing.Quadratic.InOut
+        },
+        SPEED: 500
+      },
+
+      Rotate: {
+        SPEED: 1000,
+        EASING: TWEEN.Easing.Linear.None
+      }
+    },
+
+    Geometry: {
+      PART: {
+        defaultShape: {
+          radius: 0.213,
+          segments: 16
+        },
+        ellipsoidShape: {
+          radius: 1,
+          segments: 32
+        }
+      },
+      JOINT: {
+        cylinderShape: {
+          radius: 0.1,
+          radiusSegments: 10,
+          isTransparent: false,
+          opacity: 1
+        },
+        linkShape: {
+          radius: 0.04,
+          radiusSegments: 10,
+          isTransparent: true,
+          opacity: 0.2
+        }
+      }
+    }
+  },
+
+  Table: {
+    Board: {
+      X_POS: 0,
+      Z_POS: -500,
+      WIDTH: 800,
+      THICKNESS: 50,
+      SPACING: 25,
+      TEXTURE: "https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/brick_diffuse.jpg",
+      REPEAT: 1,
+      ANISOTROPY: 8
+    },
+
+    GenotypePlank: {
+      X_POS: -70,
+      Z_POS: -470,
+      WIDTH: 600,
+      HEIGHT: 65,
+      THICKNESS: 10,
+      TEXTURE: "https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/hardwood2_diffuse.jpg",
+      REPEAT: 3,
+      ANISOTROPY: 8,
+      CAST_SHADOW: true,
+      RECEIVE_SHADOW: true,
+
+      Tween: {
+        Move: {
+          SPEED: 1000,
+          EASING: TWEEN.Easing.Quadratic.Out
+        },
+
+        Rotate: {
+          SPEED: 1000,
+          EASING: TWEEN.Easing.Quadratic.Out
+        }
+      }
+    },
+
+    FitnessPlank: {
+      X_POS: 310,
+      Z_POS: -470,
+      WIDTH: 150,
+      HEIGHT: 65,
+      THICKNESS: 10,
+      TEXTURE: "https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/hardwood2_diffuse.jpg",
+      REPEAT: 1,
+      ANISOTROPY: 8,
+      CAST_SHADOW: true,
+      RECEIVE_SHADOW: true,
+
+      Tween: {
+        Move: {
+          SPEED: 1000,
+          EASING: TWEEN.Easing.Quadratic.Out
+        },
+
+        Rotate: {
+          SPEED: 1000,
+          EASING: TWEEN.Easing.Quadratic.Out
+        }
+      }
+    }
+  },
+
+  Arrow: {
+    WIDTH: 30,
+    LENGTH: 100,
+    FACES: 32,
+    TEXTURE:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/hardwood2_diffuse.jpg",
+    REPEAT: 1,
+    ANISOTROPY: 8,
+    CAST_SHADOW: true,
+    RECEIVE_SHADOW: true,
+    X_POS: -500,
+    Z_POS: -500,
+    Z_ROT: -Math.PI / 2,
+
+    Tween: {
+
+      Scale: {
+        Up: {
+          SIZE: 1,
+          SPEED: 300,
+          EASING: TWEEN.Easing.Quadratic.InOut
+        }
+      },
+
+      Move: {
+        Right: {
+          X_POS: -450,
+          SPEED: 300,
+          EASING: TWEEN.Easing.Quadratic.InOut
+        },
+
+        Left: {
+          X_POS: -500,
+          SPEED: 300,
+          EASING: TWEEN.Easing.Quadratic.InOut
+        }
+      }
+    }
+  }
+};
Index: js/standard_expdef_demo/js/external/framsjs/visualization/jointmeshfactory.js
===================================================================
--- js/standard_expdef_demo/js/external/framsjs/visualization/jointmeshfactory.js	(revision 880)
+++ js/standard_expdef_demo/js/external/framsjs/visualization/jointmeshfactory.js	(revision 880)
@@ -0,0 +1,116 @@
+const numsegmentstube = 1;
+
+/**
+ * Helper class for creating meshes for joints in Framsticks-SDK. It combines
+ * Framsticks-SDK logic with THREE.js logic.
+ *
+ * For now shape versions of joints are hard-coded. It may need redefinition in
+ * the future.
+ */
+class JointMeshFactory {
+    /**
+     * Basic constructor that takes information of how joints should be drawn.
+     * @param {object} config basic config for joints drawing
+     * @param {object}  cylinderShape object holding following fields
+     * @param {number}  cylinderShape.radius radius of mesh
+     * @param {number}  cylinderShape.radiusSegments number of segments for mesh
+     * @param {boolean} cylinderShape.isTransparent true if transparent, false otherwise
+     * @param {number}  cylinderShape.opacity opacity of mesh
+     * @param {object}  linkShape object holding following fields
+     * @param {number}  linkShape.radius radius of mesh
+     * @param {number}  linkShape.radiusSegments number of segments for mesh
+     * @param {boolean} linkShape.isTransparent true if transparent, false otherwise
+     * @param {number}  linkShape.opacity opacity of mesh
+     */
+    constructor(config) {
+        this.config = config;
+        this.transformations = new ShapeTransformations();
+        this.jointShapes = this.transformations.getJointShapes();
+    }
+
+    /**
+     * Creates Mesh for a given Joint.
+     * @param {Module.Joint} joint Framsticks-SDK Joint class object
+     * @param {object}  shapeConfig object holding following fields
+     * @param {number}  shapeConfig.radius radius of mesh
+     * @param {number}  shapeConfig.radiusSegments number of segments for mesh
+     * @param {boolean} shapeConfig.isTransparent true if transparent, false otherwise
+     * @param {number}  shapeConfig.opacity opacity of mesh
+     * @returns {Mesh}  Mesh for a given joint
+     */
+    getNewJointMesh(joint, shapeConfig) {
+        let firstPartPosVec = new THREE.Vector3(
+            joint.get_part1().get_p().get_x(),
+            joint.get_part1().get_p().get_y(),
+            joint.get_part1().get_p().get_z());
+        let secondPartPosVec = new THREE.Vector3(
+            joint.get_part2().get_p().get_x(),
+            joint.get_part2().get_p().get_y(),
+            joint.get_part2().get_p().get_z());
+        let line = new THREE.LineCurve3(firstPartPosVec, secondPartPosVec);
+        let geometry = new THREE.TubeGeometry(line, numsegmentstube, shapeConfig.radius,
+            shapeConfig.radiusSegments, false);
+
+        let material = this.transformations.getNewMaterial(
+            joint.get_vcolor().get_x(),
+            joint.get_vcolor().get_y(),
+            joint.get_vcolor().get_z());
+        material.transparent = shapeConfig.isTransparent;
+        material.opacity = shapeConfig.opacity;
+        let mesh = new THREE.Mesh(geometry, material);
+        mesh.userData = {
+            isBodyElement: true,
+            type: 'j',
+            data: joint,
+            mesh: mesh,
+            connectedParts: [],
+            showTransparent: shapeConfig.isTransparent
+        };
+        return mesh;
+    }
+
+    /**
+     * Method finds for a given jointMesh all attached partMeshes and updates
+     * respectively for those objects their connectedParts and connectedJoints
+     * fields.
+     * @param {JointMesh}  jointMesh joint for which attached parts will be searched
+     * @param {PartMesh}  partMeshes list of available parts
+     */
+    addConnectionInfo(jointMesh, partMeshes) {
+        let p1 = jointMesh.data.get_part1();
+        let p2 = jointMesh.data.get_part2();
+        let count = 0;
+        for (let i = 0; i < partMeshes.length && count < 2; ++i) {
+            if (partMeshes[i].data === p1 || partMeshes[i].data === p2) {
+                jointMesh.connectedParts.push(partMeshes[i]);
+                partMeshes[i].connectedJoints.push(jointMesh);
+                ++count;
+            }
+        }
+    }
+
+    /**
+     * Creates mesh for a given Joint. Additional parameter partMeshes is
+     * provided to update both Joint and connected Parts with info about
+     * their connectivity.
+     * @param {Module.Joint} joint joint for which mesh is created
+     * @param {PartMesh}  partMeshes list of available parts
+     * @returns {JointMesh} new joint mesh, for properties of Object look at addConnectionInfo jointMesh param documentation
+     */
+    create(joint, partMeshes) {
+        let result;
+        let shape = joint.get_shape();
+
+        if (this.jointShapes['SHAPE_FIXED'].value == shape) {
+            result = this.getNewJointMesh(joint, this.config.linkShape);
+        } else {
+            result = this.getNewJointMesh(joint, this.config.cylinderShape);
+        }
+
+        if (partMeshes) {
+            this.addConnectionInfo(result.userData, partMeshes);
+        }
+
+        return result;
+    }
+}
Index: js/standard_expdef_demo/js/external/framsjs/visualization/partmeshfactory.js
===================================================================
--- js/standard_expdef_demo/js/external/framsjs/visualization/partmeshfactory.js	(revision 880)
+++ js/standard_expdef_demo/js/external/framsjs/visualization/partmeshfactory.js	(revision 880)
@@ -0,0 +1,170 @@
+class PartMeshFactory {
+    /**
+     * Basic constructor that takes information of how parts should be drawn.
+     * @param {object} config object holding following fields
+     * @param {object} config.defaultShape object holding following fields
+     * @param {number} config.defaultShape.radius radius of part cylinder
+     * @param {number} config.defaultShape.segments number of segments on cylinder
+     * @param {object} config.ellipsoidShape object holding following fields
+     * @param {number} config.ellipsoidShape.radius radius of part ellipsoid
+     * @param {number} config.ellipsoidShape.segments number of segments on ellipsoid
+     *
+     */
+    constructor(config) {
+        this.config = config;
+        this.transformations = new ShapeTransformations();
+        this.partShapes = this.transformations.getPartShapes();
+    }
+
+    /**
+     * Creates sphere mesh for a given part.
+     * @param {Module.Part} part part, for which Mesh is generated
+     * @param {number} sphereRadius radius of part sphere
+     * @param {number} segments number of created segments
+     * @param {boolean} applyScale true if scale for this mesh should be applied from Module.Part
+     * @returns {PartMesh} info about part mesh
+     */
+    getNewSphereMesh(part, sphereRadius, segments, applyScale) {
+        let geometry = new THREE.SphereGeometry(sphereRadius, segments, segments);
+        let material = this.transformations.getNewMaterial(
+            part.get_vcolor().get_x(),
+            part.get_vcolor().get_y(),
+            part.get_vcolor().get_z());
+        let mesh = new THREE.Mesh(geometry, material);
+        mesh.position.set(
+            part.get_p().get_x(),
+            part.get_p().get_y(),
+            part.get_p().get_z());
+
+        if (applyScale) {
+            mesh.scale.set(
+                part.get_scale().get_x(),
+                part.get_scale().get_y(),
+                part.get_scale().get_z());
+        }
+
+        let angles = part.get_o().getAngles();
+        mesh.rotation.copy(
+            this.transformations.getPartRotation(
+                angles.get_x(),
+                angles.get_y(),
+                angles.get_z())
+        );
+
+        mesh.userData = {
+            isBodyElement: true,
+            type: 'p',
+            data: part,
+            mesh: mesh,
+            connectedJoints: []
+        };
+        return mesh;
+    }
+
+    /**
+     * Creates box mesh for a given part
+     * @param {Module.Part} part part, for which Mesh is generated
+     * @returns {PartMesh} info about part mesh
+     */
+    getNewBoxMesh(part) {
+        let geometry = new THREE.BoxGeometry(2, 2, 2);
+        let material = this.transformations.getNewMaterial(
+            part.get_vcolor().get_x(),
+            part.get_vcolor().get_y(),
+            part.get_vcolor().get_z());
+        let mesh = new THREE.Mesh(geometry, material);
+        mesh.position.set(
+            part.get_p().get_x(),
+            part.get_p().get_y(),
+            part.get_p().get_z());
+
+        mesh.scale.set(
+            part.get_scale().get_x(),
+            part.get_scale().get_y(),
+            part.get_scale().get_z());
+
+        let angles = part.get_o().getAngles();
+        mesh.rotation.copy(this.transformations.getPartRotation(
+            angles.get_x(),
+            angles.get_y(),
+            angles.get_z()));
+
+        mesh.userData = {
+            isBodyElement: true,
+            type: 'p',
+            data: part,
+            mesh: mesh,
+            connectedJoints: []
+        };
+        return mesh;
+    }
+
+    /**
+     * Creates cylinder mesh for a given part
+     * @param {Module.Part} part part, for which Mesh is generated
+     * @returns {PartMesh} info about part mesh
+     */
+    getNewCylinderMesh(part) {
+        let geometry = new THREE.CylinderGeometry(1, 1, 2, 32);
+        let material = this.transformations.getNewMaterial(
+            part.get_vcolor().get_x(),
+            part.get_vcolor().get_y(),
+            part.get_vcolor().get_z());
+        let mesh = new THREE.Mesh(geometry, material);
+        mesh.position.set(
+            part.get_p().get_x(),
+            part.get_p().get_y(),
+            part.get_p().get_z());
+
+        mesh.scale.set(
+            part.get_scale().get_y(),
+            part.get_scale().get_x(),
+            part.get_scale().get_z());
+
+        let angles = part.get_o().getAngles();
+        let m = this.transformations.getCylinderPartRotationMatrix(
+            angles.get_x(),
+            angles.get_y(),
+            angles.get_z());
+
+        mesh.rotation.setFromRotationMatrix(m);
+
+        mesh.userData = {
+            isBodyElement: true,
+            type: 'p',
+            data: part,
+            mesh: mesh,
+            connectedJoints: []
+        };
+        return mesh;
+    }
+
+    /**
+     * Creates part mesh according to which shape should be applied for a given
+     * part.
+     * @param {Module.Part} part part, for which Mesh is generated
+     * @returns {PartMesh} info about part mesh
+     */
+    create(part) {
+
+        let shape = part.get_shape();
+        if (this.partShapes['SHAPE_ELLIPSOID'].value == shape) {
+            return this.getNewSphereMesh(
+                part,
+                this.config.ellipsoidShape.radius,
+                this.config.ellipsoidShape.segments, true);
+        }
+        else if (this.partShapes['SHAPE_CUBOID'].value == shape) {
+            return this.getNewBoxMesh(part);
+        }
+        else if (this.partShapes['SHAPE_CYLINDER'].value == shape) {
+            return this.getNewCylinderMesh(part);
+        }
+
+        return this.getNewSphereMesh(
+            part,
+            this.config.defaultShape.radius,
+            this.config.defaultShape.segments,
+            false);
+    }
+}
Index: js/standard_expdef_demo/js/external/framsjs/visualization/readme.txt
===================================================================
--- js/standard_expdef_demo/js/external/framsjs/visualization/readme.txt	(revision 880)
+++ js/standard_expdef_demo/js/external/framsjs/visualization/readme.txt	(revision 880)
@@ -0,0 +1,1 @@
+These source files should be shared and not copied, as they are used in other projects too.
Index: js/standard_expdef_demo/js/external/framsjs/visualization/transformations.js
===================================================================
--- js/standard_expdef_demo/js/external/framsjs/visualization/transformations.js	(revision 880)
+++ js/standard_expdef_demo/js/external/framsjs/visualization/transformations.js	(revision 880)
@@ -0,0 +1,338 @@
+class ShapeTransformations {
+
+    /**
+     * Basic constructor for Transformations class.
+     * Initializes fields partShapes and jointShapes with getPartShapes and
+     * getJointsShapes results.
+     */
+    constructor() {
+        this.partShapes = this.getPartShapes();
+        this.jointShapes = this.getJointShapes();
+    }
+
+    /**
+     * Getter for Part shapes informations.
+     * @returns {ShapeInfo[]} basic info about shapes of parts
+     */
+    getPartShapes() {
+        let shapes = [];
+        shapes["SHAPE_BALL_AND_STICK"] = { name: "Ball & Stick", value: Module.Part["SHAPE_BALL_AND_STICK"] };
+        shapes["SHAPE_ELLIPSOID"] = { name: "Elipsoid", value: Module.Part["SHAPE_ELLIPSOID"] };
+        shapes["SHAPE_CUBOID"] = { name: "Cuboid", value: Module.Part["SHAPE_CUBOID"] };
+        shapes["SHAPE_CYLINDER"] = { name: "Cylinder", value: Module.Part["SHAPE_CYLINDER"] };
+        return shapes;
+    }
+
+    /**
+     * Getter for Joint shapes informations.
+     * @returns {ShapeInfo[]} basic info about shapes of joints
+     */
+    getJointShapes() {
+        let shapes = [];
+        shapes["SHAPE_BALL_AND_STICK"] = { name: "Ball & Stick", value: Module.Joint["SHAPE_BALL_AND_STICK"] };
+        shapes["SHAPE_FIXED"] = { name: "Fixed", value: Module.Joint["SHAPE_FIXED"] };
+        return shapes;
+    }
+
+    /**
+     * Recursive method for checking which elements are affected by transformations of object.
+     * This is internal method that should not be called.
+     * @param {BodyElement} bodyElement bodyelement, for which connected elements are checked for affection
+     * @param {object} result {partElements: PartMesh[], jointElements: JointMesh[]} that is result of recursive call
+     */
+    getAffectedElements_r(bodyElement, result) {
+        if (bodyElement.marked) {
+            return;
+        }
+
+        if (bodyElement.type == "p") {
+
+            bodyElement.marked = true;
+            result.partElements.push(bodyElement);
+
+            for (let i = 0; i < bodyElement.connectedJoints.length; ++i) {
+                let joint = bodyElement.connectedJoints[i];
+                if (!joint.marked) {
+                    joint.marked = true;
+                    result.jointElements.push(joint);
+                    if (joint.data.get_usedelta()) {
+                        this.getAffectedElements_r(joint.connectedParts[1], result);
+                    }
+                }
+            }
+
+        }
+        else if (bodyElement.type == "j") {
+            bodyElement.marked = true;
+            result.jointElements.push(bodyElement);
+
+            this.getAffectedElements_r(bodyElement.connectedParts[1], result);
+        }
+    }
+
+    /**
+     * Function that finds affected elements with function getAffectedElements_r
+     * and returns all affected parts and joints
+     * @param {BodyElement} bodyElement bodyelement, for which connected elements are checked for affection
+     * @returns {object} {partElements: PartMesh[], jointElements: JointMesh[]} that is result of recursive call
+     */
+    getAffectedElements(bodyElement) {
+        let result = {
+            partElements: [],
+            jointElements: []
+        };
+
+        this.getAffectedElements_r(bodyElement, result);
+
+        for (let j = 0; j < result.partElements.length; ++j) {
+            result.partElements[j].marked = false;
+        }
+        for (let k = 0; k < result.jointElements.length; ++k) {
+            result.jointElements[k].marked = false;
+        }
+        return result;
+    }
+
+    /**
+     * Helper for clamping colors from Framsticks-SDK to 0-1 values
+     * @param {number} value density of color
+     * @returns {number} clamped color
+     */
+    calcColorComponent(value) {
+        return THREE.Math.clamp(value, 0, 1);
+    }
+
+    /**
+     * Applies color for a given element
+     * @param {BodyElement} bodyElement body element to color
+     * @param {number} component which component should be changed
+     * @param {number} value new value of component
+     */
+    applyColor(bodyElement, component, value) {
+        bodyElement.mesh.material.color[component] = this.calcColorComponent(value);
+    }
+
+    /**
+     * Creates new phong material for Mesh
+     * @param {number} r color red
+     * @param {number} g color green
+     * @param {number} b color blue
+     * @returns {MeshPhongMaterial} new material for Mesh
+     */
+    getNewMaterial(r, g, b) {
+        let color = new THREE.Color(
+            this.calcColorComponent(r),
+            this.calcColorComponent(g),
+            this.calcColorComponent(b));
+        return new THREE.MeshPhongMaterial({ color: color });
+    }
+
+    /**
+     * Returns THREE.js object representing rotation matrix based on XYZ axis
+     * rotation angles.
+     * @param {number} rotAngleX angle of rotation for X axis
+     * @param {number} rotAngleY angle of rotation for Y axis
+     * @param {number} rotAngleZ angle of rotation for Z axis
+     * @returns {THREE.Euler} rotation matrix to be applied for model
+     */
+    getPartRotation(rotAngleX, rotAngleY, rotAngleZ) {
+        return new THREE.Euler(rotAngleX, -rotAngleY, rotAngleZ, "ZYX");
+    }
+
+    /**
+     * Creates rotation matrix for cylinder Part
+     * @param {number} rotAngleX rotate coordinate
+     * @param {number} rotAngleY rotate coordinate
+     * @param {number} rotAngleZ rotate coordinate
+     * @returns {Matrix4} rotation matrix for cylinder Part
+     */
+    getCylinderPartRotationMatrix(rotAngleX, rotAngleY, rotAngleZ) {
+        let rot = this.getPartRotation(rotAngleX, rotAngleY, rotAngleZ);
+
+        let m = new THREE.Matrix4();
+        m.makeRotationZ(THREE.Math.degToRad(90));
+
+        let m2 = new THREE.Matrix4();
+        m2.makeRotationFromEuler(rot);
+        m2.multiply(m);
+        return m2;
+    }
+
+    /**
+     * Applies rotation on a single part
+     * @param {BodyElement} bodyElement element on which rotation will be performed
+     */
+    applySinglePartRotation(bodyElement) {
+        let shape = bodyElement.data.get_shape();
+        let rot = bodyElement.data.get_o().getAngles();
+
+        if (this.partShapes['SHAPE_CYLINDER'].value == shape) {
+            let m = this.getCylinderPartRotationMatrix(
+                rot.get_x(),
+                rot.get_y(),
+                rot.get_z());
+            bodyElement.mesh.rotation.setFromRotationMatrix(m);
+        }
+        else {
+            let r = this.getPartRotation(rot.get_x(), rot.get_y(), rot.get_z());
+            bodyElement.mesh.rotation.copy(r);
+        }
+    }
+
+    /**
+     * Applies rotation on a bodyElement part and all other affected elements.
+     * @param {BodyElement} bodyElement element on which rotation will be performed
+     */
+    applyPartRotation(bodyElement) {
+
+        let affectedElements = this.getAffectedElements(bodyElement);
+
+        if (affectedElements.partElements.length > 1) {
+            for (let i = 0; i < affectedElements.partElements.length; ++i) {
+                let part = affectedElements.partElements[i];
+                part.mesh.position.x = part.data.get_p().get_x();
+                part.mesh.position.y = part.data.get_p().get_y();
+                part.mesh.position.z = part.data.get_p().get_z();
+                this.applySinglePartRotation(part);
+            }
+            let jointMeshFactory = new JointMeshFactory(this.jointShapes);
+            for (let j = 0; j < affectedElements.jointElements.length; ++j) {
+                let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
+                this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
+            }
+        } else {
+            this.applySinglePartRotation(bodyElement);
+        }
+
+    }
+
+    /**
+     * Redefines all affected joints due to part changes
+     * @param {BodyElement} bodyElement starting element
+     */
+    applyJointDeltaChange(bodyElement) {
+
+        let affectedElements = this.getAffectedElements(bodyElement);
+
+        for (let i = 0; i < affectedElements.partElements.length; ++i) {
+            let part = affectedElements.partElements[i];
+            part.mesh.position.x = part.data.get_p().get_x();
+            part.mesh.position.y = part.data.get_p().get_y();
+            part.mesh.position.z = part.data.get_p().get_z();
+            this.applySinglePartRotation(part);
+        }
+        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
+        for (let j = 0; j < affectedElements.jointElements.length; ++j) {
+            let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
+            this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
+        }
+
+    }
+
+    /**
+     * Applies scale to a part
+     * @param {BodyElement} bodyElement element to be scaled
+     */
+    applyPartScale(bodyElement) {
+        let part = bodyElement.data;
+        let shape = part.get_shape();
+        if (this.partShapes['SHAPE_CYLINDER'].value == shape) {
+            bodyElement.mesh.scale.set(
+                part.get_scale().get_y(),
+                part.get_scale().get_x(),
+                part.get_scale().get_z());
+        } else if (this.partShapes['SHAPE_BALL_AND_STICK'].value != shape) {
+            bodyElement.mesh.scale.set(
+                part.get_scale().get_x(),
+                part.get_scale().get_y(),
+                part.get_scale().get_z());
+        }
+    }
+
+    /**
+     * Replaces old mesh of an element with a new mesh
+     * @param {BodyElement} element Element to be changed
+     * @param {Mesh} newMesh New mesh for element
+     */
+    changeBodyElementMesh(element, newMesh) {
+        let scene = element.mesh.parent;
+        let oldMesh = element.mesh;
+
+        let userData = oldMesh.userData;
+        userData.showTransparent = newMesh.userData.showTransparent;
+        userData.mesh = newMesh.userData.mesh;
+        newMesh.userData = userData;
+
+        scene.remove(oldMesh);
+        scene.add(newMesh);
+
+        oldMesh.geometry.dispose();
+        oldMesh.geometry = null;
+        oldMesh.material.dispose();
+        oldMesh.material = null;
+    }
+
+    /**
+     * Changes Part position.
+     * @param {BodyElement} bodyElement element to be changed
+     * @param {string} axis which axis should be changed
+     */
+    applyPartPosition(bodyElement, axis) {
+        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
+
+        let affectedElements = this.getAffectedElements(bodyElement);
+        for (let i = 0; i < affectedElements.partElements.length; ++i) {
+            affectedElements.partElements[i].mesh.position[axis] =
+                affectedElements.partElements[i].data.get_p()["get_" + axis]();
+        }
+        for (let j = 0; j < affectedElements.jointElements.length; ++j) {
+            let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
+            this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
+        }
+    }
+
+    /**
+     * Updates all affected elements positions.
+     * @param {BodyElement} bodyElement beginning element
+     */
+    updateAfterPartTranslation(bodyElement) {
+        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
+
+        let affectedElements = this.getAffectedElements(bodyElement);
+        for (let i = 0; i < affectedElements.partElements.length; ++i) {
+            let currentElement = affectedElements.partElements[i];
+            if (currentElement !== bodyElement) {
+                affectedElements.partElements[i].mesh.position.x =
+                    affectedElements.partElements[i].data.get_p().get_x();
+                affectedElements.partElements[i].mesh.position.y =
+                    affectedElements.partElements[i].data.get_p().get_y();
+                affectedElements.partElements[i].mesh.position.z =
+                    affectedElements.partElements[i].data.get_p().get_z();
+            }
+        }
+        for (let j = 0; j < affectedElements.jointElements.length; ++j) {
+            let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
+            this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
+        }
+    }
+
+    /**
+     * Creates Part shape and removes old shape.
+     * @param {PartMesh} bodyElement part to be applied
+     */
+    applyPartShape(bodyElement) {
+        let partMeshFactory = new PartMeshFactory(this.partShapes);
+        let newPartMesh = partMeshFactory.create(bodyElement.data);
+        this.changeBodyElementMesh(bodyElement, newPartMesh);
+    }
+
+    /**
+     * Creates Joint shape and removes old shape.
+     * @param {JointMesh} bodyElement joint to be applied
+     */
+    applyJointShape(bodyElement) {
+        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
+        let newJointMesh = jointMeshFactory.create(bodyElement.data);
+        this.changeBodyElementMesh(bodyElement, newJointMesh);
+    }
+}
Index: js/standard_expdef_demo/js/external/tweenjs/tween.min.js
===================================================================
--- js/standard_expdef_demo/js/external/tweenjs/tween.min.js	(revision 880)
+++ js/standard_expdef_demo/js/external/tweenjs/tween.min.js	(revision 880)
@@ -0,0 +1,13 @@
+// tween.js - http://github.com/sole/tween.js
+'use strict';var TWEEN=TWEEN||function(){var a=[];return{REVISION:"7",getAll:function(){return a},removeAll:function(){a=[]},add:function(c){a.push(c)},remove:function(c){c=a.indexOf(c);-1!==c&&a.splice(c,1)},update:function(c){if(0===a.length)return!1;for(var b=0,d=a.length,c=void 0!==c?c:Date.now();b<d;)a[b].update(c)?b++:(a.splice(b,1),d--);return!0}}}();
+TWEEN.Tween=function(a){var c={},b={},d=1E3,e=0,f=null,h=TWEEN.Easing.Linear.None,r=TWEEN.Interpolation.Linear,k=[],l=null,m=!1,n=null,p=null;this.to=function(a,c){null!==c&&(d=c);b=a;return this};this.start=function(d){TWEEN.add(this);m=!1;f=void 0!==d?d:Date.now();f+=e;for(var g in b)if(null!==a[g]){if(b[g]instanceof Array){if(0===b[g].length)continue;b[g]=[a[g]].concat(b[g])}c[g]=a[g]}return this};this.stop=function(){TWEEN.remove(this);return this};this.delay=function(a){e=a;return this};this.easing=
+function(a){h=a;return this};this.interpolation=function(a){r=a;return this};this.chain=function(){k=arguments;return this};this.onStart=function(a){l=a;return this};this.onUpdate=function(a){n=a;return this};this.onComplete=function(a){p=a;return this};this.update=function(e){if(e<f)return!0;!1===m&&(null!==l&&l.call(a),m=!0);var g=(e-f)/d,g=1<g?1:g,i=h(g),j;for(j in c){var s=c[j],q=b[j];a[j]=q instanceof Array?r(q,i):s+(q-s)*i}null!==n&&n.call(a,i);if(1==g){null!==p&&p.call(a);g=0;for(i=k.length;g<
+i;g++)k[g].start(e);return!1}return!0}};
+TWEEN.Easing={Linear:{None:function(a){return a}},Quadratic:{In:function(a){return a*a},Out:function(a){return a*(2-a)},InOut:function(a){return 1>(a*=2)?0.5*a*a:-0.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a:0.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a*
+a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return 0.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:1>(a*=2)?0.5*Math.pow(1024,a-1):0.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1-
+Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return 1>(a*=2)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4))},Out:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return b*Math.pow(2,-10*a)*Math.sin((a-c)*
+2*Math.PI/0.4)+1},InOut:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return 1>(a*=2)?-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4):0.5*b*Math.pow(2,-10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4)+1}},Back:{In:function(a){return a*a*(2.70158*a-1.70158)},Out:function(a){return--a*a*(2.70158*a+1.70158)+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*(3.5949095*a-2.5949095):0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)}},Bounce:{In:function(a){return 1-
+TWEEN.Easing.Bounce.Out(1-a)},Out:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},InOut:function(a){return 0.5>a?0.5*TWEEN.Easing.Bounce.In(2*a):0.5*TWEEN.Easing.Bounce.Out(2*a-1)+0.5}}};
+TWEEN.Interpolation={Linear:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.Linear;return 0>c?f(a[0],a[1],d):1<c?f(a[b],a[b-1],b-d):f(a[e],a[e+1>b?b:e+1],d-e)},Bezier:function(a,c){var b=0,d=a.length-1,e=Math.pow,f=TWEEN.Interpolation.Utils.Bernstein,h;for(h=0;h<=d;h++)b+=e(1-c,d-h)*e(c,h)*a[h]*f(d,h);return b},CatmullRom:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.CatmullRom;return a[0]===a[b]?(0>c&&(e=Math.floor(d=b*(1+c))),f(a[(e-
+1+b)%b],a[e],a[(e+1)%b],a[(e+2)%b],d-e)):0>c?a[0]-(f(a[0],a[0],a[1],a[1],-d)-a[0]):1<c?a[b]-(f(a[b],a[b],a[b-1],a[b-1],d-b)-a[b]):f(a[e?e-1:0],a[e],a[b<e+1?b:e+1],a[b<e+2?b:e+2],d-e)},Utils:{Linear:function(a,c,b){return(c-a)*b+a},Bernstein:function(a,c){var b=TWEEN.Interpolation.Utils.Factorial;return b(a)/b(c)/b(a-c)},Factorial:function(){var a=[1];return function(c){var b=1,d;if(a[c])return a[c];for(d=c;1<d;d--)b*=d;return a[c]=b}}(),CatmullRom:function(a,c,b,d,e){var a=0.5*(b-a),d=0.5*(d-c),f=
+e*e;return(2*c-2*b+a+d)*e*f+(-3*c+3*b-2*a-d)*f+a*e+c}}};
Index: js/standard_expdef_demo/js/simulation/core.js
===================================================================
--- js/standard_expdef_demo/js/simulation/core.js	(revision 880)
+++ js/standard_expdef_demo/js/simulation/core.js	(revision 880)
@@ -0,0 +1,387 @@
+/**
+ * @file Simulation Module
+ * @author Patryk Gliszczynski, with further modifications
+ * @version 1.0
+ */
+
+class Simulation {
+  /**
+   * Class reponsible for the control of simulation flow.
+   */
+
+  constructor(world) {
+    this.world = world;
+
+    this.genPoolText = this.getText("Genotype Pool", EVOLUTION_METHOD_TEXT_POSITION);
+    this.mutationText = this.getText("Mutation", EVOLUTION_METHOD_TEXT_POSITION);
+    this.crossoverText = this.getText("Crossover", EVOLUTION_METHOD_TEXT_POSITION);
+    this.cloningText = this.getText("Cloning", EVOLUTION_METHOD_TEXT_POSITION);
+    this.mutatedText = this.getText("Mutated", EVOLUTION_METHOD_TEXT_POSITION);
+    this.offspringText = this.getText("Offspring", EVOLUTION_METHOD_TEXT_POSITION);
+    this.clonedText = this.getText("Cloned", EVOLUTION_METHOD_TEXT_POSITION);
+    this.fitnessText = this.getText("Fitness", Config.Simulation.Element.Position.FITNESS_TEXT);
+
+    this.world.scene.add(this.genPoolText);
+    this.world.scene.add(this.mutationText);
+    this.world.scene.add(this.crossoverText);
+    this.world.scene.add(this.cloningText);
+    this.world.scene.add(this.mutatedText);
+    this.world.scene.add(this.offspringText);
+    this.world.scene.add(this.clonedText);
+    this.world.scene.add(this.fitnessText);
+
+    this.state = State.INITIAL;
+    this.timing = Config.Simulation.Timing.INITIAL;
+  }
+
+  run() {
+    switch (this.state) {
+
+      case State.INITIAL:
+        this.showText(this.genPoolText);
+
+        this.state = State.EVOLUTION_METHOD_SELECTION;
+        this.timing = Config.Simulation.Timing.INITIAL;
+        break;
+
+      case State.EVOLUTION_METHOD_SELECTION:
+        this.moveCamera(Config.Simulation.Camera.Position.TABLE);
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_METHOD_SELECTION;
+        let randomVal = Math.random()
+        if (randomVal < Config.Simulation.MUTATION_PROBABILITY) {
+          this.state = State.MUTATION_GENOTYPE_SELECTION;
+        } else if (randomVal < Config.Simulation.MUTATION_PROBABILITY +
+                               Config.Simulation.CROSSOVER_PROBABILITY){
+          this.state = State.CROSSOVER_GENOTYPE_SELECTION;
+        } else {
+          this.state = State.CLONING_GENOTYPE_SELECTION;
+        }
+        this.evolutionMethod = this.state;
+        break;
+
+      case State.MUTATION_GENOTYPE_SELECTION:
+        this.showText(this.mutationText)
+        this.hideText(this.genPoolText);
+
+        this.mutationIdx = parseInt(Math.random() * Config.Simulation.GENOTYPES.length);
+        this.mutationArrow = this.getArrow(this.mutationIdx).mesh;
+        this.world.scene.add(this.mutationArrow);
+
+        this.timing = Config.Simulation.Timing.GENOTYPE_SELECTION;
+        this.state = State.MUTATION_RESULT;
+        break;
+
+      case State.MUTATION_RESULT:
+        this.evolvedGenotype = this.getMutatedGenotype(this.world.table.genotypes[this.mutationIdx]);
+        this.evolvedFitness = Math.max(0, (parseFloat(this.world.table.fitnessPlanks[this.mutationIdx].fitness) +
+                                           (Math.random() * 20 - 10)).toFixed(2));
+        this.showEvolutionResult();
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_RESULT;
+        this.state = State.SHOW_FRAMSTICK;
+        break;
+
+      case State.MUTATION_RETURN:
+        this.moveCamera(Config.Simulation.Camera.Position.TABLE);
+        this.evolutionReturn()
+
+        this.hideText(this.mutationText);
+        this.hideText(this.mutatedText);
+        this.world.scene.remove(this.mutationArrow);
+        this.world.scene.remove(this.evolvedFramstick);
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_RETURN;
+        this.state = State.INITIAL;
+        break;
+
+      case State.CLONING_GENOTYPE_SELECTION:
+        this.showText(this.cloningText);
+        this.hideText(this.genPoolText);
+
+        this.cloningIdx = parseInt(Math.random() * Config.Simulation.GENOTYPES.length);
+        this.cloningArrow = this.getArrow(this.cloningIdx).mesh;
+        this.world.scene.add(this.cloningArrow);
+
+        this.timing = Config.Simulation.Timing.GENOTYPE_SELECTION;
+        this.state = State.CLONING_RESULT;
+        break;
+
+      case State.CLONING_RESULT:
+        this.evolvedGenotype = this.world.table.genotypes[this.cloningIdx];
+        this.evolvedFitness = Math.max(0, (parseFloat(this.world.table.fitnessPlanks[this.cloningIdx].fitness) +
+                                           (Math.random() * 2 - 1)).toFixed(2));
+        this.showEvolutionResult();
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_RESULT;
+        this.state = State.SHOW_FRAMSTICK;
+        break;
+
+      case State.CLONING_RETURN:
+        this.moveCamera(Config.Simulation.Camera.Position.TABLE);
+        this.evolutionReturn()
+
+        this.hideText(this.cloningText);
+        this.hideText(this.clonedText);
+        this.world.scene.remove(this.cloningArrow);
+        this.world.scene.remove(this.evolvedFramstick);
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_RETURN;
+        this.state = State.INITIAL;
+        break;
+
+      case State.CROSSOVER_GENOTYPE_SELECTION:
+        this.showText(this.crossoverText)
+        this.hideText(this.genPoolText);
+
+        this.crossoverIdx1 = parseInt(Math.random() * Config.Simulation.GENOTYPES.length);
+        do {
+          this.crossoverIdx2 = parseInt(Math.random() * Config.Simulation.GENOTYPES.length);
+        } while (this.crossoverIdx1 == this.crossoverIdx2);
+
+        this.crossoverArrow1 = this.getArrow(this.crossoverIdx1).mesh;
+        this.world.scene.add(this.crossoverArrow1);
+        this.crossoverArrow2 = this.getArrow(this.crossoverIdx2).mesh;
+        this.world.scene.add(this.crossoverArrow2);
+
+        this.timing = Config.Simulation.Timing.GENOTYPE_SELECTION;
+        this.state = State.CROSSOVER_RESULT;
+        break;
+
+      case State.CROSSOVER_RESULT:
+        this.evolvedGenotype = this.getCrossoveredGenotype(this.world.table.genotypes[this.crossoverIdx1],
+                                                           this.world.table.genotypes[this.crossoverIdx2]);
+        this.evolvedFitness = ((parseFloat(this.world.table.fitnessPlanks[this.crossoverIdx1].fitness) +
+                               parseFloat(this.world.table.fitnessPlanks[this.crossoverIdx2].fitness)) / 2).toFixed(2);
+        this.showEvolutionResult();
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_RESULT;
+        this.state = State.SHOW_FRAMSTICK;
+        break;
+
+      case State.CROSSOVER_RETURN:
+        this.moveCamera(Config.Simulation.Camera.Position.TABLE);
+        this.evolutionReturn()
+
+        this.hideText(this.crossoverText);
+        this.hideText(this.offspringText);
+        this.world.scene.remove(this.crossoverArrow1);
+        this.world.scene.remove(this.crossoverArrow2);
+        this.world.scene.remove(this.evolvedFramstick.mesh);
+
+        this.timing = Config.Simulation.Timing.EVOLUTION_RETURN;
+        this.state = State.INITIAL;
+        break;
+
+      case State.SHOW_FRAMSTICK:
+        this.moveCamera(Config.Simulation.Camera.Position.FRAMSTICK);
+
+        this.evolvedFramstick = new Framstick(this.evolvedGenotype);
+        this.world.scene.add(this.evolvedFramstick.mesh);
+
+        this.timing = Config.Simulation.Timing.SHOW_FRAMSTICK;
+        this.state = State.DESTROY_FRAMSTICK;
+        break;
+
+      case State.DESTROY_FRAMSTICK:
+        this.evolvedFramstick.scaleUpSize = 0.01;
+        this.evolvedFramstick.scaleDownSize = 0.01;
+
+        this.showText(this.fitnessText);
+        this.rotateText(this.fitnessText, Config.Simulation.Element.Angle.SIDE_VIEW);
+        this.evolvedFitnessPlank = new FitnessPlank(this.evolvedFitness, 10);
+        this.evolvedFitnessPlank.mesh.position.set(0, 0, 0);
+        this.evolvedFitnessPlank.textMesh.position.set(0, 0, 0);
+        this.evolvedFitnessPlank.move(Config.Simulation.Element.Position.EVOLVED_FITNESS_PLANK);
+        this.evolvedFitnessPlank.rotate(Config.Simulation.Element.Angle.SIDE_VIEW);
+        this.world.scene.add(this.evolvedFitnessPlank.mesh)
+        this.world.scene.add(this.evolvedFitnessPlank.textMesh)
+
+        this.timing = Config.Simulation.Timing.DESTROY_FRAMSTICK;
+        if (this.evolutionMethod == State.MUTATION_GENOTYPE_SELECTION) {
+          this.state = State.MUTATION_RETURN;
+        } else if (this.evolutionMethod == State.CROSSOVER_GENOTYPE_SELECTION) {
+          this.state = State.CROSSOVER_RETURN;
+        } else if (this.evolutionMethod == State.CLONING_GENOTYPE_SELECTION) {
+          this.state = State.CLONING_RETURN;
+        }
+        break;
+    }
+
+    setTimeout(this.run.bind(this), this.timing);
+  }
+
+  getMutatedGenotype(genotype) {
+    let genoF4 = new Module.Geno_f4();
+    let genoOperatorsHelper = new Module.GenoOperatorsHelper(genoF4);
+    let genetics = new Module.PreconfiguredGenetics();
+    let iteration = 0;
+
+    do {
+      if (genoOperatorsHelper.mutate(genotype.replace("/*4*/", "")) == 0) {
+        let mutated = genoOperatorsHelper.getLastMutateGeno();
+	let newGenotype = "/*4*/ " + mutated;
+
+        let stringObj = new Module.SString();
+        stringObj.set(newGenotype);
+        let genoObj = new Module.Geno(stringObj);
+
+        if (genoObj.isValid()) {
+          genotype = newGenotype;
+          iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS;
+        }
+
+        Module.destroy(stringObj);
+        Module.destroy(genoObj);
+      } else {
+        iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS;
+      }
+      iteration += 1;
+    } while(iteration < Config.Simulation.EVOLUTION_MAX_REPETITIONS);
+
+    Module.destroy(genoF4);
+    Module.destroy(genoOperatorsHelper);
+    return genotype;
+  }
+
+  getCrossoveredGenotype(genotype1, genotype2) {
+    let genoF4 = new Module.Geno_f4();
+    let genoOperatorsHelper = new Module.GenoOperatorsHelper(genoF4);
+    let genetics = new Module.PreconfiguredGenetics();
+    let iteration = 0;
+
+    do {
+      if (genoOperatorsHelper.crossOver(genotype1.replace("/*4*/", ""),
+                                        genotype2.replace("/*4*/", "")) == 0) {
+        let crossovered = genoOperatorsHelper.getLastCrossGeno1();
+        let newGenotype = "/*4*/ " + crossovered;
+
+        let stringObj = new Module.SString();
+        stringObj.set(newGenotype);
+        let genoObj = new Module.Geno(stringObj);
+
+        if (genoObj.isValid()) {
+          genotype1 = newGenotype;
+          iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS;
+        }
+
+        Module.destroy(stringObj);
+        Module.destroy(genoObj);
+      } else {
+        iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS;
+      }
+      iteration += 1;
+    } while(iteration < Config.Simulation.EVOLUTION_MAX_REPETITIONS);
+
+    Module.destroy(genoF4);
+    Module.destroy(genoOperatorsHelper);
+    return genotype1;
+  }
+
+  showEvolutionResult() {
+    this.evolvedGenotypePlank = new GenotypePlank(this.evolvedGenotype, 10);
+    this.evolvedGenotypePlank.move(Config.Simulation.Element.Position.EVOLVED_GENOTYPE_PLANK);
+    this.evolvedGenotypePlank.rotate(Config.Simulation.Element.Angle.SIDE_VIEW);
+    this.world.scene.add(this.evolvedGenotypePlank.mesh);
+    this.world.scene.add(this.evolvedGenotypePlank.textMesh);
+
+    if (this.evolutionMethod == State.MUTATION_GENOTYPE_SELECTION) {
+      this.showText(this.mutatedText);
+      this.moveText(this.mutatedText, Config.Simulation.Element.Position.RESULT_TEXT);
+      this.rotateText(this.mutatedText, Config.Simulation.Element.Angle.SIDE_VIEW);
+    } else if (this.evolutionMethod == State.CROSSOVER_GENOTYPE_SELECTION) {
+      this.showText(this.offspringText);
+      this.moveText(this.offspringText, Config.Simulation.Element.Position.RESULT_TEXT);
+      this.rotateText(this.offspringText, Config.Simulation.Element.Angle.SIDE_VIEW);
+    } else if (this.evolutionMethod == State.CLONING_GENOTYPE_SELECTION) {
+      this.showText(this.clonedText);
+      this.moveText(this.clonedText, Config.Simulation.Element.Position.RESULT_TEXT);
+      this.rotateText(this.clonedText, Config.Simulation.Element.Angle.SIDE_VIEW);
+    }
+  }
+
+  evolutionReturn() {
+    let replaceIdx = parseInt(Math.random() * Config.Simulation.GENOTYPES.length);
+    let genotypePlank = this.world.table.genotypePlanks[replaceIdx];
+    let fitnessPlank = this.world.table.fitnessPlanks[replaceIdx];
+
+    this.world.scene.remove(genotypePlank.mesh);
+    this.world.scene.remove(genotypePlank.textMesh);
+    this.world.scene.remove(fitnessPlank.mesh);
+    this.world.scene.remove(fitnessPlank.textMesh);
+
+    this.evolvedGenotypePlank.position = genotypePlank.position;
+    this.evolvedGenotypePlank.move(this.evolvedGenotypePlank.position);
+    this.evolvedGenotypePlank.rotate(Config.Simulation.Element.Angle.NORMAL);
+
+    this.evolvedFitnessPlank.position = fitnessPlank.position;
+    this.evolvedFitnessPlank.move(this.evolvedFitnessPlank.position);
+    this.evolvedFitnessPlank.rotate(Config.Simulation.Element.Angle.NORMAL);
+
+    this.world.table.genotypes[replaceIdx] = this.evolvedGenotype;
+    this.world.table.genotypePlanks[replaceIdx] = this.evolvedGenotypePlank;
+    this.world.table.fitnessPlanks[replaceIdx] = this.evolvedFitnessPlank;
+    this.hideText(this.fitnessText);
+  }
+
+  getText(text, position) {
+    let textMesh = Text.getInfoMesh(text);
+    textMesh.position.set(position.x, position.y, position.z);
+    textMesh.scale.set(0.01, 0.01, 0.01);
+    return textMesh;
+  }
+
+  showText(text) {
+    let tween = new TWEEN.Tween(text.scale).to({x: 1, y: 1, z: 1}, 500);
+    tween.easing(TWEEN.Easing.Quadratic.InOut);
+    tween.start();
+  }
+
+  hideText(text) {
+    let tween = new TWEEN.Tween(text.scale).to({x: 0.01, y: 0.01, z: 0.01}, 500);
+    tween.easing(TWEEN.Easing.Quadratic.InOut);
+    tween.start();
+  }
+
+  getFramstick(genotype) {
+    this.moveCamera(Config.Simulation.Camera.Position.FRAMSTICK);
+    return new Framstick(genotype).mesh;
+  }
+
+  getArrow(idx) {
+    return new Arrow(50 + (Config.Table.FitnessPlank.HEIGHT + Config.Table.Board.SPACING) * idx);
+  }
+
+  moveCamera(position) {
+    let tween = new TWEEN.Tween(this.world.camera.position).to(position, Config.Simulation.Camera.SPEED);
+    tween.easing(TWEEN.Easing.Quadratic.Out);
+    tween.start();
+  }
+
+  moveText(textMesh, position) {
+    let tween = new TWEEN.Tween(textMesh.position).to(position, Config.Simulation.Camera.SPEED);
+    tween.easing(TWEEN.Easing.Quadratic.Out);
+    tween.start();
+  }
+
+  rotateText(textMesh, angle) {
+    let tween = new TWEEN.Tween(textMesh.rotation).to(angle, Config.Simulation.Camera.SPEED);
+    tween.easing(TWEEN.Easing.Quadratic.Out);
+    tween.start();
+  }
+}
+
+var State = {
+  "INITIAL": 0, "EVOLUTION_METHOD_SELECTION": 1, "MUTATION_GENOTYPE_SELECTION": 2,
+  "MUTATION_SIDE_VIEW": 3, "MUTATION_RESULT": 4, "SHOW_FRAMSTICK": 5,
+  "SHOW_RESULT": 6, "MUTATION_RETURN": 7, "CROSSOVER_GENOTYPE_SELECTION": 8,
+  "CROSSOVER_SIDE_VIEW": 9, "CROSSOVER_RESULT": 10, "CROSSOVER_RETURN": 11,
+  "DESTROY_FRAMSTICK": 12, "CLONING_GENOTYPE_SELECTION": 13,
+  "CLONING_SIDE_VIEW": 14, "CLONING_RESULT": 15, "CLONING_RETURN": 16
+};
+
+var EVOLUTION_METHOD_TEXT_POSITION = {
+  x: -210,
+  y: 80 + (Config.Table.FitnessPlank.HEIGHT + Config.Table.Board.SPACING) * (Config.Simulation.GENOTYPES.length + 1),
+  z: -500
+};
Index: js/standard_expdef_demo/js/world/core.js
===================================================================
--- js/standard_expdef_demo/js/world/core.js	(revision 880)
+++ js/standard_expdef_demo/js/world/core.js	(revision 880)
@@ -0,0 +1,105 @@
+/**
+ * @file World Object
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+class World {
+  /**
+  * Class used for storing all the objects visualized in the animation.
+  */
+
+  constructor(camera, scene) {
+    this.camera = camera;
+    this.scene = scene;
+    this.ground = this.Ground();
+    this.lights = this.Lighting();
+    this.sky = this.Sky();
+    this.sun = this.Sun();
+    this.table = this.Table();
+  }
+
+  Ground() {
+    let texture = new THREE.TextureLoader().load(Config.Ground.TEXTURE);
+    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+    texture.repeat.set(Config.Ground.REPEAT, Config.Ground.REPEAT);
+    texture.anisotropy = Config.Ground.ANISOTROPY;
+
+    let geometry = new THREE.PlaneBufferGeometry(Config.Ground.WIDTH, Config.Ground.WIDTH);
+    let material = new THREE.MeshLambertMaterial({map: texture});
+    let ground = new THREE.Mesh(geometry, material);
+    ground.position.y = Config.Ground.Y_POS;
+    ground.rotation.x = Config.Ground.X_ROT;
+    ground.receiveShadow = Config.Ground.RECEIVE_SHADOW;
+    this.scene.add(ground)
+    return ground
+  }
+
+  Table() {
+    let table = new Table(Config.Simulation.GENOTYPES);
+    this.scene.add(table.board);
+    this.scene.add(table.genotypeHeader.mesh);
+    this.scene.add(table.genotypeHeader.textMesh);
+    this.scene.add(table.fitnessHeader.mesh);
+    this.scene.add(table.fitnessHeader.textMesh);
+    for (let i = 0; i < table.genotypes.length; i++) {
+      this.scene.add(table.genotypePlanks[i].mesh);
+      this.scene.add(table.genotypePlanks[i].textMesh);
+      this.scene.add(table.fitnessPlanks[i].mesh);
+      this.scene.add(table.fitnessPlanks[i].textMesh);
+    }
+    return table
+  }
+
+  Lighting() {
+    let lights = [new THREE.AmbientLight(Config.Light.Ambient.COLOR)];
+    let light = new THREE.DirectionalLight(Config.Light.Directional.COLOR,
+                                           Config.Light.Directional.INTENSITY);
+    light.position.set(Config.Light.Directional.X_POS,
+                       Config.Light.Directional.Y_POS,
+                       Config.Light.Directional.Z_POS);
+    light.castShadow = Config.Light.Directional.CAST_SHADOW;
+    light.shadow.mapSize.width = Config.Light.Directional.Shadow.MapSize.WIDTH;
+    light.shadow.mapSize.height = Config.Light.Directional.Shadow.MapSize.HEIGHT;
+    light.shadow.camera.left = Config.Light.Directional.Shadow.Camera.LEFT;
+    light.shadow.camera.right = Config.Light.Directional.Shadow.Camera.RIGHT;
+    light.shadow.camera.top = Config.Light.Directional.Shadow.Camera.TOP;
+    light.shadow.camera.bottom = Config.Light.Directional.Shadow.Camera.BOTTOM;
+    light.shadow.camera.far = Config.Light.Directional.Shadow.Camera.FAR;
+    lights.push(light);
+
+    for (let i = 0; i < lights.length; i++) {
+      this.scene.add(lights[i]);
+    }
+    return lights;
+  }
+
+  Sky() {
+    let sky = new THREE.Sky()
+    sky.scale.setScalar(Config.Sky.SCALE);
+    sky.material.uniforms.turbidity.value = Config.Sky.TURBIDITY;
+    sky.material.uniforms.rayleigh.value = Config.Sky.RAYLEIGH;
+    sky.material.uniforms.luminance.value = Config.Sky.LUMINANCE;
+    sky.material.uniforms.mieCoefficient.value = Config.Sky.MIE_COEFFICIENT;
+    sky.material.uniforms.mieDirectionalG.value = Config.Sky.MIE_DIRECTIONAL_G;
+    this.scene.add(sky);
+    return sky;
+  }
+
+  Sun() {
+    let sun = new THREE.Mesh(
+      new THREE.SphereBufferGeometry(Config.Sun.RADIUS, Config.Sun.WIDTH_SEGMENTS, Config.Sun.HEIGHT_SEGMENTS),
+      new THREE.MeshBasicMaterial({color: Config.Sun.COLOR})
+    );
+    sun.position.y = Config.Sun.Y_POS;
+
+    let theta = Math.PI * (Config.Sun.INCLINATION - 0.5);
+    let phi = 2 * Math.PI * (Config.Sun.AZIMUTH - 0.5);
+    sun.position.x = 400000 * Math.cos(phi);
+    sun.position.y = 400000 * Math.sin(phi) * Math.sin(theta);
+    sun.position.z = 400000 * Math.sin(phi) * Math.cos(theta);
+    this.sky.material.uniforms.sunPosition.value.copy(sun.position);
+    this.scene.add(sun);
+    return sun;
+  }
+}
Index: js/standard_expdef_demo/js/world/object/arrow.js
===================================================================
--- js/standard_expdef_demo/js/world/object/arrow.js	(revision 880)
+++ js/standard_expdef_demo/js/world/object/arrow.js	(revision 880)
@@ -0,0 +1,59 @@
+/**
+ * @file Arrow Object
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+class Arrow {
+  /**
+   * Class reposnsible for the visualization of the cylindrical pointer.
+   */
+
+  constructor(y_pos) {
+    var geometry = new THREE.ConeGeometry(Config.Arrow.WIDTH,
+                                          Config.Arrow.LENGTH,
+                                          Config.Arrow.FACES);
+
+    let texture = new THREE.TextureLoader().load(Config.Arrow.TEXTURE);
+    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+    texture.repeat.set(Config.Arrow.REPEAT, Config.Arrow.REPEAT);
+    texture.anisotropy = Config.Arrow.ANISOTROPY;
+
+    let material = new THREE.MeshLambertMaterial({map: texture});
+    material.opacity = 0;
+    material.blending = THREE.NoBlending;
+
+    this.mesh = new THREE.Mesh(geometry, material);
+    this.mesh.castShadow = Config.Arrow.CAST_SHADOW;
+    this.mesh.receiveShadow = Config.Arrow.RECEIVE_SHADOW;
+    this.mesh.position.set(Config.Arrow.X_POS, y_pos, Config.Arrow.Z_POS)
+    this.mesh.rotation.z += Config.Arrow.Z_ROT;
+    this.mesh.scale.set(0.01, 0.01, 0.01);
+    this.enlarge();
+    this.goRight();
+  }
+
+  enlarge() {
+    let scale = Config.Arrow.Tween.Scale.Up.SIZE;
+    let speed = Config.Arrow.Tween.Scale.Up.SPEED;
+    let tween = new TWEEN.Tween(this.mesh.scale).to({x: scale, y: scale, z: scale}, speed);
+    tween.easing(Config.Arrow.Tween.Scale.Up.EASING);
+    tween.start();
+  }
+
+  goRight() {
+    let tween = new TWEEN.Tween(this.mesh.position).to({x: Config.Arrow.Tween.Move.Right.X_POS},
+                                                       Config.Arrow.Tween.Move.Right.SPEED);
+    tween.easing(Config.Arrow.Tween.Move.Right.EASING);
+    tween.onComplete(this.goLeft.bind(this));
+    tween.start();
+  }
+
+  goLeft() {
+    let tween = new TWEEN.Tween(this.mesh.position).to({x: Config.Arrow.Tween.Move.Left.X_POS},
+                                                       Config.Arrow.Tween.Move.Left.SPEED);
+    tween.easing(Config.Arrow.Tween.Move.Left.EASING);
+    tween.onComplete(this.goRight.bind(this));
+    tween.start();
+  }
+}
Index: js/standard_expdef_demo/js/world/object/framstick.js
===================================================================
--- js/standard_expdef_demo/js/world/object/framstick.js	(revision 880)
+++ js/standard_expdef_demo/js/world/object/framstick.js	(revision 880)
@@ -0,0 +1,120 @@
+/**
+ * @file Framstick Object
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+ class Framstick {
+
+  constructor(genotype) {
+    this.genotype = genotype;
+    this.parts = [];
+    this.joints = [];
+    this.meshes = [];
+    this.partfactory = new PartMeshFactory(Config.Framstick.Geometry.PART);
+    this.jointfactory = new JointMeshFactory(Config.Framstick.Geometry.JOINT);
+
+    let helper = new THREE.Mesh();
+    this.scaleUpSize = Config.Framstick.Tween.Scale.Up.SIZE
+    this.scaleDownSize = Config.Framstick.Tween.Scale.Down.SIZE
+    this.material = new THREE.MeshLambertMaterial({color: Config.Framstick.COLOR});
+    this.loadFromGenotype(helper);
+
+    let bbox = new THREE.Box3().setFromObject(helper);
+    bbox.getCenter(helper.position);
+    helper.position.multiplyScalar(-1);
+
+    this.mesh = new THREE.Group();
+    this.mesh.add(helper);
+
+    helper.castShadow = Config.Framstick.CAST_SHADOW;
+    helper.receiveShadow = Config.Framstick.RECEIVE_SHADOW;
+
+    this.mesh.scale.set(0.01, 0.01, 0.01);
+    this.mesh.position.y = Config.Framstick.Y_POS;
+    this.mesh.rotation.x = Config.Framstick.X_ROT;
+    this.mesh.castShadow = Config.Framstick.CAST_SHADOW;
+    this.mesh.receiveShadow = Config.Framstick.RECEIVE_SHADOW;
+
+    this.scaleUp();
+    this.autoRotate();
+  }
+
+  scaleUp() {
+    let scale = this.scaleUpSize;
+    let speed = Config.Framstick.Tween.Scale.SPEED;
+    let tween = new TWEEN.Tween(this.mesh.scale).to({x: scale, y: scale, z: scale}, speed);
+    tween.easing(Config.Framstick.Tween.Scale.Up.EASING);
+    tween.onComplete(this.scaleDown.bind(this));
+    tween.start();
+  }
+
+  scaleDown() {
+    let scale = this.scaleDownSize;
+    let speed = Config.Framstick.Tween.Scale.SPEED;
+    let tween = new TWEEN.Tween(this.mesh.scale).to({x: scale, y: scale, z: scale}, speed);
+    tween.easing(Config.Framstick.Tween.Scale.Down.EASING);
+    tween.onComplete(this.scaleUp.bind(this));
+    tween.start();
+  }
+
+  autoRotate() {
+    let speed = Config.Framstick.Tween.Rotate.SPEED;
+    let tween = new TWEEN.Tween(this.mesh.rotation).to({z: this.mesh.rotation.z + Math.PI / 4}, speed);
+    tween.easing(Config.Framstick.Tween.Rotate.EASING)
+    tween.onComplete(this.autoRotate.bind(this));
+    tween.start();
+  }
+
+/**
+* Code below was taken from: http://fugue.synology.me:30000/grzegorzlatosinski/framsticks-js
+* @author Grzegorz Latosiński
+* @pull_date 22.01.2018
+*/
+  loadFromGenotype(mesh) {
+    let model = this.getModelFromGenotype(this.genotype);
+
+    if (typeof model !== 'undefined') {
+       let partsforjoints = [];
+       this.meshes = [];
+       this.parts = [];
+       this.joints = [];
+
+       for (let i = 0; i < model.getPartCount(); i++) {
+           let m = this.partfactory.create(model.getPart(i));
+           partsforjoints.push(m.userData);
+           mesh.add(m)
+           this.parts.push(m);
+		   this.meshes.push(m);
+       }
+
+       for (let i = 0; i < model.getJointCount(); i++) {
+           let m = this.jointfactory.create(model.getJoint(i), partsforjoints);
+           mesh.add(m);
+           this.joints.push(m);
+		   this.meshes.push(m);
+       }
+       Module.destroy(model);
+    }
+  }
+
+  getModelFromGenotype(genotype) {
+    let genetics = new Module.PreconfiguredGenetics();
+    let stringObj = new Module.SString();
+    stringObj.set(genotype);
+    let genoObj = new Module.Geno(stringObj);
+
+    if (!genoObj.isValid()) {
+       Module.destroy(stringObj);
+       Module.destroy(genoObj);
+       Module.destroy(genetics);
+       return;
+    }
+
+    let model = new Module.Model(genoObj, true);
+    Module.destroy(stringObj);
+    Module.destroy(genoObj);
+    Module.destroy(genetics);
+    return model;
+  }
+}
Index: js/standard_expdef_demo/js/world/object/table.js
===================================================================
--- js/standard_expdef_demo/js/world/object/table.js	(revision 880)
+++ js/standard_expdef_demo/js/world/object/table.js	(revision 880)
@@ -0,0 +1,173 @@
+/**
+ * @file Table Object
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+class Table {
+  /**
+   * Class reposnsible for the visualization of the genotype and fitness table.
+   */
+
+  constructor(genotypes) {
+    this.genotypes = genotypes
+
+    this.board = this.Board();
+    this.genotypePlanks = this.GenotypePlanks();
+    this.fitnessPlanks = this.FitnessPlanks();
+    this.genotypeHeader = this.GenotypeHeader();
+    this.fitnessHeader = this.FitnessHeader();
+  }
+
+  Board() {
+    let texture = new THREE.TextureLoader().load(Config.Table.Board.TEXTURE);
+    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+    texture.repeat.set(Config.Table.Board.REPEAT, Config.Table.Board.REPEAT);
+    texture.anisotropy = Config.Table.Board.ANISOTROPY;
+
+    let geometry = new THREE.BoxGeometry(Config.Table.Board.WIDTH,
+                                         (Config.Table.GenotypePlank.HEIGHT + Config.Table.Board.SPACING) * (this.genotypes.length + 2),
+                                         Config.Table.Board.THICKNESS);
+
+    let material = new THREE.MeshLambertMaterial({map: texture});
+    material.opacity = 0;
+    material.blending = THREE.NoBlending;
+
+    let board = new THREE.Mesh(geometry, material);
+    board.position.set(Config.Table.Board.X_POS,
+                       (Config.Table.GenotypePlank.HEIGHT + Config.Table.Board.SPACING) * (this.genotypes.length + 1) / 2,
+                       Config.Table.Board.Z_POS)
+    board.castShadow = true;
+    board.receiveShadow = true;
+    return board
+  }
+
+  GenotypePlanks() {
+    let planks = []
+    for (let i = 0; i < this.genotypes.length; i++) {
+      planks.push(new GenotypePlank(this.genotypes[i], i));
+    }
+    return planks
+  }
+
+  FitnessPlanks() {
+    let planks = []
+    for (let i = 0; i < this.genotypes.length; i++) {
+      planks.push(new FitnessPlank((Math.random() * 100).toFixed(2), i))
+    }
+    return planks;
+  }
+
+  GenotypeHeader() {
+    return new GenotypePlank("Genotype", this.genotypes.length, true)
+  }
+
+  FitnessHeader() {
+    return new FitnessPlank("Fitness", this.genotypes.length)
+  }
+}
+
+
+class GenotypePlank {
+
+  constructor(genotype, position, bold) {
+    this.genotype = genotype;
+    this.position = {
+      x: Config.Table.GenotypePlank.X_POS,
+      y: 50 + (Config.Table.GenotypePlank.HEIGHT + Config.Table.Board.SPACING) * position,
+      z: Config.Table.GenotypePlank.Z_POS
+    };
+
+    let texture = new THREE.TextureLoader().load(Config.Table.GenotypePlank.TEXTURE);
+    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+    texture.repeat.set(Config.Table.GenotypePlank.REPEAT, Config.Table.GenotypePlank.REPEAT);
+    texture.anisotropy = Config.Table.GenotypePlank.ANISOTROPY;
+
+    let geometry = new THREE.BoxGeometry(Config.Table.GenotypePlank.WIDTH,
+                                         Config.Table.GenotypePlank.HEIGHT,
+                                         Config.Table.GenotypePlank.THICKNESS);
+    let material = new THREE.MeshLambertMaterial({map: texture});
+    material.blending  = THREE.NoBlending;
+    this.mesh = new THREE.Mesh(geometry, material);
+    this.mesh.position.set(this.position.x, this.position.y, this.position.z);
+    this.mesh.castShadow = Config.Table.GenotypePlank.CAST_SHADOW;
+    this.mesh.receiveShadow = Config.Table.GenotypePlank.RECEIVE_SHADOW;
+
+    this.textMesh = Text.getGenotypeMesh(this.genotype.replace("/*4*/ ", ""));
+    if (bold == true) {
+      this.textMesh = Text.getFitnessMesh(this.genotype.replace("/*4*/ ", ""));
+    }
+    this.textMesh.position.set(this.position.x, this.position.y, this.position.z + 1);
+  }
+
+  move(position) {
+    let tweenPlank = new TWEEN.Tween(this.mesh.position).to(position, Config.Table.GenotypePlank.Tween.Move.SPEED);
+    tweenPlank.easing(Config.Table.GenotypePlank.Tween.Move.EASING);
+    tweenPlank.start();
+
+    let tweenText = new TWEEN.Tween(this.textMesh.position).to(position, Config.Table.GenotypePlank.Tween.Move.SPEED);
+    tweenText.easing(Config.Table.GenotypePlank.Tween.Move.EASING);
+    tweenText.start();
+  }
+
+  rotate(angle) {
+    let tweenPlank = new TWEEN.Tween(this.mesh.rotation).to(angle, Config.Table.GenotypePlank.Tween.Rotate.SPEED);
+    tweenPlank.easing(Config.Table.GenotypePlank.Tween.Rotate.EASING);
+    tweenPlank.start();
+
+    let tweenText = new TWEEN.Tween(this.textMesh.rotation).to(angle, Config.Table.GenotypePlank.Tween.Rotate.SPEED);
+    tweenText.easing(Config.Table.GenotypePlank.Tween.Rotate.EASING);
+    tweenText.start();
+  }
+}
+
+
+class FitnessPlank {
+
+  constructor(fitness, position) {
+    this.fitness = fitness;
+    this.position = {
+      x: Config.Table.FitnessPlank.X_POS,
+      y: 50 + (Config.Table.FitnessPlank.HEIGHT + Config.Table.Board.SPACING) * position,
+      z: Config.Table.FitnessPlank.Z_POS
+    };
+
+    let texture = new THREE.TextureLoader().load(Config.Table.FitnessPlank.TEXTURE);
+    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+    texture.repeat.set(Config.Table.FitnessPlank.REPEAT, Config.Table.FitnessPlank.REPEAT);
+    texture.anisotropy = Config.Table.FitnessPlank.ANISOTROPY;
+
+    let geometry = new THREE.BoxGeometry(Config.Table.FitnessPlank.WIDTH,
+                                         Config.Table.FitnessPlank.HEIGHT,
+                                         Config.Table.FitnessPlank.THICKNESS);
+    let material = new THREE.MeshLambertMaterial({map: texture});
+    material.blending  = THREE.NoBlending;
+    this.mesh = new THREE.Mesh(geometry, material);
+    this.mesh.position.set(this.position.x, this.position.y, this.position.z);
+    this.mesh.castShadow = Config.Table.FitnessPlank.CAST_SHADOW;
+    this.mesh.receiveShadow = Config.Table.FitnessPlank.RECEIVE_SHADOW;
+
+    this.textMesh = Text.getFitnessMesh(this.fitness);
+    this.textMesh.position.set(this.position.x, this.position.y, this.position.z + 1);
+  }
+
+  move(position) {
+    let tweenPlank = new TWEEN.Tween(this.mesh.position).to(position, Config.Table.FitnessPlank.Tween.Move.SPEED);
+    tweenPlank.easing(Config.Table.FitnessPlank.Tween.Move.EASING);
+    tweenPlank.start();
+
+    let tweenText = new TWEEN.Tween(this.textMesh.position).to(position, Config.Table.FitnessPlank.Tween.Move.SPEED);
+    tweenText.easing(Config.Table.FitnessPlank.Tween.Move.EASING);
+    tweenText.start();
+  }
+
+  rotate(angle) {
+    let tweenPlank = new TWEEN.Tween(this.mesh.rotation).to(angle, Config.Table.FitnessPlank.Tween.Rotate.SPEED);
+    tweenPlank.easing(Config.Table.FitnessPlank.Tween.Rotate.EASING);
+    tweenPlank.start();
+
+    let tweenText = new TWEEN.Tween(this.textMesh.rotation).to(angle, Config.Table.FitnessPlank.Tween.Rotate.SPEED);
+    tweenText.easing(Config.Table.FitnessPlank.Tween.Rotate.EASING);
+    tweenText.start();
+  }
+}
Index: js/standard_expdef_demo/js/world/object/text.js
===================================================================
--- js/standard_expdef_demo/js/world/object/text.js	(revision 880)
+++ js/standard_expdef_demo/js/world/object/text.js	(revision 880)
@@ -0,0 +1,31 @@
+/**
+ * @file Text Helper
+ * @author Patryk Gliszczynski
+ * @version 1.0
+ */
+
+var Text = {
+  getGenotypeMesh: function(genotype) {
+    if (genotype.length > 20) {
+      genotype = genotype.slice(0, 20) + "..."
+    }
+    var element = document.createElement("div");
+    element.className = "genotype";
+    element.textContent = genotype;
+    return new THREE.CSS3DObject(element);
+  },
+
+  getFitnessMesh: function(fitness) {
+    var element = document.createElement("div");
+    element.className = "fitness";
+    element.textContent = fitness;
+    return new THREE.CSS3DObject(element);
+  },
+
+  getInfoMesh: function(info) {
+    var element = document.createElement("div");
+    element.className = "info";
+    element.textContent = info;
+    return new THREE.CSS3DObject(element);
+  }
+}
Index: js/standard_expdef_demo/static/styles.css
===================================================================
--- js/standard_expdef_demo/static/styles.css	(revision 880)
+++ js/standard_expdef_demo/static/styles.css	(revision 880)
@@ -0,0 +1,21 @@
+.genotype {
+  font-size: 40px;
+  font-weight: 500;
+  font-family: "Helvetica", "Arial", "Verdana", serif;
+  text-align: center;
+}
+
+.fitness {
+  font-size: 42px;
+  font-weight: 700;
+  font-family: "Helvetica", "Arial", "Verdana", serif;
+  text-align: center;
+}
+
+.info {
+  font-size: 48px;
+  font-weight: 700;
+  font-family: "Helvetica", "Arial", "Verdana", serif;
+  text-align: left;
+  width: 8em;
+}
