Public
Edited
Dec 21
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
model2geom = (model) => {
const { min, max } = Math;
const subzones = [];
const P = pt;
const L = ln;
const A = (...values) => values.filter((value) => value);

for (const [i, j, h, hw, he, hs, hn, hsw, hse, hnw, hne] of model) {
if (h > 0) {
const D = 0.5;
const origin = [i + D, j + D, 0];
const elements = [];
const SW = [-D, -D];
const SE = [D, -D];
const NE = [D, D];
const NW = [-D, D];
const [pSW, pSE, pNE, pNW] = [SW, SE, NE, NW].map((dir) => pt(...dir, h));

const sS = ln(pSW, pSE);
const sE = ln(pSE, pNE);
const sN = ln(pNE, pNW);
const sW = ln(pNW, pSW);

// top of building

/*const topElements = []
if (h <= hn) {
topElements.push()
}*/
//if (h > hn && h > he && h > hs && h > hw) {
const roofNodes = [];
const roofSide = (dx, dy, h1, h2, h3) => {
const [p1, p2] = [P(dy, -dx, 0), P(-dy, dx, 0)];
const [t1, t2] = [P(0, 0, 0.4), P(-dx, -dy, 0.4)];
const elements = [];
if (h2 >= h) {
elements.push(
facet(
[p1, t1, t2],
A(h2 > h && [p1, t1], [t1, t2], h1 >= h && [t2, p1])
),
facet(
[p2, t2, t1],
A(h3 >= h && [p2, t2], [t2, t1], h2 > h && [t1, p2])
)
);
} else
elements.push(
facet(
[p1, p2, t2],
A([p1, p2], h > h3 && [p2, t2], h > h1 && [t2, p1])
)
);
return { origin: P(dx, dy, 0), elements };
};
roofNodes.push(roofSide(-D, 0, hn, hw, hs));
roofNodes.push(roofSide(0, -D, hw, hs, he));
roofNodes.push(roofSide(D, 0, hs, he, hn));
roofNodes.push(roofSide(0, D, he, hn, hw));
/*} else {
const topSegments = [];
if (h != hs) topSegments.push(sS);
if (h != he) topSegments.push(sE);
if (h != hn) topSegments.push(sN);
if (h != hw) topSegments.push(sW);
elements.push(facet([pSW, pSE, pNE, pNW], topSegments));
}*/

// side walls

const corner = (ci, cj, h1, h2, hd) => {
const vert = (h1, h2) => ln(pt(ci, cj, h1), pt(ci, cj, h2));
const segments = [];
if (h > h1 && h > h2) {
const [hmin, hmax] = [min(h1, h2), max(h1, h2)];
segments.push(vert(h, hmax));
if (hd > hmin && hmin != hmax)
segments.push(vert(min(hd, hmax), hmin));
return segments;
} else if (h < h1 && h < h2) {
if (h > hd) segments.push(vert(h, hd));
} else {
const hmin = min(h1, h2);
if (hd > hmin) segments.push(vert(min(hd, h), hmin));
}
return segments;
};

const ssw = corner(...SW, hw, hs, hsw);
const sse = corner(...SE, hs, he, hse);
const sne = corner(...NE, he, hn, hne);
const snw = corner(...NW, hn, hw, hnw);

if (h > hw) {
const quad = [pSW, pNW, pt(...NW, hw), pt(...SW, hw)];
const base = hw == 0 ? floor(SW, NW, 0, R(0.25)) : [];
elements.push(facet(quad, [sW, ...snw, ...ssw, ...base]));
}
if (h > he) {
const quad = [pNE, pSE, pt(...SE, he), pt(...NE, he)];
const base = he == 0 ? floor(NE, SE, 0, R(0.25)) : [];
elements.push(facet(quad, [sE, ...sne, ...sse, ...base]));
}
if (h > hs) {
const quad = [pSE, pSW, pt(...SW, hs), pt(...SE, hs)];
const base = hs == 0 ? floor(SE, SW, 0, R(0.25)) : [];
elements.push(facet(quad, [sS, ...ssw, ...sse, ...base]));
}
if (h > hn) {
const quad = [pNW, pNE, pt(...NE, hn), pt(...NW, hn)];
const base = hn == 0 ? floor(NW, NE, 0, R(0.25)) : [];
elements.push(facet(quad, [sN, ...snw, ...sne, ...base]));
}

subzones.push({
origin,
subzones: [
{ origin: [0, 0, 0], elements },
{ origin: [0, 0, h], subzones: roofNodes }
]
});
//subzones.push({ origin, elements });
}
}
return { origin: [0, 0, 0], subzones };
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SegmentsClipper = (EPSILON) => {
const segments = new Set();
const intersectLineAndSegment = ([ax, bx], [ay, by], [x1, y1], [x2, y2]) => {
const err = (t) =>
Math.abs(t) < EPSILON ? 0 : Math.abs(1 - t) < EPSILON ? 1 : t;
const dx = x2 - x1;
const dy = y2 - y1;
const det = ay * dx - ax * dy;
if (det == 0) return; // segment and line are parallel
const ts = err((ax * (y1 - by) - ay * (x1 - bx)) / det);
if (ts < 0 || ts > 1) return; // intersection outside of segment boundaries
return err(ax ? (x1 - bx + ts * dx) / ax : (y1 - by + ts * dy) / ay);
};
const intersectLineAndConvexQuad = ([ax, bx], [ay, by], vertices) => {
const [pt1, pt2, pt3, pt4] = vertices;
const result = new Set();
for (const [pt1, pt2] of succPairs(vertices.values())) {
const t = intersectLineAndSegment([ax, bx], [ay, by], pt1, pt2);
if (t != undefined) result.add(t);
if (result.size == 2) break;
}
return [...result];
};
return {
add: ([p1, p2]) => {
// segment could be a point: prevent or add specific logic
segments.add([p1, p2]);
},
clipWithPolygon: (polygon) => {
const newSegments = new Set();
for (const segment of segments) {
const [x1, y1] = segment[0];
const dx = segment[1][0] - x1;
const dy = segment[1][1] - y1;
const result = intersectLineAndConvexQuad([dx, x1], [dy, y1], polygon);
const pt = (t) => [x1 + t * dx, y1 + t * dy];
const seg = (t1, t2) => [pt(t1), pt(t2)];
if (result.length < 2) continue;
const [t1, t2] = [Math.min(...result), Math.max(...result)];
if (t2 <= 0 || t1 >= 1) continue;
segments.delete(segment);
if (t1 > 0 && t2 >= 1) newSegments.add(seg(0, t1));
else if (t1 <= 0 && t2 < 1) newSegments.add(seg(t2, 1));
else if (t1 > 0 && t2 < 1) {
newSegments.add(seg(0, t1));
newSegments.add(seg(t2, 1));
}
}
for (const segment of newSegments) segments.add(segment);
},
toArray: () => [...segments]
};
}
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