Procedural Shapes

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Verge3D webgl - geometry - shapes</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
      body {
        font-family: Monospace;
        background-color: #f0f0f0;
        margin: 0px;
        overflow: hidden;
      }
    </style>
  </head>
  <body>

    <script src="../build/v3d.js"></script>
    <script src="js/libs/stats.min.js"></script>

    <script>

      var container, stats;

      var camera, scene, renderer;

      var group;

      var targetRotation = 0;
      var targetRotationOnMouseDown = 0;

      var mouseX = 0;
      var mouseXOnMouseDown = 0;

      var windowHalfX = window.innerWidth / 2;

      init();
      animate();

      function init() {

        container = document.createElement('div');
        document.body.appendChild(container);

        var info = document.createElement('div');
        info.style.position = 'absolute';
        info.style.top = '10px';
        info.style.width = '100%';
        info.style.textAlign = 'center';
        info.innerHTML = 'Simple procedurally-generated shapes<br/>Drag to spin';
        container.appendChild(info);

        scene = new v3d.Scene();
        scene.background = new v3d.Color(0xf0f0f0);

        camera = new v3d.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
        camera.position.set(0, 150, 500);
        scene.add(camera);

        var light = new v3d.PointLight(0xffffff, 0.8);
        camera.add(light);

        group = new v3d.Group();
        group.position.y = 50;
        scene.add(group);

        var loader = new v3d.TextureLoader();
        var texture = loader.load("textures/UV_Grid_Sm.jpg");

        // it's necessary to apply these settings in order to correctly display the texture on a shape geometry

        texture.wrapS = texture.wrapT = v3d.RepeatWrapping;
        texture.repeat.set(0.008, 0.008);

        function addShape(shape, extrudeSettings, color, x, y, z, rx, ry, rz, s) {

          // flat shape with texture
          // note: default UVs generated by ShapeBufferGeometry are simply the x- and y-coordinates of the vertices

          var geometry = new v3d.ShapeBufferGeometry(shape);

          var mesh = new v3d.Mesh(geometry, new v3d.MeshPhongMaterial({ side: v3d.DoubleSide, map: texture }));
          mesh.position.set(x, y, z - 175);
          mesh.rotation.set(rx, ry, rz);
          mesh.scale.set(s, s, s);
          group.add(mesh);

          // flat shape

          var geometry = new v3d.ShapeBufferGeometry(shape);

          var mesh = new v3d.Mesh(geometry, new v3d.MeshPhongMaterial({ color: color, side: v3d.DoubleSide }));
          mesh.position.set(x, y, z - 125);
          mesh.rotation.set(rx, ry, rz);
          mesh.scale.set(s, s, s);
          group.add(mesh);

          // extruded shape

          var geometry = new v3d.ExtrudeBufferGeometry(shape, extrudeSettings);

          var mesh = new v3d.Mesh(geometry, new v3d.MeshPhongMaterial({ color: color }));
          mesh.position.set(x, y, z - 75);
          mesh.rotation.set(rx, ry, rz);
          mesh.scale.set(s, s, s);
          group.add(mesh);

          addLineShape(shape, color, x, y, z, rx, ry, rz, s);

        }

        function addLineShape(shape, color, x, y, z, rx, ry, rz, s) {

          // lines

          shape.autoClose = true;

          var points = shape.getPoints();
          var spacedPoints = shape.getSpacedPoints(50);

          var geometryPoints = new v3d.BufferGeometry().setFromPoints(points);
          var geometrySpacedPoints = new v3d.BufferGeometry().setFromPoints(spacedPoints);

          // solid line

          var line = new v3d.Line(geometryPoints, new v3d.LineBasicMaterial({ color: color }));
          line.position.set(x, y, z - 25);
          line.rotation.set(rx, ry, rz);
          line.scale.set(s, s, s);
          group.add(line);

          // line from equidistance sampled points

          var line = new v3d.Line(geometrySpacedPoints, new v3d.LineBasicMaterial({ color: color }));
          line.position.set(x, y, z + 25);
          line.rotation.set(rx, ry, rz);
          line.scale.set(s, s, s);
          group.add(line);

          // vertices from real points

          var particles = new v3d.Points(geometryPoints, new v3d.PointsMaterial({ color: color, size: 4 }));
          particles.position.set(x, y, z + 75);
          particles.rotation.set(rx, ry, rz);
          particles.scale.set(s, s, s);
          group.add(particles);

          // equidistance sampled points

          var particles = new v3d.Points(geometrySpacedPoints, new v3d.PointsMaterial({ color: color, size: 4 }));
          particles.position.set(x, y, z + 125);
          particles.rotation.set(rx, ry, rz);
          particles.scale.set(s, s, s);
          group.add(particles);

        }


        // California

        var californiaPts = [];

        californiaPts.push(new v3d.Vector2(610, 320));
        californiaPts.push(new v3d.Vector2(450, 300));
        californiaPts.push(new v3d.Vector2(392, 392));
        californiaPts.push(new v3d.Vector2(266, 438));
        californiaPts.push(new v3d.Vector2(190, 570));
        californiaPts.push(new v3d.Vector2(190, 600));
        californiaPts.push(new v3d.Vector2(160, 620));
        californiaPts.push(new v3d.Vector2(160, 650));
        californiaPts.push(new v3d.Vector2(180, 640));
        californiaPts.push(new v3d.Vector2(165, 680));
        californiaPts.push(new v3d.Vector2(150, 670));
        californiaPts.push(new v3d.Vector2(90, 737));
        californiaPts.push(new v3d.Vector2(80, 795));
        californiaPts.push(new v3d.Vector2(50, 835));
        californiaPts.push(new v3d.Vector2(64, 870));
        californiaPts.push(new v3d.Vector2(60, 945));
        californiaPts.push(new v3d.Vector2(300, 945));
        californiaPts.push(new v3d.Vector2(300, 743));
        californiaPts.push(new v3d.Vector2(600, 473));
        californiaPts.push(new v3d.Vector2(626, 425));
        californiaPts.push(new v3d.Vector2(600, 370));
        californiaPts.push(new v3d.Vector2(610, 320));

        for (var i = 0; i < californiaPts.length; i++) californiaPts[i].multiplyScalar(0.25);

        var californiaShape = new v3d.Shape(californiaPts);


        // Triangle

        var triangleShape = new v3d.Shape();
        triangleShape.moveTo(80, 20);
        triangleShape.lineTo(40, 80);
        triangleShape.lineTo(120, 80);
        triangleShape.lineTo(80, 20); // close path


        // Heart

        var x = 0, y = 0;

        var heartShape = new v3d.Shape(); // From http://blog.burlock.org/html5/130-paths

        heartShape.moveTo(x + 25, y + 25);
        heartShape.bezierCurveTo(x + 25, y + 25, x + 20, y, x, y);
        heartShape.bezierCurveTo(x - 30, y, x - 30, y + 35, x - 30, y + 35);
        heartShape.bezierCurveTo(x - 30, y + 55, x - 10, y + 77, x + 25, y + 95);
        heartShape.bezierCurveTo(x + 60, y + 77, x + 80, y + 55, x + 80, y + 35);
        heartShape.bezierCurveTo(x + 80, y + 35, x + 80, y, x + 50, y);
        heartShape.bezierCurveTo(x + 35, y, x + 25, y + 25, x + 25, y + 25);


        // Square

        var sqLength = 80;

        var squareShape = new v3d.Shape();
        squareShape.moveTo(0, 0);
        squareShape.lineTo(0, sqLength);
        squareShape.lineTo(sqLength, sqLength);
        squareShape.lineTo(sqLength, 0);
        squareShape.lineTo(0, 0);


        // Rectangle

        var rectLength = 120, rectWidth = 40;

        var rectShape = new v3d.Shape();
        rectShape.moveTo(0, 0);
        rectShape.lineTo(0, rectWidth);
        rectShape.lineTo(rectLength, rectWidth);
        rectShape.lineTo(rectLength, 0);
        rectShape.lineTo(0, 0);


        // Rounded rectangle

        var roundedRectShape = new v3d.Shape();

        (function roundedRect(ctx, x, y, width, height, radius) {

          ctx.moveTo(x, y + radius);
          ctx.lineTo(x, y + height - radius);
          ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
          ctx.lineTo(x + width - radius, y + height);
          ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
          ctx.lineTo(x + width, y + radius);
          ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
          ctx.lineTo(x + radius, y);
          ctx.quadraticCurveTo(x, y, x, y + radius);

        })(roundedRectShape, 0, 0, 50, 50, 20);


        // Track

        var trackShape = new v3d.Shape();

        trackShape.moveTo(40, 40);
        trackShape.lineTo(40, 160);
        trackShape.absarc(60, 160, 20, Math.PI, 0, true);
        trackShape.lineTo(80, 40);
        trackShape.absarc(60, 40, 20, 2 * Math.PI, Math.PI, true);


        // Circle

        var circleRadius = 40;
        var circleShape = new v3d.Shape();
        circleShape.moveTo(0, circleRadius);
        circleShape.quadraticCurveTo(circleRadius, circleRadius, circleRadius, 0);
        circleShape.quadraticCurveTo(circleRadius, - circleRadius, 0, - circleRadius);
        circleShape.quadraticCurveTo(- circleRadius, - circleRadius, - circleRadius, 0);
        circleShape.quadraticCurveTo(- circleRadius, circleRadius, 0, circleRadius);


        // Fish

        var x = y = 0;

        var fishShape = new v3d.Shape();

        fishShape.moveTo(x, y);
        fishShape.quadraticCurveTo(x + 50, y - 80, x + 90, y - 10);
        fishShape.quadraticCurveTo(x + 100, y - 10, x + 115, y - 40);
        fishShape.quadraticCurveTo(x + 115, y, x + 115, y + 40);
        fishShape.quadraticCurveTo(x + 100, y + 10, x + 90, y + 10);
        fishShape.quadraticCurveTo(x + 50, y + 80, x, y);


        // Arc circle

        var arcShape = new v3d.Shape();
        arcShape.moveTo(50, 10);
        arcShape.absarc(10, 10, 40, 0, Math.PI * 2, false);

        var holePath = new v3d.Path();
        holePath.moveTo(20, 10);
        holePath.absarc(10, 10, 10, 0, Math.PI * 2, true);
        arcShape.holes.push(holePath);


        // Smiley

        var smileyShape = new v3d.Shape();
        smileyShape.moveTo(80, 40);
        smileyShape.absarc(40, 40, 40, 0, Math.PI * 2, false);

        var smileyEye1Path = new v3d.Path();
        smileyEye1Path.moveTo(35, 20);
        smileyEye1Path.absellipse(25, 20, 10, 10, 0, Math.PI * 2, true);

        smileyShape.holes.push(smileyEye1Path);

        var smileyEye2Path = new v3d.Path();
        smileyEye2Path.moveTo(65, 20);
        smileyEye2Path.absarc(55, 20, 10, 0, Math.PI * 2, true);
        smileyShape.holes.push(smileyEye2Path);

        var smileyMouthPath = new v3d.Path();
        smileyMouthPath.moveTo(20, 40);
        smileyMouthPath.quadraticCurveTo(40, 60, 60, 40);
        smileyMouthPath.bezierCurveTo(70, 45, 70, 50, 60, 60);
        smileyMouthPath.quadraticCurveTo(40, 80, 20, 60);
        smileyMouthPath.quadraticCurveTo(5, 50, 20, 40);

        smileyShape.holes.push(smileyMouthPath);


        // Spline shape

        var splinepts = [];
        splinepts.push(new v3d.Vector2(70, 20));
        splinepts.push(new v3d.Vector2(80, 90));
        splinepts.push(new v3d.Vector2(- 30, 70));
        splinepts.push(new v3d.Vector2(0, 0));

        var splineShape = new v3d.Shape();
        splineShape.moveTo(0, 0);
        splineShape.splineThru(splinepts);

        var extrudeSettings = { depth: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };

        // addShape(shape, color, x, y, z, rx, ry,rz, s);

        addShape(californiaShape, extrudeSettings, 0xf08000, - 300, - 100, 0, 0, 0, 0, 1);
        addShape(triangleShape, extrudeSettings, 0x8080f0, - 180, 0, 0, 0, 0, 0, 1);
        addShape(roundedRectShape, extrudeSettings, 0x008000, - 150, 150, 0, 0, 0, 0, 1);
        addShape(trackShape, extrudeSettings, 0x008080, 200, - 100, 0, 0, 0, 0, 1);
        addShape(squareShape, extrudeSettings, 0x0040f0, 150, 100, 0, 0, 0, 0, 1);
        addShape(heartShape, extrudeSettings, 0xf00000, 60, 100, 0, 0, 0, Math.PI, 1);
        addShape(circleShape, extrudeSettings, 0x00f000, 120, 250, 0, 0, 0, 0, 1);
        addShape(fishShape, extrudeSettings, 0x404040, - 60, 200, 0, 0, 0, 0, 1);
        addShape(smileyShape, extrudeSettings, 0xf000f0, - 200, 250, 0, 0, 0, Math.PI, 1);
        addShape(arcShape, extrudeSettings, 0x804000, 150, 0, 0, 0, 0, 0, 1);
        addShape(splineShape, extrudeSettings, 0x808080, - 50, - 100, 0, 0, 0, 0, 1);

        addLineShape(arcShape.holes[0], 0x804000, 150, 0, 0, 0, 0, 0, 1);

        for (var i = 0; i < smileyShape.holes.length; i += 1) {

          addLineShape(smileyShape.holes[i], 0xf000f0, - 200, 250, 0, 0, 0, Math.PI, 1);

        }

        //

        renderer = new v3d.WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);

        stats = new Stats();
        container.appendChild(stats.dom);

        document.addEventListener('mousedown', onDocumentMouseDown, false);
        document.addEventListener('touchstart', onDocumentTouchStart, false);
        document.addEventListener('touchmove', onDocumentTouchMove, false);

        //

        window.addEventListener('resize', onWindowResize, false);

      }

      function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

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

      }

      //

      function onDocumentMouseDown(event) {

        event.preventDefault();

        document.addEventListener('mousemove', onDocumentMouseMove, false);
        document.addEventListener('mouseup', onDocumentMouseUp, false);
        document.addEventListener('mouseout', onDocumentMouseOut, false);

        mouseXOnMouseDown = event.clientX - windowHalfX;
        targetRotationOnMouseDown = targetRotation;

      }

      function onDocumentMouseMove(event) {

        mouseX = event.clientX - windowHalfX;

        targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;

      }

      function onDocumentMouseUp() {

        document.removeEventListener('mousemove', onDocumentMouseMove, false);
        document.removeEventListener('mouseup', onDocumentMouseUp, false);
        document.removeEventListener('mouseout', onDocumentMouseOut, false);

      }

      function onDocumentMouseOut() {

        document.removeEventListener('mousemove', onDocumentMouseMove, false);
        document.removeEventListener('mouseup', onDocumentMouseUp, false);
        document.removeEventListener('mouseout', onDocumentMouseOut, false);

      }

      function onDocumentTouchStart(event) {

        if (event.touches.length == 1) {

          event.preventDefault();

          mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
          targetRotationOnMouseDown = targetRotation;

        }

      }

      function onDocumentTouchMove(event) {

        if (event.touches.length == 1) {

          event.preventDefault();

          mouseX = event.touches[0].pageX - windowHalfX;
          targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.05;

        }

      }

      //

      function animate() {

        requestAnimationFrame(animate);

        render();
        stats.update();

      }

      function render() {

        group.rotation.y += (targetRotation - group.rotation.y) * 0.05;
        renderer.render(scene, camera);

      }

    </script>

  </body>
</html>