block by gabrielflorit c3ce2d1e8596104a0aabeaac16f5bc0b

Racing circuit curvatures

Full Screen

Made with blockup

This shows curvature vectors along the Melbourne, Australia racing track. The longer the red lines, the steeper the curve.

See my previous work:

script.js

import drawTrack from './drawTrack.js'

// Draw the track.
drawTrack({
  container: d3.select('.circuit'),
  track: circuitTracks[0]
})

index.html

<!DOCTYPE html>
<title>blockup</title>
<link href='https://fonts.googleapis.com/css?family=VT323' rel='stylesheet'>
<link href='dist.css' rel='stylesheet' />
<body>
  <h1><span>curvature</span> of the Australian Grand Prix racing circuit</h1>
  <div class='circuit'></div>
  <button></button>
  <script src='sylvester.js'></script>
	<script src='d3.v4.min.js'></script>
  <script src='circuitTracks.js'></script>
	<script src='dist.js'></script>
</body>

dist.css

*{box-sizing:border-box}html{background:rgba(1,11,20,.5);padding:0;margin:0}body{background:#010b14;font-family:VT323,monospace;overflow:hidden;margin:0 auto;padding:0;position:relative;width:960px;height:500px}svg{display:block}h1{color:#dacbed;text-shadow:0 0 14px rgba(218,203,237,.75);text-transform:uppercase;font-weight:400;font-style:italic;position:absolute;text-align:center;padding:0;margin:0;width:100%;font-size:2.5em}h1 span{color:#f94533}button{display:block;margin:0 auto;font-family:VT323,monospace;border:solid #dacbed 1px;background:#010b14;color:#dacbed;font-size:2em;text-transform:uppercase;position:relative;top:-1em;cursor:pointer}.circuit{width:50%;position:relative;margin:0 auto;padding:0}.circuit svg{margin:0 auto;-webkit-filter:drop-shadow(0 0 14px #f94533);filter:drop-shadow(0 0 14px #f94533)}.circuit svg path.track{fill:none;stroke:#0fd0fe;opacity:.2}.circuit svg circle.p{fill:#dacbed}.circuit svg circle.q{fill:#f94533}.circuit svg line{stroke:#f94533}.circuit svg text{fill:#f94533}

dist.js

!function(n){function g(I){if(t[I])return t[I].exports;var C=t[I]={i:I,l:!1,exports:{}};return n[I].call(C.exports,C,C.exports,g),C.l=!0,C.exports}var t={};g.m=n,g.c=t,g.i=function(n){return n},g.d=function(n,t,I){g.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:I})},g.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return g.d(t,"a",t),t},g.o=function(n,g){return Object.prototype.hasOwnProperty.call(n,g)},g.p="",g(g.s=1)}([function(module,exports,__webpack_require__){"use strict";eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nvar drawTrack = function drawTrack(_ref) {\n  var container = _ref.container,\n      track = _ref.track;\n\n  var margin = 50;\n  var dimension = container.node().offsetWidth - margin * 2;\n\n  var xExtent = d3.extent(track, function (d) {\n    return d.x;\n  });\n  var x = d3.scaleLinear().domain(xExtent);\n\n  var yExtent = d3.extent(track, function (d) {\n    return d.y;\n  });\n  var y = d3.scaleLinear().domain(yExtent);\n\n  var aspect = (xExtent[1] - xExtent[0]) / (yExtent[1] - yExtent[0]);\n\n  var width = Math.min(dimension * aspect, dimension);\n  var height = Math.min(dimension / aspect, dimension);\n\n  x.range([0, width]);\n  y.range([0, height]);\n\n  var svg = container.append('svg').attr('width', width + 2 * margin).attr('height', height + 2 * margin);\n\n  var g = svg.append('g').attr('transform', 'translate(' + margin + ', ' + margin + ')');\n\n  var line = d3.line().curve(d3.curveBasis).x(function (d) {\n    return x(d.x);\n  }).y(function (d) {\n    return y(d.y);\n  });\n\n  var path = g.append('path').attr('class', 'track').datum(track).attr('d', line).node();\n\n  var totalLength = path.getTotalLength();\n\n  var getCoords = function getCoords(pct) {\n    var length = totalLength * pct;\n    var p = path.getPointAtLength(length % totalLength);\n    return [p.x, p.y];\n  };\n\n  var centroid = d3.polygonCentroid(track.map(function (d) {\n    return [d.x, d.y];\n  }));\n\n  var steps = 1000;\n  var delta = 1 / steps;\n  var positions = d3.range(0, 1 + delta, delta).map(getCoords).slice(1).map(function (p, i, a) {\n    // Find the vector to the previous point.\n    var o = a[(i + a.length - 1) % a.length];\n    var backwards = $V([o[0] - p[0], o[1] - p[1]]);\n\n    // // Find the vector to the next point.\n    var q = a[(i + 1) % a.length];\n    var forwards = $V([q[0] - p[0], q[1] - p[1]]);\n\n    // Find the angle between the two vectors.\n    var angleBetweenVectors = backwards.angleFrom(forwards) * 180 / Math.PI;\n\n    // Find the normal vector and scale it based on the angle\n    // between the two vectors.\n    var normal = forwards.rotate(Math.PI / 2, $V([0, 0])).toUnitVector().multiply(180 - angleBetweenVectors);\n\n    // Determine concavity.\n    var isConcave = backwards.angleFrom(normal) * 180 / Math.PI >= 90;\n\n    // Create curvature vector.\n    var curvature = normal.rotate(isConcave ? 0 : Math.PI, $V([0, 0])).multiply(3);\n\n    // Create position on circle.\n    var startingOffset = 0.7;\n    var radius = dimension / 3;\n    var origin = [x(centroid[0]), y(centroid[1])];\n    var circleAngle = 2 * Math.PI * i / (a.length - 1) + Math.PI * startingOffset;\n    var c = [Math.cos(circleAngle), Math.sin(circleAngle)];\n\n    return {\n      p: p,\n      c: c,\n      origin: origin,\n      radius: radius,\n      curvature: curvature,\n      isConcave: isConcave,\n      modulus: curvature.modulus()\n    };\n  });\n\n  // Draw the normal to each point.\n  var lines = g.selectAll('line.normal').data(positions).enter().append('line').attr('class', 'normal').attr('x1', 0).attr('y1', 0).attr('transform', function (d) {\n    return 'translate(' + (d.c[0] * d.radius + d.origin[0]) + ', ' + (d.c[1] * d.radius + d.origin[1]) + ')';\n  }).attr('x2', function (d) {\n    return d.c[0] * d.curvature.modulus() * (d.isConcave ? -1 : 1);\n  }).attr('y2', function (d) {\n    return d.c[1] * d.curvature.modulus() * (d.isConcave ? -1 : 1);\n  });\n\n  var button = d3.select('button');\n\n  var showCurvature = true;\n  // copy paste here, not ashamed\n  // but maybe i am otherwise i wouldn't leave this note\n  var magic = function magic() {\n    var duration = 2000;\n    var delay = 1;\n    if (showCurvature) {\n      lines.transition().duration(duration).delay(function (d, i) {\n        return i * delay;\n      }).attr('transform', function (d) {\n        return 'translate(' + d.p + ')';\n      }).attr('x2', function (d) {\n        return d.curvature.elements[0];\n      }).attr('y2', function (d) {\n        return d.curvature.elements[1];\n      });\n      button.text('morph circuit to circle');\n    } else {\n      lines.transition().duration(duration).delay(function (d, i) {\n        return i * delay;\n      }).attr('transform', function (d) {\n        return 'translate(' + (d.c[0] * d.radius + d.origin[0]) + ', ' + (d.c[1] * d.radius + d.origin[1]) + ')';\n      }).attr('x2', function (d) {\n        return d.c[0] * d.curvature.modulus() * (d.isConcave ? -1 : 1);\n      }).attr('y2', function (d) {\n        return d.c[1] * d.curvature.modulus() * (d.isConcave ? -1 : 1);\n      });\n      button.text('morph circle to circuit');\n    }\n    showCurvature = !showCurvature;\n  };\n  magic();\n\n  button.on('click', magic);\n};\n\nexports.default = drawTrack;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9kcmF3VHJhY2suanM/MTY1OSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBkcmF3VHJhY2sgPSAoeyBjb250YWluZXIsIHRyYWNrIH0pID0+IHtcbiAgY29uc3QgbWFyZ2luID0gNTBcbiAgY29uc3QgZGltZW5zaW9uID0gY29udGFpbmVyLm5vZGUoKS5vZmZzZXRXaWR0aCAtIG1hcmdpbiAqIDJcblxuICBjb25zdCB4RXh0ZW50ID0gZDMuZXh0ZW50KHRyYWNrLCBkID0+IGQueClcbiAgY29uc3QgeCA9IGQzLnNjYWxlTGluZWFyKCkuZG9tYWluKHhFeHRlbnQpXG5cbiAgY29uc3QgeUV4dGVudCA9IGQzLmV4dGVudCh0cmFjaywgZCA9PiBkLnkpXG4gIGNvbnN0IHkgPSBkMy5zY2FsZUxpbmVhcigpLmRvbWFpbih5RXh0ZW50KVxuXG4gIGNvbnN0IGFzcGVjdCA9ICh4RXh0ZW50WzFdIC0geEV4dGVudFswXSkgLyAoeUV4dGVudFsxXSAtIHlFeHRlbnRbMF0pXG5cbiAgY29uc3Qgd2lkdGggPSBNYXRoLm1pbihkaW1lbnNpb24gKiBhc3BlY3QsIGRpbWVuc2lvbilcbiAgY29uc3QgaGVpZ2h0ID0gTWF0aC5taW4oZGltZW5zaW9uIC8gYXNwZWN0LCBkaW1lbnNpb24pXG5cbiAgeC5yYW5nZShbMCwgd2lkdGhdKVxuICB5LnJhbmdlKFswLCBoZWlnaHRdKVxuXG4gIGNvbnN0IHN2ZyA9IGNvbnRhaW5lclxuICAgIC5hcHBlbmQoJ3N2ZycpXG4gICAgLmF0dHIoJ3dpZHRoJywgd2lkdGggKyAyICogbWFyZ2luKVxuICAgIC5hdHRyKCdoZWlnaHQnLCBoZWlnaHQgKyAyICogbWFyZ2luKVxuXG4gIGNvbnN0IGcgPSBzdmcuYXBwZW5kKCdnJykuYXR0cigndHJhbnNmb3JtJywgYHRyYW5zbGF0ZSgke21hcmdpbn0sICR7bWFyZ2lufSlgKVxuXG4gIGNvbnN0IGxpbmUgPSBkM1xuICAgIC5saW5lKClcbiAgICAuY3VydmUoZDMuY3VydmVCYXNpcylcbiAgICAueChkID0+IHgoZC54KSlcbiAgICAueShkID0+IHkoZC55KSlcblxuICBjb25zdCBwYXRoID0gZ1xuICAgIC5hcHBlbmQoJ3BhdGgnKVxuICAgIC5hdHRyKCdjbGFzcycsICd0cmFjaycpXG4gICAgLmRhdHVtKHRyYWNrKVxuICAgIC5hdHRyKCdkJywgbGluZSlcbiAgICAubm9kZSgpXG5cbiAgY29uc3QgdG90YWxMZW5ndGggPSBwYXRoLmdldFRvdGFsTGVuZ3RoKClcblxuICBjb25zdCBnZXRDb29yZHMgPSBwY3QgPT4ge1xuICAgIGNvbnN0IGxlbmd0aCA9IHRvdGFsTGVuZ3RoICogcGN0XG4gICAgY29uc3QgcCA9IHBhdGguZ2V0UG9pbnRBdExlbmd0aChsZW5ndGggJSB0b3RhbExlbmd0aClcbiAgICByZXR1cm4gW3AueCwgcC55XVxuICB9XG5cbiAgY29uc3QgY2VudHJvaWQgPSBkMy5wb2x5Z29uQ2VudHJvaWQodHJhY2subWFwKGQgPT4gW2QueCwgZC55XSkpXG5cbiAgY29uc3Qgc3RlcHMgPSAxMDAwXG4gIGNvbnN0IGRlbHRhID0gMSAvIHN0ZXBzXG4gIGNvbnN0IHBvc2l0aW9ucyA9IGQzXG4gICAgLnJhbmdlKDAsIDEgKyBkZWx0YSwgZGVsdGEpXG4gICAgLm1hcChnZXRDb29yZHMpXG4gICAgLnNsaWNlKDEpXG4gICAgLm1hcCgocCwgaSwgYSkgPT4ge1xuICAgICAgLy8gRmluZCB0aGUgdmVjdG9yIHRvIHRoZSBwcmV2aW91cyBwb2ludC5cbiAgICAgIGNvbnN0IG8gPSBhWyhpICsgYS5sZW5ndGggLSAxKSAlIGEubGVuZ3RoXVxuICAgICAgY29uc3QgYmFja3dhcmRzID0gJFYoW29bMF0gLSBwWzBdLCBvWzFdIC0gcFsxXV0pXG5cbiAgICAgIC8vIC8vIEZpbmQgdGhlIHZlY3RvciB0byB0aGUgbmV4dCBwb2ludC5cbiAgICAgIGNvbnN0IHEgPSBhWyhpICsgMSkgJSBhLmxlbmd0aF1cbiAgICAgIGNvbnN0IGZvcndhcmRzID0gJFYoW3FbMF0gLSBwWzBdLCBxWzFdIC0gcFsxXV0pXG5cbiAgICAgIC8vIEZpbmQgdGhlIGFuZ2xlIGJldHdlZW4gdGhlIHR3byB2ZWN0b3JzLlxuICAgICAgY29uc3QgYW5nbGVCZXR3ZWVuVmVjdG9ycyA9IGJhY2t3YXJkcy5hbmdsZUZyb20oZm9yd2FyZHMpICogMTgwIC8gTWF0aC5QSVxuXG4gICAgICAvLyBGaW5kIHRoZSBub3JtYWwgdmVjdG9yIGFuZCBzY2FsZSBpdCBiYXNlZCBvbiB0aGUgYW5nbGVcbiAgICAgIC8vIGJldHdlZW4gdGhlIHR3byB2ZWN0b3JzLlxuICAgICAgY29uc3Qgbm9ybWFsID0gZm9yd2FyZHNcbiAgICAgICAgLnJvdGF0ZShNYXRoLlBJIC8gMiwgJFYoWzAsIDBdKSlcbiAgICAgICAgLnRvVW5pdFZlY3RvcigpXG4gICAgICAgIC5tdWx0aXBseSgxODAgLSBhbmdsZUJldHdlZW5WZWN0b3JzKVxuXG4gICAgICAvLyBEZXRlcm1pbmUgY29uY2F2aXR5LlxuICAgICAgY29uc3QgaXNDb25jYXZlID0gYmFja3dhcmRzLmFuZ2xlRnJvbShub3JtYWwpICogMTgwIC8gTWF0aC5QSSA+PSA5MFxuXG4gICAgICAvLyBDcmVhdGUgY3VydmF0dXJlIHZlY3Rvci5cbiAgICAgIGNvbnN0IGN1cnZhdHVyZSA9IG5vcm1hbFxuICAgICAgICAucm90YXRlKGlzQ29uY2F2ZSA/IDAgOiBNYXRoLlBJLCAkVihbMCwgMF0pKVxuICAgICAgICAubXVsdGlwbHkoMylcblxuICAgICAgLy8gQ3JlYXRlIHBvc2l0aW9uIG9uIGNpcmNsZS5cbiAgICAgIGNvbnN0IHN0YXJ0aW5nT2Zmc2V0ID0gMC43XG4gICAgICBjb25zdCByYWRpdXMgPSBkaW1lbnNpb24gLyAzXG4gICAgICBjb25zdCBvcmlnaW4gPSBbeChjZW50cm9pZFswXSksIHkoY2VudHJvaWRbMV0pXVxuICAgICAgY29uc3QgY2lyY2xlQW5nbGUgPVxuICAgICAgICAyICogTWF0aC5QSSAqIGkgLyAoYS5sZW5ndGggLSAxKSArIE1hdGguUEkgKiBzdGFydGluZ09mZnNldFxuICAgICAgY29uc3QgYyA9IFtNYXRoLmNvcyhjaXJjbGVBbmdsZSksIE1hdGguc2luKGNpcmNsZUFuZ2xlKV1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcCxcbiAgICAgICAgYyxcbiAgICAgICAgb3JpZ2luLFxuICAgICAgICByYWRpdXMsXG4gICAgICAgIGN1cnZhdHVyZSxcbiAgICAgICAgaXNDb25jYXZlLFxuICAgICAgICBtb2R1bHVzOiBjdXJ2YXR1cmUubW9kdWx1cygpXG4gICAgICB9XG4gICAgfSlcblxuICAvLyBEcmF3IHRoZSBub3JtYWwgdG8gZWFjaCBwb2ludC5cbiAgY29uc3QgbGluZXMgPSBnXG4gICAgLnNlbGVjdEFsbCgnbGluZS5ub3JtYWwnKVxuICAgIC5kYXRhKHBvc2l0aW9ucylcbiAgICAuZW50ZXIoKVxuICAgIC5hcHBlbmQoJ2xpbmUnKVxuICAgIC5hdHRyKCdjbGFzcycsICdub3JtYWwnKVxuICAgIC5hdHRyKCd4MScsIDApXG4gICAgLmF0dHIoJ3kxJywgMClcbiAgICAuYXR0cihcbiAgICAgICd0cmFuc2Zvcm0nLFxuICAgICAgZCA9PlxuICAgICAgICBgdHJhbnNsYXRlKCR7ZC5jWzBdICogZC5yYWRpdXMgKyBkLm9yaWdpblswXX0sICR7ZC5jWzFdICogZC5yYWRpdXMgK1xuICAgICAgICAgIGQub3JpZ2luWzFdfSlgXG4gICAgKVxuICAgIC5hdHRyKCd4MicsIGQgPT4gZC5jWzBdICogZC5jdXJ2YXR1cmUubW9kdWx1cygpICogKGQuaXNDb25jYXZlID8gLTEgOiAxKSlcbiAgICAuYXR0cigneTInLCBkID0+IGQuY1sxXSAqIGQuY3VydmF0dXJlLm1vZHVsdXMoKSAqIChkLmlzQ29uY2F2ZSA/IC0xIDogMSkpXG5cbiAgY29uc3QgYnV0dG9uID0gZDMuc2VsZWN0KCdidXR0b24nKVxuXG4gIGxldCBzaG93Q3VydmF0dXJlID0gdHJ1ZVxuICAvLyBjb3B5IHBhc3RlIGhlcmUsIG5vdCBhc2hhbWVkXG4gIC8vIGJ1dCBtYXliZSBpIGFtIG90aGVyd2lzZSBpIHdvdWxkbid0IGxlYXZlIHRoaXMgbm90ZVxuICBjb25zdCBtYWdpYyA9ICgpID0+IHtcbiAgICBjb25zdCBkdXJhdGlvbiA9IDIwMDBcbiAgICBjb25zdCBkZWxheSA9IDFcbiAgICBpZiAoc2hvd0N1cnZhdHVyZSkge1xuICAgICAgbGluZXNcbiAgICAgICAgLnRyYW5zaXRpb24oKVxuICAgICAgICAuZHVyYXRpb24oZHVyYXRpb24pXG4gICAgICAgIC5kZWxheSgoZCwgaSkgPT4gaSAqIGRlbGF5KVxuICAgICAgICAuYXR0cigndHJhbnNmb3JtJywgZCA9PiBgdHJhbnNsYXRlKCR7ZC5wfSlgKVxuICAgICAgICAuYXR0cigneDInLCBkID0+IGQuY3VydmF0dXJlLmVsZW1lbnRzWzBdKVxuICAgICAgICAuYXR0cigneTInLCBkID0+IGQuY3VydmF0dXJlLmVsZW1lbnRzWzFdKVxuICAgICAgYnV0dG9uLnRleHQoJ21vcnBoIGNpcmN1aXQgdG8gY2lyY2xlJylcbiAgICB9IGVsc2Uge1xuICAgICAgbGluZXNcbiAgICAgICAgLnRyYW5zaXRpb24oKVxuICAgICAgICAuZHVyYXRpb24oZHVyYXRpb24pXG4gICAgICAgIC5kZWxheSgoZCwgaSkgPT4gaSAqIGRlbGF5KVxuICAgICAgICAuYXR0cihcbiAgICAgICAgICAndHJhbnNmb3JtJyxcbiAgICAgICAgICBkID0+XG4gICAgICAgICAgICBgdHJhbnNsYXRlKCR7ZC5jWzBdICogZC5yYWRpdXMgKyBkLm9yaWdpblswXX0sICR7ZC5jWzFdICogZC5yYWRpdXMgK1xuICAgICAgICAgICAgICBkLm9yaWdpblsxXX0pYFxuICAgICAgICApXG4gICAgICAgIC5hdHRyKFxuICAgICAgICAgICd4MicsXG4gICAgICAgICAgZCA9PiBkLmNbMF0gKiBkLmN1cnZhdHVyZS5tb2R1bHVzKCkgKiAoZC5pc0NvbmNhdmUgPyAtMSA6IDEpXG4gICAgICAgIClcbiAgICAgICAgLmF0dHIoXG4gICAgICAgICAgJ3kyJyxcbiAgICAgICAgICBkID0+IGQuY1sxXSAqIGQuY3VydmF0dXJlLm1vZHVsdXMoKSAqIChkLmlzQ29uY2F2ZSA/IC0xIDogMSlcbiAgICAgICAgKVxuICAgICAgYnV0dG9uLnRleHQoJ21vcnBoIGNpcmNsZSB0byBjaXJjdWl0JylcbiAgICB9XG4gICAgc2hvd0N1cnZhdHVyZSA9ICFzaG93Q3VydmF0dXJlXG4gIH1cbiAgbWFnaWMoKVxuXG4gIGJ1dHRvbi5vbignY2xpY2snLCBtYWdpYylcbn1cblxuZXhwb3J0IGRlZmF1bHQgZHJhd1RyYWNrXG5cblxuXG4vLyBXRUJQQUNLIEZPT1RFUiAvL1xuLy8gZHJhd1RyYWNrLmpzIl0sIm1hcHBpbmdzIjoiOzs7OztBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUlBO0FBQ0E7QUFDQTtBQUdBO0FBQUE7QUFDQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBUEE7QUFTQTtBQUNBO0FBQ0E7QUFDQTtBQVVBO0FBQUE7QUFJQTtBQUFBO0FBQ0E7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFHQTtBQUFBO0FBQ0E7QUFBQTtBQUNBO0FBQUE7QUFDQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBR0E7QUFBQTtBQUdBO0FBQUE7QUFNQTtBQUFBO0FBSUE7QUFBQTtBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///0\n")},function(module,exports,__webpack_require__){"use strict";eval("\n\nvar _drawTrack = __webpack_require__(0);\n\nvar _drawTrack2 = _interopRequireDefault(_drawTrack);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n// Draw the track.\n(0, _drawTrack2.default)({\n  container: d3.select('.circuit'),\n  track: circuitTracks[0]\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9zY3JpcHQuanM/OWE5NSJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZHJhd1RyYWNrIGZyb20gJy4vZHJhd1RyYWNrLmpzJ1xuXG4vLyBEcmF3IHRoZSB0cmFjay5cbmRyYXdUcmFjayh7XG4gIGNvbnRhaW5lcjogZDMuc2VsZWN0KCcuY2lyY3VpdCcpLFxuICB0cmFjazogY2lyY3VpdFRyYWNrc1swXVxufSlcblxuXG5cbi8vIFdFQlBBQ0sgRk9PVEVSIC8vXG4vLyBzY3JpcHQuanMiXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTs7Ozs7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUZBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///1\n")}]);

drawTrack.js

const drawTrack = ({ container, track }) => {
  const margin = 50
  const dimension = container.node().offsetWidth - margin * 2

  const xExtent = d3.extent(track, d => d.x)
  const x = d3.scaleLinear().domain(xExtent)

  const yExtent = d3.extent(track, d => d.y)
  const y = d3.scaleLinear().domain(yExtent)

  const aspect = (xExtent[1] - xExtent[0]) / (yExtent[1] - yExtent[0])

  const width = Math.min(dimension * aspect, dimension)
  const height = Math.min(dimension / aspect, dimension)

  x.range([0, width])
  y.range([0, height])

  const svg = container
    .append('svg')
    .attr('width', width + 2 * margin)
    .attr('height', height + 2 * margin)

  const g = svg.append('g').attr('transform', `translate(${margin}, ${margin})`)

  const line = d3
    .line()
    .curve(d3.curveBasis)
    .x(d => x(d.x))
    .y(d => y(d.y))

  const path = g
    .append('path')
    .attr('class', 'track')
    .datum(track)
    .attr('d', line)
    .node()

  const totalLength = path.getTotalLength()

  const getCoords = pct => {
    const length = totalLength * pct
    const p = path.getPointAtLength(length % totalLength)
    return [p.x, p.y]
  }

  const centroid = d3.polygonCentroid(track.map(d => [d.x, d.y]))

  const steps = 1000
  const delta = 1 / steps
  const positions = d3
    .range(0, 1 + delta, delta)
    .map(getCoords)
    .slice(1)
    .map((p, i, a) => {
      // Find the vector to the previous point.
      const o = a[(i + a.length - 1) % a.length]
      const backwards = $V([o[0] - p[0], o[1] - p[1]])

      // // Find the vector to the next point.
      const q = a[(i + 1) % a.length]
      const forwards = $V([q[0] - p[0], q[1] - p[1]])

      // Find the angle between the two vectors.
      const angleBetweenVectors = backwards.angleFrom(forwards) * 180 / Math.PI

      // Find the normal vector and scale it based on the angle
      // between the two vectors.
      const normal = forwards
        .rotate(Math.PI / 2, $V([0, 0]))
        .toUnitVector()
        .multiply(180 - angleBetweenVectors)

      // Determine concavity.
      const isConcave = backwards.angleFrom(normal) * 180 / Math.PI >= 90

      // Create curvature vector.
      const curvature = normal
        .rotate(isConcave ? 0 : Math.PI, $V([0, 0]))
        .multiply(3)

      // Create position on circle.
      const startingOffset = 0.7
      const radius = dimension / 3
      const origin = [x(centroid[0]), y(centroid[1])]
      const circleAngle =
        2 * Math.PI * i / (a.length - 1) + Math.PI * startingOffset
      const c = [Math.cos(circleAngle), Math.sin(circleAngle)]

      return {
        p,
        c,
        origin,
        radius,
        curvature,
        isConcave,
        modulus: curvature.modulus()
      }
    })

  // Draw the normal to each point.
  const lines = g
    .selectAll('line.normal')
    .data(positions)
    .enter()
    .append('line')
    .attr('class', 'normal')
    .attr('x1', 0)
    .attr('y1', 0)
    .attr(
      'transform',
      d =>
        `translate(${d.c[0] * d.radius + d.origin[0]}, ${d.c[1] * d.radius +
          d.origin[1]})`
    )
    .attr('x2', d => d.c[0] * d.curvature.modulus() * (d.isConcave ? -1 : 1))
    .attr('y2', d => d.c[1] * d.curvature.modulus() * (d.isConcave ? -1 : 1))

  const button = d3.select('button')

  let showCurvature = true
  // copy paste here, not ashamed
  // but maybe i am otherwise i wouldn't leave this note
  const magic = () => {
    const duration = 2000
    const delay = 1
    if (showCurvature) {
      lines
        .transition()
        .duration(duration)
        .delay((d, i) => i * delay)
        .attr('transform', d => `translate(${d.p})`)
        .attr('x2', d => d.curvature.elements[0])
        .attr('y2', d => d.curvature.elements[1])
      button.text('morph circuit to circle')
    } else {
      lines
        .transition()
        .duration(duration)
        .delay((d, i) => i * delay)
        .attr(
          'transform',
          d =>
            `translate(${d.c[0] * d.radius + d.origin[0]}, ${d.c[1] * d.radius +
              d.origin[1]})`
        )
        .attr(
          'x2',
          d => d.c[0] * d.curvature.modulus() * (d.isConcave ? -1 : 1)
        )
        .attr(
          'y2',
          d => d.c[1] * d.curvature.modulus() * (d.isConcave ? -1 : 1)
        )
      button.text('morph circle to circuit')
    }
    showCurvature = !showCurvature
  }
  magic()

  button.on('click', magic)
}

export default drawTrack

package.json

{
  "standard": {
    "globals": [
      "drivers",
      "circuitTracks",
      "d3",
      "$V",
      "_"
    ]
  }
}

style.styl

$black = #010b14
$white = #dacbed
$blue = #0fd0fe
$yellow = #f6df83
$lightblue = darken($blue, 35%)
$red = lighten(#f11c07, 20%)

$blur-radius = 14px
$text-blur-opacity = 0.75
$svg-blur-opacity = 1
$font-family = 'VT323', monospace

*
  box-sizing border-box
  
html
  background alpha($black, 0.5)
  padding 0
  margin 0
  
body
  background $black
  font-family $font-family
  overflow hidden
  margin 0 auto
  padding 0
  position relative
  width 960px
  height 500px
//   // border solid $white 1px

svg
  display block
  
h1
  // visibility hidden
  color $white
  text-shadow 0 0 $blur-radius alpha($white, $text-blur-opacity)
  text-transform uppercase
  font-weight normal
  font-style italic
  position absolute
  text-align center
  padding 0
  margin 0
  width 100%
  font-size 2.5em
  span
    color $red
    
button
  // visibility hidden
  display block
  margin 0 auto
  font-family $font-family
  border solid $white 1px
  background $black
  color $white
  font-size 2em
  text-transform uppercase
  position relative
  top -1em
  cursor pointer

.circuit
  width 50%
  position relative
  margin 0 auto
  padding 0

  svg
    margin 0 auto
    filter drop-shadow(0 0 $blur-radius alpha($red, $svg-blur-opacity))
    
    path.track
      fill none
      stroke $blue
      opacity 0.2
            
    circle
      &.p
        fill $white
      &.q
        fill $red
        
    line
      stroke $red
      
    text
      fill $red

sylvester.js

eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9 17={3i:\'0.1.3\',16:1e-6};l v(){}v.23={e:l(i){8(i<1||i>7.4.q)?w:7.4[i-1]},2R:l(){8 7.4.q},1u:l(){8 F.1x(7.2u(7))},24:l(a){9 n=7.4.q;9 V=a.4||a;o(n!=V.q){8 1L}J{o(F.13(7.4[n-1]-V[n-1])>17.16){8 1L}}H(--n);8 2x},1q:l(){8 v.u(7.4)},1b:l(a){9 b=[];7.28(l(x,i){b.19(a(x,i))});8 v.u(b)},28:l(a){9 n=7.4.q,k=n,i;J{i=k-n;a(7.4[i],i+1)}H(--n)},2q:l(){9 r=7.1u();o(r===0){8 7.1q()}8 7.1b(l(x){8 x/r})},1C:l(a){9 V=a.4||a;9 n=7.4.q,k=n,i;o(n!=V.q){8 w}9 b=0,1D=0,1F=0;7.28(l(x,i){b+=x*V[i-1];1D+=x*x;1F+=V[i-1]*V[i-1]});1D=F.1x(1D);1F=F.1x(1F);o(1D*1F===0){8 w}9 c=b/(1D*1F);o(c<-1){c=-1}o(c>1){c=1}8 F.37(c)},1m:l(a){9 b=7.1C(a);8(b===w)?w:(b<=17.16)},34:l(a){9 b=7.1C(a);8(b===w)?w:(F.13(b-F.1A)<=17.16)},2k:l(a){9 b=7.2u(a);8(b===w)?w:(F.13(b)<=17.16)},2j:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x+V[i-1]})},2C:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x-V[i-1]})},22:l(k){8 7.1b(l(x){8 x*k})},x:l(k){8 7.22(k)},2u:l(a){9 V=a.4||a;9 i,2g=0,n=7.4.q;o(n!=V.q){8 w}J{2g+=7.4[n-1]*V[n-1]}H(--n);8 2g},2f:l(a){9 B=a.4||a;o(7.4.q!=3||B.q!=3){8 w}9 A=7.4;8 v.u([(A[1]*B[2])-(A[2]*B[1]),(A[2]*B[0])-(A[0]*B[2]),(A[0]*B[1])-(A[1]*B[0])])},2A:l(){9 m=0,n=7.4.q,k=n,i;J{i=k-n;o(F.13(7.4[i])>F.13(m)){m=7.4[i]}}H(--n);8 m},2Z:l(x){9 a=w,n=7.4.q,k=n,i;J{i=k-n;o(a===w&&7.4[i]==x){a=i+1}}H(--n);8 a},3g:l(){8 S.2X(7.4)},2d:l(){8 7.1b(l(x){8 F.2d(x)})},2V:l(x){8 7.1b(l(y){8(F.13(y-x)<=17.16)?x:y})},1o:l(a){o(a.K){8 a.1o(7)}9 V=a.4||a;o(V.q!=7.4.q){8 w}9 b=0,2b;7.28(l(x,i){2b=x-V[i-1];b+=2b*2b});8 F.1x(b)},3a:l(a){8 a.1h(7)},2T:l(a){8 a.1h(7)},1V:l(t,a){9 V,R,x,y,z;2S(7.4.q){27 2:V=a.4||a;o(V.q!=2){8 w}R=S.1R(t).4;x=7.4[0]-V[0];y=7.4[1]-V[1];8 v.u([V[0]+R[0][0]*x+R[0][1]*y,V[1]+R[1][0]*x+R[1][1]*y]);1I;27 3:o(!a.U){8 w}9 C=a.1r(7).4;R=S.1R(t,a.U).4;x=7.4[0]-C[0];y=7.4[1]-C[1];z=7.4[2]-C[2];8 v.u([C[0]+R[0][0]*x+R[0][1]*y+R[0][2]*z,C[1]+R[1][0]*x+R[1][1]*y+R[1][2]*z,C[2]+R[2][0]*x+R[2][1]*y+R[2][2]*z]);1I;2P:8 w}},1t:l(a){o(a.K){9 P=7.4.2O();9 C=a.1r(P).4;8 v.u([C[0]+(C[0]-P[0]),C[1]+(C[1]-P[1]),C[2]+(C[2]-(P[2]||0))])}1d{9 Q=a.4||a;o(7.4.q!=Q.q){8 w}8 7.1b(l(x,i){8 Q[i-1]+(Q[i-1]-x)})}},1N:l(){9 V=7.1q();2S(V.4.q){27 3:1I;27 2:V.4.19(0);1I;2P:8 w}8 V},2n:l(){8\'[\'+7.4.2K(\', \')+\']\'},26:l(a){7.4=(a.4||a).2O();8 7}};v.u=l(a){9 V=25 v();8 V.26(a)};v.i=v.u([1,0,0]);v.j=v.u([0,1,0]);v.k=v.u([0,0,1]);v.2J=l(n){9 a=[];J{a.19(F.2F())}H(--n);8 v.u(a)};v.1j=l(n){9 a=[];J{a.19(0)}H(--n);8 v.u(a)};l S(){}S.23={e:l(i,j){o(i<1||i>7.4.q||j<1||j>7.4[0].q){8 w}8 7.4[i-1][j-1]},33:l(i){o(i>7.4.q){8 w}8 v.u(7.4[i-1])},2E:l(j){o(j>7.4[0].q){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][j-1])}H(--n);8 v.u(a)},2R:l(){8{2D:7.4.q,1p:7.4[0].q}},2D:l(){8 7.4.q},1p:l(){8 7.4[0].q},24:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(7.4.q!=M.q||7.4[0].q!=M[0].q){8 1L}9 b=7.4.q,15=b,i,G,10=7.4[0].q,j;J{i=15-b;G=10;J{j=10-G;o(F.13(7.4[i][j]-M[i][j])>17.16){8 1L}}H(--G)}H(--b);8 2x},1q:l(){8 S.u(7.4)},1b:l(a){9 b=[],12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;b[i]=[];J{j=10-G;b[i][j]=a(7.4[i][j],i+1,j+1)}H(--G)}H(--12);8 S.u(b)},2i:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4.q==M.q&&7.4[0].q==M[0].q)},2j:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x+M[i-1][j-1]})},2C:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x-M[i-1][j-1]})},2B:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4[0].q==M.q)},22:l(a){o(!a.4){8 7.1b(l(x){8 x*a})}9 b=a.1u?2x:1L;9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2B(M)){8 w}9 d=7.4.q,15=d,i,G,10=M[0].q,j;9 e=7.4[0].q,4=[],21,20,c;J{i=15-d;4[i]=[];G=10;J{j=10-G;21=0;20=e;J{c=e-20;21+=7.4[i][c]*M[c][j]}H(--20);4[i][j]=21}H(--G)}H(--d);9 M=S.u(4);8 b?M.2E(1):M},x:l(a){8 7.22(a)},32:l(a,b,c,d){9 e=[],12=c,i,G,j;9 f=7.4.q,1p=7.4[0].q;J{i=c-12;e[i]=[];G=d;J{j=d-G;e[i][j]=7.4[(a+i-1)%f][(b+j-1)%1p]}H(--G)}H(--12);8 S.u(e)},31:l(){9 a=7.4.q,1p=7.4[0].q;9 b=[],12=1p,i,G,j;J{i=1p-12;b[i]=[];G=a;J{j=a-G;b[i][j]=7.4[j][i]}H(--G)}H(--12);8 S.u(b)},1y:l(){8(7.4.q==7.4[0].q)},2A:l(){9 m=0,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(F.13(7.4[i][j])>F.13(m)){m=7.4[i][j]}}H(--G)}H(--12);8 m},2Z:l(x){9 a=w,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(7.4[i][j]==x){8{i:i+1,j:j+1}}}H(--G)}H(--12);8 w},30:l(){o(!7.1y){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][i])}H(--n);8 v.u(a)},1K:l(){9 M=7.1q(),1c;9 n=7.4.q,k=n,i,1s,1n=7.4[0].q,p;J{i=k-n;o(M.4[i][i]==0){2e(j=i+1;j<k;j++){o(M.4[j][i]!=0){1c=[];1s=1n;J{p=1n-1s;1c.19(M.4[i][p]+M.4[j][p])}H(--1s);M.4[i]=1c;1I}}}o(M.4[i][i]!=0){2e(j=i+1;j<k;j++){9 a=M.4[j][i]/M.4[i][i];1c=[];1s=1n;J{p=1n-1s;1c.19(p<=i?0:M.4[j][p]-M.4[i][p]*a)}H(--1s);M.4[j]=1c}}}H(--n);8 M},3h:l(){8 7.1K()},2z:l(){o(!7.1y()){8 w}9 M=7.1K();9 a=M.4[0][0],n=M.4.q-1,k=n,i;J{i=k-n+1;a=a*M.4[i][i]}H(--n);8 a},3f:l(){8 7.2z()},2y:l(){8(7.1y()&&7.2z()===0)},2Y:l(){o(!7.1y()){8 w}9 a=7.4[0][0],n=7.4.q-1,k=n,i;J{i=k-n+1;a+=7.4[i][i]}H(--n);8 a},3e:l(){8 7.2Y()},1Y:l(){9 M=7.1K(),1Y=0;9 a=7.4.q,15=a,i,G,10=7.4[0].q,j;J{i=15-a;G=10;J{j=10-G;o(F.13(M.4[i][j])>17.16){1Y++;1I}}H(--G)}H(--a);8 1Y},3d:l(){8 7.1Y()},2W:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}9 T=7.1q(),1p=T.4[0].q;9 b=T.4.q,15=b,i,G,10=M[0].q,j;o(b!=M.q){8 w}J{i=15-b;G=10;J{j=10-G;T.4[i][1p+j]=M[i][j]}H(--G)}H(--b);8 T},2w:l(){o(!7.1y()||7.2y()){8 w}9 a=7.4.q,15=a,i,j;9 M=7.2W(S.I(a)).1K();9 b,1n=M.4[0].q,p,1c,2v;9 c=[],2c;J{i=a-1;1c=[];b=1n;c[i]=[];2v=M.4[i][i];J{p=1n-b;2c=M.4[i][p]/2v;1c.19(2c);o(p>=15){c[i].19(2c)}}H(--b);M.4[i]=1c;2e(j=0;j<i;j++){1c=[];b=1n;J{p=1n-b;1c.19(M.4[j][p]-M.4[i][p]*M.4[j][i])}H(--b);M.4[j]=1c}}H(--a);8 S.u(c)},3c:l(){8 7.2w()},2d:l(){8 7.1b(l(x){8 F.2d(x)})},2V:l(x){8 7.1b(l(p){8(F.13(p-x)<=17.16)?x:p})},2n:l(){9 a=[];9 n=7.4.q,k=n,i;J{i=k-n;a.19(v.u(7.4[i]).2n())}H(--n);8 a.2K(\'\\n\')},26:l(a){9 i,4=a.4||a;o(1g(4[0][0])!=\'1f\'){9 b=4.q,15=b,G,10,j;7.4=[];J{i=15-b;G=4[i].q;10=G;7.4[i]=[];J{j=10-G;7.4[i][j]=4[i][j]}H(--G)}H(--b);8 7}9 n=4.q,k=n;7.4=[];J{i=k-n;7.4.19([4[i]])}H(--n);8 7}};S.u=l(a){9 M=25 S();8 M.26(a)};S.I=l(n){9 a=[],k=n,i,G,j;J{i=k-n;a[i]=[];G=k;J{j=k-G;a[i][j]=(i==j)?1:0}H(--G)}H(--n);8 S.u(a)};S.2X=l(a){9 n=a.q,k=n,i;9 M=S.I(n);J{i=k-n;M.4[i][i]=a[i]}H(--n);8 M};S.1R=l(b,a){o(!a){8 S.u([[F.1H(b),-F.1G(b)],[F.1G(b),F.1H(b)]])}9 d=a.1q();o(d.4.q!=3){8 w}9 e=d.1u();9 x=d.4[0]/e,y=d.4[1]/e,z=d.4[2]/e;9 s=F.1G(b),c=F.1H(b),t=1-c;8 S.u([[t*x*x+c,t*x*y-s*z,t*x*z+s*y],[t*x*y+s*z,t*y*y+c,t*y*z-s*x],[t*x*z-s*y,t*y*z+s*x,t*z*z+c]])};S.3b=l(t){9 c=F.1H(t),s=F.1G(t);8 S.u([[1,0,0],[0,c,-s],[0,s,c]])};S.39=l(t){9 c=F.1H(t),s=F.1G(t);8 S.u([[c,0,s],[0,1,0],[-s,0,c]])};S.38=l(t){9 c=F.1H(t),s=F.1G(t);8 S.u([[c,-s,0],[s,c,0],[0,0,1]])};S.2J=l(n,m){8 S.1j(n,m).1b(l(){8 F.2F()})};S.1j=l(n,m){9 a=[],12=n,i,G,j;J{i=n-12;a[i]=[];G=m;J{j=m-G;a[i][j]=0}H(--G)}H(--12);8 S.u(a)};l 14(){}14.23={24:l(a){8(7.1m(a)&&7.1h(a.K))},1q:l(){8 14.u(7.K,7.U)},2U:l(a){9 V=a.4||a;8 14.u([7.K.4[0]+V[0],7.K.4[1]+V[1],7.K.4[2]+(V[2]||0)],7.U)},1m:l(a){o(a.W){8 a.1m(7)}9 b=7.U.1C(a.U);8(F.13(b)<=17.16||F.13(b-F.1A)<=17.16)},1o:l(a){o(a.W){8 a.1o(7)}o(a.U){o(7.1m(a)){8 7.1o(a.K)}9 N=7.U.2f(a.U).2q().4;9 A=7.K.4,B=a.K.4;8 F.13((A[0]-B[0])*N[0]+(A[1]-B[1])*N[1]+(A[2]-B[2])*N[2])}1d{9 P=a.4||a;9 A=7.K.4,D=7.U.4;9 b=P[0]-A[0],2a=P[1]-A[1],29=(P[2]||0)-A[2];9 c=F.1x(b*b+2a*2a+29*29);o(c===0)8 0;9 d=(b*D[0]+2a*D[1]+29*D[2])/c;9 e=1-d*d;8 F.13(c*F.1x(e<0?0:e))}},1h:l(a){9 b=7.1o(a);8(b!==w&&b<=17.16)},2T:l(a){8 a.1h(7)},1v:l(a){o(a.W){8 a.1v(7)}8(!7.1m(a)&&7.1o(a)<=17.16)},1U:l(a){o(a.W){8 a.1U(7)}o(!7.1v(a)){8 w}9 P=7.K.4,X=7.U.4,Q=a.K.4,Y=a.U.4;9 b=X[0],1z=X[1],1B=X[2],1T=Y[0],1S=Y[1],1M=Y[2];9 c=P[0]-Q[0],2s=P[1]-Q[1],2r=P[2]-Q[2];9 d=-b*c-1z*2s-1B*2r;9 e=1T*c+1S*2s+1M*2r;9 f=b*b+1z*1z+1B*1B;9 g=1T*1T+1S*1S+1M*1M;9 h=b*1T+1z*1S+1B*1M;9 k=(d*g/f+h*e)/(g-h*h);8 v.u([P[0]+k*b,P[1]+k*1z,P[2]+k*1B])},1r:l(a){o(a.U){o(7.1v(a)){8 7.1U(a)}o(7.1m(a)){8 w}9 D=7.U.4,E=a.U.4;9 b=D[0],1l=D[1],1k=D[2],1P=E[0],1O=E[1],1Q=E[2];9 x=(1k*1P-b*1Q),y=(b*1O-1l*1P),z=(1l*1Q-1k*1O);9 N=v.u([x*1Q-y*1O,y*1P-z*1Q,z*1O-x*1P]);9 P=11.u(a.K,N);8 P.1U(7)}1d{9 P=a.4||a;o(7.1h(P)){8 v.u(P)}9 A=7.K.4,D=7.U.4;9 b=D[0],1l=D[1],1k=D[2],1w=A[0],18=A[1],1a=A[2];9 x=b*(P[1]-18)-1l*(P[0]-1w),y=1l*((P[2]||0)-1a)-1k*(P[1]-18),z=1k*(P[0]-1w)-b*((P[2]||0)-1a);9 V=v.u([1l*x-1k*z,1k*y-b*x,b*z-1l*y]);9 k=7.1o(P)/V.1u();8 v.u([P[0]+V.4[0]*k,P[1]+V.4[1]*k,(P[2]||0)+V.4[2]*k])}},1V:l(t,a){o(1g(a.U)==\'1f\'){a=14.u(a.1N(),v.k)}9 R=S.1R(t,a.U).4;9 C=a.1r(7.K).4;9 A=7.K.4,D=7.U.4;9 b=C[0],1E=C[1],1J=C[2],1w=A[0],18=A[1],1a=A[2];9 x=1w-b,y=18-1E,z=1a-1J;8 14.u([b+R[0][0]*x+R[0][1]*y+R[0][2]*z,1E+R[1][0]*x+R[1][1]*y+R[1][2]*z,1J+R[2][0]*x+R[2][1]*y+R[2][2]*z],[R[0][0]*D[0]+R[0][1]*D[1]+R[0][2]*D[2],R[1][0]*D[0]+R[1][1]*D[1]+R[1][2]*D[2],R[2][0]*D[0]+R[2][1]*D[1]+R[2][2]*D[2]])},1t:l(a){o(a.W){9 A=7.K.4,D=7.U.4;9 b=A[0],18=A[1],1a=A[2],2N=D[0],1l=D[1],1k=D[2];9 c=7.K.1t(a).4;9 d=b+2N,2h=18+1l,2o=1a+1k;9 Q=a.1r([d,2h,2o]).4;9 e=[Q[0]+(Q[0]-d)-c[0],Q[1]+(Q[1]-2h)-c[1],Q[2]+(Q[2]-2o)-c[2]];8 14.u(c,e)}1d o(a.U){8 7.1V(F.1A,a)}1d{9 P=a.4||a;8 14.u(7.K.1t([P[0],P[1],(P[2]||0)]),7.U)}},1Z:l(a,b){a=v.u(a);b=v.u(b);o(a.4.q==2){a.4.19(0)}o(b.4.q==2){b.4.19(0)}o(a.4.q>3||b.4.q>3){8 w}9 c=b.1u();o(c===0){8 w}7.K=a;7.U=v.u([b.4[0]/c,b.4[1]/c,b.4[2]/c]);8 7}};14.u=l(a,b){9 L=25 14();8 L.1Z(a,b)};14.X=14.u(v.1j(3),v.i);14.Y=14.u(v.1j(3),v.j);14.Z=14.u(v.1j(3),v.k);l 11(){}11.23={24:l(a){8(7.1h(a.K)&&7.1m(a))},1q:l(){8 11.u(7.K,7.W)},2U:l(a){9 V=a.4||a;8 11.u([7.K.4[0]+V[0],7.K.4[1]+V[1],7.K.4[2]+(V[2]||0)],7.W)},1m:l(a){9 b;o(a.W){b=7.W.1C(a.W);8(F.13(b)<=17.16||F.13(F.1A-b)<=17.16)}1d o(a.U){8 7.W.2k(a.U)}8 w},2k:l(a){9 b=7.W.1C(a.W);8(F.13(F.1A/2-b)<=17.16)},1o:l(a){o(7.1v(a)||7.1h(a)){8 0}o(a.K){9 A=7.K.4,B=a.K.4,N=7.W.4;8 F.13((A[0]-B[0])*N[0]+(A[1]-B[1])*N[1]+(A[2]-B[2])*N[2])}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;8 F.13((A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2])}},1h:l(a){o(a.W){8 w}o(a.U){8(7.1h(a.K)&&7.1h(a.K.2j(a.U)))}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=F.13(N[0]*(A[0]-P[0])+N[1]*(A[1]-P[1])+N[2]*(A[2]-(P[2]||0)));8(b<=17.16)}},1v:l(a){o(1g(a.U)==\'1f\'&&1g(a.W)==\'1f\'){8 w}8!7.1m(a)},1U:l(a){o(!7.1v(a)){8 w}o(a.U){9 A=a.K.4,D=a.U.4,P=7.K.4,N=7.W.4;9 b=(N[0]*(P[0]-A[0])+N[1]*(P[1]-A[1])+N[2]*(P[2]-A[2]))/(N[0]*D[0]+N[1]*D[1]+N[2]*D[2]);8 v.u([A[0]+D[0]*b,A[1]+D[1]*b,A[2]+D[2]*b])}1d o(a.W){9 c=7.W.2f(a.W).2q();9 N=7.W.4,A=7.K.4,O=a.W.4,B=a.K.4;9 d=S.1j(2,2),i=0;H(d.2y()){i++;d=S.u([[N[i%3],N[(i+1)%3]],[O[i%3],O[(i+1)%3]]])}9 e=d.2w().4;9 x=N[0]*A[0]+N[1]*A[1]+N[2]*A[2];9 y=O[0]*B[0]+O[1]*B[1]+O[2]*B[2];9 f=[e[0][0]*x+e[0][1]*y,e[1][0]*x+e[1][1]*y];9 g=[];2e(9 j=1;j<=3;j++){g.19((i==j)?0:f[(j+(5-i)%3)%3])}8 14.u(g,c)}},1r:l(a){9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=(A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2];8 v.u([P[0]+N[0]*b,P[1]+N[1]*b,(P[2]||0)+N[2]*b])},1V:l(t,a){9 R=S.1R(t,a.U).4;9 C=a.1r(7.K).4;9 A=7.K.4,N=7.W.4;9 b=C[0],1E=C[1],1J=C[2],1w=A[0],18=A[1],1a=A[2];9 x=1w-b,y=18-1E,z=1a-1J;8 11.u([b+R[0][0]*x+R[0][1]*y+R[0][2]*z,1E+R[1][0]*x+R[1][1]*y+R[1][2]*z,1J+R[2][0]*x+R[2][1]*y+R[2][2]*z],[R[0][0]*N[0]+R[0][1]*N[1]+R[0][2]*N[2],R[1][0]*N[0]+R[1][1]*N[1]+R[1][2]*N[2],R[2][0]*N[0]+R[2][1]*N[1]+R[2][2]*N[2]])},1t:l(a){o(a.W){9 A=7.K.4,N=7.W.4;9 b=A[0],18=A[1],1a=A[2],2M=N[0],2L=N[1],2Q=N[2];9 c=7.K.1t(a).4;9 d=b+2M,2p=18+2L,2m=1a+2Q;9 Q=a.1r([d,2p,2m]).4;9 e=[Q[0]+(Q[0]-d)-c[0],Q[1]+(Q[1]-2p)-c[1],Q[2]+(Q[2]-2m)-c[2]];8 11.u(c,e)}1d o(a.U){8 7.1V(F.1A,a)}1d{9 P=a.4||a;8 11.u(7.K.1t([P[0],P[1],(P[2]||0)]),7.W)}},1Z:l(a,b,c){a=v.u(a);a=a.1N();o(a===w){8 w}b=v.u(b);b=b.1N();o(b===w){8 w}o(1g(c)==\'1f\'){c=w}1d{c=v.u(c);c=c.1N();o(c===w){8 w}}9 d=a.4[0],18=a.4[1],1a=a.4[2];9 e=b.4[0],1W=b.4[1],1X=b.4[2];9 f,1i;o(c!==w){9 g=c.4[0],2l=c.4[1],2t=c.4[2];f=v.u([(1W-18)*(2t-1a)-(1X-1a)*(2l-18),(1X-1a)*(g-d)-(e-d)*(2t-1a),(e-d)*(2l-18)-(1W-18)*(g-d)]);1i=f.1u();o(1i===0){8 w}f=v.u([f.4[0]/1i,f.4[1]/1i,f.4[2]/1i])}1d{1i=F.1x(e*e+1W*1W+1X*1X);o(1i===0){8 w}f=v.u([b.4[0]/1i,b.4[1]/1i,b.4[2]/1i])}7.K=a;7.W=f;8 7}};11.u=l(a,b,c){9 P=25 11();8 P.1Z(a,b,c)};11.2I=11.u(v.1j(3),v.k);11.2H=11.u(v.1j(3),v.i);11.2G=11.u(v.1j(3),v.j);11.36=11.2I;11.35=11.2H;11.3j=11.2G;9 $V=v.u;9 $M=S.u;9 $L=14.u;9 $P=11.u;',62,206,'||||elements|||this|return|var||||||||||||function|||if||length||||create|Vector|null|||||||||Math|nj|while||do|anchor||||||||Matrix||direction||normal||||kj|Plane|ni|abs|Line|ki|precision|Sylvester|A2|push|A3|map|els|else||undefined|typeof|contains|mod|Zero|D3|D2|isParallelTo|kp|distanceFrom|cols|dup|pointClosestTo|np|reflectionIn|modulus|intersects|A1|sqrt|isSquare|X2|PI|X3|angleFrom|mod1|C2|mod2|sin|cos|break|C3|toRightTriangular|false|Y3|to3D|E2|E1|E3|Rotation|Y2|Y1|intersectionWith|rotate|v12|v13|rank|setVectors|nc|sum|multiply|prototype|eql|new|setElements|case|each|PA3|PA2|part|new_element|round|for|cross|product|AD2|isSameSizeAs|add|isPerpendicularTo|v22|AN3|inspect|AD3|AN2|toUnitVector|PsubQ3|PsubQ2|v23|dot|divisor|inverse|true|isSingular|determinant|max|canMultiplyFromLeft|subtract|rows|col|random|ZX|YZ|XY|Random|join|N2|N1|D1|slice|default|N3|dimensions|switch|liesIn|translate|snapTo|augment|Diagonal|trace|indexOf|diagonal|transpose|minor|row|isAntiparallelTo|ZY|YX|acos|RotationZ|RotationY|liesOn|RotationX|inv|rk|tr|det|toDiagonalMatrix|toUpperTriangular|version|XZ'.split('|'),0,{}))