lots more tidying

This commit is contained in:
Christian Lawson-Perfect 2025-05-07 16:46:54 +01:00
parent bdf1890ac0
commit 25000efeae
2 changed files with 208 additions and 381 deletions

View file

@ -32,7 +32,7 @@ textarea {
} }
.tile { .tile {
color: #ccc; color: #eee;
} }
.tile.highlight { .tile.highlight {
@ -60,10 +60,6 @@ textarea {
<section id="controls"> <section id="controls">
<fieldset> <fieldset>
<legend>Viewbox</legend> <legend>Viewbox</legend>
<label for="ox">ox</label>
<input type="number" min="-500" max="500" value="0" id="ox">
<label for="oy">oy</label>
<input type="number" min="-500" max="500" value="0" id="oy">
<label for="scale">scale</label> <label for="scale">scale</label>
<input type="range" min="1" max="100" value="19" id="scale"> <input type="range" min="1" max="100" value="19" id="scale">
<label for="num_iterations">Number of iterations</label> <label for="num_iterations">Number of iterations</label>

577
script.js
View file

@ -15,15 +15,15 @@ class Point {
this.y = y; this.y = y;
} }
add( q ) { add(q) {
return { x : this.x + q.x, y : this.y + q.y }; return { x : this.x + q.x, y : this.y + q.y };
} }
sub( q ) { sub(q) {
return { x : this.x - q.x, y : this.y - q.y }; return { x : this.x - q.x, y : this.y - q.y };
} }
frame( p, q, a, b ) { frame(p, q, a, b) {
return{ x : this.x + a*p.x + b*q.x, y : this.y + a*p.y + b*q.y }; return{ x : this.x + a*p.x + b*q.x, y : this.y + a*p.y + b*q.y };
} }
@ -52,7 +52,7 @@ class Matrix {
]); ]);
} }
mul( other ) { mul(other) {
const A = this.mat; const A = this.mat;
const B = other.mat; const B = other.mat;
return new Matrix([ return new Matrix([
@ -67,9 +67,9 @@ class Matrix {
} }
// Rotation matrix // Rotation matrix
static rotation( ang ) { static rotation(ang) {
const c = cos( ang ); const c = cos(ang);
const s = sin( ang ); const s = sin(ang);
return new Matrix([c, -s, 0, s, c, 0]); return new Matrix([c, -s, 0, s, c, 0]);
} }
@ -77,16 +77,16 @@ class Matrix {
return new Matrix([x,0,0,0,y,0]); return new Matrix([x,0,0,0,y,0]);
} }
static translate( tx, ty ) { static translate(tx, ty) {
return new Matrix([1, 0, tx, 0, 1, ty]); return new Matrix([1, 0, tx, 0, 1, ty]);
} }
// Translation matrix moving p to q // Translation matrix moving p to q
static translateTo( p, q ) { static translateTo(p, q) {
return Matrix.translate( q.x - p.x, q.y - p.y ); return Matrix.translate(q.x - p.x, q.y - p.y);
} }
transform( P ) { transform(P) {
const M = this.mat; const M = this.mat;
return new Point(M[0]*P.x + M[1]*P.y + M[2], M[3]*P.x + M[4]*P.y + M[5]); return new Point(M[0]*P.x + M[1]*P.y + M[2], M[3]*P.x + M[4]*P.y + M[5]);
} }
@ -117,7 +117,7 @@ const base_quad = [spectre[3], spectre[5], spectre[7], spectre[11]];
function getsvg(event) { function getsvg(event) {
let t = event.target; let t = event.target;
while(t && t.tagName.toLowerCase()!='svg') { while(t && t.tagName.toLowerCase()!='svg') {
t = t.parentElement; t = t.parentElement;
} }
return t; return t;
} }
@ -134,114 +134,52 @@ function getcoords(event) {
return position; return position;
} }
// Match unit interval to line segment p->q class Tile {
function matchSeg( p, q ) constructor(pts, quad) {
{ this.pts = pts;
return [q.x-p.x, p.y-q.y, p.x, q.y-p.y, q.x-p.x, p.y]; this.quad = quad;
};
// Match line segment p1->q1 to line segment p2->q2 this.pts = [pts[pts.length-1]];
function matchTwo( p1, q1, p2, q2 ) for(const p of pts) {
{ const prev = this.pts[this.pts.length-1];
return matchSeg( p2, q2 ).mul(matchSeg( p1, q1 ).inverse()); const v = p.sub(prev);
}; const w = new Point(-v.y, v.x);
this.pts.push(prev.frame(v, w, 0.5, -0.3));
this.pts.push(prev.frame(v, w, 0.5, 0.3));
this.pts.push(p);
}
}
function drawPolygon( shape, T, f, s, w ) streamSVG(S, stream) {
{ const tpts = this.pts.map(p => S.transform(p));
beginShape();
for( let p of shape ) {
const tp = T.transform( p );
vertex( tp.x, tp.y );
}
endShape( CLOSE );
}
class Shape
{
constructor( pts, quad) {
this.pts = pts;
this.quad = quad;
this.pts = [pts[pts.length-1]];
for( const p of pts ) {
const prev = this.pts[this.pts.length-1];
const v = p.sub(prev);
const w = new Point( -v.y, v.x );
this.pts.push( prev.frame(v, w, 0.5, -0.3 ) );
this.pts.push( prev.frame(v, w, 0.5, 0.3 ) );
this.pts.push( p );
}
}
streamSVG( S, stream ) {
const tpts = this.pts.map(p => S.transform( p ));
const [a,c,e,b,d,f] = S.mat; const [a,c,e,b,d,f] = S.mat;
const matS = [a,b,c,d,e,f].map(p=>p.toFixed(3)); const matS = [a,b,c,d,e,f].map(p=>p.toFixed(3));
num_pieces += 1; num_pieces += 1;
stream.push(`<g class="tile"> stream.push(`<g class="tile">
<use href="#spectre" transform="matrix(${matS.join(',')}) "/> <use href="#spectre" transform="matrix(${matS.join(',')}) "/>
<text font-size="0.5" dominant-baseline="middle" text-anchor="middle" transform="matrix(${matS.join(',')}) translate(1 1.6)">${num_pieces}</text> <text font-size="0.5" dominant-baseline="middle" text-anchor="middle" transform="matrix(${matS.join(',')}) translate(1 1.6)">${num_pieces}</text>
<text font-size="1" dominant-baseline="middle" text-anchor="middle" transform="matrix(${matS.join(',')}) translate(1 1)"></text> <text font-size="1" dominant-baseline="middle" text-anchor="middle" transform="matrix(${matS.join(',')}) translate(1 1)"></text>
</g>`); </g>`);
}
bounds(S) {
const points = this.pts.map(p => S.transform(p));
return {
minx: Math.min(...points.map(p => p.x)),
miny: Math.min(...points.map(p => p.y)),
maxx: Math.max(...points.map(p => p.x)),
maxy: Math.max(...points.map(p => p.y)),
};
} }
* flatten(S) {
const points = this.pts.map(p => S.transform(p));
const ymax = Math.max(...points.map(p => p.y));
yield {points, ymax, shape: this};
}
} }
class Meta { class Metatile {
constructor() { constructor() {
this.geoms = []; this.geoms = [];
this.quad = []; this.quad = [];
}
addChild( g, T ) {
this.geoms.push( { geom : g, xform: T } );
}
draw( S ) {
for( let g of this.geoms ) {
g.geom.draw( S.mul( g.xform ) );
}
}
streamSVG( S, stream ) {
const {minx,miny,maxx,maxy} = this.bounds(S);
for( let g of this.geoms ) {
g.geom.streamSVG( S.mul(g.xform), stream );
}
}
bounds(S) {
const sub_bounds = this.geoms.map(g => g.geom.bounds(S.mul(g.xform)));
return {
minx: Math.min(...sub_bounds.map(b=>b.minx)),
miny: Math.min(...sub_bounds.map(b=>b.miny)),
maxx: Math.max(...sub_bounds.map(b=>b.maxx)),
maxy: Math.max(...sub_bounds.map(b=>b.maxy)),
};
} }
* flatten(S) { addChild(g, T) {
this.geoms.push({ geom : g, xform: T });
}
streamSVG(S, stream) {
for(let g of this.geoms) { for(let g of this.geoms) {
yield* g.geom.flatten(S.mul(g.xform)); g.geom.streamSVG(S.mul(g.xform), stream);
} }
} }
} }
@ -263,9 +201,9 @@ function tiles(level, label) {
case 'Psi': case 'Psi':
out.push(ident); out.push(ident);
case 'Gamma': case 'Gamma':
const mystic = new Meta(); const mystic = new Metatile();
out.push(ident); out.push(ident);
out.push(Matrix.translate( spectre[8].x, spectre[8].y ).mul( Matrix.rotation( PI / 6 ) )); out.push(Matrix.translate(spectre[8].x, spectre[8].y).mul(Matrix.rotation(PI / 6)));
} }
} else { } else {
/* /*
@ -283,7 +221,7 @@ function tiles(level, label) {
'Theta', 'Theta',
'Lambda', 'Lambda',
'Xi', 'Xi',
'Pi', 'Pi',
'Sigma', 'Sigma',
'Phi', 'Phi',
'Psi' 'Psi'
@ -311,15 +249,15 @@ function tiles(level, label) {
let total_ang = 0; let total_ang = 0;
let rot = ident; let rot = ident;
let tquad = [...subquad]; let tquad = [...subquad];
for( const [ang,from,to] of t_rules ) { for(const [ang,from,to] of t_rules) {
total_ang += ang; total_ang += ang;
if( ang != 0 ) { if(ang != 0) {
rot = Matrix.rotation( radians( total_ang ) ); rot = Matrix.rotation(radians(total_ang));
tquad = subquad.map(q => rot.transform(q)); tquad = subquad.map(q => rot.transform(q));
} }
const ttt = Matrix.translateTo( tquad[to], Ts[Ts.length-1].transform( subquad[from] ) ); const ttt = Matrix.translateTo(tquad[to], Ts[Ts.length-1].transform(subquad[from]));
Ts.push( ttt.mul( rot ) ); Ts.push(ttt.mul(rot));
} }
Ts = Ts.map(t => mul(reflection, t)); Ts = Ts.map(t => mul(reflection, t));
@ -338,224 +276,15 @@ function tiles(level, label) {
'Psi' : ['Psi','Delta','Psi','Phi','Sigma','Psi','Phi','Gamma'] 'Psi' : ['Psi','Delta','Psi','Phi','Sigma','Psi','Phi','Gamma']
}; };
const super_quad = [ const super_quad = [
Ts[6].transform(subquad[2] ), Ts[6].transform(subquad[2]),
Ts[5].transform(subquad[1] ), Ts[5].transform(subquad[1]),
Ts[3].transform(subquad[2] ), Ts[3].transform(subquad[2]),
Ts[0].transform(subquad[1] ) ]; Ts[0].transform(subquad[1]) ];
} }
return {quad, tiles: out}; return {quad, tiles: out};
} }
function buildSpectreBase()
{
const ret = {};
for( lab of ['Delta', 'Theta', 'Lambda', 'Xi',
'Pi', 'Sigma', 'Phi', 'Psi'] ) {
ret[lab] = new Shape( spectre, base_quad, lab );
}
const mystic = new Meta();
mystic.addChild( new Shape( spectre, base_quad, 'Gamma1' ), ident );
mystic.addChild( new Shape( spectre, base_quad, 'Gamma2' ),
Matrix.translate( spectre[8].x, spectre[8].y ).mul( Matrix.rotation( PI / 6 ) ) );
mystic.quad = base_quad;
ret['Gamma'] = mystic;
return ret;
}
function buildHatTurtleBase( hat_dominant )
{
const r3 = 1.7320508075688772;
const hr3 = 0.8660254037844386;
function hexPt( x, y ) {
return new Point( x + 0.5*y, -hr3*y );
}
function hexPt2( x, y ) {
return new Point( x + hr3*y, -0.5*y );
}
const hat = [
hexPt(-1, 2), hexPt(0, 2), hexPt(0, 3), hexPt(2, 2), hexPt(3, 0),
hexPt(4, 0), hexPt(5,-1), hexPt(4,-2), hexPt(2,-1), hexPt(2,-2),
hexPt( 1, -2), hexPt(0,-2), hexPt(-1,-1), hexPt(0, 0) ];
const turtle = [
hexPt(0,0), hexPt(2,-1), hexPt(3,0), hexPt(4,-1), hexPt(4,-2),
hexPt(6,-3), hexPt(7,-5), hexPt(6,-5), hexPt(5,-4), hexPt(4,-5),
hexPt(2,-4), hexPt(0,-3), hexPt(-1,-1), hexPt(0,-1)
];
const hat_keys = [
hat[3], hat[5], hat[7], hat[11]
];
const turtle_keys = [
turtle[3], turtle[5], turtle[7], turtle[11]
];
const ret = {};
if( hat_dominant ) {
for( lab of ['Delta', 'Theta', 'Lambda', 'Xi',
'Pi', 'Sigma', 'Phi', 'Psi'] ) {
ret[lab] = new Shape( hat, hat_keys, lab );
}
const mystic = new Meta();
mystic.addChild( new Shape( hat, hat_keys, 'Gamma1' ), ident );
mystic.addChild( new Shape( turtle, turtle_keys, 'Gamma2' ),
Matrix.translate( hat[8].x, hat[8].y ) );
mystic.quad = hat_keys;
ret['Gamma'] = mystic;
} else {
for( lab of ['Delta', 'Theta', 'Lambda', 'Xi',
'Pi', 'Sigma', 'Phi', 'Psi'] ) {
ret[lab] = new Shape( turtle, turtle_keys, lab );
}
const mystic = new Meta();
mystic.addChild( new Shape( turtle, turtle_keys, 'Gamma1' ), ident );
mystic.addChild( new Shape( hat, hat_keys, 'Gamma2' ),
Matrix.translate( turtle[9].x, turtle[9].y ).mul( Matrix.rotation( PI/3 ) ) );
mystic.quad = turtle_keys;
ret['Gamma'] = mystic;
}
return ret;
}
function buildHexBase()
{
const hr3 = 0.8660254037844386;
const hex = [
new Point(0, 0),
new Point(1.0, 0.0),
new Point(1.5, hr3),
new Point(1, 2*hr3),
new Point(0, 2*hr3),
new Point(-0.5, hr3)
];
const hex_keys = [ hex[1], hex[2], hex[3], hex[5] ];
const ret = {};
for( lab of ['Gamma', 'Delta', 'Theta', 'Lambda', 'Xi',
'Pi', 'Sigma', 'Phi', 'Psi'] ) {
ret[lab] = new Shape( hex, hex_keys, lab );
}
return ret;
}
function buildSupertiles( sys )
{
// First, use any of the nine-unit tiles in sys to obtain
// a list of transformation matrices for placing tiles within
// supertiles.
const quad = sys['Delta'].quad;
const R = new Matrix([-1,0,0,0,1,0]);
const t_rules = [
[60, 3, 1], [0, 2, 0], [60, 3, 1], [60, 3, 1],
[0, 2, 0], [60, 3, 1], [-120, 3, 3] ];
const Ts = [ident];
let total_ang = 0;
let rot = ident;
const tquad = [...quad];
for( const [ang,from,to] of t_rules ) {
total_ang += ang;
if( ang != 0 ) {
rot = Matrix.rotation( radians( total_ang ) );
for( i = 0; i < 4; ++i ) {
tquad[i] = rot.transform(quad[i] );
}
}
const ttt = Matrix.translateTo( tquad[to],
Ts[Ts.length-1].transform(quad[from] ) );
Ts.push( ttt.mul( rot ) );
}
for( let idx = 0; idx < Ts.length; ++idx ) {
Ts[idx] = R.mul( Ts[idx] );
}
// Now build the actual supertiles, labelling appropriately.
const super_rules = {
'Gamma' : ['Pi','Delta','null','Theta','Sigma','Xi','Phi','Gamma'],
'Delta' : ['Xi','Delta','Xi','Phi','Sigma','Pi','Phi','Gamma'],
'Theta' : ['Psi','Delta','Pi','Phi','Sigma','Pi','Phi','Gamma'],
'Lambda' : ['Psi','Delta','Xi','Phi','Sigma','Pi','Phi','Gamma'],
'Xi' : ['Psi','Delta','Pi','Phi','Sigma','Psi','Phi','Gamma'],
'Pi' : ['Psi','Delta','Xi','Phi','Sigma','Psi','Phi','Gamma'],
'Sigma' : ['Xi','Delta','Xi','Phi','Sigma','Pi','Lambda','Gamma'],
'Phi' : ['Psi','Delta','Psi','Phi','Sigma','Pi','Phi','Gamma'],
'Psi' : ['Psi','Delta','Psi','Phi','Sigma','Psi','Phi','Gamma'] };
const super_quad = [
Ts[6].transform(quad[2] ),
Ts[5].transform(quad[1] ),
Ts[3].transform(quad[2] ),
Ts[0].transform(quad[1] ) ];
const ret = {};
for( const [lab, subs] of Object.entries( super_rules ) ) {
const sup = new Meta();
for( let idx = 0; idx < 8; ++idx ) {
if( subs[idx] == 'null' ) {
continue;
}
sup.addChild( sys[subs[idx]], Ts[idx] );
}
sup.quad = super_quad;
ret[lab] = sup;
}
return ret;
}
/* modified from https://css-tricks.com/converting-color-spaces-in-javascript/
*/
function hexToHSL(H) {
const [r,g,b] = [0,1,2].map(i=>H.slice(2*i+1,2*i+3)).map(n=>parseInt(n,16)/255);
let cmin = Math.min(r,g,b),
cmax = Math.max(r,g,b),
delta = cmax - cmin,
h = 0,
s = 0,
l = 0;
if (delta == 0)
h = 0;
else if (cmax == r)
h = ((g - b) / delta) % 6;
else if (cmax == g)
h = (b - r) / delta + 2;
else
h = (r - g) / delta + 4;
h = Math.round(h * 60);
if (h < 0)
h += 360;
l = (cmax + cmin) / 2;
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
return {h,s,l};
}
let last_num_iterations; let last_num_iterations;
function get_settings() { function get_settings() {
@ -564,70 +293,175 @@ function get_settings() {
); );
} }
function rebuild() { class Builder {
num_pieces = 0; constructor(settings) {
const svg = document.querySelector('svg'); this.settings = settings;
const settings = get_settings();
let sys = buildSpectreBase(false);
for(let i=0;i<settings.num_iterations;i++) {
sys = buildSupertiles( sys );
} }
sys = sys['Delta']; build() {
window.sys = sys; const {settings} = this;
const drawing = []; num_pieces = 0;
const board = svg.querySelector('#board');
board.innerHTML = '';
sys.streamSVG(ident, drawing);
board.innerHTML = drawing.join(' ');
const viewbox = svg.getBoundingClientRect(); let sys = this.buildSpectreBase();
function visit(g, t) {
for(let c of g.children) { for(let i=0;i<settings.num_iterations;i++) {
visit(c, t+Math.random()*10-5); sys = this.buildSupertiles(sys);
}
if(g.tagName=='path') {
const b = g.getBoundingClientRect();
const x = (b.x - viewbox.x) / viewbox.width;
const y = (b.y - viewbox.y) / viewbox.height;
g.style.color = '#ccc';
} }
sys = sys['Delta'];
this.sys = sys;
} }
visit(board,50);
svg.addEventListener('click', e => { buildSpectreBase() {
console.log('click'); const ret = {};
if(!last_click) {
return; for(let lab of ['Delta', 'Theta', 'Lambda', 'Xi',
} 'Pi', 'Sigma', 'Phi', 'Psi']) {
let tile = e.target; ret[lab] = new Tile(spectre, base_quad, lab);
while(tile && !tile.classList.contains('tile')) {
tile = tile.parentElement;
}
if(!tile) {
return;
} }
for(let el of svg.querySelectorAll('.highlight')) { const mystic = new Metatile();
el.classList.remove('highlight'); mystic.addChild(new Tile(spectre, base_quad, 'Gamma1'), ident);
mystic.addChild(new Tile(spectre, base_quad, 'Gamma2'),
Matrix.translate(spectre[8].x, spectre[8].y).mul(Matrix.rotation(PI / 6)));
mystic.quad = base_quad;
ret['Gamma'] = mystic;
return ret;
}
buildSupertiles(sys) {
// First, use any of the nine-unit tiles in sys to obtain
// a list of transformation matrices for placing tiles within
// supertiles.
const quad = sys['Delta'].quad;
const R = new Matrix([-1,0,0,0,1,0]);
const t_rules = [
[60, 3, 1], [0, 2, 0], [60, 3, 1], [60, 3, 1],
[0, 2, 0], [60, 3, 1], [-120, 3, 3] ];
const Ts = [ident];
let total_ang = 0;
let rot = ident;
const tquad = [...quad];
for(const [ang,from,to] of t_rules) {
total_ang += ang;
if(ang != 0) {
rot = Matrix.rotation(radians(total_ang));
for(let i = 0; i < 4; ++i) {
tquad[i] = rot.transform(quad[i]);
}
}
const ttt = Matrix.translateTo(tquad[to],
Ts[Ts.length-1].transform(quad[from]));
Ts.push(ttt.mul(rot));
} }
tile.classList.add('highlight');
}); for(let idx = 0; idx < Ts.length; ++idx) {
Ts[idx] = R.mul(Ts[idx]);
}
// Now build the actual supertiles, labelling appropriately.
const super_rules = {
'Gamma' : ['Pi','Delta','null','Theta','Sigma','Xi','Phi','Gamma'],
'Delta' : ['Xi','Delta','Xi','Phi','Sigma','Pi','Phi','Gamma'],
'Theta' : ['Psi','Delta','Pi','Phi','Sigma','Pi','Phi','Gamma'],
'Lambda' : ['Psi','Delta','Xi','Phi','Sigma','Pi','Phi','Gamma'],
'Xi' : ['Psi','Delta','Pi','Phi','Sigma','Psi','Phi','Gamma'],
'Pi' : ['Psi','Delta','Xi','Phi','Sigma','Psi','Phi','Gamma'],
'Sigma' : ['Xi','Delta','Xi','Phi','Sigma','Pi','Lambda','Gamma'],
'Phi' : ['Psi','Delta','Psi','Phi','Sigma','Pi','Phi','Gamma'],
'Psi' : ['Psi','Delta','Psi','Phi','Sigma','Psi','Phi','Gamma'] };
const super_quad = [
Ts[6].transform(quad[2]),
Ts[5].transform(quad[1]),
Ts[3].transform(quad[2]),
Ts[0].transform(quad[1]) ];
const ret = {};
for(const [lab, subs] of Object.entries(super_rules)) {
const sup = new Metatile();
for(let idx = 0; idx < 8; ++idx) {
if(subs[idx] == 'null') {
continue;
}
sup.addChild(sys[subs[idx]], Ts[idx]);
}
sup.quad = super_quad;
ret[lab] = sup;
}
return ret;
}
draw() {
const {sys} = this;
const drawing = [];
const svg = document.querySelector('svg');
const board = svg.querySelector('#board');
board.innerHTML = '';
const R = new Matrix([-1,0,0,0,1,0]);
const m = this.settings.num_iterations % 2 == 0 ? ident : R;
sys.streamSVG(m, drawing);
board.innerHTML = drawing.join(' ');
const viewbox = svg.getBoundingClientRect();
function visit(g, t) {
for(let c of g.children) {
visit(c, t+Math.random()*10-5);
}
if(g.tagName=='path') {
const b = g.getBoundingClientRect();
const x = (b.x - viewbox.x) / viewbox.width;
const y = (b.y - viewbox.y) / viewbox.height;
g.style.color = '#ccc';
}
}
visit(board,50);
svg.addEventListener('click', e => {
if(!last_click) {
return;
}
let tile = e.target;
while(tile && !tile.classList.contains('tile')) {
tile = tile.parentElement;
}
if(!tile) {
return;
}
for(let el of svg.querySelectorAll('.highlight')) {
el.classList.remove('highlight');
}
tile.classList.add('highlight');
});
}
} }
function update_display() { function update_display() {
const settings = get_settings(); const settings = get_settings();
const svg = document.querySelector('svg'); const svg = document.querySelector('svg');
//const bounds = sys.bounds(ident); svg.setAttribute('viewBox',`${-settings.scale/2} ${-settings.scale/2} ${settings.scale} ${settings.scale}`);
svg.setAttribute('viewBox',`${settings.ox - settings.scale/2} ${settings.oy - settings.scale/2} ${settings.scale} ${settings.scale}`);
const mx = spectre.map(p=>p.x).reduce((a,b)=>a+b)/spectre.length; const mx = spectre.map(p=>p.x).reduce((a,b)=>a+b)/spectre.length;
const my = spectre.map(p=>p.y).reduce((a,b)=>a+b)/spectre.length; const my = spectre.map(p=>p.y).reduce((a,b)=>a+b)/spectre.length;
document.getElementById('spectre').setAttribute('transform',`translate(${mx},${my}) translate(${-mx},${-my})`); document.getElementById('spectre').setAttribute('transform',`translate(${mx},${my}) translate(${-mx},${-my})`);
if(settings.num_iterations != last_num_iterations) { if(settings.num_iterations != last_num_iterations) {
rebuild(); const builder = new Builder(settings);
builder.build();
builder.draw();
last_num_iterations = settings.num_iterations; last_num_iterations = settings.num_iterations;
} }
} }
@ -649,7 +483,6 @@ const svg = document.querySelector('svg');
svg.addEventListener('pointerdown', e => { svg.addEventListener('pointerdown', e => {
opos = getcoords(e); opos = getcoords(e);
dragging = true; dragging = true;
console.log('dragstart');
}); });
svg.addEventListener('pointermove', e => { svg.addEventListener('pointermove', e => {
@ -665,9 +498,7 @@ svg.addEventListener('pointerup', e => {
dragging = false; dragging = false;
const [dx,dy] = [npan.x - pan.x, npan.y - pan.y]; const [dx,dy] = [npan.x - pan.x, npan.y - pan.y];
const d = Math.sqrt(dx*dx + dy*dy); const d = Math.sqrt(dx*dx + dy*dy);
console.log(d);
last_click = d < 0.5; last_click = d < 0.5;
pan = npan; pan = npan;
console.log('dragend');
}); });