|
class Sensor{ |
|
constructor(){ |
|
this.rayCount=5; |
|
this.rayLength=150; |
|
this.raySpread=Math.PI/4; |
|
|
|
this.rays=[]; |
|
this.readings=[]; |
|
} |
|
|
|
update(x,y,angle,roadBorders,traffic){ |
|
this.#castRays(x,y,angle); |
|
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=[]; |
|
|
|
roadBorders.forEach(border => { |
|
for(let i=1;i<border.length;i++){ |
|
const touch=getIntersection( |
|
ray[0], |
|
ray[1], |
|
border[i-1], |
|
border[i] |
|
); |
|
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); |
|
} |
|
} |
|
} |
|
|
|
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(x,y,angle){ |
|
this.rays=[]; |
|
for(let i=0;i<this.rayCount;i++){ |
|
const rayAngle=lerp( |
|
this.raySpread/2, |
|
-this.raySpread/2, |
|
this.rayCount==1?0.5:i/(this.rayCount-1) |
|
)+angle; |
|
|
|
const start={x,y}; |
|
const end={ |
|
x:x-Math.sin(rayAngle)*this.rayLength, |
|
y:y-Math.cos(rayAngle)*this.rayLength |
|
}; |
|
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"; |
|
ctx.moveTo( |
|
this.rays[i][0].x, |
|
this.rays[i][0].y |
|
); |
|
ctx.lineTo( |
|
end.x, |
|
end.y |
|
); |
|
ctx.stroke(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
} |
|
} |