xxxxxxxxxx
83
/*
* Simple Line Fitting
* Keith J. O'Hara <kohara2@swarthmore.edu>
* March 2024
*/
// uses ML.js https://github.com/mljs/ml?tab=readme-ov-file
// because jsfeat svd is broken :(
// the points we are fitting
let p = [];
function setup() {
createCanvas(640, 480);
noStroke();
background(255);
}
function mouseDragged() {
p.push(new p5.Vector(mouseX, mouseY, 1));
}
function estimate_least_squares() {
let x = new p5.Vector(0, 0, 0);
if (p.length >= 2) {
as = []
bs = []
// Creates the estimation matrix
for (let i = 0; i < p.length; i++) {
as.push([p[i].x, 1]);
bs.push([p[i].y]);
}
let mls_a = new ML.Matrix(as);
let mls_b = new ML.Matrix(bs);
let mls_x = ML.MatrixLib.solve(mls_a, mls_b);
let a = mls_x.data[0][0];
let b = -1;
let c = mls_x.data[1][0];
x = new p5.Vector(a, b, c);
}
return x;
}
function draw() {
background(255);
stroke(0);
fill(0);
for (let i = 0; i < p.length; i++) {
ellipse(p[i].x, p[i].y, 5, 5);
}
if (p.length >= 2) {
stroke(255, 0, 0);
strokeWeight(2);
let ln = estimate_least_squares();
plotLine(ln);
let tot = 0;
for (let i = 0; i < p.length; i++) {
let error = ln.dot(p[i]);
tot += sq(error);
// show error
strokeWeight(0.5);
stroke(200, 0, 0);
line(p[i].x, p[i].y, p[i].x, p[i].y + error);
}
textSize(24);
text("total error^2 = " + round(tot), 20, 20);
}
}
function plotLine(v) {
if (abs(v.x) < abs(v.y)) {
// mostly horizontal
line(0, v.z/-v.y, width, (v.x*width + v.z)/-v.y);
}
else {
//mostly vertical
line(v.z/-v.x, 0, (v.y * height + v.z)/-v.x, height);
}
}