I'm trying to define racecourse
with a dynamic line that user can draw on a canvas element. So when line has been drawn, program should add sidelines for it as shown in the picture below:
I have managed to mimic the idea already by using line normals but can't get it done correctly. At the moment I put point in the midway of the lines in the direction of the line normals and draw outlines using those points. While generated line is relatively smooth in cases on large turns, tight turns tend to produce loops.
As seen in image below:
Here is current code that generates points for side lines above (I'm using p5.js JavaScript library):
var sketch = function (p) {
with(p) {
let handpoints;
let walkhandpoints;
let collect;
let parsepath;
let shapse;
let step;
let tmp;
let dorender;
let lineoffset;
p.setup = function() {
createCanvas(600, 600);
handpoints = [];
walkhandpoints = 10;
collect = true;
parsepath = false;
shapes = [];
step = 2;
tmp = [];
dorender = true;
lineoffset = 15;
};
p.draw = function() {
if(dorender) {
background(220);
update();
for (let shape of shapes) {
shape.show();
}
}
};
function update() {
if (mouseIsPressed) {
if (collect) {
let mouse = createVector(mouseX, mouseY);
handpoints.push(mouse);
Shape.drawPath(handpoints);
parsepath = true;
}
} else if (parsepath) {
let tmp1 = Shape.cleanPath(handpoints, step);
let s1 = new Shape(tmp1, 1, 'line', color(175));
shapes.push(s1);
let tmp2 = Line.sidePoints(tmp1, lineoffset);
let s2 = new Shape(tmp2.sideA, 1, 'line', color(175,120,0));
let s3 = new Shape(tmp2.sideB, 1, 'line', color(175,0, 120));
shapes.push(s2);
shapes.push(s3);
handpoints = [];
parsepath = false;
//dorender = false;
}
}
class Shape {
constructor(points, mag, type = 'line', shader = color(200, 0, 100)) {
this.points = points.slice().map(item => item.copy());
this.type = type;
this.mag = mag;
this.shader = shader;
}
static cleanPath(points, step) {
let tmp = [];
let output = [];
for (let i = 1; i < points.length; i++) {
let prev = points[i - 1];
let curr = points[i];
if (!prev.equals(curr)) {
tmp.push(prev.copy())
if (i === points.length - 1) {
tmp.push(curr.copy())
}
}
}
for (let i = 0; i < tmp.length; i++) {
if(i % step === 0) {
output.push(tmp[i]);
}
}
output.push(output[0]);
return output;
}
static drawPath(points, mag = 1, type = 'line', shader = color(175)) {
let s = new Shape(points, mag, type, shader);
s.show();
}
show() {
for (let i = 0; i < this.points.length; i++) {
if (this.type === 'line' && i > 0) {
let prev = this.points[i - 1];
let curr = this.points[i];
strokeWeight(this.mag);
stroke(this.shader);
line(prev.x, prev.y, curr.x, curr.y);
} else if (this.type === 'point') {
noStroke();
fill(this.shader);
ellipse(this.points[i].x, this.points[i].y, this.mag * 2, this.mag * 2);
}
}
}
}
class Line {
static sidePoints(points, lineoffset) {
let sideA = [];
let sideB = [];
for(let i = 1; i < points.length; i++) {
// take consecutive points
let prev = points[i-1];
let curr = points[i];
// calculate normals
let dx = curr.x-prev.x;
let dy = curr.y-prev.y;
let a = createVector(-dy, dx).normalize();
let b = createVector(dy, -dx).normalize();
// calculate midway of the two points
let px = (prev.x+curr.x)/2;
let py = (prev.y+curr.y)/2;
let p = createVector(px,py);
// put created points back along drawed line
a.mult(lineoffset).add(p);
b.mult(lineoffset).add(p);
sideA.push(a);
sideB.push(b);
}
// close paths
if(!sideA[0].equals(sideA[sideA.length-1])) {
sideA.push(sideA[0]);
}
if(!sideB[0].equals(sideB[sideB.length-1])) {
sideB.push(sideB[0]);
}
return {sideA, sideB};
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
- Firstly I'd like to find a way to draw those points in the corresponding corner points of the drawn line so when drawn line has only few points the outlines would retain the drawn shape.
- Secondly is there some good way to reduce points on the areas where there are several of them to reduce those loops in small corners and other type errors in generated lines?
- Idea is to get points for lines, so it would be easy to detect with line intersection if race car velocity vector crosses it.
- Unfortunately I'm not very familiar with math notations so please try to use easy to understand version of them if there is some fancy math that would do the job.