Transform = {
function Slider (options={}) {
let {title="", min=-100, max=100, step=1, size="20em", value=0, callback=()=>0 } = options;
let input = html`<input type=range min=${min} max=${max} step=${step} style='width:${size}'>`;
let output = html`<output>${value}</output>`;
input.value = value;
input.oninput = (e) => {
value = input.value;
output.value = value
callback(value)
}
return html`${title}: ${input} ${output}`;
}
function XYSlider (options) {
let {title="", min=-100, max=100, step=1, size="10em", value=Vec(0,0), callback=()=>0 } = options;
let xslider = Slider({title:"x", min:min, max:max, step:step, value:value.x,
callback : x=> {value.x = +x; callback(value) }});
let yslider = Slider({title:"y", min:min, max:max, step:step, value:value.y,
callback : y=> {value.y = +y; callback(value) }});
return html`${title} ${xslider} , ${yslider}`;
}
function matrixTex (m) {
let fmt = (x) => x.toFixed(4) == x ? x : x.toFixed(4);
return `\\begin{bmatrix}
${fmt(m.a)} & ${fmt(m.c)} & ${fmt(m.e)} \\\\
${fmt(m.b)} & ${fmt(m.d)} & ${fmt(m.f)} \\\\
0 & 0 & 1
\\end{bmatrix}`
}
class Transform {
constructor (callback = (t) => {}) {
this.callback = callback;
this.matrices = [];
this.sliders = [];
}
reset () {
this.matrices = [];
this.sliders = [];
this.callback(this);
return this
}
addTranslate () {
let i = this.matrices.length;
this.matrices.push (new Matrix())
this.sliders.push (XYSlider({title : "Translate ", min:-100, max:100, step:1, value:Vec(0,0),
callback : t => { this.matrices[i] = Matrix.translate(t.x,t.y); this.callback(this) }}));
return this;
}
addScale() {
let i = this.matrices.length;
this.matrices.push (new Matrix())
this.sliders.push (XYSlider({title : "Scale ", min:0.2, max:4, step:0.2, value:Vec(1,1),
callback: s => { this.matrices[i] = Matrix.scale(s.x,s.y); this.callback(this) }}));
return this;
}
addShear() {
let i = this.matrices.length;
this.matrices.push (new Matrix())
this.sliders.push (XYSlider({title : "Shear ", min:-4, max:4, step:0.2, value:Vec(0,0),
callback: s => { this.matrices[i] = Matrix.shear(s.x,s.y); this.callback(this) }}));
return this;
}
addRotate() {
let i = this.matrices.length;
this.matrices.push (new Matrix())
this.sliders.push (Slider({title:"Rotate", min:-180, max:180, step:5, value:0,
callback : a => { this.matrices[i] = Matrix.rotate(Math.PI*a/180);
this.callback(this) }}));
return this;
}
UI () {
let ui = html`<div></div>`;
for (let slider of this.sliders) {
ui.append(slider)
ui.append(html`<br>`)
}
return ui
}
matrix() {
if (this.matrices.length == 0) return new Matrix()
if (this.matrices.length == 1) return this.matrices[0];
return this.matrices.reduce((a,b) => a.mult(b))
}
tex() {
if (this.matrices.length < 2) return tex`${matrixTex(this.matrix())}`;
return tex`${(this.matrices.map(matrixTex)).join("\\times")+"="+matrixTex(this.matrix())}`;
}
}
return Transform
}