Skip to content

Commit

Permalink
visualize proximate clusters
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisShank committed Nov 28, 2024
1 parent ef33828 commit 8da1016
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 46 deletions.
188 changes: 144 additions & 44 deletions demo/proximity-maps.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Maps</title>
<title>Proximity Maps</title>
<style>
* {
box-sizing: border-box;
Expand All @@ -27,6 +27,7 @@
}

geo-wiki {
background: white;
border: solid 2px black;
border-radius: 5px;
ul {
Expand All @@ -37,7 +38,7 @@
}
}

olk-hull {
folk-cluster {
display: block;
position: absolute;
inset: 0 0 0 0;
Expand All @@ -47,17 +48,19 @@
</style>
</head>
<body>
<fc-geometry id="1" x="25" y="100" width="400" height="200">
<folk-map coordinates="52.09, 5.12" zoom="13"></folk-map>
</fc-geometry>
<folk-proximity>
<fc-geometry id="g1" x="25" y="100" width="400" height="200">
<folk-map coordinates="52.09, 5.12" zoom="13"></folk-map>
</fc-geometry>

<fc-geometry id="2" x="50" y="550" width="400" height="250">
<folk-map coordinates="51.50404120260676, -0.14007568359375003" zoom="13"></folk-map>
</fc-geometry>
<fc-geometry id="g2" x="50" y="550" width="400" height="250">
<folk-map coordinates="51.50404120260676, -0.14007568359375003" zoom="13"></folk-map>
</fc-geometry>

<fc-geometry id="3" x="500" y="400" width="500" height="300">
<geo-wiki coordinates="51.50404120260676, -0.14007568359375003"></geo-wiki>
</fc-geometry>
<fc-geometry id="g3" x="500" y="400" width="500" height="300">
<geo-wiki coordinates="51.50404120260676, -0.14007568359375003"></geo-wiki>
</fc-geometry>
</folk-proximity>

<script type="module">
import { FolkGeometry } from '../src/canvas/fc-geometry.ts';
Expand All @@ -69,39 +72,6 @@
FolkMap.register();
FolkHull.register();

const geometries = Array.from(document.querySelectorAll('fc-geometry'));

const proximityMap = new Map(geometries.map((el) => [el, new Set()]));

function handleProximity(e) {
proximityMap.forEach((set, el) => {
if (el !== e.target) {
const alreadyIntersection = set.has(e.target);
// TODO: refactor this hack once resizing and the vertices API are figured out
const isNowIntersecting = collisionDetection(el.getClientRect(), e.target.getClientRect(), 100);
if (isNowIntersecting && !alreadyIntersection) {
set.add(e.target);
proximityMap.get(e.target)?.add(el);
} else if (alreadyIntersection && !isNowIntersecting) {
set.delete(e.target);
proximityMap.get(e.target)?.delete(el);
}
}
});
}

document.addEventListener('move', handleProximity);
document.addEventListener('resize', handleProximity);
document.addEventListener('recenter', (e) => {
proximityMap.get(e.target.parentElement)?.forEach((el) => {
const content = el.firstElementChild;
if (content instanceof GeoWiki) {
const { lat, lng } = e.target.coordinates;
content.coordinates = [lat, lng];
}
});
});

class GeoWiki extends HTMLElement {
static tagName = 'geo-wiki';

Expand Down Expand Up @@ -166,6 +136,136 @@
}

GeoWiki.register();

const PROXIMITY = 50;

class FolkCluster extends FolkHull {
static tagName = 'folk-cluster';

#data = new Map();

isElementInCluster(element) {
return this.sourceElements.includes(element);
}

isElementInProximity(element) {
for (const el of this.sourceElements) {
if (collisionDetection(el.getClientRect(), element.getClientRect(), PROXIMITY)) return true;
}
return false;
}

addElements(...elements) {
this.sources += elements.map((el) => `#${el.id}`).join(', ');
}

removeElement(element) {
this.sources = this.sourceElements
.filter((el) => el !== element)
.map((el) => `#${el.id}`)
.join(', ');
}
}

FolkCluster.register();

class FolkProximity extends HTMLElement {
static tagName = 'folk-proximity';

static register() {
customElements.define(this.tagName, this);
}

#clusters = new Set();
#geometries = Array.from(this.querySelectorAll('fc-geometry'));

constructor() {
super();

this.addEventListener('move', this.#handleProximity);
this.addEventListener('resize', this.#handleProximity);
// document.addEventListener('recenter', (e) => {
// proximityMap.get(e.target.parentElement)?.forEach((el) => {
// const content = el.firstElementChild;
// if (content instanceof GeoWiki) {
// const { lat, lng } = e.target.coordinates;
// content.coordinates = [lat, lng];
// }
// });
// });
}

#handleProximity = (e) => {
const el = e.target;
const cluster = this.#findCluster(el);

if (cluster === null) {
for (const cluster of this.#clusters) {
// what if its in proximity to multiple clusters?
if (cluster.isElementInProximity(element)) {
cluster.addElements(element);
return;
}
}

for (const geometry of this.#geometries) {
if (geometry === el) break;

if (collisionDetection(geometry.getClientRect(), el.getClientRect(), PROXIMITY)) {
const cluster = document.createElement('folk-cluster');
cluster.addElements(geometry, el);
this.#clusters.add(cluster);
this.appendChild(cluster);
return;
}
}
} else {
const isInCluster = cluster.sourceElements
.filter((element) => el !== element)
.some((element) => collisionDetection(el.getClientRect(), element.getClientRect(), PROXIMITY));

if (!isInCluster) {
cluster.removeElement(el);

if (cluster.sourcesMap.size === 1) {
this.#clusters.delete(cluster);
cluster.remove();
}
}
}
};

#findCluster(element) {
for (const cluster of this.#clusters) {
if (cluster.isElementInCluster(element)) return cluster;
}
return null;
}
}

FolkProximity.register();

/* proximityMap.forEach((set, el) => {
if (el !== e.target) {
const alreadyIntersection = set.has(e.target);
// TODO: refactor this hack once resizing and the vertices API are figured out
const isNowIntersecting = collisionDetection(el.getClientRect(), e.target.getClientRect(), 100);
if (isNowIntersecting && !alreadyIntersection) {
console.log('create cluster');
set.add(e.target);
proximityMap.get(e.target)?.add(el);
// hull = document.createElement('folk-hull');
// hull.sources = `#${el.id}, #${e.target.id}`;
// document.body.append(hull);
} else if (alreadyIntersection && !isNowIntersecting) {
console.log('delete cluster');
set.delete(e.target);
proximityMap.get(e.target)?.delete(el);
// hull.remove();
// hull = null;
}
}
}); */
</script>
</body>
</html>
10 changes: 8 additions & 2 deletions src/folk-hull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import { Vertex, verticesToPolygon } from './arrows/utils';
export class FolkHull extends FolkSet {
static tagName = 'folk-hull';

#hull: Vertex[] = [];

get hull(): ReadonlyArray<Vertex> {
return this.#hull;
}

update() {
if (this.sourcesMap.size === 0) {
this.style.clipPath = '';
return;
}

const rects = Array.from(this.sourcesMap.values());
const hull = makeHull(rects);
this.style.clipPath = verticesToPolygon(hull);
this.#hull = makeHull(rects);
this.style.clipPath = verticesToPolygon(this.#hull);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/folk-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class FolkSet extends HTMLElement {
return this.#sourcesMap;
}

get sourceElements() {
return Array.from(this.#sourcesMap.keys());
}

#sourcesCallback = (entry: ClientRectObserverEntry) => {
this.#sourcesMap.set(entry.target, entry.contentRect);
this.update();
Expand Down

0 comments on commit 8da1016

Please sign in to comment.