The car accelerates and brakes depending on the curvature vectors of the Australian Grand Prix circuit. The longer the vector lines, the steeper the curve.
Don’t be fooled by the car’s incredibly life-like speed! It is not!
See my previous work:
import createTrack from './createTrack.js'
import createDash from './createDash.js'
import drawNormals from './drawNormals.js'
import getCurvature from './getCurvature.js'
import drawDash from './drawDash.js'
import drawCar from './drawCar.js'
import moveCar, { moveCarOneLap } from './moveCar.js'
import constants from './constants.js'
import createTimingsChart from './createTimingsChart.js'
import drawTimingsChart from './drawTimingsChart.js'
const before = Date.now()
// Create the track.
const { g: gCircuit, getCoords, turns } = createTrack({
container: d3.select('.circuit'),
track: circuitTracks[0]
})
// Create the timings chart.
const timingsBits = createTimingsChart({
container: d3.select('.timings'),
turns
})
// Create curvature data.
const steps = 1000
const delta = 1 / steps
const positionsTemp = d3
.range(0, 1, delta)
.concat(1)
.map(pct => ({
pct,
...getCurvature({ pct, getCoords })
}))
const alphaExtent = d3.extent(positionsTemp, d => d.curvature.modulus())
const alpha = d3
.scaleLinear()
.domain(alphaExtent)
.range([0, 65])
const positions = positionsTemp
.map(d => ({
...d,
modulus: d.curvature.modulus()
}))
.map(d => ({
...d,
curvature: d.curvature.toUnitVector().multiply(alpha(d.modulus))
}))
// Create the dash.
const { g: gDash, alpha: alphaDash } = createDash(d3.select('.dash'))
const blankCar = () => ({
// mph
number: 1,
positionIndex: 0,
measures: [
{
pct: 0,
speed: constants.MAX
}
]
})
const drawSummary = ({ car, elapsed }) => {
const formatTime = d3.timeFormat('%-M:%S.%L')
d3.select('.other').html(`
<div>LAP ${formatTime(elapsed || car.elapsed)}</div>
<div>MAX ${Math.round(d3.max(car.measures, d => d.speed))} KM/H</div>
<div>MIN ${Math.round(d3.min(car.measures, d => d.speed))} KM/H</div>
`)
}
// car = moveCarOneLap({ car, positions })
// drawTimingsChart({ car, ...timingsBits })
// // drawSummary(car)
// drawCar({ car, g: gCircuit, getCoords, positions })
let timer
const doIt = () => {
let now = 0
if (timer) {
timer.stop()
}
let car = blankCar()
// Every tick:
timer = d3.interval(elapsed => {
const delta = elapsed - now
// Draw the car.
drawCar({ car, g: gCircuit, getCoords })
// Draw the timings chart.
drawTimingsChart({ car, ...timingsBits })
// Draw the dash.
drawDash({ car, g: gDash, alpha: alphaDash })
// Draw curvature normals.
drawNormals({ g: gCircuit, positions, car })
// Move the car.
car = moveCar({ car, delta, positions })
now = elapsed
if (car.measures.slice(-1)[0].pct >= 1) {
doIt()
// timer.stop()
drawSummary({ car, elapsed })
}
})
}
doIt()
<!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 class='title'>speedy racer</span>
<span>the car accelerates and brakes depending on the circuit curvature vectors. go speedy go!</span>
</h1>
<div class='circuit'>
<h1>Australian Grand Prix</h1>
</div>
<div class='timings'></div>
<div class='dash'></div>
<div class='other'></div>
<script src='sylvester.js'></script>
<script src='d3.v4.min.js'></script>
<script src='lodash.min.js'></script>
<script src='circuits-flatter.js'></script>
<script src='dist.js'></script>
</body>
var circuitTracks = [[{"x":753.546,"y":644.215},{"x":604.791,"y":681.341},{"x":497.29300000000006,"y":708.044},{"x":483.8740000000001,"y":708.8149999999999},{"x":470.9910000000001,"y":690.7529999999999},{"x":454.8270000000001,"y":671.175},{"x":424.2860000000001,"y":660.236},{"x":397.93200000000013,"y":661.193},{"x":381.30900000000014,"y":665.624},{"x":339.3410000000001,"y":679.119},{"x":241.04900000000012,"y":700.604},{"x":221.90700000000012,"y":703.5550000000001},{"x":139.39600000000013,"y":708.7080000000001},{"x":111.69700000000013,"y":709.1500000000001},{"x":61.02000000000013,"y":706.44},{"x":49.596,"y":700},{"x":47.48,"y":689.413},{"x":50.605,"y":673.8870000000001},{"x":55.651999999999994,"y":649.321},{"x":51.553999999999995,"y":621.763},{"x":40.562,"y":598.765},{"x":22.505999999999997,"y":584.122},{"x":-13.114,"y":564.2869999999999},{"x":-40.587,"y":549.3779999999999},{"x":-48.745000000000005,"y":543.4279999999999},{"x":-55.580000000000005,"y":527.4179999999999},{"x":-65.354,"y":446.2249999999999},{"x":-61.221,"y":404.5849999999999},{"x":-49.378,"y":346.8599999999999},{"x":-48.75,"y":328.5639999999999},{"x":-48.75,"y":328.5639999999999},{"x":-49.115,"y":280.0729999999999},{"x":-41.84,"y":266.8829999999999},{"x":-21.798000000000002,"y":260.17999999999995},{"x":-0.1460000000000008,"y":248.67099999999994},{"x":19.671,"y":230.54099999999994},{"x":43.442,"y":198.74699999999993},{"x":67.33,"y":181.40499999999992},{"x":92.672,"y":168.99599999999992},{"x":118.503,"y":162.69199999999992},{"x":144.065,"y":162.45999999999992},{"x":199.239,"y":176.9279999999999},{"x":261.597,"y":200.84199999999993},{"x":318.155,"y":220.65199999999993},{"x":325.80499999999995,"y":226.01299999999992},{"x":326.88699999999994,"y":233.9509999999999},{"x":320.11699999999996,"y":263.6259999999999},{"x":324.68399999999997,"y":277.02399999999994},{"x":328.368,"y":284.50199999999995},{"x":328.153,"y":284.25399999999996},{"x":357.954,"y":315.236},{"x":436.978,"y":378.216},{"x":475.152,"y":398.772},{"x":521.252,"y":412.077},{"x":538.501,"y":414.061},{"x":589.313,"y":413.734},{"x":629.251,"y":404.86199999999997},{"x":684.3589999999999,"y":387.37199999999996},{"x":708.2189999999999,"y":378.268},{"x":730.7379999999999,"y":369.765},{"x":738.377,"y":362.503},{"x":742.2729999999999,"y":348.918},{"x":754.6949999999999,"y":320.11},{"x":762.8969999999999,"y":302.028},{"x":769.655,"y":295.159},{"x":788.404,"y":288.93399999999997},{"x":910.443,"y":247.93899999999996},{"x":956.076,"y":240.99399999999997},{"x":1001.582,"y":247.12399999999997},{"x":1158.785,"y":286.051},{"x":1182.028,"y":295.223},{"x":1183.336,"y":301.44100000000003},{"x":1165.914,"y":413.06100000000004},{"x":1158.567,"y":448.324},{"x":1142.578,"y":465.862},{"x":1124.681,"y":471.608},{"x":1002.322,"y":469.969},{"x":993.977,"y":472.945},{"x":990.192,"y":482.81399999999996},{"x":998.598,"y":505.849},{"x":1010.05,"y":542.403},{"x":1009.924,"y":552.133},{"x":1002.761,"y":566.1120000000001},{"x":998.079,"y":575.6360000000001},{"x":986.0459999999999,"y":582.8580000000001},{"x":945.6299999999999,"y":595.663},{"x":842.41,"y":621.948},{"x":753.546,"y":644.215},{"x":753.546,"y":644.215}]];
const constants = {
MIN: 30,
MAX: 320
}
export default constants
import constants from './constants.js'
const toRadians = d => d * 2 * Math.PI / 360
const createDash = container => {
const margins = { top: 3, right: 0, bottom: 6, left: 0 }
const outerWidth = container.node().offsetWidth
const outerHeight = outerWidth / 2
const width = outerWidth - margins.right - margins.left
const height = outerHeight - margins.top - margins.bottom
const g = container
.append('svg')
.attr('width', outerWidth)
.attr('height', outerHeight)
.append('g')
.attr(
'transform',
`translate(${width / 2 + margins.left}, ${height + margins.top})`
)
// Create tick scale.
const alpha = d3
.scaleLinear()
.domain([0, constants.MAX])
.range([10, 170])
// .range([0, 180])
.nice()
const tickWidth = 4
// Create tick data.
const ticks = d3.range(...alpha.domain(), 50).concat(alpha.domain()[1])
// Define arc.
const arc = d3.arc()
.innerRadius(height)
.outerRadius(height)
.startAngle(toRadians(alpha.range()[0]))
.endAngle(toRadians(alpha.range()[1]))
// Draw arc.
g.append('path')
.attr('class', 'arc')
.attr('d', arc)
.attr('transform', 'rotate(-90)')
g.append('text')
.attr('class', 'label')
.text('KM/H')
.attr('text-anchor', 'middle')
.attr('dy', -40)
// Draw tick texts.
g
.selectAll('text.tick')
.data(ticks)
.enter()
.append('text')
.attr('class', 'tick')
.attr('text-anchor', 'middle')
.attr('dy', 6)
.attr(
'transform',
d =>
`rotate(${alpha(d)}) translate(${-height * 0.85} 0) rotate(${-alpha(
d
)} 0 0)`
)
.text(d => d)
// Draw tick lines.
g
.selectAll('line.tick')
.data(ticks)
.enter()
.append('line')
.attr('class', 'tick')
.attr('transform', d => `rotate(${alpha(d)}) translate(${-height} 0)`)
.attr('x2', tickWidth)
// Draw dial anchor.
g.append('circle')
.attr('class', 'dial')
.attr('r', 5)
// Draw dial.
g.append('line')
.attr('class', 'dial')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', -height + tickWidth * 1.5)
.attr('y2', 0)
.attr('transform', `rotate(${alpha.range()[0]})`)
return { g, alpha }
}
export default createDash
import constants from './constants.js'
const createTimingsChart = ({ container, turns }) => {
const margins = { top: 45, right: 5, bottom: 20, left: 45 }
const width = container.node().offsetWidth - margins.right - margins.left
const height = width / 4.5
const svg = container
.append('svg')
.attr('width', width + margins.right + margins.left)
.attr('height', height + margins.top + margins.bottom)
const g = svg
.append('g')
.attr('transform', `translate(${margins.left}, ${margins.top})`)
const x = d3
.scaleLinear()
.range([0, width])
.domain([0, 1])
const y = d3
.scaleLinear()
.range([height, 0])
.domain([0, constants.MAX])
.nice()
const line = d3
.line()
.curve(d3.curveBasisOpen)
.x(d => x(d.pct % 1))
.y(d => y(d.speed))
const xAxis = g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0, ${height})`)
.call(
d3
.axisBottom(x)
.tickSize(0)
.ticks([])
)
xAxis
.append('text')
.text('Finish')
.attr('x', x(1))
.attr('text-anchor', 'end')
.attr('dy', 14)
const yAxis = g
.append('g')
.attr('class', 'axis axis--y')
.call(
d3
.axisLeft(y)
.tickSize(0)
.ticks(5)
)
yAxis
.append('text')
.text('(KM/H)')
.attr('dx', -3)
.attr('dy', 6 - 18)
.attr('text-anchor', 'end')
yAxis
.append('text')
.text('Speed')
.attr('dx', -3)
.attr('dy', 6 - 18 - 18)
.attr('text-anchor', 'end')
g
.selectAll('line.turn')
.data(turns)
.enter()
.append('line')
.attr('class', 'turn')
.attr('transform', d => `translate(${x(d.pct)} 0)`)
.attr('y2', height)
g
.selectAll('text.turn')
.data(turns)
.enter()
.append('text')
.attr('class', 'turn')
.attr('transform', d => `translate(${x(d.pct)} 0)`)
.attr('text-anchor', 'middle')
.text(d => (d.number === 1 ? 'Turn ' : '') + d.number)
.attr('dy', -8)
return {
g,
x,
y,
line
}
}
export default createTimingsChart
import getAngle from './getAngle.js'
const drawFinishLine = ({ g, getCoords }) => {
const delta = 0.01
const before = getCoords({ pct: 1 - delta })
const zero = getCoords({ pct: 0 })
const after = getCoords({ pct: delta })
g
.append('line')
.attr('class', 'finish-line')
.attr('x1', before[0])
.attr('y1', before[1])
.attr('x2', after[0])
.attr('y2', after[1])
.attr('transform', `rotate(90, ${zero})`)
}
const drawTrack = ({ container, track }) => {
const margins = { top: 10, right: 20, bottom: 30, left: 20 }
const dimension = container.node().offsetWidth - margins.right - margins.left
const data = track.map(d => [d.x, d.y])
const xExtent = d3.extent(data, d => d[0])
const x = d3.scaleLinear().domain(xExtent)
const yExtent = d3.extent(data, d => d[1])
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 + margins.right + margins.left)
.attr('height', height + margins.top + margins.bottom)
const g = svg.append('g').attr('transform', `translate(${margins.left}, ${margins.top})`)
const line = d3
.line()
.curve(d3.curveBasis)
.x(d => x(d[0]))
.y(d => y(d[1]))
const path = g
.append('path')
.attr('class', 'track')
.datum(data)
.attr('d', line)
.node()
const totalLength = path.getTotalLength()
const getCoords = ({ pct = 0, delta = 0 }) => {
const length = totalLength * pct + delta
const pxy = path.getPointAtLength(length % totalLength)
const qxy = path.getPointAtLength((length + 1) % totalLength)
const p = [pxy.x, pxy.y]
const q = [qxy.x, qxy.y]
const angle = getAngle([p, q])
const array = [...p]
array.angle = angle
return array
}
// Draw finish line.
drawFinishLine({ g, getCoords })
// Create turn coords.
const turns = [
{ number: 1, pct: 0.0825, dx: 0.02, dy: 0.07 },
{ number: 3, pct: 0.22, dx: 0.01, dy: -0.01 },
{ number: 5, pct: 0.282, dx: 0.01, dy: -0.01 },
{ number: 6, pct: 0.36, dx: 0.01, dy: 0.04 },
{ number: 9, pct: 0.496, dx: -0.025, dy: 0.01, textAnchor: 'end' },
{ number: 11, pct: 0.637, dx: 0.01, dy: -0.03, textAnchor: 'end' },
{ number: 13, pct: 0.8, dx: -0.01, dy: 0.02, textAnchor: 'end' },
{ number: 14, pct: 0.845, dx: -0.01, dy: -0.01, textAnchor: 'end' },
{ number: 15, pct: 0.9, dx: 0.01, dy: 0.015 },
].map(d => ({
...d,
coords: getCoords({ pct: d.pct }),
dx: (xExtent[1] - xExtent[0]) * (d.dx || 0) + xExtent[0],
dy: (yExtent[1] - yExtent[0]) * (d.dy || 0) + yExtent[0]
}))
g
.selectAll('text.turn')
.data(turns)
.enter()
.append('text')
.attr('class', 'turn')
.attr('transform', d => `translate(${d.coords})`)
.attr('text-anchor', d => d.textAnchor || 'begin')
.attr('dx', d => x(d.dx))
.attr('dy', d => y(d.dy))
.text(d => (d.number === 1 ? 'Turn ' : '') + (d.number))
return { g, getCoords, turns }
}
export default drawTrack
*{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:.5em;position:relative;max-width:960px;max-height:500px}svg{display:block}h1{color:#dacbed;text-transform:uppercase;font-weight:400;font-style:italic;float:left;text-align:center;padding:0;margin:.5em 0 0 0;width:25%;font-size:2em}h1 span{text-shadow:0 0 14px rgba(218,203,237,.75);display:inline-block}h1 span.title{text-shadow:0 0 14px rgba(249,69,51,.75);font-size:1.5em;color:#f94533}.other{position:absolute;bottom:.5em;color:#018dae}.timings{width:75%;margin-left:25%;position:relative}.timings svg{-webkit-filter:drop-shadow(0 0 14px rgba(15,208,254,.5));filter:drop-shadow(0 0 14px rgba(15,208,254,.5))}.timings svg path.car{fill:none;stroke:#0fd0fe}.timings svg line.turn{stroke:#018dae;stroke-dasharray:1 5}.timings svg text.turn{fill:#018dae;font-size:1.25em}.timings svg circle.car{fill:#f94533}.timings svg .axis path{stroke:#018dae;stroke-dasharray:1 10}.timings svg .axis text{font-size:1.75em;font-family:VT323,monospace;fill:#018dae}.dash{width:25%;position:absolute;bottom:5em;left:.5em}.dash svg{-webkit-filter:drop-shadow(0 0 14px rgba(15,208,254,.5));filter:drop-shadow(0 0 14px rgba(15,208,254,.5))}.dash path.arc{stroke:#dacbed}.dash line.dial{stroke:#f94533;stroke-width:2px}.dash circle.dial{fill:#018dae}.dash text{fill:#018dae;font-size:1.25em}.dash text.label{font-size:.9em}.dash line{stroke:#dacbed}.circuit{width:60%;float:left;position:relative;margin:0 0 0 7.5%;padding:0}.circuit h1{position:absolute;font-size:1.25em;width:100%;text-align:center;top:7em;color:#018dae;text-shadow:0 0 14px rgba(1,141,174,.75)}.circuit svg{-webkit-filter:drop-shadow(0 0 14px #dacbed);filter:drop-shadow(0 0 14px #dacbed);overflow:visible}.circuit svg path.track{fill:none;stroke:#0fd0fe;opacity:.2}.circuit svg path.car{fill:#f94533}.circuit svg circle{fill:#f94533}.circuit svg circle.turn{fill:#f94533}.circuit svg line.normal{stroke:#dacbed}.circuit svg line.finish-line{stroke:#dacbed;stroke-width:2px}.circuit svg text{fill:#dacbed}.circuit svg text.turn{fill:#018dae;font-size:1.25em}
const drawCar = ({ car, g, getCoords }) => {
const data = [car].map(d => getCoords({ pct: d.measures.slice(-1)[0].pct }))
const triangles = g.selectAll('path.car').data(data)
triangles
.enter()
.append('path')
.attr('class', 'car')
.attr('d', d =>
d3
.symbol()
.type(d3.symbolTriangle)
.size(100)()
)
.merge(triangles)
.attr(
'transform',
d =>
`rotate(${-d.angle +
90} ${d[0]} ${d[1]}) translate(${d[0]} ${d[1]}) scale(1 1.5)`
)
triangles.exit().remove()
}
export default drawCar
const drawDash = ({ car, g, alpha }) => {
g.select('line.dial')
.attr('transform', `rotate(${alpha(car.measures.slice(-1)[0].speed)})`)
}
export default drawDash
const drawNormals = ({ g, positions, car }) => {
const lines = g
.selectAll('line.normal')
.data(positions.slice(0, car.positionIndex + 1))
lines
.attr('x2', d => d.curvature.elements[0])
.attr('y2', d => d.curvature.elements[1])
lines
.enter()
.append('line')
.attr('class', 'normal')
.attr('transform', d => `translate(${d.p[0]}, ${d.p[1]})`)
.attr('x2', 0)
.attr('y2', 0)
.transition()
.duration(100)
.attr('x2', d => d.curvature.elements[0])
.attr('y2', d => d.curvature.elements[1])
lines.exit().remove()
}
export default drawNormals
const drawTimingsChart = ({ g, x, y, line, car }) => {
// join
const paths = g.selectAll('path.car').data([car])
// enter + update
paths
.enter()
.append('path')
.attr('class', 'car')
.merge(paths)
.attr('d', d => line(d.measures))
// remove
paths.exit().remove()
// join
const circles = g.selectAll('circle.car').data([car])
// enter + update
circles
.enter()
.append('circle')
.attr('class', 'car')
.attr('r', 5)
.merge(circles)
.attr(
'transform',
d =>
`translate(${x(d.measures.slice(-1)[0].pct)} ${y(
d.measures.slice(-1)[0].speed
)})`
)
// remove
circles.exit().remove()
}
export default drawTimingsChart
const easing = {
// no easing, no acceleration
linear: function (t) { return t },
// accelerating from zero velocity
easeInQuad: function (t) { return t*t },
// decelerating to zero velocity
easeOutQuad: function (t) { return t*(2-t) },
// acceleration until halfway, then deceleration
easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t },
// accelerating from zero velocity
easeInCubic: function (t) { return t*t*t },
// decelerating to zero velocity
easeOutCubic: function (t) { return (--t)*t*t+1 },
// acceleration until halfway, then deceleration
easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 },
// accelerating from zero velocity
easeInQuart: function (t) { return t*t*t*t },
// decelerating to zero velocity
easeOutQuart: function (t) { return 1-(--t)*t*t*t },
// acceleration until halfway, then deceleration
easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t },
// accelerating from zero velocity
easeInQuint: function (t) { return t*t*t*t*t },
// decelerating to zero velocity
easeOutQuint: function (t) { return 1+(--t)*t*t*t*t },
// acceleration until halfway, then deceleration
easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t }
}
export default easing
const getAngle = ([a, b]) => {
const x = b[0] - a[0]
const y = -(b[1] - a[1])
const angle = Math.atan2(y, x) * 180 / Math.PI
return angle
}
export default getAngle
const getCurvature = ({ pct, getCoords, delta = 1 }) => {
// Get this point.
const p = getCoords({ pct })
// Get a point n pixels backward
// so we can draw a line to it.
const o = getCoords({ pct: 1 + pct, delta: -delta })
// Get a point n pixels forward
// so we can draw a line to it.
const q = getCoords({ pct, delta })
// Find po vector.
const po = $V([o[0] - p[0], o[1] - p[1]])
// Find pq vector.
const pq = $V([q[0] - p[0], q[1] - p[1]])
// Find the angle between the two vectors.
const angle = po.angleFrom(pq) * 180 / Math.PI
// Find the normal vector and scale it based on the angle
// between the two vectors.
const normal = pq
.rotate(Math.PI / 2, $V([0, 0]))
.toUnitVector()
.multiply(180 - angle)
// Determine concavity.
const isConcave = po.angleFrom(normal) * 180 / Math.PI >= 90
// Create curvature vector.
const curvature = normal.rotate(isConcave ? 0 : Math.PI, $V([0, 0]))
return {
p,
curvature
}
}
export default getCurvature
const getRandomArbitrary = (min, max) => Math.random() * (max - min) + min
export default getRandomArbitrary
import constants from './constants.js'
import getRandomArbitrary from './getRandomArbitrary.js'
import easing from './easing.js'
const moveCar = ({ car, delta, positions }) => {
const { measures } = car
const last = measures.slice(-1)[0]
// First, find the closest upcoming curvature measurement.
// We will use this to determine whether to slow down,
// speed up, or maintain current speed.
const positionIndex = _.findIndex(positions, d => d.pct >= last.pct % 1)
// This is the curvature strength.
const modulus = positions[positionIndex].curvature.modulus()
// If modulus is above a certain threshold,
let newSpeed = last.speed
if (modulus > 7) {
// brake.
newSpeed = last.speed * 0.87
} else {
// Otherwise accelerate.
newSpeed = last.speed +
_.clamp(3 * constants.MAX / last.speed, 0, 10)
}
// Make sure we're not too slow or too fast.
newSpeed = _.clamp(newSpeed, constants.MIN, constants.MAX)
// Calculate new pct.
const newPct = last.pct + delta * last.speed / 3600000
return {
...car,
positionIndex,
measures: [
...measures,
{
pct: newPct,
speed: newSpeed
}
]
}
}
const moveCarOneLap = ({ car, positions }) => {
let movedCar = {
...car,
measures: [
{
pct: 0,
speed: constants.MAX
}
],
elapsed: 0
}
const delta = 1000 / 60
let i = 0
while (movedCar.measures.slice(-1)[0].pct < 1 && i < 10000) {
movedCar = {
...moveCar({ car: movedCar, delta, positions }),
elapsed: movedCar.elapsed + delta
}
i++
}
if (movedCar.measures.slice(-1)[0].pct > 1) {
movedCar = {
...movedCar,
measures: movedCar.measures.slice(0, movedCar.measures.length - 1),
elapsed: movedCar.elapsed - delta
}
}
// console.log(JSON.stringify(movedCar, null, 2))
return movedCar
}
export { moveCarOneLap }
export default moveCar
{
"standard": {
"globals": [
"circuitTracks",
"d3",
"$V",
"_"
]
}
}
$black = #010b14
$white = #dacbed
$blue = #0fd0fe
$yellow = #f6df83
$light-blue = darken($blue, 35%)
$red = lighten(#f11c07, 20%)
$outer-padding = 0.5em
$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 $outer-padding
position relative
max-width 960px
max-height 500px
// // border solid $white 1px
svg
display block
h1
// visibility hidden
// border solid red 1px
color $white
// text-shadow 0 0 $blur-radius alpha($white, $text-blur-opacity)
text-transform uppercase
font-weight normal
font-style italic
float left
text-align center
padding 0 0 0 0em
margin 0.5em 0 0 0
width 25%
font-size 2em
span
text-shadow 0 0 $blur-radius alpha($white, $text-blur-opacity)
display inline-block
&.title
text-shadow 0 0 $blur-radius alpha($red, $text-blur-opacity)
font-size 1.5em
color $red
.other
position absolute
bottom 0.5em
color $light-blue
.timings
width 75%
margin-left 25%
position relative
svg
// shape-rendering crispEdges
filter drop-shadow(0 0 $blur-radius alpha($blue, $svg-blur-opacity * 0.5))
path.car
fill none
stroke $blue
line.turn
stroke $light-blue
stroke-dasharray 1 5
text.turn
fill $light-blue
font-size 1.25em
circle.car
fill $red
.axis
path
stroke $light-blue
stroke-dasharray 1 10
text
font-size 1.75em
font-family $font-family
fill $light-blue
.dash
width 25%
position absolute
bottom 5em
left $outer-padding
svg
// shape-rendering crispEdges
filter drop-shadow(0 0 $blur-radius alpha($blue, $svg-blur-opacity * 0.5))
path.arc
stroke $white
line.dial
stroke $red
stroke-width 2px
circle.dial
fill $light-blue
text
fill $light-blue
font-size 1.25em
&.label
font-size 0.9em
line
stroke $white
.circuit
width 60%
// border solid blue 1px
float left
// height 500px
position relative
margin 0 0 0 7.5%
padding 0
h1
position absolute
font-size 1.25em
width 100%
text-align center
top 7em
color $light-blue
text-shadow 0 0 $blur-radius alpha($light-blue, $text-blur-opacity)
svg
// margin 0 auto
filter drop-shadow(0 0 $blur-radius alpha($white, $svg-blur-opacity))
// shape-rendering crispEdges
overflow visible
path.track
fill none
stroke $blue
opacity 0.2
path.car
fill $red
circle
fill $red
&.turn
fill $red
line
&.normal
stroke $white
&.finish-line
stroke $white
stroke-width 2px
text
fill $white
&.turn
fill $light-blue
font-size 1.25em
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,{}))