[
  {
    "path": ".gitignore",
    "content": "bundle.js\nnode_modules\n*.swp\ndist\n.idea\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n  \"esversion\": 6\n}\n"
  },
  {
    "path": ".vimrc",
    "content": "set tabstop=2\nset shiftwidth=2\nset softtabstop=2\n\n"
  },
  {
    "path": "LICENSE.md",
    "content": "This software is released under the MIT license:\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# boids #\n\nA lightweight JavaScript implementation of [boids](http://en.wikipedia.org/wiki/Boids). \n\nThis is a fork of [hughsk/boids](https://github.com/hughsk/boids) in terms of UI, and basic build structure. However the flocking algorithm is rewritten ground up.\n\n[Check out the demo](http://anoopelias.github.io/boids)\n\n"
  },
  {
    "path": "benchmark.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Benchmark Boids</title>\n    </head>\n    <body>Please check console for benchmark</body>\n</html>\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html class=\"no-js\">\n    <head>\n        <meta charset=\"utf-8\">\n        <title>Boids</title>\n        <meta name=\"viewport\" content=\"width=device-width\">\n        <link href='http://fonts.googleapis.com/css?family=Roboto+Slab:100|Open+Sans:300' rel='stylesheet' type='text/css'>\n        <style type=\"text/css\">\n          html, body {\n            margin:0;\n            padding:0;\n            font-family:'Open Sans', Helvetica, Arial, sans-serif;\n            color:#543D5E;\n            color:rgba(84,61,94,0.75);\n            background-color:#FFF1EB;\n          }\n          #title {\n            color:#543D5E;\n            font-size:2.5em;\n            color:rgba(84,61,94,0.9);\n            font-family: 'Roboto Slab', 'Times New Roman', Times, serif;\n            margin:0; padding:0;\n          }\n          a {\n            color:rgba(84,61,94,0.9);\n            text-decoration:none;\n            border-bottom:1px dotted #543D5E;\n          }\n          #text {\n            position:absolute;\n            max-width:300px;\n            top:16px;\n            left:16px;\n            background-color:rgba(255,241,235,0.55);\n            box-shadow:0 0 15px 5px rgba(255,241,235,0.55);\n          }\n          .source {\n            margin-bottom:0.5em;\n          }\n          @media screen and (max-width: 640px) {\n            body {\n              font-size: 12px;\n            }\n          }\n        </style>\n    </head>\n    <body>\n\t<script>\n\t  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n\t  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n\t  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n\t  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');\n\n\t  ga('create', 'UA-61328150-1', 'auto');\n\t  ga('send', 'pageview');\n\n\t</script>\t\n      <article role=\"main\" id=\"text\">\n        <h1 id=\"title\">boids</h1>\n        <div id=\"description\">\n          <p>\n            This is a lightweight 2D JavaScript implementation of Craig Reynolds'\n            <a href=\"http://en.wikipedia.org/wiki/Boids\">Boids</a> algorithm.\n            It's a classic example of <em>emergence</em>\n            and a suprisingly simple way of mimicking\n            not only flocks, but any form of swarm or herd or crowd.\n          </p>\n          <p>\n            Right now you're watching <strong data-count>many</strong> boids being\n            simulated at <strong data-fps>60</strong> fps.\n          </p>\n          <p class=\"source\">\n            <a href=\"http://github.com/anoopelias/boids\">Source on GitHub</a>\n          </p>\n          <p class=\"source\">\n            Fork of <a href=\"http://hughsk.io/boids/\">hughsk/boids</a>\n          </p>\n        </div>\n      </article>\n    </body>\n</html>\n"
  },
  {
    "path": "js/benchmark.js",
    "content": "import Boids from './index.js';\n\nfunction benchmark(boids) {\n  function test(count, ticks) {\n    var b = boids({ boids: count }),\n      i = ticks,\n      start;\n\n    var warmup = 10000;\n    while (warmup--) b.tick();\n\n    start = +new Date();\n    while (i--) b.tick();\n\n    return ticks / ((new Date() - start) / 1000);\n  }\n\n  for (var i = 50; i <= 500; i += 50) {\n    console.log(i + \" boids: \" + ~~test(i, 5000) + \" ticks/sec\");\n  }\n}\n\nbenchmark(Boids);\n"
  },
  {
    "path": "js/boid.js",
    "content": "export default function Boid(position, speed) {\n  this.position = position;\n  this.speed = speed;\n}\n\nBoid.prototype.compare = function(that, isEven) {\n  return this.position.compare(that.position, isEven);\n};\n\nBoid.prototype.toString = function() {\n  return this.position.toString();\n};\n"
  },
  {
    "path": "js/bootstrap.js",
    "content": "/* jshint ignore:start */\n// A dependency graph that contains any wasm must all be imported\n// asynchronously. This `bootstrap.js` file does the single async import, so\n// that no one else needs to worry about it again.\nimport(\"./demo.js\")\n  .catch(e => console.error(\"Error importing `index.js`:\", e));\n/* jshint ignore:end */\n\n"
  },
  {
    "path": "js/browser-benchmark.js",
    "content": "import Boids from './index.js';\n\nfunction tickBenchmark() {\n  const boidNums = [150, 300, 450, 600, 750];\n\n  let numReports = [];\n  console.log(\"Running benchmark, please wait..\");\n  for (let n of boidNums) {\n    const boids = Boids({ boids: n });\n\n    for (let i = 0; i < 1000; i++) {\n      const startTime = performance.now();\n      boids.tick();\n      (numReports[i] = numReports[i] || []).push(performance.now() - startTime);\n    }\n  }\n\n  let report = boidNums.map(boidNum => boidNum + \" Boids\").join(\", \") + \"\\n\";\n  report += numReports.map(numReport => numReport.join(\", \")).join(\"\\n\");\n\n  console.log(report);\n}\n\ntickBenchmark();\n"
  },
  {
    "path": "js/demo.js",
    "content": "import fps from \"fps\";\nimport ticker from \"ticker\";\nimport debounce from \"debounce\";\nimport Boids from \"./index.js\";\nimport Vector from \"./vector.js\";\nimport Boid from \"./boid.js\";\n\nconst anchor = document.createElement(\"a\"),\n  canvas = document.createElement(\"canvas\"),\n  ctx = canvas.getContext(\"2d\"),\n  boids = Boids();\n\ncanvas.addEventListener(\"click\", function(e) {\n  let x = e.pageX,\n    y = e.pageY,\n    halfHeight = canvas.height / 2,\n    halfWidth = canvas.width / 2;\n  x = x - halfWidth;\n  y = y - halfHeight;\n  if (boids.boids.length < 500)\n    boids.boids.push(\n      new Boid(\n        new Vector(x, y),\n        new Vector(Math.random() * 6 - 3, Math.random() * 6 - 3)\n      )\n    );\n});\n\nwindow.onresize = debounce(function() {\n  canvas.width = window.innerWidth;\n  canvas.height = window.innerHeight;\n}, 100);\nwindow.onresize();\n\nanchor.setAttribute(\"href\", \"#\");\nanchor.appendChild(canvas);\ndocument.body.style.margin = \"0\";\ndocument.body.style.padding = \"0\";\ndocument.body.appendChild(anchor);\n\nticker(window, 60)\n  .on(\"tick\", function() {\n    frames.tick();\n    boids.tick();\n  })\n  .on(\"draw\", function() {\n    const boidData = boids.boids,\n      halfHeight = canvas.height / 2,\n      halfWidth = canvas.width / 2;\n\n    ctx.fillStyle = \"rgba(255,241,235,0.25)\"; // '#FFF1EB'\n    ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n    ctx.fillStyle = \"#543D5E\";\n    for (let i = 0, l = boidData.length, x, y; i < l; i += 1) {\n      x = boidData[i].position.x;\n      y = boidData[i].position.y;\n      // wrap around the screen\n      boidData[i].position.x =\n        x > halfWidth ? -halfWidth : -x > halfWidth ? halfWidth : x;\n      boidData[i].position.y =\n        y > halfHeight ? -halfHeight : -y > halfHeight ? halfHeight : y;\n      ctx.fillRect(x + halfWidth, y + halfHeight, 2, 2);\n    }\n  });\n\nconst frameText = document.querySelector(\"[data-fps]\");\nconst countText = document.querySelector(\"[data-count]\");\nconst frames = fps({ every: 10, decay: 0.04 }).on(\"data\", function(rate) {\n  for (let i = 0; i < 3; i += 1) {\n    if (rate <= 56 && boids.boids.length > 10) boids.boids.pop();\n    if (rate >= 60 && boids.boids.length < 300)\n      boids.boids.push(\n        new Boid(\n          new Vector(0, 0),\n          new Vector(Math.random() * 6 - 3, Math.random() * 6 - 3)\n        )\n      );\n  }\n  frameText.innerHTML = String(Math.round(rate));\n  countText.innerHTML = String(boids.boids.length);\n});\n"
  },
  {
    "path": "js/index.js",
    "content": "import Vector from \"./vector.js\";\nimport Boid from \"./boid.js\";\n\nexport default function Boids(opts) {\n  if (!(this instanceof Boids)) return new Boids(opts);\n\n  opts = opts || {};\n\n  this.speedLimit = opts.speedLimit || 1;\n  this.accelerationLimit = opts.accelerationLimit || 0.03;\n  this.separationDistance = opts.separationDistance || 30;\n  this.separationDistanceSq = Math.pow(this.separationDistance, 2);\n  this.alignmentDistance = opts.alignmentDistance || 60;\n  this.alignmentDistanceSq = Math.pow(this.alignmentDistance, 2);\n  this.cohesionDistance = opts.cohesionDistance || 60;\n  this.cohesionDistanceSq = Math.pow(this.cohesionDistance, 2);\n  /* jshint laxbreak: true */\n  this.separationForce = !isNaN(opts.separationForce)\n    ? opts.separationForce\n    : 2;\n  this.cohesionForce = !isNaN(opts.cohesionForce) ? opts.cohesionForce : 1;\n  this.alignmentForce = !isNaN(opts.alignmentForce) ? opts.alignmentForce : 1;\n  this.maxDistSq = Math.max(\n    this.separationDistanceSq,\n    this.cohesionDistanceSq,\n    this.alignmentDistanceSq\n  );\n\n  const boids = (this.boids = []);\n\n  for (\n    let i = 0, l = opts.boids === undefined ? 150 : opts.boids;\n    i < l;\n    i += 1\n  ) {\n    boids[i] = new Boid(\n      new Vector(Math.random() * 100 - 50, Math.random() * 100 - 50),\n      new Vector(0, 0)\n    );\n  }\n}\n\nBoids.prototype.findNeighbors = function(point) {\n  const neighbors = [];\n  for (let i = 0; i < this.boids.length; i++) {\n    const boid = this.boids[i];\n\n    if (point === boid.position) {\n      continue;\n    }\n\n    const distSq = boid.position.distSquared(point);\n    if (distSq < this.maxDistSq) {\n      neighbors.push({\n        neighbor: this.boids[i],\n        distSq: distSq\n      });\n    }\n  }\n\n  return neighbors;\n};\n\nBoids.prototype.calcCohesion = function(boid, neighbors) {\n  let total = new Vector(0, 0),\n    count = 0;\n\n  for (let i = 0; i < neighbors.length; i++) {\n    const target = neighbors[i].neighbor;\n    if (boid === target) continue;\n\n    const distSq = neighbors[i].distSq;\n    if (\n      distSq < this.cohesionDistanceSq &&\n      isInFrontOf(boid, target.position)\n    ) {\n      total = total.add(target.position);\n      count++;\n    }\n  }\n\n  if (count === 0) return new Vector(0, 0);\n\n  return total\n    .divideBy(count)\n    .subtract(boid.position)\n    .normalize()\n    .subtract(boid.speed)\n    .limit(this.accelerationLimit);\n};\n\nBoids.prototype.calcSeparation = function(boid, neighbors) {\n  let total = new Vector(0, 0),\n    count = 0;\n\n  for (let i = 0; i < neighbors.length; i++) {\n    const target = neighbors[i].neighbor;\n    if (boid === target) continue;\n\n    const distSq = neighbors[i].distSq;\n    if (distSq < this.separationDistanceSq) {\n      total = total.add(\n        target.position\n          .subtract(boid.position)\n          .normalize()\n          .divideBy(target.position.distance(boid.position))\n      );\n      count++;\n    }\n  }\n\n  if (count === 0) return new Vector(0, 0);\n\n  return total\n    .divideBy(count)\n    .normalize()\n    .add(boid.speed) // Adding speed instead of subtracting because separation is repulsive\n    .limit(this.accelerationLimit);\n};\n\nBoids.prototype.calcAlignment = function(boid, neighbors) {\n  let total = new Vector(0, 0),\n    count = 0;\n\n  for (let i = 0; i < neighbors.length; i++) {\n    const target = neighbors[i].neighbor;\n    if (boid === target) continue;\n\n    const distSq = neighbors[i].distSq;\n    if (\n      distSq < this.alignmentDistanceSq &&\n      isInFrontOf(boid, target.position)\n    ) {\n      total = total.add(target.speed);\n      count++;\n    }\n  }\n\n  if (count === 0) return new Vector(0, 0);\n\n  return total\n    .divideBy(count)\n    .normalize()\n    .subtract(boid.speed)\n    .limit(this.accelerationLimit);\n};\n\nBoids.prototype.tick = function() {\n  const accelerations = [];\n  for (let i = 0; i < this.boids.length; i++) {\n    let boid = this.boids[i];\n    let neighbors = this.findNeighbors(boid.position);\n\n    const acceleration = this.calcCohesion(boid, neighbors)\n      .multiplyBy(this.cohesionForce)\n      .add(this.calcAlignment(boid, neighbors).multiplyBy(this.alignmentForce))\n      .subtract(this.calcSeparation(boid, neighbors).multiplyBy(this.separationForce));\n\n    accelerations.push(acceleration);\n  }\n\n  for (let j = 0; j < this.boids.length; j++) {\n    const boid = this.boids[j];\n    boid.speed = boid.speed.add(accelerations[j]).limit(this.speedLimit);\n    boid.position = boid.position.add(boid.speed);\n  }\n};\n\nfunction isInFrontOf(boid, point) {\n  return (\n    boid.position.angle(boid.position.add(boid.speed), point) <= Math.PI / 3\n  );\n}\n"
  },
  {
    "path": "js/vector.js",
    "content": "function Vector(x, y) {\n  this.x = x;\n  this.y = y;\n}\n\nVector.prototype.add = function(v) {\n  return new Vector(this.x + v.x, this.y + v.y);\n};\n\nVector.prototype.distSquared = function(v) {\n  return Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2);\n};\n\nVector.prototype.distance = function(v) {\n  return Math.sqrt(this.distSquared(v));\n};\n\nVector.prototype.multiplyBy = function(s) {\n  return new Vector(this.x * s, this.y * s);\n};\n\nVector.prototype.neg = function(v) {\n  return new Vector(-this.x, -this.y);\n};\n\nVector.prototype.magnitude = function() {\n  return this.distance(new Vector(0, 0));\n};\n\nVector.prototype.normalize = function() {\n  const magnitude = this.magnitude();\n\n  if (magnitude === 0) return new Vector(0, 0);\n\n  return new Vector(this.x / magnitude, this.y / magnitude);\n};\n\nVector.prototype.subtract = function(v) {\n  return this.add(v.neg());\n};\n\nVector.prototype.divideBy = function(s) {\n  if (s === 0) return new Vector(0, 0);\n  return this.multiplyBy(1 / s);\n};\n\nVector.prototype.limit = function(s) {\n  if (this.magnitude() > s) return this.normalize().multiplyBy(s);\n\n  return this;\n};\n\nVector.prototype.angle = function(p1, p2) {\n  const v1 = this.subtract(p1).normalize(),\n    v2 = this.subtract(p2).normalize(),\n    // Rounding is because sometimes the value goes beyond 1.0\n    // due to floating point precision errors\n    cos = Math.round((v1.x * v2.x + v1.y * v2.y) * 10000) / 10000;\n\n  return Math.acos(cos);\n};\n\nVector.prototype.compare = function(that, y) {\n  return (\n    (y && (this.y - that.y || this.x - that.x)) ||\n    (this.x - that.x || this.y - that.y)\n  );\n};\n\nVector.prototype.toString = function() {\n  return \"{x:\" + this.x + \", y:\" + this.y + \"}\";\n};\n\nexport default Vector;\n"
  },
  {
    "path": "js/wasm-boids.js",
    "content": "import { Boids as WasmBoids } from '../wasm/pkg/wasm.js';\nimport { memory } from '../wasm/pkg/wasm_bg.wasm';\n\nlet wasmBoids = WasmBoids.new();\nlet boidsPtr = wasmBoids.get_boids();\nconst boids = new Float64Array(memory.buffer, boidsPtr, 4);\nconsole.log(boids);\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"boids\",\n  \"description\": \"A fast JavaScript implementation of the boids algorithm \",\n  \"version\": \"1.0.0\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.4.0\",\n    \"@babel/preset-env\": \"7.23.9\",\n    \"@babel/register\": \"^7.23.7\",\n    \"babel-loader\": \"^9.1.3\",\n    \"debounce\": \"1.2.0\",\n    \"fps\": \"0.0.3\",\n    \"html-webpack-plugin\": \"^5.6.0\",\n    \"inherits\": \"~2.0.3\",\n    \"jshint\": \"^2.13.6\",\n    \"mocha\": \"^10.3.0\",\n    \"prettier\": \"^1.9.2\",\n    \"ticker\": \"0.1.0\",\n    \"webpack\": \"^5.90.3\",\n    \"webpack-cli\": \"^5.1.4\",\n    \"webpack-dev-server\": \"^5.0.2\"\n  },\n  \"main\": \"index.html\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"prettier\": \"prettier --write js/** test/**\",\n    \"test\": \"jshint js/ test/ && mocha test\",\n    \"start\": \"webpack-dev-server --port 3000\",\n    \"build-wasm-nodejs\": \"cd wasm && wasm-pack build --target nodejs\",\n    \"build-wasm\": \"cd wasm && wasm-pack build\",\n    \"build\": \"webpack\",\n    \"benchmark\": \"node js/benchmark.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/anoopelias/boids.git\"\n  },\n  \"keywords\": [\n    \"boids\",\n    \"flocking\",\n    \"algorithm\",\n    \"emergence\"\n  ],\n  \"author\": \"Anoop Elias<anoopelias@gmail.com> (http:/anoopelias.github.io/)\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "test/indexTest.js",
    "content": "import assert from 'assert';\nimport Boids from '../js/index.js';\nimport Vector from '../js/vector.js';\nimport Boid from '../js/boid.js';\n\nfunction makeOptions(force) {\n  return {\n    speedLimit: 1,\n    accelerationLimit: 0.03,\n    separationDistance: 30,\n    alignmentDistance: 60,\n    cohesionDistance: 60,\n    separationForce: !isNaN(force && force[0]) ? force[0] : 2,\n    cohesionForce: !isNaN(force && force[1]) ? force[1] : 1,\n    alignmentForce: !isNaN(force && force[2]) ? force[2] : 2\n  };\n}\n\nfunction newBoid(posX, posY, velX, velY) {\n  return new Boid(new Vector(posX, posY), new Vector(velX, velY));\n}\n\ndescribe(\"Boid\", function() {\n  describe(\"Separation\", function() {\n    it(\"should slow down for a boid in front\", function() {\n      const boids = new Boids(makeOptions([1, 0, 0]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(10, 10, 0, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].position.x, 0.4788);\n      assertApprox(boids.boids[0].position.y, 0.4788);\n    });\n\n    it(\"should ignore far away boids\", function() {\n      const boids = new Boids(makeOptions([1, 0, 0]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(60, 60, 0, 0)];\n      boids.tick();\n      assert.equal(boids.boids[0].position.x, 0.5);\n      assert.equal(boids.boids[0].position.y, 0.5);\n    });\n\n    it(\"should speed up for boids behind\", function() {\n      const boids = new Boids(makeOptions([1, 0, 0]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(-10, -10, 0, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].position.x, 0.5212);\n      assertApprox(boids.boids[0].position.y, 0.5212);\n    });\n  });\n\n  describe(\"Cohesion\", function() {\n    it(\"should move towards the nearby boids\", function() {\n      const boids = new Boids(makeOptions([0, 1, 0]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(10, 10, 0, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].position.x, 0.5212);\n      assertApprox(boids.boids[0].position.y, 0.5212);\n    });\n    it(\"should ignore far away boids\", function() {\n      const boids = new Boids(makeOptions([0, 1, 0]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(60, 60, 1, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].position.x, 0.5);\n      assertApprox(boids.boids[0].position.y, 0.5);\n    });\n    it(\"should ignore boids behind\", function() {\n      const boids = new Boids(makeOptions([0, 1, 0]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(-10, -10, 1, 1)];\n      boids.tick();\n      assertApprox(boids.boids[0].position.x, 0.5);\n      assertApprox(boids.boids[0].position.y, 0.5);\n    });\n  });\n\n  describe(\"Alignment\", function() {\n    it(\"should align towards nearby boid\", function() {\n      const boids = new Boids(makeOptions([0, 0, 1]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(10, 10, 0.5, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].speed.x, 0.5212);\n      assertApprox(boids.boids[0].speed.y, 0.4788);\n    });\n    it(\"should avoid far away boids\", function() {\n      const boids = new Boids(makeOptions([0, 0, 1]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(60, 60, 0, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].speed.x, 0.5);\n      assertApprox(boids.boids[0].speed.y, 0.5);\n    });\n    it(\"should avoid boids behind\", function() {\n      const boids = new Boids(makeOptions([0, 0, 1]));\n      boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(-10, -10, 0.5, 0)];\n      boids.tick();\n      assertApprox(boids.boids[0].speed.x, 0.5);\n      assertApprox(boids.boids[0].speed.y, 0.5);\n    });\n  });\n\n  it(\"should tick\", function() {\n    const boids = new Boids(makeOptions());\n    const boid1 = newBoid(0, 0, 0.5, 0.5),\n      boid2 = newBoid(10, 10, 0, 0),\n      boid3 = newBoid(60, 60, 0, 0),\n      boid4 = newBoid(-10, -10, 0, 0);\n    boids.boids = [boid1, boid2, boid3, boid4];\n\n    boids.tick();\n    assertBoid(boid1, [0.4364, 0.4364, 0.4364, 0.4364]);\n    assertBoid(boid2, [10.0424, 10.0424, 0.0424, 0.0424]);\n    assertBoid(boid3, [60, 60, 0, 0]);\n    assertBoid(boid4, [-10.0424, -10.0424, -0.0424, -0.0424]);\n\n    boids.tick();\n    assertBoid(boid1, [0.8939, 0.8939, 0.4576, 0.4576]);\n    assertBoid(boid2, [10.1273, 10.1273, 0.0849, 0.0849]);\n    assertBoid(boid3, [60, 60, 0, 0]);\n    assertBoid(boid4, [-10.1273, -10.1273, -0.0849, -0.0849]);\n\n    boids.tick();\n    assertBoid(boid1, [1.3727, 1.3727, 0.4788, 0.4788]);\n    assertBoid(boid2, [10.2546, 10.2546, 0.1273, 0.1273]);\n    assertBoid(boid3, [60, 60, 0, 0]);\n    assertBoid(boid4, [-10.2546, -10.2546, -0.1273, -0.1273]);\n\n    boids.tick();\n    assertBoid(boid1, [1.8727, 1.8727, 0.5, 0.5]);\n    assertBoid(boid2, [10.4243, 10.4243, 0.1697, 0.1697]);\n    assertBoid(boid3, [60, 60, 0, 0]);\n    assertBoid(boid4, [-10.4243, -10.4243, -0.1697, -0.1697]);\n  });\n\n  function assertBoid(boid, val) {\n    assertApprox(boid.position.x, val[0]);\n    assertApprox(boid.position.y, val[1]);\n\n    assertApprox(boid.speed.x, val[2]);\n    assertApprox(boid.speed.y, val[3]);\n  }\n\n  function assertApprox(val, val2) {\n    assert.equal(val.toFixed(4), val2.toFixed(4));\n  }\n});\n"
  },
  {
    "path": "test/vectorTest.js",
    "content": "import assert from 'assert';\nimport Vector from '../js/vector.js';\ndescribe(\"Vector\", function() {\n  it(\"should compare on x\", function() {\n    let c = new Vector(5, 19).compare(new Vector(10, 16), false);\n    assert(c < 0);\n\n    c = new Vector(5, 15).compare(new Vector(1, 25), false);\n    assert(c > 0);\n  });\n\n  it(\"should compare on y if x is equal\", function() {\n    let c = new Vector(5, 15).compare(new Vector(5, 25), false);\n    assert(c < 0);\n\n    c = new Vector(5, 45).compare(new Vector(5, 25), false);\n    assert(c > 0);\n  });\n\n  it(\"should compare on x by default\", function() {\n    let c = new Vector(5, 19).compare(new Vector(10, 16));\n    assert(c < 0);\n\n    c = new Vector(5, 15).compare(new Vector(1, 25));\n    assert(c > 0);\n  });\n\n  it(\"should compare on y\", function() {\n    let c = new Vector(5, 19).compare(new Vector(10, 16), true);\n    assert(c > 0);\n\n    c = new Vector(5, 15).compare(new Vector(1, 25), true);\n    assert(c < 0);\n  });\n\n  it(\"should compare on x if y is equal\", function() {\n    let c = new Vector(5, 19).compare(new Vector(10, 19), true);\n    assert(c < 0);\n\n    c = new Vector(5, 15).compare(new Vector(1, 15), true);\n    assert(c > 0);\n  });\n\n  it(\"should return zero if both x and y are equal\", function() {\n    let c = new Vector(5, 19).compare(new Vector(5, 19), true);\n    assert(c === 0);\n\n    c = new Vector(5, 45).compare(new Vector(5, 45), false);\n    assert(c === 0);\n  });\n});\n"
  },
  {
    "path": "wasm/.gitignore",
    "content": "/target\n**/*.rs.bk\nCargo.lock\nbin/\npkg/\nwasm-pack.log\n"
  },
  {
    "path": "wasm/Cargo.toml",
    "content": "[package]\nname = \"wasm\"\nversion = \"0.1.0\"\nauthors = [\"Anoop Elias <anoopelias@gmail.com>\"]\nedition = \"2018\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[features]\ndefault = [\"console_error_panic_hook\"]\n\n[dependencies]\ncfg-if = \"0.1.2\"\nwasm-bindgen = \"0.2.84\"\n\n# The `console_error_panic_hook` crate provides better debugging of panics by\n# logging them with `console.error`. This is great for development, but requires\n# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for\n# code size when deploying.\nconsole_error_panic_hook = { version = \"0.1.7\", optional = true }\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.34\"\n\n[dependencies.web-sys]\nversion = \"0.3\"\nfeatures = [\n  \"console\",\n]\n\n[profile.release]\n# Tell `rustc` to optimize for small code size.\nopt-level = \"s\"\n"
  },
  {
    "path": "wasm/src/lib.rs",
    "content": "extern crate wasm_bindgen;\nextern crate web_sys;\n\nmod utils;\n\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    #[wasm_bindgen(js_namespace = console)]\n    fn log(s: &str);\n}\n\npub struct Boid {\n    pos_x: f64,\n    pos_y: f64,\n    vel_x: f64,\n    vel_y: f64,\n}\n\n#[wasm_bindgen]\npub struct Boids {\n    boids: Vec<Boid>,\n}\n\nimpl Boid {\n    fn new() -> Boid {\n        Boid {\n            pos_x: 20.5,\n            pos_y: 40.2,\n            vel_x: 50.9,\n            vel_y: 51.9,\n        }\n    }\n}\n\n#[wasm_bindgen]\nimpl Boids {\n    pub fn new() -> Boids {\n        let boids = vec![Boid::new(), Boid::new()];\n        Boids { boids }\n    }\n    pub fn get_boids(&self) -> *const Boid {\n        self.boids.as_ptr()\n    }\n}\n"
  },
  {
    "path": "wasm/src/utils.rs",
    "content": "pub fn set_panic_hook() {\n    // When the `console_error_panic_hook` feature is enabled, we can call the\n    // `set_panic_hook` function at least once during initialization, and then\n    // we will get better error messages if our code ever panics.\n    //\n    // For more details see\n    // https://github.com/rustwasm/console_error_panic_hook#readme\n    #[cfg(feature = \"console_error_panic_hook\")]\n    console_error_panic_hook::set_once();\n}\n"
  },
  {
    "path": "wasm/tests/web.rs",
    "content": "//! Test suite for the Web and headless browsers.\n\n#![cfg(target_arch = \"wasm32\")]\n\nextern crate wasm_bindgen_test;\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nfn pass() {\n    assert_eq!(1 + 1, 2);\n}\n"
  },
  {
    "path": "webpack.config.babel.js",
    "content": "import path from 'path';\nimport { fileURLToPath } from 'url'; // Import fileURLToPath\nimport HtmlWebpackPlugin from 'html-webpack-plugin';\n\n// Derive __dirname using import.meta.url\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport default {\n  entry: {\n    bundle: './js/bootstrap.js',\n    browserBenchmark: './js/browser-benchmark.js',\n  },\n  experiments: {\n    asyncWebAssembly: true,\n  },\n  mode: 'development',\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        // Use the derived __dirname\n        include: path.resolve(__dirname, 'js'), \n        use: {\n          loader: 'babel-loader',\n          options: {\n            presets: [\n              ['@babel/preset-env', { targets: \"defaults\" }]\n            ]\n          }\n        }\n      }\n    ]\n  },\n  output: {\n    clean: true,\n  },\n  plugins: [\n    new HtmlWebpackPlugin({\n        template: 'index.html',\n        favicon: 'favicon.ico',\n        chunks: ['bundle'],\n    }),\n    new HtmlWebpackPlugin({\n        filename: 'benchmark.html',\n        template: 'benchmark.html',\n        chunks: ['browserBenchmark'],\n    }),\n  ]\n};\n"
  }
]