xxxxxxxxxx
195
// Alfredo Canziani, 18 Aug 2021
let V = []
let U = []
let N = 16
let A = []
const norm = 50
let svd
const red = '#fb8072'
const green = '#b3de69'
const orange = '#fdb462'
const blue = '#80b1d3'
const blue_orange = [blue, orange]
function setup() {
const w = Math.min(window.innerWidth, 600)
const h = Math.min(window.innerHeight, 400)
// console.log(w, h)
createCanvas(w, h);
// Centre coordinates
cx = width / 2;
cy = height / 2;
for (let n = 0; n < N; n++) {
V[n] = [];
V[n][0] = norm * cos(n * 2 * PI / N);
V[n][1] = norm * sin(n * 2 * PI / N);
U[n] = [V[n]];
}
A[0] = [1, 0]
A[1] = [0, 1]
// Create reset button
button = createButton('Reset')
button.position(0, height-20)
button.mousePressed(reset)
button.style('background-color', 'black')
button.style('color', '#bbbbbb')
button.style('border', 'none')
bases = [
new Draggable(V[0][0]+cx, V[0][1]+cy, red),
new Draggable(V[12][0]+cx, V[12][1]+cy, green)
]
s_vectors = [
new Draggable(0, 0, blue),
new Draggable(0, 0, orange)
]
compute_singular_vectors()
}
function draw() {
background("black")
let d = 5
noStroke()
for (let n = 0; n < N; n++) {
// Update u vectors
U[n] = mult(V[n])
// Move u and v to the centre
let ux = U[n][0] + cx
let uy = U[n][1] + cy
let vx = V[n][0] + cx
let vy = V[n][1] + cy
// Draw the input points in grey
// Draw i and j in R and G
fill('grey')
switch (n) {
case 0: fill(red); break
case 12: fill(green); break
}
ellipse(vx, vy, d)
// Set output points in white
fill('white')
// Draw output points
ellipse(ux, uy, d)
}
// I don't really know…
// for (let i = 0; i < 2; i++) {
// let vx = -svd.V.data[0][i] * norm * det + cx
// let vy = svd.V.data[1][i] * norm + cy
// fill(blue_orange[i])
// ellipse(vx, vy, d)
// }
let over = false
for (let i = 0; i < 2; i++) {
let b = bases[i]
let xy = b.update()
if (b.dragging) {
A[0][i] = (xy[0]-cx)/norm
A[1][i] = (xy[1]-cy)/norm
compute_singular_vectors()
}
over = b.over() || over
b.show()
let s = s_vectors[i]
xy = s.update()
over = s.over() || over
s.show()
stroke(s.c)
line(xy[0], xy[1], 2 * cx - xy[0], 2 * cy - xy[1])
if (s.dragging) {
let ux = svd.U.data[0][i]
let uy = svd.U.data[1][i]
let x = s.x - cx
let y = s.y - cy
let len = sqrt((x)**2 + (y)**2)
let theta = -atan2(x*uy-y*ux, x*ux+y*uy)
svd.s[i] = len / norm
// console.log(angle / PI * 180)
let rot = new mlMatrix.Matrix([
[cos(theta), -sin(theta)],
[sin(theta), cos(theta)]
])
let new_U = rot.mmul(svd.U)
let R = new_U.mmul(mlMatrix.Matrix.diag(svd.s)).mmul(svd.V.transpose())
A = R.to2DArray()
update_bases()
let sx, sy
sx = new_U.data[1-i][0] * svd.s[1-i] * norm + cx
sy = -new_U.data[1-i][1] * svd.s[1-i] * norm * det + cy
s_vectors[1-i].x = sx
s_vectors[1-i].y = sy
}
}
if (over) cursor('grab')
else cursor('default')
}
function mult(v) {
let ux, uy
ux = A[0][0] * v[0] + A[0][1] * v[1]
uy = A[1][0] * v[0] + A[1][1] * v[1]
return [ux, uy]
}
function mousePressed() {
bases.forEach(b => b.pressed())
s_vectors.forEach(s => s.pressed())
}
function mouseReleased() {
bases.forEach(b => b.released())
s_vectors.forEach(s => s.released())
}
function mouseDragged() {
}
function compute_singular_vectors() {
svd = new mlMatrix.SVD(A)
let S = mlMatrix.Matrix.diag(svd.s)
// Fucking determinant!
det = mlMatrix.determinant(svd.U)
// Update s'vectors
for (let i = 0; i < 2; i++) {
let sx, sy
sx = svd.U.data[i][0] * svd.s[i] * norm + cx
sy = -svd.U.data[i][1] * svd.s[i] * norm * det + cy
// sx = -svd.U.data[i][0] * svd.s[i] * det * norm + cx
// sy = svd.U.data[i][1] * svd.s[i] * norm + cy
s_vectors[i].x = sx
s_vectors[i].y = sy
}
}
function update_bases() {
let xy = mult(V[0])
bases[0].x = xy[0] + cx
bases[0].y = xy[1] + cy
xy = mult(V[12])
bases[1].x = xy[0] + cx
bases[1].y = xy[1] + cy
}
function reset() {
A[0] = [1, 0]
A[1] = [0, 1]
compute_singular_vectors()
update_bases()
}