diff --git a/car.js b/car.js index 11832fa..905d4e4 100644 --- a/car.js +++ b/car.js @@ -1,75 +1,196 @@ -class Car { - constructor(x, y, width, height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - - this.speed = 0; - this.acceleration = 0.2; - this.maxSpeed = 3; - this.friction = 0.05; - this.angle = 0; - - this.sensor = new Sensor(this); - this.controls = new Controls(); - } - - update() { - this.#move(); - this.sensor.update(); - } - - #move() { - if (this.controls.forward) { - this.speed += this.acceleration; - } - if (this.controls.reverse) { - this.speed -= this.acceleration; - } +/* +MIT License - if (this.speed > this.maxSpeed) { - this.speed = this.maxSpeed; - } +Copyright (c) 2022 Radu Mariescu-Istodor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ +class Car{ + constructor(x,y,width,height,controlType,maxSpeed=3,color="blue"){ + this.x=x; + this.y=y; + this.width=width; + this.height=height; + + this.speed=0; + this.acceleration=0.2; + this.maxSpeed=maxSpeed; + this.friction=0.05; + this.angle=0; + this.damaged=false; + this.fitness=0; + + this.useBrain=controlType=="AI"; + + if(controlType!="DUMMY"){ + this.sensor=new Sensor(this); + this.brain=new NeuralNetwork( + [this.sensor.rayCount,6,4] + ); + } + this.controls=new Controls(controlType); + + this.img=new Image(); + this.img.src="car.png" - if (this.speed < -this.maxSpeed / 2) { - this.speed = -this.maxSpeed / 2; + this.mask=document.createElement("canvas"); + this.mask.width=width; + this.mask.height=height; + + const maskCtx=this.mask.getContext("2d"); + this.img.onload=()=>{ + maskCtx.fillStyle=color; + maskCtx.rect(0,0,this.width,this.height); + maskCtx.fill(); + + maskCtx.globalCompositeOperation="destination-atop"; + maskCtx.drawImage(this.img,0,0,this.width,this.height); + } } - if (this.speed > 0) { - this.speed -= this.friction; + update(road,traffic){ + if(!this.damaged){ + const old={x:this.x,y:this.y} + this.#move(); + this.fitness+=old.y-this.y; + + const laneWidth=road.width/road.laneCount; + const penalty=Math.abs((this.x-road.left)%laneWidth-laneWidth/2); + this.fitness-=penalty*0.05; + this.polygon=this.#createPolygon(); + this.damaged=this.#assessDamage(road.borders,traffic); + } + if(this.sensor){ + this.sensor.update(road.borders,traffic); + const offsets=this.sensor.readings.map( + s=>s==null?0:1-s.offset + ); + const outputs=NeuralNetwork.feedForward(offsets,this.brain); + + if(this.useBrain){ + this.controls.forward=outputs[0]; + this.controls.left=outputs[1]; + this.controls.right=outputs[2]; + this.controls.reverse=outputs[3]; + } + } } - if (this.speed < 0) { - this.speed += this.friction; + + #assessDamage(roadBorders,traffic){ + for(let i=0;i 0 ? 1 : -1; + #move(){ + if(this.controls.forward){ + this.speed+=this.acceleration; + } + if(this.controls.reverse){ + this.speed-=this.acceleration; + } + + if(this.speed>this.maxSpeed){ + this.speed=this.maxSpeed; + } + if(this.speed<-this.maxSpeed/2){ + this.speed=-this.maxSpeed/2; + } - if (this.controls.left) { - this.angle += 0.03 * flip; - } - if (this.controls.right) { - this.angle -= 0.03 * flip; - } + if(this.speed>0){ + this.speed-=this.friction; + } + if(this.speed<0){ + this.speed+=this.friction; + } + if(Math.abs(this.speed)0?1:-1; + if(this.controls.left){ + this.angle+=0.03*flip; + } + if(this.controls.right){ + this.angle-=0.03*flip; + } + } + + this.x-=Math.sin(this.angle)*this.speed; + this.y-=Math.cos(this.angle)*this.speed; } - this.x -= Math.sin(this.angle) * this.speed; - this.y -= Math.cos(this.angle) * this.speed; - } - draw(ctx) { - ctx.save(); - ctx.translate(this.x, this.y); - ctx.rotate(-this.angle); - ctx.beginPath(); - ctx.rect(-this.width / 2, -this.height / 2, this.width, this.height); - - ctx.fill(); - ctx.restore(); - this.sensor.draw(ctx); - } -} + draw(ctx,drawSensor=false){ + if(this.sensor && drawSensor){ + this.sensor.draw(ctx); + } + + ctx.save(); + ctx.translate(this.x,this.y); + ctx.rotate(-this.angle); + if(!this.damaged){ + ctx.drawImage(this.mask, + -this.width/2, + -this.height/2, + this.width, + this.height); + ctx.globalCompositeOperation="multiply"; + } + ctx.drawImage(this.img, + -this.width/2, + -this.height/2, + this.width, + this.height); + ctx.restore(); + + } +} \ No newline at end of file diff --git a/car.png b/car.png new file mode 100644 index 0000000..d969908 Binary files /dev/null and b/car.png differ diff --git a/controls.js b/controls.js index e2f52ec..fcadc3f 100644 --- a/controls.js +++ b/controls.js @@ -1,46 +1,76 @@ -class Controls { - constructor() { - this.forward = false; - this.left = false; - this.right = false; - this.reverse = false; +/* +MIT License - this.#addKeyBoardListeners(); - } +Copyright (c) 2022 Radu Mariescu-Istodor - #addKeyBoardListeners() { - document.onkeydown = (event) => { - switch (event.key) { - case 'ArrowLeft': - this.left = true; - break; - case 'ArrowRight': - this.right = true; - break; - case 'ArrowUp': - this.forward = true; - break; - case 'ArrowDown': - this.reverse = true; - break; - } - }; +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - document.onkeyup = (event) => { - switch (event.key) { - case 'ArrowLeft': - this.left = false; - break; - case 'ArrowRight': - this.right = false; - break; - case 'ArrowUp': - this.forward = false; - break; - case 'ArrowDown': - this.reverse = false; - break; - } - }; - } -} +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ +class Controls{ + constructor(type){ + this.forward=false; + this.left=false; + this.right=false; + this.reverse=false; + + switch(type){ + case "KEYS": + this.#addKeyboardListeners(); + break; + case "DUMMY": + this.forward=true; + break; + } + } + + #addKeyboardListeners(){ + document.onkeydown=(event)=>{ + switch(event.key){ + case "ArrowLeft": + this.left=true; + break; + case "ArrowRight": + this.right=true; + break; + case "ArrowUp": + this.forward=true; + break; + case "ArrowDown": + this.reverse=true; + break; + } + } + document.onkeyup=(event)=>{ + switch(event.key){ + case "ArrowLeft": + this.left=false; + break; + case "ArrowRight": + this.right=false; + break; + case "ArrowUp": + this.forward=false; + break; + case "ArrowDown": + this.reverse=false; + break; + } + } + } +} \ No newline at end of file diff --git a/index.html b/index.html index 0abcdca..94df2dd 100644 --- a/index.html +++ b/index.html @@ -1,18 +1,73 @@ + - - - - - - Self Driving Car - - - + + Self-driving car - No libraries + + + + + +
+ + + + +
+ + + - - + + \ No newline at end of file diff --git a/main.js b/main.js index d3226ad..b163f86 100644 --- a/main.js +++ b/main.js @@ -1,23 +1,123 @@ -const canvas = document.getElementById('myCanvas'); +/* +MIT License -canvas.width = 200; +Copyright (c) 2022 Radu Mariescu-Istodor -const ctx = canvas.getContext('2d'); +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -const road = new Road(canvas.width / 2, canvas.width); -const car = new Car(road.getLaneCenter(1), 100, 30, 50); -car.draw(ctx); +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ +document.getElementById('carCount').value= + localStorage.getItem("carCount") || 1; +document.getElementById('mutationAmount').value= + localStorage.getItem("mutationAmount") || '0.5'; + +const carCanvas=document.getElementById("carCanvas"); +carCanvas.width=200; +const networkCanvas=document.getElementById("networkCanvas"); +networkCanvas.width=500; + +const carCtx = carCanvas.getContext("2d"); +const networkCtx = networkCanvas.getContext("2d"); + +const road=new Road(carCanvas.width/2,carCanvas.width*0.9); + +const N=Number(document.getElementById('carCount').value); +const cars=generateCars(N); +let bestCar=cars[0]; + + +if(!localStorage.getItem("beenHereBefore")){ + localStorage.setItem("beenHereBefore","true"); + localStorage.setItem("bestBrain",'{"levels":[{"inputs":[0.7134037395397386,0.4704391617329813,0,0,0],"outputs":[1,0,1,0,1,1],"biases":[-0.2857065784574137,0.15198050810320685,-0.13924253314793095,0.11578316440435818,-0.16178778119340148,-0.3000903730682978],"weights":[[-0.13515557540433842,0.07300698004528629,0.0040602602910803365,-0.06511672542187383,0.2351613353184897,-0.2012322725070096],[0.11375478789219415,0.19674169055322577,-0.10038853369062525,-0.08805459204957476,0.21925787393967702,0.15340538795108272],[0.006771304836725459,0.19973693546258126,-0.054730364561966574,-0.31855113026094745,-0.18011218120061373,-0.07367159677011853],[0.10788433228858771,-0.08604768151855152,-0.16601129743851203,-0.00019124113279275767,-0.2387343271874623,0.024133579587637094],[0.016549344407090646,0.07491472818610108,0.01776519928759422,0.061508369764843265,-0.22340799373690096,-0.04158178250497961]]},{"inputs":[1,0,1,0,1,1],"outputs":[1,1,1,0],"biases":[-0.06116069156441874,-0.0723274390864902,0.040602477018632524,0.36214587805918513],"weights":[[0.08902059474663654,-0.15740460212292795,-0.33036790341757627,-0.09502772355825113],[-0.025343350670927654,-0.3306790265709002,-0.015918008460683405,-0.3003507035765679],[0.3227266376583849,0.38057846051375693,0.18707727773028537,0.053444661926534916],[-0.2025644147140744,0.1184725671846302,-0.09069904024640596,-0.12430190885008531],[0.07527020196440684,-0.230469927588571,0.3007741658810536,0.22218297028978307],[-0.01361908920713352,0.16609276689516542,-0.09581290649261008,-0.039394098855477074]]}]}'); +} +if(localStorage.getItem("bestBrain")){ + for(let i=0;ic.fitness==Math.max( + ...cars.map(c=>c.fitness) + )); + + carCanvas.height=window.innerHeight; + networkCanvas.height=window.innerHeight; + + carCtx.save(); + carCtx.translate(0,-bestCar.y+carCanvas.height*0.7); + + road.draw(carCtx); + for(let i=0;i { + for(let i=0;ilevel.biases[i]){ + level.outputs[i]=1; + }else{ + level.outputs[i]=0; + } + } + + return level.outputs; + } +} \ No newline at end of file diff --git a/road.js b/road.js index 08b125b..738928f 100644 --- a/road.js +++ b/road.js @@ -1,55 +1,80 @@ -class Road { - constructor(x, width, laneCount = 3) { - this.x = x; - this.width = width; - this.laneCount = laneCount; - - this.left = x - width / 2; - this.right = x + width / 2; - - const infinity = 1000000; - this.top = -infinity; - this.bottom = infinity; - - const topLeft = { x: this.left, y: this.top }; - const topRight = { x: this.right, y: this.top }; - const bottomLeft = { x: this.left, y: this.bottom }; - const bottomRight = { x: this.right, y: this.bottom }; - this.borders = [ - [topLeft, bottomLeft], - [topRight, bottomRight], - ]; - } - - getLaneCenter(laneIndex) { - const laneWidth = this.width / this.laneCount; - return ( - this.left + - laneWidth / 2 + - Math.min(laneIndex, this.laneCount - 1) * laneWidth - ); - } - - draw(ctx) { - ctx.lineWidth = 5; - ctx.strokeStyle = 'white'; - - for (let i = 1; i <= this.laneCount - 1; i++) { - const x = lerp(this.left, this.right, i / this.laneCount); - - ctx.setLineDash([20, 20]); - ctx.beginPath(); - ctx.moveTo(x, this.top); - ctx.lineTo(x, this.bottom); - ctx.stroke(); +/* +MIT License + +Copyright (c) 2022 Radu Mariescu-Istodor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ +class Road{ + constructor(x,width,laneCount=3){ + this.x=x; + this.width=width; + this.laneCount=laneCount; + + this.left=x-width/2; + this.right=x+width/2; + + const infinity=1000000; + this.top=-infinity; + this.bottom=infinity; + + const topLeft={x:this.left,y:this.top}; + const topRight={x:this.right,y:this.top}; + const bottomLeft={x:this.left,y:this.bottom}; + const bottomRight={x:this.right,y:this.bottom}; + this.borders=[ + [topLeft,bottomLeft], + [topRight,bottomRight] + ]; + } + + getLaneCenter(laneIndex){ + const laneWidth=this.width/this.laneCount; + return this.left+laneWidth/2+ + Math.min(laneIndex,this.laneCount-1)*laneWidth; } - ctx.setLineDash([]); - this.borders.forEach((border) => { - ctx.beginPath(); - ctx.moveTo(border[0].x, border[0].y); - ctx.lineTo(border[1].x, border[1].y); - ctx.stroke(); - }); - } -} + draw(ctx){ + ctx.lineWidth=5; + ctx.strokeStyle="white"; + + for(let i=1;i<=this.laneCount-1;i++){ + const x=lerp( + this.left, + this.right, + i/this.laneCount + ); + + ctx.setLineDash([20,20]); + ctx.beginPath(); + ctx.moveTo(x,this.top); + ctx.lineTo(x,this.bottom); + ctx.stroke(); + } + + ctx.setLineDash([]); + this.borders.forEach(border=>{ + ctx.beginPath(); + ctx.moveTo(border[0].x,border[0].y); + ctx.lineTo(border[1].x,border[1].y); + ctx.stroke(); + }); + } +} \ No newline at end of file diff --git a/sensor.js b/sensor.js index 6f4e925..4a70ef9 100644 --- a/sensor.js +++ b/sensor.js @@ -1,57 +1,84 @@ +/* +MIT License + +Copyright (c) 2022 Radu Mariescu-Istodor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ class Sensor { constructor(car) { this.car = car; - this.rayCount = 3; + this.rayCount = 5; this.rayLength = 150; this.raySpread = Math.PI / 2; this.rays = []; this.readings = []; - this.#castRays(); } - update() { + update(roadBorders, traffic) { this.#castRays(); + this.readings = []; + for (let i = 0; i < this.rays.length; i++) { + this.readings.push(this.#getReading(this.rays[i], roadBorders, traffic)); + } } - // #getReading(ray, roadBorders, traffic) { - // let touches = []; + #getReading(ray, roadBorders, traffic) { + let touches = []; - // for (let i = 0; i < roadBorders.length; i++) { - // const touch = getIntersection( - // ray[0], - // ray[1], - // roadBorders[i][0], - // roadBorders[i][1] - // ); - // if (touch) { - // touches.push(touch); - // } - // } + for (let i = 0; i < roadBorders.length; i++) { + const touch = getIntersection( + ray[0], + ray[1], + roadBorders[i][0], + roadBorders[i][1] + ); + if (touch) { + touches.push(touch); + } + } - // for (let i = 0; i < traffic.length; i++) { - // const poly = traffic[i].polygon; - // for (let j = 0; j < poly.length; j++) { - // const value = getIntersection( - // ray[0], - // ray[1], - // poly[j], - // poly[(j + 1) % poly.length] - // ); - // if (value) { - // touches.push(value); - // } - // } - // } + for (let i = 0; i < traffic.length; i++) { + const poly = traffic[i].polygon; + for (let j = 0; j < poly.length; j++) { + const value = getIntersection( + ray[0], + ray[1], + poly[j], + poly[(j + 1) % poly.length] + ); + if (value) { + touches.push(value); + } + } + } - // if (touches.length == 0) { - // return null; - // } else { - // const offsets = touches.map((e) => e.offset); - // const minOffset = Math.min(...offsets); - // return touches.find((e) => e.offset == minOffset); - // } - // } + if (touches.length == 0) { + return null; + } else { + const offsets = touches.map((e) => e.offset); + const minOffset = Math.min(...offsets); + return touches.find((e) => e.offset == minOffset); + } + } #castRays() { this.rays = []; @@ -71,14 +98,26 @@ class Sensor { this.rays.push([start, end]); } } + draw(ctx) { for (let i = 0; i < this.rayCount; i++) { + let end = this.rays[i][1]; + if (this.readings[i]) { + end = this.readings[i]; + } + ctx.beginPath(); ctx.lineWidth = 2; ctx.strokeStyle = 'yellow'; - console.log(this.rays); ctx.moveTo(this.rays[i][0].x, this.rays[i][0].y); - ctx.lineTo(this.rays[i][1].x, this.rays[i][1].y); + ctx.lineTo(end.x, end.y); + ctx.stroke(); + + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.strokeStyle = 'black'; + ctx.moveTo(this.rays[i][1].x, this.rays[i][1].y); + ctx.lineTo(end.x, end.y); ctx.stroke(); } } diff --git a/style.css b/style.css index d8d3527..57a6ee0 100644 --- a/style.css +++ b/style.css @@ -1,10 +1,65 @@ -body { - margin: 0; - background: darkgray; - overflow: center; - text-align: center; +/* +MIT License + +Copyright (c) 2022 Radu Mariescu-Istodor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ +body{ + margin:0; + background:darkgray; + overflow:hidden; + display:flex; + justify-content:center; + align-items:center; +} + +#verticalButtons{ + display:flex; + flex-direction:column; } -#myCanvas { - background: lightgray; +button{ + border: none; + border-radius: 5px; + padding: 5px 5px 7px 5px; + margin: 2px; + cursor:pointer; +} +button:hover{ + background:blue; +} + +#networkCanvas{ + background:black; +} +#carCanvas{ + background:lightgray; } +.dropdown{ + display:flex; + flex-direction:column; + align-items:center; + margin:2px; + border: none; + border-radius: 5px; + background:whitesmoke; + padding:2px; +} \ No newline at end of file diff --git a/utils.js b/utils.js index 8564ef9..9a37369 100644 --- a/utils.js +++ b/utils.js @@ -1,3 +1,78 @@ -function lerp(A, B, t) { - return A + (B - A) * t; +/* +MIT License + +Copyright (c) 2022 Radu Mariescu-Istodor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +© 2022 GitHub, Inc. +*/ +function lerp(A,B,t){ + return A+(B-A)*t; } + +function getIntersection(A,B,C,D){ + const tTop=(D.x-C.x)*(A.y-C.y)-(D.y-C.y)*(A.x-C.x); + const uTop=(C.y-A.y)*(A.x-B.x)-(C.x-A.x)*(A.y-B.y); + const bottom=(D.y-C.y)*(B.x-A.x)-(D.x-C.x)*(B.y-A.y); + + if(bottom!=0){ + const t=tTop/bottom; + const u=uTop/bottom; + if(t>=0 && t<=1 && u>=0 && u<=1){ + return { + x:lerp(A.x,B.x,t), + y:lerp(A.y,B.y,t), + offset:t + } + } + } + + return null; +} + +function polysIntersect(poly1, poly2){ + for(let i=0;i0?0:255; + return "rgba("+R+","+G+","+B+","+alpha+")"; +} + +function getRandomColor(){ + const hue=290+Math.random()*260; + return "hsl("+hue+", 100%, 60%)"; +} + \ No newline at end of file diff --git a/visualizer.js b/visualizer.js new file mode 100644 index 0000000..f923d4e Binary files /dev/null and b/visualizer.js differ