Got it to work, but I’d looooove an explanation!
bloom tutorial example
import panel as pn; pn.extension();
import param
class BloomDiagram(pn.custom.JSComponent):
width = param.Integer(default=400, doc="Width of the canvas")
height = param.Integer(default=400, doc="Height of the canvas")
_imports = {
'bloom': {'module': "https://penrose.cs.cmu.edu/bloom.min.js", 'name': '*'}
}
_esm = """
export async function render({ model, el }) {
const bloom = await import('https://penrose.cs.cmu.edu/bloom.min.js');
if (!bloom ) { // || !bloom.canvas || !bloom.DiagramBuilder
console.error('Bloom library not properly loaded');
return;
}
const id = Math.random().toString(36).substr(2, 9);
const container = document.createElement('div');
container.id = 'bloom_' + id;
container.style.width = model.width + 'px';
container.style.height = model.height + 'px';
container.style.border = '1px solid black';
el.appendChild(container);
// ========================================================= Canvas
// Canvas: local coordinate system
const canvas = bloom.canvas(model.width, model.height);
console.log("CANVAS:", canvas);
// ========================================================= DiagramBuilder
// has methods to declare types, substances, shapes, constraints, etc
const seed="abcd";
const db = new bloom.DiagramBuilder(canvas, seed, 1);
const { type, predicate, forall, forallWhere, ensure, circle, line } = db;
console.log( db);
// ========================================================= Diagram Code
// Declare objects and relationships
const Point = type();
const Arrow = type();
const Connects = predicate();
// ---------------------------------------------------------
// instantiate objects and relationships
const p1 = Point();
const p2 = Point();
const arrow = Arrow();
Connects(arrow, p1, p2);
console.log( "Point", p1);
// ---------------------------------------------------------
// style objects and relationships
const pointRad = 30;
const pointMargin = 10;
forall({ p: Point }, ({ p }) => { // style for points
p.icon = circle({ r: pointRad, drag: true, }); // attach "icon" to p
});
forallWhere( // style for arrows
{ a: Arrow, p: Point, q: Point },
({ a, p, q }) => Connects.test(a, p, q),
({ a, p, q }) => {
const pq = bloom.ops.vsub(q.icon.center, p.icon.center); // vector from p to q
const pqNorm = bloom.ops.vnormalize(pq); // direction from p to q
const pStart = bloom.ops.vmul(pointRad + pointMargin, pqNorm); // vector from p to line start
const start = bloom.ops.vadd(p.icon.center, pStart); // line start
const end = bloom.ops.vsub(q.icon.center, pStart); // line end
a.icon = line({ start: start, end: end, endArrowhead: "straight", });
ensure( // constraints: points separated by more than radius_1+radius_2+padding
bloom.constraints.greaterThan(
bloom.ops.vdist(p.icon.center, q.icon.center),
2 * (pointRad + pointMargin) + 20,
),
);
},
);
// ========================================================= Diagram Code
const diagram = await db.build();
console.log( diagram);
const interactiveElement = diagram.getInteractiveElement();
container.appendChild(interactiveElement);
return container;
}
"""
# Usage
bloom_diagram = BloomDiagram()
pn.Column(bloom_diagram).servable()