<commit_before>"use strict";

var City = function() {
  var COLOR_GROUND = 0xaaaaaa;

  var city = {};

  city.STREET_WIDTH = 3;
  city.STREET_DEPTH = 3;
  city.BLOCK_WIDTH = 8;
  city.BLOCK_DEPTH = 8;
  city.BLOCK_ROWS = 60;
  city.BLOCK_COLUMNS = 60;
  city.MIN_BUILDING_HEIGHT = 1.2;
  city.MAX_BUILDING_HEIGHT = 40;
  city.MAX_BUILDING_MATERIALS = 50;
  city.TOTAL_SCENE_WIDTH = (city.BLOCK_WIDTH * city.BLOCK_ROWS) + (city.STREET_WIDTH * (city.BLOCK_ROWS - 1));
  city.HALF_SCENE_WIDTH = city.TOTAL_SCENE_WIDTH / 2;
  city.TOTAL_SCENE_DEPTH = (city.BLOCK_DEPTH * city.BLOCK_COLUMNS) + (city.STREET_DEPTH * (city.BLOCK_COLUMNS - 1));
  city.HALF_SCENE_DEPTH = city.TOTAL_SCENE_DEPTH / 2;

  city.BLOCK_LAYOUTS = [
    [ { left:     0.0,  right: 1.0,  top: 0.0,  bottom: 1.0, } ],

    [ { left:     0.0,  right: 0.5,  top: 0.0,  bottom:  1.0 },
      { left:     0.5,  right: 1.0,  top: 0.0,  bottom:  1.0 } ],

    [ { left:     0.0,  right: 1.0,  top: 0.0,  bottom: 0.5 },
      { left:     0.0,  right: 1.0,  top: 0.5,  bottom: 1.0 } ],

    [ { left:     0.0,  right: 0.5,  top: 0.0,  bottom: 1.0 },
      { left:     0.5,  right: 1.0,  top: 0.0,  bottom: 0.5 },
      { left:     0.5,  right: 1.0,  top: 0.5,  bottom: 1.0 } ],

    [ { left:     0.0,  right: 0.5,  top: 0.0,  bottom: 0.5, },
      { left:     0.5,  right: 1.0,  top: 0.0,  bottom: 0.5, },
      { left:     0.0,  right: 0.5,  top: 0.5,  bottom: 1.0, },
      { left:     0.5,  right: 1.0,  top: 0.5,  bottom: 1.0, } ],

    [ { left:     0.0,  right: (1 / 3),  top: 0.0,  bottom:  0.5 },
      { left: (1 / 3),  right: (2 / 3),  top: 0.0,  bottom:  0.5 },
      { left: (2 / 3),  right:     1.0,  top: 0.0,  bottom:  0.5 },
      { left:     0.0,  right:     0.5,  top: 0.5,  bottom: 1.0 },
      { left:     0.5,  right:     1.0,  top: 0.5,  bottom: 1.0 } ],
  ];

  city.buildScene = function() {
    var scene = new THREE.Scene();

    scene.add(buildGroundGeometry());

    var terrainCoordinates = buildTerrainCoordinates();

    var terrainMeshes = buildTerrainGeometry(terrainCoordinates);
    terrainMeshes.forEach(function(terrainMesh) {
      scene.add(terrainMesh);
    });

    var buildingMaterials = buildMaterials();
    var buildingGeometries = buildEmptyGeometriesForBuildings();

    var unitBlocks = generateUnitBlocks(terrainCoordinates);

    generateSceneBlocks(unitBlocks, buildingGeometries);

    for (var i = 0; i < city.MAX_BUILDING_MATERIALS; i++) {
      scene.add(new THREE.Mesh(buildingGeometries[i], buildingMaterials[i]));
    }

    return scene;
  };

  var buildGroundGeometry = function() {
    var groundMaterial = new THREE.MeshBasicMaterial({ color: COLOR_GROUND, });
    var groundGeometry = new THREE.Mesh(new THREE.PlaneGeometry(city.TOTAL_SCENE_WIDTH * 25, city.TOTAL_SCENE_DEPTH * 25), groundMaterial);
    groundGeometry.rotation.x = -(Math.PI / 2);

    return groundGeometry;
  };

  var buildTerrainCoordinates = function() {
    var MAX_HEIGHT = 4;
    var i, j;

    var terrainCoordinates = [];
    for (i = 0; i <= city.BLOCK_ROWS; i++) {
      terrainCoordinates[i] = [];

      for (j = 0; j <= city.BLOCK_COLUMNS; j++) {
        terrainCoordinates[i][j] = Math.floor(Math.random() * MAX_HEIGHT);
      }
    }
    console.log(terrainCoordinates);

    return terrainCoordinates;
  };

  var buildTerrainGeometry = function(terrainCoordinates) {
    var i, j, x, z;
    var terrainGeometry1 = new THREE.Geometry();
    var terrainGeometry2 = new THREE.Geometry();
    var terrainMaterial1 = new THREE.MeshLambertMaterial({ color: new THREE.Color(0, 200, 0) });
    var terrainMaterial2 = new THREE.MeshLambertMaterial({ color: new THREE.Color(255, 25, 0) });

    var triangle, v1, v2, v3;

    x = -city.HALF_SCENE_WIDTH;
    for (i = 0; i < city.BLOCK_ROWS; i++) {
      z = -city.HALF_SCENE_DEPTH;

      for (j = 0; j < city.BLOCK_COLUMNS; j++) {
        triangle = new THREE.Geometry();

        v1 = new THREE.Vector3(x, terrainCoordinates[i][j], z);
        v2 = new THREE.Vector3(x, terrainCoordinates[i][j + 1], z + city.BLOCK_DEPTH + city.STREET_DEPTH);
        v3 = new THREE.Vector3(x + city.BLOCK_WIDTH + city.STREET_WIDTH, terrainCoordinates[i + 1][j], z);

        triangle.vertices.push(v1);
        triangle.vertices.push(v2);
        triangle.vertices.push(v3);

        triangle.faces.push(new THREE.Face3(0, 1, 2));
        triangle.computeFaceNormals();

        terrainGeometry1.merge(triangle);

        triangle = new THREE.Geometry();
        v1 = new THREE.Vector3(x, terrainCoordinates[i][j + 1], z + city.BLOCK_DEPTH + city.STREET_DEPTH);
        v2 = new THREE.Vector3(x + city.BLOCK_WIDTH + city.STREET_WIDTH, terrainCoordinates[i + 1][j + 1], z + city.BLOCK_DEPTH + city.STREET_DEPTH);
        v3 = new THREE.Vector3(x + city.BLOCK_WIDTH + city.STREET_WIDTH, terrainCoordinates[i + 1][j], z);

        triangle.vertices.push(v1);
        triangle.vertices.push(v2);
        triangle.vertices.push(v3);

        triangle.faces.push(new THREE.Face3(0, 1, 2));
        triangle.computeFaceNormals();

        terrainGeometry2.merge(triangle);

        z += city.BLOCK_DEPTH + city.STREET_DEPTH;
      }

      x += city.BLOCK_WIDTH + city.STREET_WIDTH;
    }

    var mesh1 = new THREE.Mesh(terrainGeometry1, terrainMaterial1);
    var mesh2 = new THREE.Mesh(terrainGeometry2, terrainMaterial2);

    return [mesh1, mesh2];
  };

  var buildMaterials = function() {
    var buildingMaterials = [];

    for (var i = 0; i < city.MAX_BUILDING_MATERIALS; i++) {
      var random = Math.random() * 0.6;
      var r = random;
      var g = random;
      var b = random;

      buildingMaterials.push(new THREE.MeshLambertMaterial({ color: new THREE.Color(r, g, b), }));
    }

    return buildingMaterials;
  };

  var buildEmptyGeometriesForBuildings = function() {
    var buildingGeometries = [];

    for (var i = 0; i < city.MAX_BUILDING_MATERIALS; i++) {
      buildingGeometries.push(new THREE.Geometry());
    }

    return buildingGeometries;
  };

  var generateUnitBlocks = function(terrainCoordinates) {
    var blocks = [];
    var block;
    var i, j, l;
    var lotLayout, buildingHeight, buildingBottom;

    for (i = 0; i < city.BLOCK_ROWS; i++) {
      blocks[i] = [];

      for (j = 0; j < city.BLOCK_COLUMNS; j++) {
        var blockLayout = city.BLOCK_LAYOUTS[Math.floor(Math.random() * city.BLOCK_LAYOUTS.length)];

        var blockTerrainCoordinates = [
          terrainCoordinates[i][j],
          terrainCoordinates[i + 1][j],
          terrainCoordinates[i][j + 1],
          terrainCoordinates[i + 1][j + 1],
        ];
        var minimumTerrainHeight = Math.min(...blockTerrainCoordinates);
        var maximumTerrainHeight = Math.max(...blockTerrainCoordinates);

        block = [];
        for (l = 0; l < blockLayout.length; l++) {
          lotLayout = blockLayout[l];
          buildingHeight = calculateBuildingHeight(i, j);
          buildingBottom = 0;

          block.push({
            left: lotLayout.left,
            right: lotLayout.right,
            top: lotLayout.top,
            bottom: lotLayout.bottom,
            yMin: buildingBottom,
            yMax: buildingHeight,
          });
        }

        blocks[i][j] = block;
      }
    }

    console.log(blocks);
    return blocks;
  };

  var generateSceneBlocks = function(unitBlocks, buildingGeometries) {
    var i, j, b, x, z;
    var block;
    var unitBuilding, building, materialIndex;
    var maxAboveGroundHeight = 5;

    x = -city.HALF_SCENE_WIDTH;
    for (i = 0; i < unitBlocks.length; i++) {
      z = -city.HALF_SCENE_DEPTH;

      for (j = 0; j < unitBlocks[i].length; j++) {
        block = unitBlocks[i][j];

        for (b = 0; b < block.length; b++) {
          unitBuilding = block[b];

          var unitWidth = unitBuilding.right - unitBuilding.left;
          var unitDepth = unitBuilding.bottom - unitBuilding.top;
          var xUnitMid = unitBuilding.left + (unitWidth / 2);
          var zUnitMid = unitBuilding.top + (unitDepth / 2);

          building = new THREE.Mesh(new THREE.BoxGeometry(unitWidth * city.BLOCK_WIDTH, 1, unitDepth * city.BLOCK_WIDTH));

          building.position.x = x + (city.BLOCK_WIDTH * xUnitMid);
          building.scale.y =  (Math.random() * maxAboveGroundHeight) + city.MIN_BUILDING_HEIGHT + (unitBuilding.yMax - unitBuilding.yMin);
          building.position.y = (building.scale.y / 2) + unitBuilding.yMin;
          building.position.z = z + (city.BLOCK_DEPTH * zUnitMid);
          building.updateMatrix();

          materialIndex = Math.floor(Math.random() * city.MAX_BUILDING_MATERIALS);
          buildingGeometries[materialIndex].merge(building.geometry, building.matrix);
        }

        z += city.BLOCK_DEPTH + city.STREET_DEPTH;
      }

      x += city.BLOCK_WIDTH + city.STREET_WIDTH;
    }
  };

  var calculateBuildingHeight = function(i, j) {
    var squareRootOfMaxBuildingHeight = Math.sqrt(city.MAX_BUILDING_HEIGHT);

    var halfRows = city.BLOCK_ROWS / 2;
    var halfColumns = city.BLOCK_COLUMNS / 2;

    var multiplierX = squareRootOfMaxBuildingHeight * ((halfRows - Math.abs(halfRows - i)) / halfRows);
    var multiplierZ = squareRootOfMaxBuildingHeight * ((halfColumns - Math.abs(halfColumns - j)) / halfColumns);
    var multiplier = Math.min(multiplierX, multiplierZ);

    return multiplier * multiplier;
  };

  return city;
};
<commit_msg>Initial pass at terrain generated using midpoint displacement<commit_after>"use strict";

var City = function() {
  var COLOR_GROUND = 0xaaaaaa;

  var city = {};

  city.STREET_WIDTH = 3;
  city.STREET_DEPTH = 3;
  city.BLOCK_WIDTH = 8;
  city.BLOCK_DEPTH = 8;
  city.BLOCK_ROWS = 64;
  city.BLOCK_COLUMNS = 64;
  city.MIN_BUILDING_HEIGHT = 1.2;
  city.MAX_BUILDING_HEIGHT = 40;
  city.MAX_BUILDING_MATERIALS = 50;
  city.TOTAL_SCENE_WIDTH = (city.BLOCK_WIDTH * city.BLOCK_ROWS) + (city.STREET_WIDTH * (city.BLOCK_ROWS - 1));
  city.HALF_SCENE_WIDTH = city.TOTAL_SCENE_WIDTH / 2;
  city.TOTAL_SCENE_DEPTH = (city.BLOCK_DEPTH * city.BLOCK_COLUMNS) + (city.STREET_DEPTH * (city.BLOCK_COLUMNS - 1));
  city.HALF_SCENE_DEPTH = city.TOTAL_SCENE_DEPTH / 2;

  city.BLOCK_LAYOUTS = [
    [ { left:     0.0,  right: 1.0,  top: 0.0,  bottom: 1.0, } ],

    [ { left:     0.0,  right: 0.5,  top: 0.0,  bottom:  1.0 },
      { left:     0.5,  right: 1.0,  top: 0.0,  bottom:  1.0 } ],

    [ { left:     0.0,  right: 1.0,  top: 0.0,  bottom: 0.5 },
      { left:     0.0,  right: 1.0,  top: 0.5,  bottom: 1.0 } ],

    [ { left:     0.0,  right: 0.5,  top: 0.0,  bottom: 1.0 },
      { left:     0.5,  right: 1.0,  top: 0.0,  bottom: 0.5 },
      { left:     0.5,  right: 1.0,  top: 0.5,  bottom: 1.0 } ],

    [ { left:     0.0,  right: 0.5,  top: 0.0,  bottom: 0.5, },
      { left:     0.5,  right: 1.0,  top: 0.0,  bottom: 0.5, },
      { left:     0.0,  right: 0.5,  top: 0.5,  bottom: 1.0, },
      { left:     0.5,  right: 1.0,  top: 0.5,  bottom: 1.0, } ],

    [ { left:     0.0,  right: (1 / 3),  top: 0.0,  bottom:  0.5 },
      { left: (1 / 3),  right: (2 / 3),  top: 0.0,  bottom:  0.5 },
      { left: (2 / 3),  right:     1.0,  top: 0.0,  bottom:  0.5 },
      { left:     0.0,  right:     0.5,  top: 0.5,  bottom: 1.0 },
      { left:     0.5,  right:     1.0,  top: 0.5,  bottom: 1.0 } ],
  ];

  city.buildScene = function() {
    var scene = new THREE.Scene();

    scene.add(buildGroundGeometry());

    var terrainCoordinates = buildTerrainCoordinates();

    var terrainMeshes = buildTerrainGeometry(terrainCoordinates);
    terrainMeshes.forEach(function(terrainMesh) {
      scene.add(terrainMesh);
    });

    var buildingMaterials = buildMaterials();
    var buildingGeometries = buildEmptyGeometriesForBuildings();

    var unitBlocks = generateUnitBlocks(terrainCoordinates);

    generateSceneBlocks(unitBlocks, buildingGeometries);

    for (var i = 0; i < city.MAX_BUILDING_MATERIALS; i++) {
      scene.add(new THREE.Mesh(buildingGeometries[i], buildingMaterials[i]));
    }

    return scene;
  };

  var buildGroundGeometry = function() {
    var groundMaterial = new THREE.MeshBasicMaterial({ color: COLOR_GROUND, });
    var groundGeometry = new THREE.Mesh(new THREE.PlaneGeometry(city.TOTAL_SCENE_WIDTH * 25, city.TOTAL_SCENE_DEPTH * 25), groundMaterial);
    groundGeometry.rotation.x = -(Math.PI / 2);

    return groundGeometry;
  };

  var buildTerrainCoordinates = function() {
    var MAX_HEIGHT = 6;
    var i, j;

    var terrainCoordinates = [];
    for (i = 0; i <= city.BLOCK_ROWS; i++) {
      terrainCoordinates[i] = [];

      for (j = 0; j <= city.BLOCK_COLUMNS; j++) {
        terrainCoordinates[i][j] = 0.0;
      }
    }

    // Initial randomization of corners
    terrainCoordinates[0][0] = Math.floor(Math.random() * MAX_HEIGHT);
    terrainCoordinates[0][city.BLOCK_COLUMNS] = Math.floor(Math.random() * MAX_HEIGHT);
    terrainCoordinates[city.BLOCK_ROWS][0] = Math.floor(Math.random() * MAX_HEIGHT);
    terrainCoordinates[city.BLOCK_ROWS][city.BLOCK_COLUMNS] = Math.floor(Math.random() * MAX_HEIGHT);

    // City must be (2^n + 1) blocks on both x and z dimensions for this to work
    midpointDisplace(terrainCoordinates, 100, 0.5, 0, city.BLOCK_ROWS, city.BLOCK_COLUMNS, 0);

    console.log(terrainCoordinates);

    return terrainCoordinates;
  };

  // Adapted from http://stevelosh.com/blog/2016/02/midpoint-displacement/
  var midpointDisplace = function(terrainCoordinates, jitterAmount, jitterDecay, top, right, bottom, left) {
    var topLeft = terrainCoordinates[top][left];
    var topRight = terrainCoordinates[top][right];
    var bottomLeft = terrainCoordinates[bottom][left];
    var bottomRight = terrainCoordinates[bottom][right];

    var midY = top + ((bottom - top) / 2);
    var midX = left + ((right - left) / 2);

    var jitter;

    // Left column
    jitter = (Math.random() * jitterAmount) - (jitterAmount / 2);
    terrainCoordinates[midY][left] = ((topLeft + bottomLeft) / 2) + jitter;
    // Right column
    jitter = (Math.random() * jitterAmount) - (jitterAmount / 2);
    terrainCoordinates[midY][right] = ((topRight + bottomRight) / 2) + jitter;
    // Top row
    jitter = (Math.random() * jitterAmount) - (jitterAmount / 2);
    terrainCoordinates[top][midX] = ((topLeft + bottomLeft) / 2) + jitter;
    // Bottom row
    jitter = (Math.random() * jitterAmount) - (jitterAmount / 2);
    terrainCoordinates[bottom][midX] = ((topLeft + bottomLeft) / 2) + jitter;

    // Middle
    var middleAverage = (terrainCoordinates[midY][left] + terrainCoordinates[midY][right] + terrainCoordinates[top][midX] + terrainCoordinates[bottom][midX]) / 4
    terrainCoordinates[midY][midX] = middleAverage;

    if ((midY - top) >= 2) {
      midpointDisplace(terrainCoordinates, (jitterAmount * jitterDecay), jitterDecay, top, midX, midY, left);
      midpointDisplace(terrainCoordinates, (jitterAmount * jitterDecay), jitterDecay, top, right, midY, midX);
      midpointDisplace(terrainCoordinates, (jitterAmount * jitterDecay), jitterDecay, midY, midX, bottom, left);
      midpointDisplace(terrainCoordinates, (jitterAmount * jitterDecay), jitterDecay, midY, right, bottom, midX);
    }
  };

  var buildTerrainGeometry = function(terrainCoordinates) {
    var i, j, x, z;
    var terrainGeometry1 = new THREE.Geometry();
    var terrainGeometry2 = new THREE.Geometry();
    var terrainMaterial1 = new THREE.MeshLambertMaterial({ color: new THREE.Color(0, 200, 0) });
    var terrainMaterial2 = new THREE.MeshLambertMaterial({ color: new THREE.Color(255, 25, 0) });

    var triangle, v1, v2, v3;

    x = -city.HALF_SCENE_WIDTH;
    for (i = 0; i < city.BLOCK_ROWS; i++) {
      z = -city.HALF_SCENE_DEPTH;

      for (j = 0; j < city.BLOCK_COLUMNS; j++) {
        triangle = new THREE.Geometry();

        v1 = new THREE.Vector3(x, terrainCoordinates[i][j], z);
        v2 = new THREE.Vector3(x, terrainCoordinates[i][j + 1], z + city.BLOCK_DEPTH + city.STREET_DEPTH);
        v3 = new THREE.Vector3(x + city.BLOCK_WIDTH + city.STREET_WIDTH, terrainCoordinates[i + 1][j], z);

        triangle.vertices.push(v1);
        triangle.vertices.push(v2);
        triangle.vertices.push(v3);

        triangle.faces.push(new THREE.Face3(0, 1, 2));
        triangle.computeFaceNormals();

        terrainGeometry1.merge(triangle);

        triangle = new THREE.Geometry();
        v1 = new THREE.Vector3(x, terrainCoordinates[i][j + 1], z + city.BLOCK_DEPTH + city.STREET_DEPTH);
        v2 = new THREE.Vector3(x + city.BLOCK_WIDTH + city.STREET_WIDTH, terrainCoordinates[i + 1][j + 1], z + city.BLOCK_DEPTH + city.STREET_DEPTH);
        v3 = new THREE.Vector3(x + city.BLOCK_WIDTH + city.STREET_WIDTH, terrainCoordinates[i + 1][j], z);

        triangle.vertices.push(v1);
        triangle.vertices.push(v2);
        triangle.vertices.push(v3);

        triangle.faces.push(new THREE.Face3(0, 1, 2));
        triangle.computeFaceNormals();

        terrainGeometry2.merge(triangle);

        z += city.BLOCK_DEPTH + city.STREET_DEPTH;
      }

      x += city.BLOCK_WIDTH + city.STREET_WIDTH;
    }

    var mesh1 = new THREE.Mesh(terrainGeometry1, terrainMaterial1);
    var mesh2 = new THREE.Mesh(terrainGeometry2, terrainMaterial2);

    return [mesh1, mesh2];
  };

  var buildMaterials = function() {
    var buildingMaterials = [];

    for (var i = 0; i < city.MAX_BUILDING_MATERIALS; i++) {
      var random = Math.random() * 0.6;
      var r = random;
      var g = random;
      var b = random;

      buildingMaterials.push(new THREE.MeshLambertMaterial({ color: new THREE.Color(r, g, b), }));
    }

    return buildingMaterials;
  };

  var buildEmptyGeometriesForBuildings = function() {
    var buildingGeometries = [];

    for (var i = 0; i < city.MAX_BUILDING_MATERIALS; i++) {
      buildingGeometries.push(new THREE.Geometry());
    }

    return buildingGeometries;
  };

  var generateUnitBlocks = function(terrainCoordinates) {
    var blocks = [];
    var block;
    var i, j, l;
    var lotLayout, buildingHeight, buildingBottom;

    for (i = 0; i < city.BLOCK_ROWS; i++) {
      blocks[i] = [];

      for (j = 0; j < city.BLOCK_COLUMNS; j++) {
        var blockLayout = city.BLOCK_LAYOUTS[Math.floor(Math.random() * city.BLOCK_LAYOUTS.length)];

        var blockTerrainCoordinates = [
          terrainCoordinates[i][j],
          terrainCoordinates[i + 1][j],
          terrainCoordinates[i][j + 1],
          terrainCoordinates[i + 1][j + 1],
        ];
        var minimumTerrainHeight = Math.min(...blockTerrainCoordinates);
        var maximumTerrainHeight = Math.max(...blockTerrainCoordinates);

        block = [];
        for (l = 0; l < blockLayout.length; l++) {
          lotLayout = blockLayout[l];
          buildingHeight = calculateBuildingHeight(i, j);
          buildingBottom = 0;

          block.push({
            left: lotLayout.left,
            right: lotLayout.right,
            top: lotLayout.top,
            bottom: lotLayout.bottom,
            yMin: buildingBottom,
            yMax: buildingHeight,
          });
        }

        blocks[i][j] = block;
      }
    }

    console.log(blocks);
    return blocks;
  };

  var generateSceneBlocks = function(unitBlocks, buildingGeometries) {
    var i, j, b, x, z;
    var block;
    var unitBuilding, building, materialIndex;
    var maxAboveGroundHeight = 5;

    x = -city.HALF_SCENE_WIDTH;
    for (i = 0; i < unitBlocks.length; i++) {
      z = -city.HALF_SCENE_DEPTH;

      for (j = 0; j < unitBlocks[i].length; j++) {
        block = unitBlocks[i][j];

        for (b = 0; b < block.length; b++) {
          unitBuilding = block[b];

          var unitWidth = unitBuilding.right - unitBuilding.left;
          var unitDepth = unitBuilding.bottom - unitBuilding.top;
          var xUnitMid = unitBuilding.left + (unitWidth / 2);
          var zUnitMid = unitBuilding.top + (unitDepth / 2);

          building = new THREE.Mesh(new THREE.BoxGeometry(unitWidth * city.BLOCK_WIDTH, 1, unitDepth * city.BLOCK_WIDTH));

          building.position.x = x + (city.BLOCK_WIDTH * xUnitMid);
          building.scale.y =  (Math.random() * maxAboveGroundHeight) + city.MIN_BUILDING_HEIGHT + (unitBuilding.yMax - unitBuilding.yMin);
          building.position.y = (building.scale.y / 2) + unitBuilding.yMin;
          building.position.z = z + (city.BLOCK_DEPTH * zUnitMid);
          building.updateMatrix();

          materialIndex = Math.floor(Math.random() * city.MAX_BUILDING_MATERIALS);
          buildingGeometries[materialIndex].merge(building.geometry, building.matrix);
        }

        z += city.BLOCK_DEPTH + city.STREET_DEPTH;
      }

      x += city.BLOCK_WIDTH + city.STREET_WIDTH;
    }
  };

  var calculateBuildingHeight = function(i, j) {
    var squareRootOfMaxBuildingHeight = Math.sqrt(city.MAX_BUILDING_HEIGHT);

    var halfRows = city.BLOCK_ROWS / 2;
    var halfColumns = city.BLOCK_COLUMNS / 2;

    var multiplierX = squareRootOfMaxBuildingHeight * ((halfRows - Math.abs(halfRows - i)) / halfRows);
    var multiplierZ = squareRootOfMaxBuildingHeight * ((halfColumns - Math.abs(halfColumns - j)) / halfColumns);
    var multiplier = Math.min(multiplierX, multiplierZ);

    return multiplier * multiplier;
  };

  return city;
};
<|endoftext|>


<commit_before>const u = require('./testUtil')
const base64png = require('./base64png')
const $answer = $('.answer')
const {makeRichText} = require('../app/rich-text-editor')
const {saveScreenshot} = require('../proto/saver')
let savedValues = []

const richTextOptions = id => ({
    screenshot: {
        saver: data => saveScreenshot(id)(data),
        limit: 10
    }
})

$answer.each((i, answer) => makeRichText(answer, richTextOptions(answer.id), onValueChange))
function onValueChange(data) {
    savedValues.push(data)
}

window.locale = 'FI'
window.IS_TEST = true
mocha.setup({
    ui: 'bdd',
//    reporter: WebConsole
})
window.expect = chai.expect

const $el = {}

describe('rich text editor', () => {
    before('wait for tools to hide', u.waitUntil(() => u.isOutsideViewPort($('[data-js="tools"]'))))
    before('wait answer field to initialize', u.waitUntil(() => $('.answer1').attr('contenteditable')))
    before(() => {
        $el.answer1 = $('.answer1')
        $el.answer2 = $('.answer2')
        $el.latexField = $('[data-js="latexField"]')
        $el.equationField = $('[data-js="equationField"]')
        $el.equationFieldTextArea = $el.equationField.find('textarea')
        $el.mathToolbar = $('[data-js="mathToolbar"]')
        $el.tools = $('[data-js="tools"]')
        $el.mathEditor = $('[data-js="mathEditor"]')
    })
    before('focus', () => $el.answer1.focus())
    before('wait for tools visible', u.waitUntil(() => $el.tools.is(':visible')))

    it('shows character list', () => expect($('[data-js="charactersList"]')).to.be.visible)
    it('hide math tools', () => expect($el.mathToolbar).to.be.hidden)
    it('answer has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))

    describe('when pasting images', () => {
        describe('png', () => {
            before('paste image', done => {
                $el.answer1.append(base64png)
                $el.answer1.find('img:last').get(0).onload = e => {
                    if (~e.target.src.indexOf('http')) done()
                }
                $el.answer1.trigger(u.pasteEventMock())
            })
            it('saves pasted image', () => {
                expect($el.answer1.find('img:last')).to.have.attr('src').match(/\/screenshot/)
            })
            it('saves markup', () => {
                const lastData = savedValues.pop()
                expect(lastData.answerHTML).to.match(/^$/)
                expect(lastData.answerText).to.equal('')
                expect(lastData.imageCount).to.equal(1)
            })
        })
        describe('not png', () => {
            let currentImgAmout
            before('#3', done => {
                currentImgAmout = $el.answer1.find('img').length
                $el.answer1
                    .append('')
                $('.answer1 img:last').get(0).onload = e => {
                    done()
                }
                $el.answer1.trigger(u.pasteEventMock())
            })
            it('ignores other than png images', () => {
                expect($el.answer1.find('img').length).to.equal(currentImgAmout)
            })
        })
    })

    describe('start math', () => {
        before('wait for tools visible', u.waitUntil(() => $el.tools.is(':visible')))
        before(() => $('[data-js="newEquation"]').mousedown())

        it('shows math tools', () => expect($el.mathToolbar).to.be.visible)
        it('shows math editor', () => expect($el.mathEditor).to.be.visible)
        it('answer still has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))

        describe('when focus in latex field', () => {
            before('type', () => $el.latexField.focus().val('xy').trigger('input'))
            before(u.delay)
            before(() => $('.rich-text-editor-toolbar-characters-group button:eq(2)').mousedown())
            before(u.delay)
            before('focus', () => $el.answer1.focus())

            it('answer still has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))
            it('shows math in equation field', () => expect($el.equationField).to.have.text('xy±'))
            it('shows math in latex field', () => expect($el.latexField).to.have.value('xy\\pm'))
            it('shows math in img', () => expect($('img:first')).to.have.attr('src', '/math.svg?latex=xy%5Cpm'))
        })

        describe('when focus in equation field, pasting content and adding char', () => {
            before(() => $el.answer1.blur())
            before(u.delay)
            before(() => $('img:first').trigger({type: 'click', which: 1}))
            before(() => $el.latexField.val('').trigger('input'))
            before(() => $el.equationFieldTextArea.val('a+b').trigger('paste'))
            before(u.delay)
            before(() => $('.rich-text-editor-toolbar-characters-group button:eq(3)').mousedown())
            before(u.delay)
            before(() => $el.answer1.focus())

            it('answer still has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))
            it('shows math in equation field', () => expect($el.equationField).to.have.text('a+b∞'))
            it('shows math in latex field', () => expect($el.latexField).to.have.value('a+b\\infty'))
            it('shows math in img', () => expect($('img:first')).to.have.attr('src', '/math.svg?latex=a%2Bb%5Cinfty'))
            it('stores latest data', () => {
                const lastData = savedValues.pop()
                expect(lastData.answerHTML).to.match(/^$/)
                expect(lastData.answerText).to.equal('')
                expect(lastData.imageCount).to.equal(1)
            })
        })

        describe('equation click opens and esc closes math editor', () => {
            before(() => $el.answer1.blur())
            it('editor is visible and then hidden', () => {
                expect($el.answer1).to.not.have.class('rich-text-focused')
                $('img:first').trigger({type: 'click', which: 1})
                expect($el.answer1).to.have.class('rich-text-focused')
                expect($el.mathEditor).to.be.visible
                $el.equationFieldTextArea.trigger({type: 'keydown', keyCode: 27})
                expect($el.mathEditor).to.be.hidden
            })
        })

        describe('when clicking special character from toolbar', () => {
            before(() => $('.rich-text-editor-toolbar-characters-group button:first').mousedown())

            it('inserts special character to answer body', () => {
                expect($el.answer1).to.have.text('°')
            })
            it('stores latest data', () => {
                const lastData = savedValues.pop()
                expect(lastData.answerHTML).to.match(/^°$/)
                expect(lastData.answerText).to.equal('°')
                expect(lastData.imageCount).to.equal(1)
            })

        })
    })

    describe('when leaving answer box', () => {
        before(() => $el.answer1.blur())
        before(u.delayFor(100))

        it('removes focus style from answer', () => expect($el.answer1).to.not.have.class('rich-text-focused'))
        it('hides toolbaar', () => expect($el.tools.position().top).to.be.below(-10))

    })

    describe('when moving to next answer while math is open', () => {
        before(() => $el.answer1.focus())
        before(() => $('[data-js="newEquation"]').mousedown())
        before(() => $el.answer1.blur())
        before(u.delay)
        before(() => $el.answer2.focus())
        before(u.delayFor(300))

        it('removes focus style from previous answer', () => expect($el.answer1).to.not.have.class('rich-text-focused'))
        it('adds focus tyle to current answer', () => expect($el.answer2).to.have.class('rich-text-focused'))
        it('keeps toolbar visible', () => expect($el.tools.position().top).to.equal(0))
    })

    describe('when trying to insert unsanitized content', () => {
        before('pasting banned tags', () => {
            $el.answer1.trigger(u.pasteEventMock('
foo

bar
link text '))
        })
        it('inserts sanitized content', () => {
            const lastData = savedValues.pop()
            expect(lastData.answerHTML).to.equal('
foo

bar
link text ')
            expect(lastData.answerText).to.equal('foo\nbar\nlink text ')
            expect(lastData.imageCount).to.equal(0)
        })
    })
})
<commit_msg>Write tests for drop event sanitation<commit_after>const u = require('./testUtil')
const base64png = require('./base64png')
const $answer = $('.answer')
const {makeRichText} = require('../app/rich-text-editor')
const {saveScreenshot} = require('../proto/saver')
let savedValues = []

const richTextOptions = id => ({
    screenshot: {
        saver: data => saveScreenshot(id)(data),
        limit: 10
    }
})

$answer.each((i, answer) => makeRichText(answer, richTextOptions(answer.id), onValueChange))
function onValueChange(data) {
    savedValues.push(data)
}

window.locale = 'FI'
window.IS_TEST = true
mocha.setup({
    ui: 'bdd',
//    reporter: WebConsole
})
window.expect = chai.expect

const $el = {}

describe('rich text editor', () => {
    before('wait for tools to hide', u.waitUntil(() => u.isOutsideViewPort($('[data-js="tools"]'))))
    before('wait answer field to initialize', u.waitUntil(() => $('.answer1').attr('contenteditable')))
    before(() => {
        $el.answer1 = $('.answer1')
        $el.answer2 = $('.answer2')
        $el.latexField = $('[data-js="latexField"]')
        $el.equationField = $('[data-js="equationField"]')
        $el.equationFieldTextArea = $el.equationField.find('textarea')
        $el.mathToolbar = $('[data-js="mathToolbar"]')
        $el.tools = $('[data-js="tools"]')
        $el.mathEditor = $('[data-js="mathEditor"]')
    })
    before('focus', () => $el.answer1.focus())
    before('wait for tools visible', u.waitUntil(() => $el.tools.is(':visible')))

    it('shows character list', () => expect($('[data-js="charactersList"]')).to.be.visible)
    it('hide math tools', () => expect($el.mathToolbar).to.be.hidden)
    it('answer has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))

    describe('when pasting images', () => {
        describe('png', () => {
            before('paste image', done => {
                $el.answer1.append(base64png)
                $el.answer1.find('img:last').get(0).onload = e => {
                    if (~e.target.src.indexOf('http')) done()
                }
                $el.answer1.trigger(u.pasteEventMock())
            })
            it('saves pasted image', () => {
                expect($el.answer1.find('img:last')).to.have.attr('src').match(/\/screenshot/)
            })
            it('saves markup', () => {
                const lastData = savedValues.pop()
                expect(lastData.answerHTML).to.match(/^$/)
                expect(lastData.answerText).to.equal('')
                expect(lastData.imageCount).to.equal(1)
            })
        })
        describe('not png', () => {
            let currentImgAmout
            before('#3', done => {
                currentImgAmout = $el.answer1.find('img').length
                $el.answer1
                    .append('')
                $('.answer1 img:last').get(0).onload = e => {
                    done()
                }
                $el.answer1.trigger(u.pasteEventMock())
            })
            it('ignores other than png images', () => {
                expect($el.answer1.find('img').length).to.equal(currentImgAmout)
            })
        })
    })

    describe('start math', () => {
        before('wait for tools visible', u.waitUntil(() => $el.tools.is(':visible')))
        before(() => $('[data-js="newEquation"]').mousedown())

        it('shows math tools', () => expect($el.mathToolbar).to.be.visible)
        it('shows math editor', () => expect($el.mathEditor).to.be.visible)
        it('answer still has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))

        describe('when focus in latex field', () => {
            before('type', () => $el.latexField.focus().val('xy').trigger('input'))
            before(u.delay)
            before(() => $('.rich-text-editor-toolbar-characters-group button:eq(2)').mousedown())
            before(u.delay)
            before('focus', () => $el.answer1.focus())

            it('answer still has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))
            it('shows math in equation field', () => expect($el.equationField).to.have.text('xy±'))
            it('shows math in latex field', () => expect($el.latexField).to.have.value('xy\\pm'))
            it('shows math in img', () => expect($('img:first')).to.have.attr('src', '/math.svg?latex=xy%5Cpm'))
        })

        describe('when focus in equation field, pasting content and adding char', () => {
            before(() => $el.answer1.blur())
            before(u.delay)
            before(() => $('img:first').trigger({type: 'click', which: 1}))
            before(() => $el.latexField.val('').trigger('input'))
            before(() => $el.equationFieldTextArea.val('a+b').trigger('paste'))
            before(u.delay)
            before(() => $('.rich-text-editor-toolbar-characters-group button:eq(3)').mousedown())
            before(u.delay)
            before(() => $el.answer1.focus())

            it('answer still has focus style', () => expect($el.answer1).to.have.class('rich-text-focused'))
            it('shows math in equation field', () => expect($el.equationField).to.have.text('a+b∞'))
            it('shows math in latex field', () => expect($el.latexField).to.have.value('a+b\\infty'))
            it('shows math in img', () => expect($('img:first')).to.have.attr('src', '/math.svg?latex=a%2Bb%5Cinfty'))
            it('stores latest data', () => {
                const lastData = savedValues.pop()
                expect(lastData.answerHTML).to.match(/^$/)
                expect(lastData.answerText).to.equal('')
                expect(lastData.imageCount).to.equal(1)
            })
        })

        describe('equation click opens and esc closes math editor', () => {
            before(() => $el.answer1.blur())
            it('editor is visible and then hidden', () => {
                expect($el.answer1).to.not.have.class('rich-text-focused')
                $('img:first').trigger({type: 'click', which: 1})
                expect($el.answer1).to.have.class('rich-text-focused')
                expect($el.mathEditor).to.be.visible
                $el.equationFieldTextArea.trigger({type: 'keydown', keyCode: 27})
                expect($el.mathEditor).to.be.hidden
            })
        })

        describe('when clicking special character from toolbar', () => {
            before(() => $('.rich-text-editor-toolbar-characters-group button:first').mousedown())

            it('inserts special character to answer body', () => {
                expect($el.answer1).to.have.text('°')
            })
            it('stores latest data', () => {
                const lastData = savedValues.pop()
                expect(lastData.answerHTML).to.match(/^°$/)
                expect(lastData.answerText).to.equal('°')
                expect(lastData.imageCount).to.equal(1)
            })

        })
    })

    describe('when leaving answer box', () => {
        before(() => $el.answer1.blur())
        before(u.delayFor(100))

        it('removes focus style from answer', () => expect($el.answer1).to.not.have.class('rich-text-focused'))
        it('hides toolbaar', () => expect($el.tools.position().top).to.be.below(-10))

    })

    describe('when moving to next answer while math is open', () => {
        before(() => $el.answer1.focus())
        before(() => $('[data-js="newEquation"]').mousedown())
        before(() => $el.answer1.blur())
        before(u.delay)
        before(() => $el.answer2.focus())
        before(u.delayFor(300))

        it('removes focus style from previous answer', () => expect($el.answer1).to.not.have.class('rich-text-focused'))
        it('adds focus tyle to current answer', () => expect($el.answer2).to.have.class('rich-text-focused'))
        it('keeps toolbar visible', () => expect($el.tools.position().top).to.equal(0))
    })

    describe('when trying to insert unsanitized content', () => {
        before('pasting banned tags', () => {
            $el.answer1.trigger(u.pasteEventMock('
paste

bar
link text '))
        })
        it('inserts sanitized content', () => {
            const lastData = savedValues.pop()
            expect(lastData.answerHTML).to.equal('
paste

bar
link text ')
            expect(lastData.answerText).to.equal('paste\nbar\nlink text ')
            expect(lastData.imageCount).to.equal(0)
        })
    })

    describe('when trying to drag unsanitized content', () => {
        before('dropping banned tags', () => {
            $el.answer1.html('
drop

bar
link text ')
            $el.answer1.trigger('drop')
            $el.answer1.trigger('input')
        })
        before(u.delay)

        it('inserts sanitized content', () => {
            expect($el.answer1).to.have.html('
drop

bar
link text ')
            const lastData = savedValues.pop()
            expect(lastData.answerHTML).to.equal('
drop

bar
link text ')
            expect(lastData.answerText).to.equal('drop\nbar\nlink text')
            expect(lastData.imageCount).to.equal(0)
        })
    })
})
<|endoftext|>


<commit_before>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name CurveLocation
 *
 * @class CurveLocation objects describe a location on {@link Curve}
 * objects, as defined by the curve {@link #parameter}, a value between
 * {@code 0} (beginning of the curve) and {@code 1} (end of the curve). If
 * the curve is part of a {@link Path} item, its {@link #index} inside the
 * {@link Path#curves} array is also provided.
 *
 * The class is in use in many places, such as
 * {@link Path#getLocationAt(offset, isParameter)},
 * {@link Path#getLocationOf(point)},
 * {@link Path#getNearestLocation(point),
 * {@link PathItem#getIntersections(path)},
 * etc.
 */
var CurveLocation = Base.extend(/** @lends CurveLocation# */{
    _class: 'CurveLocation',
    // Enforce creation of beans, as bean getters have hidden parameters.
    // See #getSegment() below.
    beans: true,

    // DOCS: CurveLocation class description: add these back when the  mentioned
    // functioned have been added: {@link Path#split(location)}
    /**
     * Creates a new CurveLocation object.
     *
     * @param {Curve} curve
     * @param {Number} parameter
     * @param {Point} [point]
     */
    initialize: function CurveLocation(curve, parameter, point,
            _distance, _overlap, _intersection) {
        // Merge intersections very close to the end of a curve to the
        // beginning of the next curve.
        if (parameter >= 1 - /*#=*/Numerical.CURVETIME_EPSILON) {
            var next = curve.getNext();
            if (next) {
                parameter = 0;
                curve = next;
            }
        }
        // Define this CurveLocation's unique id.
        // NOTE: We do not use the same pool as the rest of the library here,
        // since this is only required to be unique at runtime among other
        // CurveLocation objects.
        this._id = UID.get(CurveLocation);
        var path = curve._path;
        this._version = path ? path._version : 0;
        this._curve = curve;
        this._parameter = parameter;
        this._point = point || curve.getPointAt(parameter, true);
        this._distance = _distance;
        this._overlap = _overlap;
        this._intersection = _intersection;
        if (_intersection) {
            _intersection._intersection = this;
            // TODO: Remove this once debug logging is removed.
            _intersection._other = true;
        }
        this._segment = null; // To be determined, see #getSegment()
        // Also store references to segment1 and segment2, in case path
        // splitting / dividing is going to happen, in which case the segments
        // can be used to determine the new curves, see #getCurve(true)
        this._segment1 = curve._segment1;
        this._segment2 = curve._segment2;
    },

    /**
     * The segment of the curve which is closer to the described location.
     *
     * @type Segment
     * @bean
     */
    getSegment: function(_preferFirst) {
        if (!this._segment) {
            var curve = this.getCurve(),
                parameter = this.getParameter();
            if (parameter === 1) {
                this._segment = curve._segment2;
            } else if (parameter === 0 || _preferFirst) {
                this._segment = curve._segment1;
            } else if (parameter == null) {
                return null;
            } else {
                // Determine the closest segment by comparing curve lengths
                this._segment = curve.getPartLength(0, parameter)
                    < curve.getPartLength(parameter, 1)
                        ? curve._segment1
                        : curve._segment2;
            }
        }
        return this._segment;
    },

    /**
     * The curve that this location belongs to.
     *
     * @type Curve
     * @bean
     */
    getCurve: function() {
        var curve = this._curve,
            path = curve && curve._path;
        if (path && path._version !== this._version) {
            // If the path's segments have changed in the meantime, clear the
            // internal _parameter value and force refetching of the correct
            // curve again here.
            curve = null;
            this._parameter = null;
        }
        if (!curve) {
            // If we're asked to get the curve uncached, access current curve
            // objects through segment1 / segment2. Since path splitting or
            // dividing might have happened in the meantime, try segment1's
            // curve, and see if _point lies on it still, otherwise assume it's
            // the curve before segment2.
            curve = this._segment1.getCurve();
            if (curve.getParameterOf(this._point) == null)
                curve = this._segment2.getPrevious().getCurve();
            this._curve = curve;
            // Fetch path again as it could be on a new one through split()
            path = curve._path;
            this._version = path ? path._version : 0;
        }
        return curve;
    },

    /**
     * The path this curve belongs to, if any.
     *
     * @type Item
     * @bean
     */
    getPath: function() {
        var curve = this.getCurve();
        return curve && curve._path;
    },

    /**
     * The index of the curve within the {@link Path#curves} list, if the
     * curve is part of a {@link Path} item.
     *
     * @type Index
     * @bean
     */
    getIndex: function() {
        var curve = this.getCurve();
        return curve && curve.getIndex();
    },

    /**
     * The curve parameter, as used by various bezier curve calculations. It is
     * value between {@code 0} (beginning of the curve) and {@code 1} (end of
     * the curve).
     *
     * @type Number
     * @bean
     */
    getParameter: function() {
        var curve = this.getCurve(),
            parameter = this._parameter;
        return curve && parameter == null
            ? this._parameter = curve.getParameterOf(this._point)
            : parameter;
    },

    /**
     * The point which is defined by the {@link #curve} and
     * {@link #parameter}.
     *
     * @type Point
     * @bean
     */
    getPoint: function() {
        return this._point;
    },

    /**
     * The length of the path from its beginning up to the location described
     * by this object. If the curve is not part of a path, then the length
     * within the curve is returned instead.
     *
     * @type Number
     * @bean
     */
    getOffset: function() {
        var path = this.getPath();
        return path ? path._getOffset(this) : this.getCurveOffset();
    },

    /**
     * The length of the curve from its beginning up to the location described
     * by this object.
     *
     * @type Number
     * @bean
     */
    getCurveOffset: function() {
        var curve = this.getCurve(),
            parameter = this.getParameter();
        return parameter != null && curve && curve.getPartLength(0, parameter);
    },

    /**
     * The curve location on the intersecting curve, if this location is the
     * result of a call to {@link PathItem#getIntersections(path)} /
     * {@link Curve#getIntersections(curve)}.
     *
     * @type CurveLocation
     * @bean
     */
    getIntersection: function() {
        return this._intersection;
    },

    /**
     * The tangential vector to the {@link #curve} at the given location.
     *
     * @name Item#tangent
     * @type Point
     */

    /**
     * The normal vector to the {@link #curve} at the given location.
     *
     * @name Item#normal
     * @type Point
     */

    /**
     * The curvature of the {@link #curve} at the given location.
     *
     * @name Item#curvature
     * @type Number
     */

    /**
     * The distance from the queried point to the returned location.
     *
     * @type Number
     * @bean
     * @see Curve#getNearestLocation(point)
     * @see Path#getNearestLocation(point)
     */
    getDistance: function() {
        return this._distance;
    },

    // DOCS: divide(), split()

    divide: function() {
        var curve = this.getCurve();
        return curve && curve.divide(this.getParameter(), true);
    },

    split: function() {
        var curve = this.getCurve();
        return curve && curve.split(this.getParameter(), true);
    },

    /**
     * Checks whether tow CurveLocation objects are describing the same location
     * on a path, by applying the same tolerances as elsewhere when dealing with
     * curve time parameters.
     *
     * @param {CurveLocation} location
     * @return {Boolean} {@true if the locations are equal}
     */
    equals: function(loc, _ignoreIntersection) {
        return this === loc
            || loc instanceof CurveLocation
                // Call getCurve() and getParameter() to keep in sync
                && this.getCurve() === loc.getCurve()
                && this.getPoint().isClose(loc.getPoint(),
                        /*#=*/Numerical.GEOMETRIC_EPSILON)
                && (_ignoreIntersection
                    || (!this._intersection && !loc._intersection
                        || this._intersection && this._intersection.equals(
                                loc._intersection, true)))
            || false;
    },

    /**
     * @return {String} a string representation of the curve location
     */
    toString: function() {
        var parts = [],
            point = this.getPoint(),
            f = Formatter.instance;
        if (point)
            parts.push('point: ' + point);
        var index = this.getIndex();
        if (index != null)
            parts.push('index: ' + index);
        var parameter = this.getParameter();
        if (parameter != null)
            parts.push('parameter: ' + f.number(parameter));
        if (this._distance != null)
            parts.push('distance: ' + f.number(this._distance));
        return '{ ' + parts.join(', ') + ' }';
    },

    statics: {
        sort: function(locations) {
            locations.sort(function compare(l1, l2) {
                var curve1 = l1._curve,
                    curve2 = l2._curve,
                    path1 = curve1._path,
                    path2 = curve2._path,
                    res;
                // Sort by path-id, curve, parameter, curve2, parameter2 so we
                // can easily remove duplicates with calls to equals() after.
                // NOTE: We don't call getCurve() / getParameter() here, since
                // this code is used internally in boolean operations where all
                // this information remains valid during processing.
                if (path1 === path2) {
                    if (curve1 === curve2) {
                        var diff = l1._parameter - l2._parameter;
                        if (Math.abs(diff) < /*#=*/Numerical.CURVETIME_EPSILON){
                            var i1 = l1._intersection,
                                i2 = l2._intersection,
                                curve21 = i1 && i1._curve,
                                curve22 = i2 && l2._curve;
                            res = curve21 === curve22 // equal or both null
                                ? i1 && i2 ? i1._parameter - i2._parameter : 0
                                : curve21 && curve22
                                    ? curve21.getIndex() - curve22.getIndex()
                                    : curve21 ? 1 : -1;
                        } else {
                            res = diff;
                        }
                    } else {
                        res = curve1.getIndex() - curve2.getIndex();
                    }
                } else {
                    // Sort by path id to group all locs on the same path.
                    res = path1._id - path2._id;
                }
                return res;
            });
        }
    }
}, Base.each(Curve.evaluateMethods, function(name) {
    // Produce getters for #getTangent() / #getNormal() / #getCurvature()
    if (name !== 'getPoint') {
        var get = name + 'At';
        this[name] = function() {
            var parameter = this.getParameter(),
                curve = this.getCurve();
            return parameter != null && curve && curve[get](parameter, true);
        };
    }
}, {}));
<commit_msg>Implement correct CurveLocation#isCrossing() check.<commit_after>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name CurveLocation
 *
 * @class CurveLocation objects describe a location on {@link Curve}
 * objects, as defined by the curve {@link #parameter}, a value between
 * {@code 0} (beginning of the curve) and {@code 1} (end of the curve). If
 * the curve is part of a {@link Path} item, its {@link #index} inside the
 * {@link Path#curves} array is also provided.
 *
 * The class is in use in many places, such as
 * {@link Path#getLocationAt(offset, isParameter)},
 * {@link Path#getLocationOf(point)},
 * {@link Path#getNearestLocation(point),
 * {@link PathItem#getIntersections(path)},
 * etc.
 */
var CurveLocation = Base.extend(/** @lends CurveLocation# */{
    _class: 'CurveLocation',
    // Enforce creation of beans, as bean getters have hidden parameters.
    // See #getSegment() below.
    beans: true,

    // DOCS: CurveLocation class description: add these back when the  mentioned
    // functioned have been added: {@link Path#split(location)}
    /**
     * Creates a new CurveLocation object.
     *
     * @param {Curve} curve
     * @param {Number} parameter
     * @param {Point} [point]
     */
    initialize: function CurveLocation(curve, parameter, point,
            _distance, _overlap, _intersection) {
        // Merge intersections very close to the end of a curve to the
        // beginning of the next curve.
        if (parameter >= 1 - /*#=*/Numerical.CURVETIME_EPSILON) {
            var next = curve.getNext();
            if (next) {
                parameter = 0;
                curve = next;
            }
        }
        // Define this CurveLocation's unique id.
        // NOTE: We do not use the same pool as the rest of the library here,
        // since this is only required to be unique at runtime among other
        // CurveLocation objects.
        this._id = UID.get(CurveLocation);
        this._setCurve(curve);
        this._parameter = parameter;
        this._point = point || curve.getPointAt(parameter, true);
        this._distance = _distance;
        this._overlap = _overlap;
        this._crossing = null;
        this._intersection = _intersection;
        if (_intersection) {
            _intersection._intersection = this;
            // TODO: Remove this once debug logging is removed.
            _intersection._other = true;
        }
    },

    _setCurve: function(curve) {
        var path = curve._path;
        this._version = path ? path._version : 0;
        this._curve = curve;
        this._segment = null; // To be determined, see #getSegment()
        // Also store references to segment1 and segment2, in case path
        // splitting / dividing is going to happen, in which case the segments
        // can be used to determine the new curves, see #getCurve(true)
        this._segment1 = curve._segment1;
        this._segment2 = curve._segment2;
    },

    /**
     * The segment of the curve which is closer to the described location.
     *
     * @type Segment
     * @bean
     */
    getSegment: function() {
        // Request curve first, so _segment gets invalidated if it's out of sync
        var curve = this.getCurve(),
            segment = this._segment;
        if (!segment) {
            var parameter = this.getParameter();
            if (parameter === 0) {
                segment = curve._segment1;
            } else if (parameter === 1) {
                segment = curve._segment2;
            } else if (parameter != null) {
                // Determine the closest segment by comparing curve lengths
                segment = curve.getPartLength(0, parameter)
                    < curve.getPartLength(parameter, 1)
                        ? curve._segment1
                        : curve._segment2;
            }
            this._segment = segment;
        }
        return segment;
    },

    /**
     * The curve that this location belongs to.
     *
     * @type Curve
     * @bean
     */
    getCurve: function() {
        var curve = this._curve,
            path = curve && curve._path,
            that = this;
        if (path && path._version !== this._version) {
            // If the path's segments have changed in the meantime, clear the
            // internal _parameter value and force refetching of the correct
            // curve again here.
            curve = this._parameter = this._curve = null;
        }

        // If path is out of sync, access current curve objects through segment1
        // / segment2. Since path splitting or dividing might have happened in
        // the meantime, try segment1's curve, and see if _point lies on it
        // still, otherwise assume it's the curve before segment2.
        function trySegment(segment) {
            var curve = segment && segment.getCurve();
            if (curve && (that._parameter = curve.getParameterOf(that._point))
                    != null) {
                // Fetch path again as it could be on a new one through split()
                that._setCurve(curve);
                that._segment = segment;
                return curve;
            }
        }

        return curve
            || trySegment(this._segment)
            || trySegment(this._segment1)
            || trySegment(this._segment2.getPrevious());
    },

    /**
     * The path this curve belongs to, if any.
     *
     * @type Item
     * @bean
     */
    getPath: function() {
        var curve = this.getCurve();
        return curve && curve._path;
    },

    /**
     * The index of the curve within the {@link Path#curves} list, if the
     * curve is part of a {@link Path} item.
     *
     * @type Index
     * @bean
     */
    getIndex: function() {
        var curve = this.getCurve();
        return curve && curve.getIndex();
    },

    /**
     * The curve parameter, as used by various bezier curve calculations. It is
     * value between {@code 0} (beginning of the curve) and {@code 1} (end of
     * the curve).
     *
     * @type Number
     * @bean
     */
    getParameter: function() {
        var curve = this.getCurve(),
            parameter = this._parameter;
        return curve && parameter == null
            ? this._parameter = curve.getParameterOf(this._point)
            : parameter;
    },

    /**
     * The point which is defined by the {@link #curve} and
     * {@link #parameter}.
     *
     * @type Point
     * @bean
     */
    getPoint: function() {
        return this._point;
    },

    /**
     * The length of the path from its beginning up to the location described
     * by this object. If the curve is not part of a path, then the length
     * within the curve is returned instead.
     *
     * @type Number
     * @bean
     */
    getOffset: function() {
        var path = this.getPath();
        return path ? path._getOffset(this) : this.getCurveOffset();
    },

    /**
     * The length of the curve from its beginning up to the location described
     * by this object.
     *
     * @type Number
     * @bean
     */
    getCurveOffset: function() {
        var curve = this.getCurve(),
            parameter = this.getParameter();
        return parameter != null && curve && curve.getPartLength(0, parameter);
    },

    /**
     * The curve location on the intersecting curve, if this location is the
     * result of a call to {@link PathItem#getIntersections(path)} /
     * {@link Curve#getIntersections(curve)}.
     *
     * @type CurveLocation
     * @bean
     */
    getIntersection: function() {
        return this._intersection;
    },

    /**
     * The tangential vector to the {@link #curve} at the given location.
     *
     * @name Item#tangent
     * @type Point
     */

    /**
     * The normal vector to the {@link #curve} at the given location.
     *
     * @name Item#normal
     * @type Point
     */

    /**
     * The curvature of the {@link #curve} at the given location.
     *
     * @name Item#curvature
     * @type Number
     */

    /**
     * The distance from the queried point to the returned location.
     *
     * @type Number
     * @bean
     * @see Curve#getNearestLocation(point)
     * @see Path#getNearestLocation(point)
     */
    getDistance: function() {
        return this._distance;
    },

    // DOCS: divide(), split()

    divide: function() {
        var curve = this.getCurve();
        return curve && curve.divide(this.getParameter(), true);
    },

    split: function() {
        var curve = this.getCurve();
        return curve && curve.split(this.getParameter(), true);
    },

    isCrossing: function(report) {
        // Implementation based on work by Andy Finnell:
        // http://losingfight.com/blog/2011/07/09/how-to-implement-boolean-operations-on-bezier-paths-part-3/
        // https://bitbucket.org/andyfinnell/vectorboolean
        var intersection = this._intersection,
            crossing = this._crossing;
        if (crossing != null || !intersection)
            return crossing || false;
        // TODO: isTangent() ?
        // TODO: isAtEndPoint() ?
        // -> Return if it's a tangent, or if not at an end point, only end
        // point intersections need more checking!
        var tMin = /*#=*/Numerical.CURVETIME_EPSILON,
            tMax = 1 - tMin,
            PI = Math.PI,
            PI_2 = PI * 2,
            // TODO: Make getCurve() sync work in boolean ops after splitting!!!
            c2 = this._curve,
            c1 = c2.getPrevious(),
            c4 = intersection._curve,
            c3 = c4.getPrevious();
        if (!c1 || !c3)
            return this._crossing = false;
        if (report) {
            new Path.Circle({
                center: this.getPoint(),
                radius: 10,
                strokeColor: 'red'
            });
            new Path({
                segments: [c1.getSegment1(), c1.getSegment2(), c2.getSegment2()],
                strokeColor: 'red'
            });
            new Path({
                segments: [c3.getSegment1(), c3.getSegment2(), c4.getSegment2()],
                strokeColor: 'orange'
            });
            console.log(c1.getValues(), c2.getValues(), c3.getValues(), c4.getValues());
        }

        function getAngle(tangent) {
            var a = tangent.getAngleInRadians();
            return a < -PI ? a + PI_2 : a >= PI ? a - PI_2 : a;
        }

        function isInRange(angle, min, max) {
            return min < max
                ? angle > min && angle < max
                // The range wraps around 0:
                : angle > min && angle <= PI || angle >= -PI && angle < max;
        }

        // Calculate angles for all four tangents at the intersection point
        var a1 = getAngle(c1.getTangentAt(tMax, true).negate()),
            a2 = getAngle(c2.getTangentAt(tMin, true)),
            a3 = getAngle(c3.getTangentAt(tMax, true).negate()),
            a4 = getAngle(c4.getTangentAt(tMin, true));

        // Count how many times curve2 angles appear between the curve1 angles
        // If each pair of angles split the other two, then the edges cross.
        return (isInRange(a3, a1, a2) ^ isInRange(a4, a1, a2))
            && (isInRange(a3, a2, a1) ^ isInRange(a4, a2, a1));
    },

    /**
     * Checks whether tow CurveLocation objects are describing the same location
     * on a path, by applying the same tolerances as elsewhere when dealing with
     * curve time parameters.
     *
     * @param {CurveLocation} location
     * @return {Boolean} {@true if the locations are equal}
     */
    equals: function(loc, _ignoreIntersection) {
        return this === loc
            || loc instanceof CurveLocation
                // Call getCurve() and getParameter() to keep in sync
                && this.getCurve() === loc.getCurve()
                && this.getPoint().isClose(loc.getPoint(),
                        /*#=*/Numerical.GEOMETRIC_EPSILON)
                && (_ignoreIntersection
                    || (!this._intersection && !loc._intersection
                        || this._intersection && this._intersection.equals(
                                loc._intersection, true)))
            || false;
    },

    /**
     * @return {String} a string representation of the curve location
     */
    toString: function() {
        var parts = [],
            point = this.getPoint(),
            f = Formatter.instance;
        if (point)
            parts.push('point: ' + point);
        var index = this.getIndex();
        if (index != null)
            parts.push('index: ' + index);
        var parameter = this.getParameter();
        if (parameter != null)
            parts.push('parameter: ' + f.number(parameter));
        if (this._distance != null)
            parts.push('distance: ' + f.number(this._distance));
        return '{ ' + parts.join(', ') + ' }';
    },

    statics: {
        sort: function(locations) {
            locations.sort(function compare(l1, l2) {
                var curve1 = l1._curve,
                    curve2 = l2._curve,
                    path1 = curve1._path,
                    path2 = curve2._path,
                    res;
                // Sort by path-id, curve, parameter, curve2, parameter2 so we
                // can easily remove duplicates with calls to equals() after.
                // NOTE: We don't call getCurve() / getParameter() here, since
                // this code is used internally in boolean operations where all
                // this information remains valid during processing.
                if (path1 === path2) {
                    if (curve1 === curve2) {
                        var diff = l1._parameter - l2._parameter;
                        if (Math.abs(diff) < /*#=*/Numerical.CURVETIME_EPSILON){
                            var i1 = l1._intersection,
                                i2 = l2._intersection,
                                curve21 = i1 && i1._curve,
                                curve22 = i2 && l2._curve;
                            res = curve21 === curve22 // equal or both null
                                ? i1 && i2 ? i1._parameter - i2._parameter : 0
                                : curve21 && curve22
                                    ? curve21.getIndex() - curve22.getIndex()
                                    : curve21 ? 1 : -1;
                        } else {
                            res = diff;
                        }
                    } else {
                        res = curve1.getIndex() - curve2.getIndex();
                    }
                } else {
                    // Sort by path id to group all locs on the same path.
                    res = path1._id - path2._id;
                }
                return res;
            });
        }
    }
}, Base.each(Curve.evaluateMethods, function(name) {
    // Produce getters for #getTangent() / #getNormal() / #getCurvature()
    if (name !== 'getPoint') {
        var get = name + 'At';
        this[name] = function() {
            var parameter = this.getParameter(),
                curve = this.getCurve();
            return parameter != null && curve && curve[get](parameter, true);
        };
    }
}, {}));
<|endoftext|>


<commit_before>const { compose, applyMiddleware, createStore, combineReducers } = require('redux');
const reduxThunk = require('redux-thunk');
const { channelMiddleware, channelStore } = require('./lib/redux');
const reducers = require('./reducers');
const { replaceable } = require('./lib/replaceable-state');
const { mergeObj } = require('./lib/util');

module.exports = (state, version) => {
  const create = compose(
    applyMiddleware(
      reduxThunk,
      channelMiddleware,
      () => next => action => {
        // Our own logger because none of the fancy loggers supports
        // server-side rendering.
        // console.log(action);
        return next(action);
      }
    ),
    channelStore,
    createStore
  );

  return create(
    replaceable(combineReducers(reducers), version),
    state
  );
};
<commit_msg>minor cleanup<commit_after>const { compose, applyMiddleware, createStore, combineReducers } = require('redux');
const reduxThunk = require('redux-thunk');
const { channelMiddleware, channelStore } = require('./lib/redux');
const reducers = require('./reducers');
const { replaceable } = require('./lib/replaceable-state');
const { mergeObj } = require('./lib/util');

module.exports = (state, version) => {
  const create = compose(
    applyMiddleware(
      reduxThunk,
      channelMiddleware,
      () => next => action => {
        // Our own logger because none of the fancy loggers supports
        // server-side rendering.
        // console.log(action);
        return next(action);
      }
    ),
    channelStore,
    createStore
  );

  return create(
    replaceable(combineReducers(reducers)),
    state
  );
};
<|endoftext|>


<commit_before>const resolvePaths = require('./utils/resolvePaths');
const circularize = require('./utils/circularize');
const createJob = require('./createJob');
const { log } = require('./utils/log');
const getId = require('./utils/getId');
const { defaultOEM } = require('./constants/config');
const {
  defaultOptions,
  spawnWorker,
  terminateWorker,
  onMessage,
  loadImage,
  send,
} = require('./worker/node');

let workerCounter = 0;

module.exports = (_options = {}) => {
  const id = getId('Worker', workerCounter);
  const {
    logger,
    ...options
  } = resolvePaths({
    ...defaultOptions,
    ..._options,
  });
  const resolves = {};
  const rejects = {};
  let worker = spawnWorker(options);

  workerCounter += 1;

  const setResolve = (action, res) => {
    resolves[action] = res;
  };

  const setReject = (action, rej) => {
    rejects[action] = rej;
  };

  const startJob = ({ id: jobId, action, payload }) => (
    new Promise((resolve, reject) => {
      log(`[${id}]: Start ${jobId}, action=${action}`);
      setResolve(action, resolve);
      setReject(action, reject);
      send(worker, {
        workerId: id,
        jobId,
        action,
        payload,
      });
    })
  );

  const load = jobId => (
    startJob(createJob({
      id: jobId, action: 'load', payload: { options },
    }))
  );

  const loadLanguage = (langs = 'eng', jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'loadLanguage',
      payload: { langs, options },
    }))
  );

  const initialize = (langs = 'eng', oem = defaultOEM, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'initialize',
      payload: { langs, oem },
    }))
  );

  const setParameters = (params = {}, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'setParameters',
      payload: { params },
    }))
  );

  const recognize = async (image, opts = {}, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'recognize',
      payload: { image: await loadImage(image), options: opts },
    }))
  );

  const getPDF = (title = 'Tesseract OCR Result', textonly = false, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'getPDF',
      payload: { title, textonly },
    }))
  );

  const detect = async (image, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'detect',
      payload: { image: await loadImage(image) },
    }))
  );

  const terminate = async (jobId) => {
    if (worker !== null) {
      await startJob(createJob({
        id: jobId,
        action: 'terminate',
      }));
      terminateWorker(worker);
      worker = null;
    }
    return Promise.resolve();
  };

  onMessage(worker, ({
    workerId, jobId, status, action, data,
  }) => {
    if (status === 'resolve') {
      log(`[${workerId}]: Complete ${jobId}`);
      let d = data;
      if (action === 'recognize') {
        d = circularize(data);
      } else if (action === 'getPDF') {
        d = Array.from({ ...data, length: Object.keys(data).length });
      }
      resolves[action]({ jobId, data: d });
    } else if (status === 'reject') {
      rejects[action](data);
    } else if (status === 'progress') {
      logger(data);
    }
  });

  return {
    id,
    worker,
    setResolve,
    setReject,
    load,
    loadLanguage,
    initialize,
    setParameters,
    recognize,
    getPDF,
    detect,
    terminate,
  };
};
<commit_msg>add errorHandler<commit_after>const resolvePaths = require('./utils/resolvePaths');
const circularize = require('./utils/circularize');
const createJob = require('./createJob');
const { log } = require('./utils/log');
const getId = require('./utils/getId');
const { defaultOEM } = require('./constants/config');
const {
  defaultOptions,
  spawnWorker,
  terminateWorker,
  onMessage,
  loadImage,
  send,
} = require('./worker/node');

let workerCounter = 0;

module.exports = (_options = {}) => {
  const id = getId('Worker', workerCounter);
  const {
    logger,
    errorHandler,
    ...options
  } = resolvePaths({
    ...defaultOptions,
    ..._options,
  });
  const resolves = {};
  const rejects = {};
  let worker = spawnWorker(options);

  workerCounter += 1;

  const setResolve = (action, res) => {
    resolves[action] = res;
  };

  const setReject = (action, rej) => {
    rejects[action] = rej;
  };

  const startJob = ({ id: jobId, action, payload }) => (
    new Promise((resolve, reject) => {
      log(`[${id}]: Start ${jobId}, action=${action}`);
      setResolve(action, resolve);
      setReject(action, reject);
      send(worker, {
        workerId: id,
        jobId,
        action,
        payload,
      });
    })
  );

  const load = jobId => (
    startJob(createJob({
      id: jobId, action: 'load', payload: { options },
    }))
  );

  const loadLanguage = (langs = 'eng', jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'loadLanguage',
      payload: { langs, options },
    }))
  );

  const initialize = (langs = 'eng', oem = defaultOEM, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'initialize',
      payload: { langs, oem },
    }))
  );

  const setParameters = (params = {}, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'setParameters',
      payload: { params },
    }))
  );

  const recognize = async (image, opts = {}, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'recognize',
      payload: { image: await loadImage(image), options: opts },
    }))
  );

  const getPDF = (title = 'Tesseract OCR Result', textonly = false, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'getPDF',
      payload: { title, textonly },
    }))
  );

  const detect = async (image, jobId) => (
    startJob(createJob({
      id: jobId,
      action: 'detect',
      payload: { image: await loadImage(image) },
    }))
  );

  const terminate = async (jobId) => {
    if (worker !== null) {
      await startJob(createJob({
        id: jobId,
        action: 'terminate',
      }));
      terminateWorker(worker);
      worker = null;
    }
    return Promise.resolve();
  };

  onMessage(worker, ({
    workerId, jobId, status, action, data,
  }) => {
    if (status === 'resolve') {
      log(`[${workerId}]: Complete ${jobId}`);
      let d = data;
      if (action === 'recognize') {
        d = circularize(data);
      } else if (action === 'getPDF') {
        d = Array.from({ ...data, length: Object.keys(data).length });
      }
      resolves[action]({ jobId, data: d });
    } else if (status === 'reject') {
      rejects[action](data);
      if (errorHandler) {
        errorHandler(data);
      } else {
        throw Error(data);
      }
    } else if (status === 'progress') {
      logger(data);
    }
  });

  return {
    id,
    worker,
    setResolve,
    setReject,
    load,
    loadLanguage,
    initialize,
    setParameters,
    recognize,
    getPDF,
    detect,
    terminate,
  };
};
<|endoftext|>


<commit_before>/* istanbul ignore next */
exports.get = function get(deviceIdentifier) {
  switch (deviceIdentifier) {
    case Tinkerforge.BrickDC.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickDC.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickIMU.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickIMU.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickMaster.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickMaster.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickServo.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickServo.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickStepper.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickStepper.DEVICE_DISPLAY_NAME);
      // BRICKLET'S
    case Tinkerforge.BrickletAccelerometer.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAccelerometer.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletAmbientLight.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAmbientLight.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletAmbientLightV2.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAmbientLightV2.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletAnalogIn.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAnalogIn.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletAnalogInV2.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAnalogInV2.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletAnalogOut.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAnalogOut.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletAnalogOutV2.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletAnalogOutV2.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletBarometer.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletBarometer.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletColor.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletColor.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletCurrent12.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletCurrent12.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletCurrent25.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletCurrent25.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletDistanceIR.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletDistanceIR.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletDistanceUS.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletDistanceUS.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletDualButton.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletDualButton.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletDualRelay.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletDualRelay.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletDustDetector.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletDustDetector.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletGPS.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletGPS.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletHallEffect.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletHallEffect.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletHumidity.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletHumidity.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIndustrialAnalogOut.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIndustrialAnalogOut.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIndustrialDigitalIn4.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIndustrialDigitalIn4.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIndustrialDigitalOut4.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIndustrialDigitalOut4.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIndustrialDual020mA.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIndustrialDual020mA.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIndustrialQuadRelay.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIndustrialQuadRelay.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIO16.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIO16.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletIO4.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletIO4.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletJoystick.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletJoystick.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLaserRangeFinder.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLaserRangeFinder.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLCD16x2.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLCD16x2.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLCD20x4.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLCD20x4.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLEDStrip.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLEDStrip.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLine.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLine.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLinearPoti.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLinearPoti.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletLoadCell.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletLoadCell.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletMoisture.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletMoisture.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletMotionDetector.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletMotionDetector.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletMultiTouch.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletMultiTouch.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletNFCRFID.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletNFCRFID.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletPiezoBuzzer.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletPiezoBuzzer.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletPiezoSpeaker.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletPiezoSpeaker.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletPTC.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletPTC.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletRemoteSwitch.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletRemoteSwitch.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletRotaryEncoder.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletRotaryEncoder.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletRotaryPoti.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletRotaryPoti.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletRS232.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletRS232.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletSegmentDisplay4x7.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletSegmentDisplay4x7.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletSolidStateRelay.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletSolidStateRelay.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletSoundIntensity.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletSoundIntensity.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletTemperature.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletTemperature.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletTemperatureIR.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletTemperatureIR.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletTilt.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletTilt.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletVoltage.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletVoltage.DEVICE_DISPLAY_NAME);
    case Tinkerforge.BrickletVoltageCurrent.DEVICE_IDENTIFIER:
      return (Tinkerforge.BrickletVoltageCurrent.DEVICE_DISPLAY_NAME);
    default:
      return ('unknown');
  }
};
<commit_msg>removed ( )<commit_after>/* istanbul ignore next */
exports.get = function get(deviceIdentifier) {
  switch (deviceIdentifier) {
    case Tinkerforge.BrickDC.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickDC.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickIMU.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickIMU.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickMaster.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickMaster.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickServo.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickServo.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickStepper.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickStepper.DEVICE_DISPLAY_NAME;
      // BRICKLET'S
    case Tinkerforge.BrickletAccelerometer.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAccelerometer.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletAmbientLight.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAmbientLight.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletAmbientLightV2.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAmbientLightV2.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletAnalogIn.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAnalogIn.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletAnalogInV2.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAnalogInV2.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletAnalogOut.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAnalogOut.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletAnalogOutV2.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletAnalogOutV2.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletBarometer.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletBarometer.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletColor.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletColor.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletCurrent12.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletCurrent12.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletCurrent25.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletCurrent25.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletDistanceIR.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletDistanceIR.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletDistanceUS.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletDistanceUS.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletDualButton.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletDualButton.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletDualRelay.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletDualRelay.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletDustDetector.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletDustDetector.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletGPS.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletGPS.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletHallEffect.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletHallEffect.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletHumidity.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletHumidity.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIndustrialAnalogOut.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIndustrialAnalogOut.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIndustrialDigitalIn4.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIndustrialDigitalIn4.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIndustrialDigitalOut4.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIndustrialDigitalOut4.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIndustrialDual020mA.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIndustrialDual020mA.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIndustrialQuadRelay.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIndustrialQuadRelay.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIO16.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIO16.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletIO4.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletIO4.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletJoystick.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletJoystick.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLaserRangeFinder.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLaserRangeFinder.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLCD16x2.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLCD16x2.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLCD20x4.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLCD20x4.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLEDStrip.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLEDStrip.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLine.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLine.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLinearPoti.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLinearPoti.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletLoadCell.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletLoadCell.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletMoisture.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletMoisture.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletMotionDetector.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletMotionDetector.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletMultiTouch.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletMultiTouch.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletNFCRFID.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletNFCRFID.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletPiezoBuzzer.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletPiezoBuzzer.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletPiezoSpeaker.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletPiezoSpeaker.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletPTC.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletPTC.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletRemoteSwitch.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletRemoteSwitch.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletRotaryEncoder.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletRotaryEncoder.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletRotaryPoti.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletRotaryPoti.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletRS232.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletRS232.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletSegmentDisplay4x7.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletSegmentDisplay4x7.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletSolidStateRelay.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletSolidStateRelay.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletSoundIntensity.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletSoundIntensity.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletTemperature.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletTemperature.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletTemperatureIR.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletTemperatureIR.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletTilt.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletTilt.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletVoltage.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletVoltage.DEVICE_DISPLAY_NAME;
    case Tinkerforge.BrickletVoltageCurrent.DEVICE_IDENTIFIER:
      return Tinkerforge.BrickletVoltageCurrent.DEVICE_DISPLAY_NAME;
    default:
      return 'unknown';
  }
};
<|endoftext|>