<!DOCTYPE html>
<html lang="en">
<head>
    <title>pythonocc {{ occ_version }} WebGL renderer</title>
    <meta name='Author' content='Thomas Paviot - tpaviot@gmail.com'>
    <meta name='Keywords' content='WebGl, pythonocc'>
    <meta charset="utf-8">
    <style>
        body {
            background: linear-gradient({{ render_cfg._bg_gradient_color1 }}, {{ render_cfg._bg_gradient_color2 }});
            margin: 0px;
            overflow: hidden;
        }
        #pythonocc_rocks {
            padding: 5px;
            position: absolute;
            left: 1%;
            bottom: 2%;
            height: 38px;
            width: 280px;
            border-radius: 5px;
            border: 2px solid #f7941e;
            opacity: 0.7;
            font-family: Arial;
            background-color: #414042;
            color: #ffffff;
            font-size: 14px;
            opacity: 0.5;
        }
        #commands {
            padding: 5px;
            position: absolute;
            right: 1%;
            top: 2%;
            height: 80px;
            width: 180px;
            border-radius: 5px;
            border: 2px solid #f7941e;
            opacity: 0.7;
            font-family: Arial;
            background-color: #414042;
            color: #ffffff;
            font-size: 14px;
            opacity: 0.5;
        }
        a {
            color: #f7941e;
            text-decoration: none;
        }
        a:hover {
            color: #ffffff;
        }
    </style>
</head>
<body>
    <div id="container"></div>
    <div id="pythonocc_rocks">
        pythonocc-{{ occ_version }} <a href="https://github.com/mrdoob/three.js" target="_blank">three.js {{ threejs_version }}</a> renderer
        <br>Check our blog at
        <a href=http://www.pythonocc.org>http://www.pythonocc.org</a>
    </div>
    <div id="commands">
        <b>t</b> view/hide shape<br>
        <b>w</b> toggle wireframe/shaded<br>
        <b>g</b> view/hide grid<br>
        <b>a</b> view/hide axis<br>
        <b>f</b> fit to scene<br>
    </div>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/{{ threejs_version }}/build/three.min.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/{{ threejs_version }}/examples/js/controls/TrackballControls.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/{{ threejs_version }}/examples/js/libs/stats.min.js"></script>



    {% if render_cfg._vertex_shader %}
    <script type="x-shader/x-vertex" id="vertexShader">{{ render_cfg._vertex_shader }}</script>
    {% endif %}
    {% if render_cfg._fragment_shader %}
    <script type="x-shader/x-fragment" id="fragmentShader">{{ render_cfg._fragment_shader }}</script>
    {% endif %}

    <script type="text/javascript">
        var camera, scene, renderer, object, stats, container, shape_material;
        var mouseX = 0;
        var mouseXOnMouseDown = 0;
        var mouseY = 0;
        var mouseYOnMouseDown = 0;
        var moveForward = false;
        var moveBackward = false;
        var moveLeft = false;
        var moveRight = false;
        var moveUp = false;
        var moveDown = false;
        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;
        var selected_target_color_r = 0;
        var selected_target_color_g = 0;
        var selected_target_color_b = 0;
        var selected_target = null;
        init();
        animate();

        function init() {
            container = document.getElementById( 'container' );

            camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 200);
            camera.position.z = 100;
            //controls = new THREE.OrbitControls(camera);
            //controls = new THREE.OrbitControls(camera);
            // for selection
            raycaster = new THREE.Raycaster();
            mouse = new THREE.Vector2();
            // create scene
            scene = new THREE.Scene();
            scene.add(new THREE.AmbientLight(0x101010));
            directionalLight = new THREE.DirectionalLight(0xffffff);
            directionalLight.position.x = 1;
            directionalLight.position.y = -1;
            directionalLight.position.z = 2;
            directionalLight.position.normalize();
            scene.add(directionalLight);
            light1 = new THREE.PointLight(0xffffff);
            scene.add(light1);

            {% if render_cfg._uniforms %}
            uniforms = render_cfg._uniforms
            {% else %}
            uniforms = {};
            {% endif %}

            {% if (render_cfg._vertex_shader is not none) and (render_cfg._fragment_shader is not none) %}
            var vertexShader = document.getElementById('vertexShader').textContent;
            var fragmentShader = document.getElementById('fragmentShader').textContent;
            var shader_material = new THREE.ShaderMaterial({uniforms: uniforms,
                                                            vertexShader: vertexShader,
                                                            fragmentShader: fragmentShader});
            {% endif %}

            // Here comes the shape definition
            loader = new THREE.BufferGeometryLoader();
            {% block shape_add %}

            {% if occ_shapes %}
            {% for shape_hash, value in occ_shapes.items() %}
            console.log('{{ shape_hash }}');
            export_edges = {{ value[0]|lower }};
            color = {{ value[1] }};
            specular_color = {{ value[2] }};
            shininess = {{ value[3] }};
            transparency = {{ value[4] }};
            line_color = {{ value[5] }};
            line_width = {{ value[6] }};
            {{ shape_hash}}_phong_material = new THREE.MeshPhongMaterial(
                {color:color,specular:specular_color,shininess:shininess,side: THREE.DoubleSide,});
            json_shape = JSON.parse({{ value[-1] | tojson }})
            var geometry = loader.parse(json_shape);
            mesh = new THREE.Mesh(geometry, {{ shape_hash }}_phong_material);
            mesh.castShadow = true;
            mesh.receiveShadow = true;
            scene.add(mesh);
            {% endfor %}
            {% endif %}

            {% if occ_edges %}
            {% for edge_hash, value in occ_edges.items() %}
            console.log('{{ edge_hash }}');
            color = {{ value[0] }};
            line_width = {{ value[1] }};
            var line_material = new THREE.LineBasicMaterial({color: color, linewidth: line_width});
            json_edge = JSON.parse({{ value[-1] | tojson }});
            var geometry = loader.parse(json_edge);
            line = new THREE.Line(geometry, line_material);
            scene.add(line);
            {% endfor %}
            {% endif %}

            {% if occ_vertex %}
            {% for vertex_hash, value in occ_vertex.items() %}
              console.log('Vertex: {{ vertex_hash }}');
              var color = {{ value[0] }};
              var point_size = {{ value[1] }};
              var vertices = [];
              {% for v in  value[-1] %}
                vertices.push( {{ v[0] }},{{ v[1] }},{{ v[2] }})
              {% endfor %}
              var geometry = new THREE.BufferGeometry();
              geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
              var material = new THREE.PointsMaterial( { color: color, size: point_size } );
              var points = new THREE.Points( geometry, material );
              scene.add( points );
            {% endfor %}
            {% endif %}

            {% endblock %}


            renderer = new THREE.WebGLRenderer({antialias:true, alpha: true});
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setPixelRatio( window.devicePixelRatio );
            container.appendChild(renderer.domElement);
            //renderer.gammaInput = true;
            //renderer.gammaOutput = true;
            // for shadow rendering
            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFShadowMap;
            controls = new THREE.TrackballControls(camera, renderer.domElement);
            // show stats, is it really useful ?
            stats = new Stats();
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.top = '2%';
            stats.domElement.style.left = '1%';
            container.appendChild(stats.domElement);
            // add events
            document.addEventListener('keypress', onDocumentKeyPress, false);
            document.addEventListener('click', onDocumentMouseClick, false);
            window.addEventListener('resize', onWindowResize, false);
            fit_to_scene();
        }
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            render();
            stats.update();
        }
        function update_lights() {
            if (directionalLight != undefined) {
                directionalLight.position.copy(camera.position);
            }
        }
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }
        function onDocumentKeyPress(event) {
          event.preventDefault();
          if (event.key=="t") {  // t key
              if (selected_target) {
                    selected_target.material.visible = !selected_target.material.visible;
                }
          }
          else if (event.key=="g") { // g key, toggle grid visibility
               gridHelper.visible = !gridHelper.visible;
          }
          else if (event.key=="a") { // a key, toggle axisHelper visibility
               axisHelper.visible = !axisHelper.visible;
          }
          else if (event.key=="w") { // w key, toggle wireframe mode
               if (selected_target) {
                    selected_target.material.wireframe = !selected_target.material.wireframe;
                }
          }
          else if (event.key=="f") { // f key, fit to scene
               fit_to_scene();
          }
        }
        function onDocumentMouseClick(event) {
            event.preventDefault();
            mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
            mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
            // restore previous selected target color
            if (selected_target) {
                selected_target.material.color.setRGB(selected_target_color_r,
                    selected_target_color_g,
                    selected_target_color_b);
            }
            // perform selection
            raycaster.setFromCamera(mouse, camera);
            var intersects = raycaster.intersectObjects(scene.children);
            if (intersects.length > 0) {
                var target = intersects[0].object;
                selected_target_color_r = target.material.color.r;
                selected_target_color_g = target.material.color.g;
                selected_target_color_b = target.material.color.b;
                target.material.color.setRGB(1., 0.65, 0.);
                console.log(target);
                selected_target = target;
            }
        }
        function fit_to_scene() {
            // compute bounding sphere of whole scene
            var center = new THREE.Vector3(0,0,0);
            var radiuses = new Array();
            var positions = new Array();
            // compute center of all objects
            scene.traverse(function(child) {
                if (child instanceof THREE.Mesh || child instanceof THREE.Points) {
                    child.geometry.computeBoundingBox();
                    var box = child.geometry.boundingBox;
                    var curCenter = new THREE.Vector3().copy(box.min).add(box.max).multiplyScalar(0.5);
                    var radius = new THREE.Vector3().copy(box.max).distanceTo(box.min)/2.;
                    center.add(curCenter);
                    positions.push(curCenter);
                    radiuses.push(radius);
                }
            });
            if (radiuses.length > 0) {
                center.divideScalar(radiuses.length*0.7);
            }
            var maxRad = 1.;
            // compute bounding radius
            for (var ichild = 0; ichild < radiuses.length; ++ichild) {
                var distToCenter = positions[ichild].distanceTo(center);
                var totalDist = distToCenter + radiuses[ichild];
                if (totalDist > maxRad) {
                    maxRad = totalDist;
                }
            }
            maxRad = maxRad * 0.7; // otherwise the scene seems to be too far away
            camera.lookAt(center);
            var direction = new THREE.Vector3().copy(camera.position).sub(controls.target);
            var len = direction.length();
            direction.normalize();

            // compute new distance of camera to middle of scene to fit the object to screen
            var lnew = maxRad / Math.sin(camera.fov/180. * Math.PI / 2.);
            direction.multiplyScalar(lnew);

            var pnew = new THREE.Vector3().copy(center).add(direction);
            // change near far values to avoid culling of objects
            camera.position.set(pnew.x, pnew.y, pnew.z);
            camera.far = lnew*50;
            camera.near = lnew*50*0.001;
            camera.updateProjectionMatrix();
            controls.target = center;
            controls.update();
            // adds and adjust a grid helper if needed
            gridHelper = new THREE.GridHelper(maxRad*4, 10)
            scene.add(gridHelper);
            // axisHelper
            axisHelper = new THREE.AxesHelper(maxRad);
            scene.add(axisHelper);
        }
        function render() {
            //@IncrementTime@  TODO UNCOMMENT
            update_lights();
            renderer.render(scene, camera);
        }
    </script>
</body>
</html>
