segment-lens / index.html
SharpMeow's picture
Phone layout: nothing cut off at the right
d27240d verified
Raw History Blame Contribute Delete
19.9 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Segment Lens · every published Herculaneum segment, measured</title>
<meta name="description" content="Traced segments from the Vesuvius Challenge open data as 3D surfaces, with holes, folds and over-stretched grid edges painted on, beside the Segment Atlas numbers.">
<meta name="theme-color" content="#05080b">
<meta name="color-scheme" content="dark">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%2305080b'/%3E%3Cpath d='M5 22c5-9 9 3 14-6s6-6 8-4' fill='none' stroke='%23d8e4ea' stroke-width='2'/%3E%3Ccircle cx='12' cy='17' r='2.4' fill='%23ff5c5c'/%3E%3Ccircle cx='22' cy='13' r='2.4' fill='%23ffa733'/%3E%3C/svg%3E">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=2">
<!-- space-onescroll:start -->
<!-- one page scroll bar: written by python -m kit spaceconnect render; do not edit -->
<style>iframe[srcdoc]{display:block;max-height:none!important}</style>
<script>
(function () {
if (window.__oneScroll) return;
window.__oneScroll = true;
var CSS = 'html,body{height:auto!important;min-height:0!important}html{overflow:hidden!important}';
function size(f) {
var d, st = f.__os;
try { d = f.contentDocument; } catch (e) { return; }
if (!d || !d.body || st.off) return;
var de = d.documentElement, r = Math.ceil(de.getBoundingClientRect().height), over = de.scrollHeight;
var ch = f.clientHeight, t = r;
if (over > ch + 1) t = Math.max(r, over);
else if (r >= st.r) t = Math.max(r, ch);
st.r = r;
if (Math.abs(t - ch) < 2) return;
var now = Date.now();
st.n = now - st.t0 < 4000 ? st.n + 1 : 1;
if (st.n === 1) st.t0 = now;
if (st.n > 40) { st.off = true; f.removeAttribute('scrolling'); var g = d.getElementById('space-onescroll'); if (g) g.remove(); return; }
f.style.setProperty('height', t + 'px', 'important');
}
function attach(f) {
var d, st = f.__os;
try { d = f.contentDocument; } catch (e) { return; }
if (!d || !d.documentElement || st.doc === d) return;
st.doc = d; st.r = Infinity; st.n = 0; st.off = false;
if (!d.getElementById('space-onescroll')) {
var s = d.createElement('style'); s.id = 'space-onescroll'; s.textContent = CSS;
(d.head || d.documentElement).appendChild(s);
}
f.setAttribute('scrolling', 'no');
f.style.setProperty('min-height', '0', 'important');
f.style.setProperty('max-height', 'none', 'important');
var w = d.defaultView, go = function () { size(f); };
if (w && w.ResizeObserver) { var ro = new w.ResizeObserver(go); ro.observe(d.documentElement); if (d.body) ro.observe(d.body); }
d.addEventListener('load', go, true);
go();
}
function bind(f) {
if (f.__os || f.getAttribute('data-scroll') === 'inner') return;
try { if (!f.contentDocument) return; } catch (e) { return; }
f.__os = {t0: 0, n: 0, r: 0};
f.addEventListener('load', function () { attach(f); });
if (f.contentDocument.readyState !== 'loading') attach(f);
else f.contentDocument.addEventListener('DOMContentLoaded', function () { attach(f); });
}
function scan() { var fs = document.getElementsByTagName('iframe'); for (var i = 0; i < fs.length; i++) bind(fs[i]); }
function start() {
scan();
new MutationObserver(scan).observe(document.documentElement, {childList: true, subtree: true});
window.addEventListener('resize', function () {
var fs = document.getElementsByTagName('iframe');
for (var i = 0; i < fs.length; i++) if (fs[i].__os) size(fs[i]);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();
})();
</script>
<!-- space-onescroll:end -->
<script type="importmap">
{ "imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
} }
</script>
</head>
<body>
<canvas id="gl" aria-label="3D view of the selected segment"></canvas>
<header class="hud tl">
<div class="kicker mono dim"><span class="k-src">VESUVIUS CHALLENGE<span class="sep">/</span>OPEN DATA<span class="sep">/</span></span><span id="k-count">…</span> MESHES</div>
<h1>Segment <em>Lens</em></h1>
<p class="lede">Traced segments as surfaces, with the grid's problems painted on: <span class="c-hole">holes</span>, <span class="c-fold">folds</span> and <span class="c-str">over-stretched</span> edges. Numbers from the <a href="https://huggingface.co/datasets/SharpMeow/vesuvius-segment-atlas" target="_blank" rel="noopener">Segment Atlas</a>.</p>
</header>
<nav class="hud tr" aria-label="View">
<div class="seg" role="tablist">
<button class="chip on" data-mode="lens" role="tab">3D</button>
<button class="chip" data-mode="table" role="tab">Table</button>
</div>
<button class="tbtn" id="b-about">Guide</button>
</nav>
<aside class="hud bl panel" id="picker" aria-label="Pick a segment">
<label class="mono small dim" for="sel-scroll">SCROLL</label>
<select id="sel-scroll"></select>
<label class="row mono small"><input type="checkbox" id="only-top" checked> top-level meshes only</label>
<input id="filter" class="mono small" type="search" placeholder="filter segments" aria-label="Filter segments">
<ol id="list" class="list mono small"></ol>
</aside>
<aside class="hud br panel" id="card" aria-live="polite">
<div class="mono small dim" id="c-kicker">SELECT A SEGMENT</div>
<h2 id="c-title">…</h2>
<details id="c-more" class="more" open>
<summary class="mono dim">NUMBERS</summary>
<dl id="c-nums" class="nums mono small"></dl>
</details>
<div class="legend mono small">
<label><input type="checkbox" id="t-hole" checked> <span class="sw hole"></span>holes</label>
<label><input type="checkbox" id="t-fold" checked> <span class="sw fold"></span>folds</label>
<label><input type="checkbox" id="t-str" checked> <span class="sw str"></span>stretched</label>
<label><input type="checkbox" id="t-wire"> grid</label>
</div>
<div class="actions mono small">
<button id="b-copy" class="lk">copy kit command</button>
<a id="a-bucket" class="lk" target="_blank" rel="noopener">mesh folder</a>
</div>
<p class="mono tiny dim" id="c-note"></p>
</aside>
<section id="table-view" class="sheet" hidden>
<div class="sheet-head">
<h2>Every mesh in the atlas</h2>
<p class="mono small dim" id="t-meta"></p>
<input id="t-filter" class="mono small" type="search" placeholder="filter by scroll or segment" aria-label="Filter the table">
</div>
<div class="tablewrap">
<table class="mono small" id="tbl">
<thead><tr>
<th data-k="scroll">scroll</th><th data-k="segment">segment</th><th data-k="voxel_um" class="n">voxel µm</th>
<th data-k="area_cm2" class="n">area cm²</th><th data-k="filled_share" class="n">filled</th>
<th data-k="within_10pct" class="n">edges ±10%</th><th data-k="holes_mm2" class="n">holes mm²</th>
<th data-k="folds_mm2" class="n">folds mm²</th><th data-k="stretched_mm2" class="n">stretched mm²</th>
<th data-k="lens">3D</th>
</tr></thead>
<tbody></tbody>
</table>
</div>
</section>
<dialog id="about" class="sheet-dialog">
<form method="dialog"><button class="x" aria-label="Close">&times;</button></form>
<h2>Guide</h2>
<p>A <b>segment</b> is a piece of the rolled papyrus surface that someone (or a program) traced through the X-ray CT of a scroll. It is stored as a <b>tifxyz</b> mesh: three images, x.tif, y.tif and z.tif, that together hold a grid of 3D points in the scan's voxels. The open data maps each segment onto several scan volumes, so one segment can have several meshes.</p>
<p>The colors mark what <code>kit tifxyz check</code> flags on the full grid:</p>
<ul>
<li><span class="c-hole">Holes</span>: missing grid nodes inside the sheet (not joined to its border). The preview draws the rim around them.</li>
<li><span class="c-fold">Folds</span>: neighboring cells whose surface normals turn by more than 90 degrees, where the surface doubles back.</li>
<li><span class="c-str">Stretched</span>: grid edges more than 1.5 times longer or shorter than the spacing the mesh's meta.json promises.</li>
</ul>
<p>"Edges ±10%" is the share of grid edges within 10 percent of that spacing: how close the grid is to isometric, which matters for flattened renders. A clean mesh can still jump from one wrap of papyrus to the next; nothing here looks at the CT or judges ink, and none of it is an official villa or prize check.</p>
<p>3D previews are shown only for scans on none of the prize lists (Grand Prize, First Letters, and the title prize's PHerc. Paris 4, from kit's dated snapshot); scrolls on a list appear in the table with numbers only. Previews keep every k-th node, at most 256 per side, with each flag pooled over the block it stands for.</p>
<!-- space-connect:start -->
<section class="scc" id="connect" aria-label="Connect your tool"><style>.scc{--a:var(--scc-accent,#4ea8de);color:inherit;font:inherit;line-height:1.5;max-width:60rem}
.scc h2{font-size:1.15em;margin:.2em 0 .3em}.scc h3{font-size:1em;margin:0 0 .2em}
.scc p{margin:.3em 0}.scc a{color:var(--a)}
.scc .scc-src{border:1px solid color-mix(in srgb,currentColor 18%,transparent);border-radius:10px;padding:.7em .9em;margin:.7em 0;background:color-mix(in srgb,currentColor 4%,transparent)}
.scc .scc-meta{font-size:.85em;opacity:.8}
.scc details{border-top:1px solid color-mix(in srgb,currentColor 12%,transparent);padding:.35em 0}
.scc summary{cursor:pointer;font-weight:600;list-style-position:inside}
.scc .scc-b{font-weight:400;font-size:.75em;padding:.05em .5em;border-radius:99px;margin-left:.5em;border:1px solid currentColor;opacity:.75;white-space:nowrap}
.scc .scc-b.ok{color:#3fb37f}.scc .scc-b.src{color:var(--a)}.scc .scc-b.warn{color:#e0a030}
.scc .scc-code{position:relative;margin:.4em 0 .2em}
.scc pre{margin:0;padding:.7em .8em;border-radius:8px;overflow:auto;font-size:.82em;background:color-mix(in srgb,currentColor 9%,transparent);white-space:pre}
.scc code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.scc button.scc-copy{all:unset;box-sizing:border-box;position:absolute;top:.35em;right:.35em;font-size:.75em;line-height:1.4;padding:.1em .6em;border-radius:6px;border:1px solid color-mix(in srgb,currentColor 30%,transparent);background:color-mix(in srgb,currentColor 8%,transparent);color:inherit;cursor:pointer}
.scc button.scc-copy:hover{border-color:var(--a)}
.scc .scc-url{position:relative}.scc .scc-url pre{padding-right:4.5em;white-space:pre-wrap;word-break:break-all}
.scc .scc-go{display:inline-block;margin:.3em 0;padding:.25em .8em;border-radius:6px;border:1px solid var(--a);text-decoration:none}
.scc footer{font-size:.8em;opacity:.75;margin-top:.6em}</style>
<h2>Connect your tool</h2>
<p>Every number and preview on this page comes from the Segment Atlas dataset, and every mesh it measures is a tifxyz folder on the open-data bucket. Both read straight from these URLs, with no account.</p>
<div class="scc-src" id="connect-atlas"><h3>Segment Atlas (one row per published mesh)</h3>
<p class="scc-meta">CC BY-NC 4.0 (derived from Vesuvius Challenge open data) · Each row has the mesh&#x27;s open-data url, its grid size, area, filled share, edge statistics and the holes, folds and stretched area that kit tifxyz check reports. lens/ holds the decimated 3D previews.</p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://huggingface.co/datasets/SharpMeow/vesuvius-segment-atlas</code></pre></div>
<details><summary>Python (huggingface_hub)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install huggingface_hub
from huggingface_hub import hf_hub_download, snapshot_download
path = hf_hub_download(&quot;SharpMeow/vesuvius-segment-atlas&quot;, &quot;atlas.jsonl&quot;, repo_type=&quot;dataset&quot;) # one file, cached
folder = snapshot_download(&quot;SharpMeow/vesuvius-segment-atlas&quot;, repo_type=&quot;dataset&quot;)</code></pre></div></details>
<details><summary>Python (datasets)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install datasets
from datasets import load_dataset
default = load_dataset(&quot;SharpMeow/vesuvius-segment-atlas&quot;, &quot;default&quot;, split=&quot;train&quot;)</code></pre></div></details>
<details><summary>pandas<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install pandas huggingface_hub
import pandas as pd
df = pd.read_json(&quot;hf://datasets/SharpMeow/vesuvius-segment-atlas/atlas.jsonl&quot;, lines=True)</code></pre></div></details>
<details><summary>Command line (hf)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>pip install -U huggingface_hub
hf download SharpMeow/vesuvius-segment-atlas atlas.jsonl --repo-type dataset --local-dir vesuvius-segment-atlas</code></pre></div></details>
<details><summary>curl<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>curl -L -O https://huggingface.co/datasets/SharpMeow/vesuvius-segment-atlas/resolve/main/atlas.jsonl</code></pre></div></details>
</div>
<div class="scc-src" id="connect-mesh"><h3>A measured mesh (tifxyz), example PHerc0009B 20250510172639</h3>
<p class="scc-meta">CC BY-NC 4.0 (Vesuvius Challenge open data) · Every row of the atlas names its mesh folder in url; the table&#x27;s mesh folder link opens it. Read live from the open-data bucket, never copied here.</p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0009B/segments/20250510172639/mesh/20250510172639-on-20250521125136-8.64um.tifxyz</code></pre></div>
<details><summary>Python (numpy, tifffile)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install numpy tifffile
import io, json, urllib.request
import numpy as np, tifffile
T = &quot;https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0009B/segments/20250510172639/mesh/20250510172639-on-20250521125136-8.64um.tifxyz&quot;
meta = json.load(urllib.request.urlopen(T + &quot;/meta.json&quot;)) # scale, bbox, format
get = lambda c: tifffile.imread(io.BytesIO(urllib.request.urlopen(f&quot;{T}/{c}.tif&quot;).read()))
xyz = np.stack([get(c) for c in &quot;xyz&quot;], -1) # xyz[i, j] = (x, y, z) in scan voxels; -1 = no surface</code></pre></div></details>
</div>
<div class="scc-src" id="connect-page"><h3>This page&#x27;s code and data</h3>
<p class="scc-meta">Page code Apache 2.0; data as above · python -m kit atlas show SCROLL and python -m kit tifxyz check MESH_URL reproduce the numbers from a clone of ChaseHendrick/Scrolls.</p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://sharpmeow-segment-lens.hf.space/</code></pre></div>
<details><summary>Links and embed<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>The whole atlas as one CSV: https://huggingface.co/datasets/SharpMeow/vesuvius-segment-atlas/resolve/main/atlas.csv
API and MCP for agents (list_scrolls, list_segments, get_segment, segment_picture): https://sharpmeow-segment-lens.hf.space/agents/
Page source: https://github.com/ChaseHendrick/Scrolls/tree/main/spaces/segment-lens</code></pre></div></details>
</div>
<footer>Machine-readable: <a href="https://huggingface.co/spaces/SharpMeow/segment-lens/blob/main/connect.json" target="_blank" rel="noopener">connect.json</a> and <a href="https://huggingface.co/spaces/SharpMeow/segment-lens/blob/main/CONNECT.md" target="_blank" rel="noopener">CONNECT.md</a>. Checked 2026-10-24. "Tested" means the snippet ran against this public data on that date. Scan data keeps its own license (the Vesuvius Challenge open data is CC BY-NC 4.0).</footer></section>
<!-- space-connect:end -->
<h3>On your own machine</h3>
<pre class="mono small">git clone https://github.com/ChaseHendrick/Scrolls &amp;&amp; cd Scrolls
python -m pip install numpy tifffile imagecodecs
python -m kit atlas show PHerc0139 --top 10
python -m kit tifxyz check MESH_URL --png check.png</pre>
<h3>Credits</h3>
<ul class="small">
<li>Meshes: Vesuvius Challenge open data, CC BY-NC 4.0. The atlas numbers and previews carry the same license.</li>
<li>This page: Apache 2.0, by Chase Hendrick (<a href="https://github.com/ChaseHendrick/Scrolls/tree/main/spaces/segment-lens" target="_blank" rel="noopener">source</a>). three.js is MIT.</li>
</ul>
</dialog>
<div id="toast" class="toast mono small" role="status" hidden></div>
<script type="module" src="app.js?v=2"></script>
</body>
</html>