Published
Edited
Jul 13, 2021
1 fork
Importers
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function showBallTree(tree, name = "") {
let title = md `### Ball tree demonstration:
Red balls are leaves on ball tree.`;

let width = 900;
let canvas = DOM.canvas(width*2, width);
let ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = true;

ctx.font = '15px serif';
ctx.fillText(` Tree height: ${tree.height}`, 10, 20);
ctx.lineWidth = 1.8;
ctx.fillStyle = '#0000000f';
ctx.strokeStyle = '#0000000f';
tree.draw(ctx,true,false,false,'#ee000000','#0000004f','#ee00000f','#ee00007f');

return html`<div>${title}
${canvas}</div>`;
}
Insert cell
class Patch {
constructor(img,imgD) {
this.parent = null;
this.selected = false;
this.pinned = false;
this.collision = false;
this.visible = true;
this.balls = powerDiagram(img,imgD);
this.tree = Q(this.balls,0.5);
this.tree.tree.computeEnclosingCircles();

this.img = img;
this.x = 0;
this.y = 0;
this.w = img.width;
this.h = img.height;
this.level = 0;
this.neighbors = [];
}
translate(p) {
this.tree.translate(p);
this.x += p.x;
this.y += p.y
}
origin() {
let dx = -(this.tree.ball.x);
let dy = -(this.tree.ball.y);
this.x += dx;
this.y += dy;
this.tree.origin();
}

draw(ctx,tree) {
if (tree) {
this.tree.draw(ctx,true,true,false,"#0000004f","#0000007f","#0000004f","#0000007f");
}
ctx.drawImage(this.img,this.x,this.y);
}

box() {
return [ Vec(this.x,this.y), Vec(this.x+this.w, this.y),
Vec(this.x+this.w,this.y+this.h), Vec(this.x,this.y+this.h) ]
}

contains(p) {
return this.tree.intersecting(p);
}

likelyNeighbor (other, gap) {
return this.tree.tree.ball.distance(other.tree.tree.ball) <= gap;
}
}
Insert cell
class PatchCluster extends Array {
constructor() {
super();
this.parent = null;
this.dirty = false;
}

insert (patch) {
patch.parent = this;
this.push(patch);
}

union (other) {
for (let o of other) {
this.insert(o);
}
other = null;
}


// Returns a bounding box for the cluster
box () {
if (this.length == 0) return null;
var box = this[0].box();
for (var i = 1; i < this.length; i++) {
let other = this[i].box();
let x1 = Math.min (box[0].x, other[0].x);
let y1 = Math.min (box[0].y, other[0].y);
let x2 = Math.max (box[2].x, other[2].x);
let y2 = Math.max (box[2].y, other[2].y);
box = [Vec(x1,y1),Vec(x2,y1),Vec(x2,y2),Vec(x1,y2)];
}
return box;
}

intersect(b1,b2) {
if ( ((b2[0].x > b1[0].x && b2[0].x < b1[1].x) ||
(b2[0].x < b1[0].x && b2[1].x > b1[0].x) ) &&
((b2[0].y > b1[0].y && b2[0].y < b1[2].y) ||
(b2[0].y < b1[0].y && b2[2].y > b1[0].y) ) ) {
return true;
}
return false;
}

dist (other) {
let box = this.box();
let otherbox = other.box();
if (this.intersect(box,otherbox)) return [0,0];
return [ Math.min(Math.abs(otherbox[0].x-box[1].x), Math.abs(otherbox[1].x-box[0].x)),
Math.min(Math.abs(otherbox[0].y-box[3].y), Math.abs(otherbox[3].y-box[0].y)) ];
}

// True if PatchCluster contains point p
contains (p) {
for (let e of this) {
if (e.contains (p)) return true;
}
return false;
/*let box = [ p, p, p, p ] ;
if (this.intersect(box,this.box())) return true;
return false;*/
}
translate (p) {
for (let patch of this) {
patch.translate(p);
}
}

/* cluster (other) {
}

cluster (other) {
let level = -1;
let query1 = other.closestSegsNew(this.box[1],this.box[2]);
if (query1.dist <= 2) { //a character
level = 0;
} else {
(query1.dist <= 8) { // a word
level = 1;
}
}
if (level < 0) {
}
return level;
}*/
}
Insert cell
function powerDiagram (img, imgD) {
let I = _imgToArray(img,imgD);
let F = Pow2Array(I);
let dma = DMA(F);
if (dma.length > 0) {
let PD = new PowerDiagram(dma);
let balls=[];
for (let i=0; i< PD.sites.length; i++) {
balls.push(new Ball(PD.sites[i].center.x, PD.sites[i].center.y, PD.sites[i].radius));
}
if (balls.length == 0) return [new Ball (0,0,0.5)];
return balls;
} else {
return [new Ball (0,0,0.5)];
}
}
Insert cell
function pyramidal(F) {
let Fn = Pow2Array(F);
let levels = Math.log2(Fn.length)+ 1;
let dma = DMA(Fn);
if (dma.length > 0) {
let PDlevel = new PowerDiagram(dma);
if (PDlevel.sites.length == 0) PDlevel.sites.push(new Site(Vec(0,0),0.5));
return PDlevel.sites;
}
return [new Site(Vec(0,0),0.5)];
}
Insert cell
function _imgToArray(img, imgData) {
var _rows = img.height;
var _cols = img.width;

// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgD = imgData;

var mmap = [];
for (var x=0; x < _cols; x++) {
mmap[x] = [];
for (var y =0; y < _rows; y++) {
var ind = (x + (y*_cols))*4;
var alpha = imgD.data[ind+3];
if (alpha < 10) {
mmap[x][y] = 0;
} else {
mmap[x][y] = 1;
}
}
}
return mmap;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more