lots more tidying
This commit is contained in:
parent
bdf1890ac0
commit
25000efeae
2 changed files with 208 additions and 381 deletions
|
@ -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>
|
||||||
|
|
281
script.js
281
script.js
|
@ -134,30 +134,7 @@ function getcoords(event) {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match unit interval to line segment p->q
|
class Tile {
|
||||||
function matchSeg( p, q )
|
|
||||||
{
|
|
||||||
return [q.x-p.x, p.y-q.y, p.x, q.y-p.y, q.x-p.x, p.y];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Match line segment p1->q1 to line segment p2->q2
|
|
||||||
function matchTwo( p1, q1, p2, q2 )
|
|
||||||
{
|
|
||||||
return matchSeg( p2, q2 ).mul(matchSeg( p1, q1 ).inverse());
|
|
||||||
};
|
|
||||||
|
|
||||||
function drawPolygon( shape, T, f, s, w )
|
|
||||||
{
|
|
||||||
beginShape();
|
|
||||||
for( let p of shape ) {
|
|
||||||
const tp = T.transform( p );
|
|
||||||
vertex( tp.x, tp.y );
|
|
||||||
}
|
|
||||||
endShape( CLOSE );
|
|
||||||
}
|
|
||||||
|
|
||||||
class Shape
|
|
||||||
{
|
|
||||||
constructor(pts, quad) {
|
constructor(pts, quad) {
|
||||||
this.pts = pts;
|
this.pts = pts;
|
||||||
this.quad = quad;
|
this.quad = quad;
|
||||||
|
@ -186,24 +163,9 @@ class Shape
|
||||||
</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) {
|
class Metatile {
|
||||||
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 {
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.geoms = [];
|
this.geoms = [];
|
||||||
this.quad = [];
|
this.quad = [];
|
||||||
|
@ -213,36 +175,12 @@ class Meta {
|
||||||
this.geoms.push({ geom : g, xform: 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) {
|
streamSVG(S, stream) {
|
||||||
const {minx,miny,maxx,maxy} = this.bounds(S);
|
|
||||||
|
|
||||||
for(let g of this.geoms) {
|
for(let g of this.geoms) {
|
||||||
g.geom.streamSVG(S.mul(g.xform), stream);
|
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) {
|
|
||||||
for(let g of this.geoms) {
|
|
||||||
yield* g.geom.flatten(S.mul(g.xform));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tiles(level, label) {
|
function tiles(level, label) {
|
||||||
|
@ -263,7 +201,7 @@ 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)));
|
||||||
}
|
}
|
||||||
|
@ -347,18 +285,46 @@ function tiles(level, label) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildSpectreBase()
|
let last_num_iterations;
|
||||||
{
|
|
||||||
const ret = {};
|
|
||||||
|
|
||||||
for( lab of ['Delta', 'Theta', 'Lambda', 'Xi',
|
function get_settings() {
|
||||||
'Pi', 'Sigma', 'Phi', 'Psi'] ) {
|
return Object.fromEntries(
|
||||||
ret[lab] = new Shape( spectre, base_quad, lab );
|
Array.from(document.querySelectorAll('input,textarea')).map(i => [i.id, i.type=='number' ? i.valueAsNumber : i.value])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mystic = new Meta();
|
class Builder {
|
||||||
mystic.addChild( new Shape( spectre, base_quad, 'Gamma1' ), ident );
|
constructor(settings) {
|
||||||
mystic.addChild( new Shape( spectre, base_quad, 'Gamma2' ),
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
const {settings} = this;
|
||||||
|
|
||||||
|
num_pieces = 0;
|
||||||
|
|
||||||
|
let sys = this.buildSpectreBase();
|
||||||
|
|
||||||
|
for(let i=0;i<settings.num_iterations;i++) {
|
||||||
|
sys = this.buildSupertiles(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
sys = sys['Delta'];
|
||||||
|
|
||||||
|
this.sys = sys;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildSpectreBase() {
|
||||||
|
const ret = {};
|
||||||
|
|
||||||
|
for(let lab of ['Delta', 'Theta', 'Lambda', 'Xi',
|
||||||
|
'Pi', 'Sigma', 'Phi', 'Psi']) {
|
||||||
|
ret[lab] = new Tile(spectre, base_quad, lab);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mystic = new Metatile();
|
||||||
|
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)));
|
Matrix.translate(spectre[8].x, spectre[8].y).mul(Matrix.rotation(PI / 6)));
|
||||||
mystic.quad = base_quad;
|
mystic.quad = base_quad;
|
||||||
ret['Gamma'] = mystic;
|
ret['Gamma'] = mystic;
|
||||||
|
@ -366,95 +332,7 @@ function buildSpectreBase()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildHatTurtleBase( hat_dominant )
|
buildSupertiles(sys) {
|
||||||
{
|
|
||||||
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
|
// First, use any of the nine-unit tiles in sys to obtain
|
||||||
// a list of transformation matrices for placing tiles within
|
// a list of transformation matrices for placing tiles within
|
||||||
// supertiles.
|
// supertiles.
|
||||||
|
@ -474,7 +352,7 @@ function buildSupertiles( sys )
|
||||||
total_ang += ang;
|
total_ang += ang;
|
||||||
if(ang != 0) {
|
if(ang != 0) {
|
||||||
rot = Matrix.rotation(radians(total_ang));
|
rot = Matrix.rotation(radians(total_ang));
|
||||||
for( i = 0; i < 4; ++i ) {
|
for(let i = 0; i < 4; ++i) {
|
||||||
tquad[i] = rot.transform(quad[i]);
|
tquad[i] = rot.transform(quad[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -508,7 +386,7 @@ function buildSupertiles( sys )
|
||||||
const ret = {};
|
const ret = {};
|
||||||
|
|
||||||
for(const [lab, subs] of Object.entries(super_rules)) {
|
for(const [lab, subs] of Object.entries(super_rules)) {
|
||||||
const sup = new Meta();
|
const sup = new Metatile();
|
||||||
for(let idx = 0; idx < 8; ++idx) {
|
for(let idx = 0; idx < 8; ++idx) {
|
||||||
if(subs[idx] == 'null') {
|
if(subs[idx] == 'null') {
|
||||||
continue;
|
continue;
|
||||||
|
@ -523,64 +401,19 @@ function buildSupertiles( sys )
|
||||||
return ret;
|
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)
|
draw() {
|
||||||
h = 0;
|
const {sys} = this;
|
||||||
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;
|
|
||||||
|
|
||||||
function get_settings() {
|
|
||||||
return Object.fromEntries(
|
|
||||||
Array.from(document.querySelectorAll('input,textarea')).map(i => [i.id, i.type=='number' ? i.valueAsNumber : i.value])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function rebuild() {
|
|
||||||
num_pieces = 0;
|
|
||||||
const svg = document.querySelector('svg');
|
|
||||||
const settings = get_settings();
|
|
||||||
let sys = buildSpectreBase(false);
|
|
||||||
|
|
||||||
for(let i=0;i<settings.num_iterations;i++) {
|
|
||||||
sys = buildSupertiles( sys );
|
|
||||||
}
|
|
||||||
|
|
||||||
sys = sys['Delta'];
|
|
||||||
window.sys = sys;
|
|
||||||
|
|
||||||
const drawing = [];
|
const drawing = [];
|
||||||
|
|
||||||
|
const svg = document.querySelector('svg');
|
||||||
|
|
||||||
const board = svg.querySelector('#board');
|
const board = svg.querySelector('#board');
|
||||||
board.innerHTML = '';
|
board.innerHTML = '';
|
||||||
sys.streamSVG(ident, drawing);
|
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(' ');
|
board.innerHTML = drawing.join(' ');
|
||||||
|
|
||||||
const viewbox = svg.getBoundingClientRect();
|
const viewbox = svg.getBoundingClientRect();
|
||||||
|
@ -598,7 +431,6 @@ function rebuild() {
|
||||||
visit(board,50);
|
visit(board,50);
|
||||||
|
|
||||||
svg.addEventListener('click', e => {
|
svg.addEventListener('click', e => {
|
||||||
console.log('click');
|
|
||||||
if(!last_click) {
|
if(!last_click) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -616,18 +448,20 @@ function rebuild() {
|
||||||
tile.classList.add('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');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue