vcabeli commited on
Commit
bb2e076
·
verified ·
1 Parent(s): 7bb4296

Draw the medallion as roots and show the full model-size range

Browse files

Replace leaf shapes and time rings with tapered, gently irregular branches using the existing family hierarchy. Retain all 102 family tips and corpus counts, with ancestry highlighting on hover. Spacing is illustrative and the age scale is removed. Update the Overview heading to 318M to 3.2B parameters. Verified family data, hover behaviour, motion preferences, light/dark layouts and prior removals.

Files changed (4) hide show
  1. README.md +5 -2
  2. assets/hero.js +51 -22
  3. assets/site.css +4 -7
  4. index.html +1 -1
README.md CHANGED
@@ -39,8 +39,11 @@ Static landing page for the Botanic1 models (Living Models), linking to the mode
39
  Living Models' palette (pale sage paper, near-black ink, the neon lime as a fill,
40
  pill buttons) with two devices of the Space's own: the wordmark set as a row of
41
  token cells whose last cell is masked and then predicted (`assets/hero.js`), and the
42
- corpus's dated family tree drawn as a radial medallion on the report page
43
- (`data/tree.json`, built from `figures/data/plants_family_timetree5.nwk`). Manrope for
 
 
 
44
  headings and controls, Spectral for reading, IBM Plex Mono for tokens, labels and
45
  numbers. Detailed charts remain on the benchmark and corpus pages. The overview
46
  states a few results from the report and links to their evaluations. The original
 
39
  Living Models' palette (pale sage paper, near-black ink, the neon lime as a fill,
40
  pill buttons) with two devices of the Space's own: the wordmark set as a row of
41
  token cells whose last cell is masked and then predicted (`assets/hero.js`), and the
42
+ corpus's family tree drawn as spreading roots in the landing-page medallion
43
+ (`data/tree.json`, built from `figures/data/plants_family_timetree5.nwk`). The drawing
44
+ retains the family relationships, with tapered branches and an uneven perimeter;
45
+ its spacing is illustrative. Hovering over a tip shows its family and corpus counts.
46
+ Manrope for
47
  headings and controls, Spectral for reading, IBM Plex Mono for tokens, labels and
48
  numbers. Detailed charts remain on the benchmark and corpus pages. The overview
49
  states a few results from the report and links to their evaluations. The original
assets/hero.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Hero: the corpus's dated family tree as a radial dendrogram (the medallion),
2
  and the masked token in every wordmark. Needs d3 when a medallion is present. */
3
  (function () {
4
  // Private Hugging Face Spaces authorise requests through the __sign token of the
@@ -57,10 +57,46 @@
57
  const spans = d.children.map(k => d3.extent(k.leaves(), l => l.x)).sort((p, q) => p[0] - q[0]);
58
  d.x = spans.length === 1 ? d.children[0].x : (spans[0][1] + spans[1][0]) / 2;
59
  });
60
- root.each(d => { d.y = (d.age / maxAge) * R; });
61
- const link = d3.linkRadial().angle(d => d.x).radius(d => d.y);
62
- const rMax = d3.max(root.leaves(), d => d.data.windows);
63
- const rScale = d3.scaleSqrt().domain([0, rMax]).range([1.4, 9]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  const svg = d3.create('svg').attr('viewBox', `0 0 ${size} ${size}`).attr('aria-hidden', 'true');
66
  // Rotation is driven from JavaScript on a nested group: a CSS transform would
@@ -68,24 +104,17 @@
68
  const centre = svg.append('g').attr('transform', `translate(${c},${c})`);
69
  const g = centre.append('g').attr('class', 'spin');
70
  if (!reduced) { const period = 480000; let t0 = null; const tick = (ts) => { if (t0 === null) t0 = ts; g.attr('transform', `rotate(${((ts - t0) / period) * 360})`); requestAnimationFrame(tick); }; requestAnimationFrame(tick); }
71
- const rings = d3.range(100, maxAge, 100);
72
- g.selectAll('circle.ring').data(rings).join('circle').attr('class', 'ring').attr('r', d => ((maxAge - d) / maxAge) * R);
73
- g.selectAll('path.link').data(root.links()).join('path').attr('class', 'link').attr('d', link);
74
- // Each family is a small leaf at its tip, sized by its windows, pointing along the
75
- // branch with a slight deterministic tilt.
76
- const lScale = d3.scaleSqrt().domain([0, rMax]).range([5, 16]);
77
- const leafPath = (L) => { const w = L * 0.36; return `M0,0 Q${L * 0.45},${-w} ${L},0 Q${L * 0.45},${w} 0,0 Z`; };
78
- const tips = g.selectAll('g.leaf').data(root.leaves()).join('g').attr('class', 'leaf')
79
- .attr('transform', (d, i) => `rotate(${(d.x * 180 / Math.PI) - 90}) translate(${d.y - 2},0) rotate(${((i * 37) % 41) - 20})`);
80
- tips.append('path').attr('class', 'blade').attr('d', d => leafPath(lScale(d.data.windows)));
81
- tips.append('path').attr('class', 'rib').attr('d', d => `M1,0 L${lScale(d.data.windows) * 0.8},0`);
82
- tips.append('circle').attr('class', 'hit').attr('r', d => Math.max(6, lScale(d.data.windows) * 0.6)).attr('cx', d => lScale(d.data.windows) * 0.5);
83
- tips.on('mousemove', (ev, d) => showTip(`<b>${d.data.name}</b> · ${d.data.order}<br>${d.data.n_species} species · ${d.data.windows.toLocaleString()} windows`, ev))
84
- .on('mouseleave', hideTip);
85
  const lab = svg.append('g').attr('transform', `translate(${c},${c})`);
86
- lab.selectAll('text').data([]).join('text').attr('class', 'ring-label')
87
- .attr('x', 4).attr('y', d => -((maxAge - d) / maxAge) * R - 4).text(d => `${d} Ma`);
88
- lab.append('text').attr('class', 'ring-label').attr('x', 4).attr('y', -R - 6).text(`${Math.round(maxAge)} Ma · 102 families · 320 species`);
89
  el.appendChild(svg.node());
90
  }).catch(() => {});
91
  })();
 
1
+ /* Hero: the corpus's family tree drawn as spreading roots (the medallion),
2
  and the masked token in every wordmark. Needs d3 when a medallion is present. */
3
  (function () {
4
  // Private Hugging Face Spaces authorise requests through the __sign token of the
 
57
  const spans = d.children.map(k => d3.extent(k.leaves(), l => l.x)).sort((p, q) => p[0] - q[0]);
58
  d.x = spans.length === 1 ? d.children[0].x : (spans[0][1] + spans[1][0]) / 2;
59
  });
60
+ // Keep the family relationships and ordering, with an uneven outer silhouette.
61
+ // This is an illustration of the tree, so no time rings or age scale are shown.
62
+ const seed = name => Array.from(name).reduce((value, ch) => (value * 31 + ch.charCodeAt(0)) >>> 0, 17);
63
+ root.eachBefore(d => {
64
+ const reach = R - 12 + 12 * Math.sin(d.x * 3 + .6) + 7 * Math.sin(d.x * 7 - .8);
65
+ const tipOffset = d.children ? 0 : (seed(d.data.name) % 25) - 12;
66
+ d.y = Math.max(d.parent ? d.parent.y : 0, Math.pow(d.age / maxAge, 1.45) * reach + tipOffset);
67
+ d.width = 1 + 7 * Math.pow(d.data._c / nLeaves, .65);
68
+ });
69
+ const polar = (angle, radius) => [Math.sin(angle) * radius, -Math.cos(angle) * radius];
70
+ const rootPath = ({ source, target }) => {
71
+ const middle = (source.y + target.y) / 2;
72
+ const controls = [polar(source.x, source.y), polar(source.x, middle), polar(target.x, middle), polar(target.x, target.y)];
73
+ const distance = controls.slice(1).reduce((sum, point, i) => sum + Math.hypot(point[0] - controls[i][0], point[1] - controls[i][1]), 0);
74
+ const phase = (seed(target.data.name || String(target.x)) % 628) / 100;
75
+ const amplitude = Math.min(3.5, distance * .025);
76
+ const steps = Math.max(12, Math.ceil(distance / 5));
77
+ const points = d3.range(steps + 1).map(i => {
78
+ const t = i / steps, u = 1 - t;
79
+ const weights = [u ** 3, 3 * u * u * t, 3 * u * t * t, t ** 3];
80
+ const point = [0, 1].map(axis => controls.reduce((sum, p, j) => sum + weights[j] * p[axis], 0));
81
+ const tangent = [0, 1].map(axis => 3 * u * u * (controls[1][axis] - controls[0][axis]) + 6 * u * t * (controls[2][axis] - controls[1][axis]) + 3 * t * t * (controls[3][axis] - controls[2][axis]));
82
+ const length = Math.hypot(...tangent) || 1;
83
+ // The bend fades at each end so adjoining branches meet smoothly.
84
+ const bend = amplitude * Math.sin(Math.PI * t) ** 2 * Math.sin(2 * Math.PI * t + phase);
85
+ return [point[0] - tangent[1] / length * bend, point[1] + tangent[0] / length * bend];
86
+ });
87
+ const sides = [[], []];
88
+ const startWidth = Math.min(source.width, target.width * 1.65);
89
+ const endWidth = target.children ? target.width : .08;
90
+ points.forEach((point, i) => {
91
+ const t = i / steps;
92
+ const before = points[Math.max(0, i - 1)], after = points[Math.min(steps, i + 1)];
93
+ const dx = after[0] - before[0], dy = after[1] - before[1], length = Math.hypot(dx, dy) || 1;
94
+ const width = (startWidth * (1 - t) + endWidth * t) * (1 + .06 * Math.sin(3 * Math.PI * t + phase) * Math.sin(Math.PI * t)) / 2;
95
+ sides[0].push([point[0] - dy / length * width, point[1] + dx / length * width]);
96
+ sides[1].push([point[0] + dy / length * width, point[1] - dx / length * width]);
97
+ });
98
+ return d3.line()(sides[0].concat(sides[1].reverse())) + 'Z';
99
+ };
100
 
101
  const svg = d3.create('svg').attr('viewBox', `0 0 ${size} ${size}`).attr('aria-hidden', 'true');
102
  // Rotation is driven from JavaScript on a nested group: a CSS transform would
 
104
  const centre = svg.append('g').attr('transform', `translate(${c},${c})`);
105
  const g = centre.append('g').attr('class', 'spin');
106
  if (!reduced) { const period = 480000; let t0 = null; const tick = (ts) => { if (t0 === null) t0 = ts; g.attr('transform', `rotate(${((ts - t0) / period) * 360})`); requestAnimationFrame(tick); }; requestAnimationFrame(tick); }
107
+ const branches = g.selectAll('path.root-branch').data(root.links()).join('path').attr('class', 'root-branch').attr('d', rootPath);
108
+ // Each fine tip is still one family. Invisible targets retain the data tooltip.
109
+ const tips = g.selectAll('circle.root-tip').data(root.leaves()).join('circle').attr('class', 'root-tip')
110
+ .attr('cx', d => polar(d.x, d.y)[0]).attr('cy', d => polar(d.x, d.y)[1]).attr('r', 7);
111
+ tips.on('mousemove', (ev, d) => {
112
+ const ancestors = new Set(d.ancestors());
113
+ branches.classed('highlighted', link => ancestors.has(link.target));
114
+ showTip(`<b>${d.data.name}</b> · ${d.data.order}<br>${d.data.n_species} species · ${d.data.windows.toLocaleString()} windows`, ev);
115
+ }).on('mouseleave', () => { branches.classed('highlighted', false); hideTip(); });
 
 
 
 
 
116
  const lab = svg.append('g').attr('transform', `translate(${c},${c})`);
117
+ lab.append('text').attr('class', 'tree-label').attr('x', 0).attr('y', -R - 16).attr('text-anchor', 'middle').text(`${nLeaves} families · ${data.tree.n_species} species`);
 
 
118
  el.appendChild(svg.node());
119
  }).catch(() => {});
120
  })();
assets/site.css CHANGED
@@ -122,13 +122,10 @@ em, i { font-style: italic; }
122
  .medallion { position: relative; width: min(780px, 94vw); aspect-ratio: 1; display: grid; place-items: center; }
123
  .medallion .hero-art { position: absolute; inset: 0; }
124
  .medallion .hero-art svg { width: 100%; height: 100%; display: block; overflow: visible; }
125
- .hero-art .link { fill: none; stroke: var(--tree); stroke-width: 1.1; }
126
- .hero-art .leaf { cursor: default; }
127
- .hero-art .leaf .blade { fill: var(--tree-2); stroke: var(--sage); stroke-width: 0.8; transition: fill 0.2s ease; }
128
- .hero-art .leaf .rib { fill: none; stroke: var(--sage); stroke-width: 0.7; opacity: 0.9; }
129
- .hero-art .leaf:hover .blade { fill: var(--lime); stroke: var(--ink); }
130
- .hero-art .ring { fill: none; stroke: var(--rule); stroke-dasharray: 2 5; }
131
- .hero-art .ring-label { font-family: var(--mono); font-size: 10.5px; fill: var(--ink-3); letter-spacing: 0.08em; }
132
  .medallion .centre { position: relative; z-index: 2; text-align: center; padding: 44px 40px 40px; border-radius: 50%; background: radial-gradient(closest-side, var(--sage) 58%, color-mix(in srgb, var(--sage) 70%, transparent) 80%, transparent 100%); pointer-events: none; max-width: 100%; }
133
  .medallion .centre > * { pointer-events: auto; }
134
  .medallion .org { font-family: var(--sans); font-weight: 600; font-size: 13px; color: var(--ink-3); margin: 0; }
 
122
  .medallion { position: relative; width: min(780px, 94vw); aspect-ratio: 1; display: grid; place-items: center; }
123
  .medallion .hero-art { position: absolute; inset: 0; }
124
  .medallion .hero-art svg { width: 100%; height: 100%; display: block; overflow: visible; }
125
+ .hero-art .root-branch { fill: var(--tree-2); opacity: .65; transition: fill .2s ease, opacity .2s ease; }
126
+ .hero-art .root-branch.highlighted { fill: var(--accent); opacity: 1; }
127
+ .hero-art .root-tip { fill: transparent; cursor: default; }
128
+ .hero-art .tree-label { font-family: var(--mono); font-size: 10.5px; fill: var(--ink-3); letter-spacing: 0.08em; }
 
 
 
129
  .medallion .centre { position: relative; z-index: 2; text-align: center; padding: 44px 40px 40px; border-radius: 50%; background: radial-gradient(closest-side, var(--sage) 58%, color-mix(in srgb, var(--sage) 70%, transparent) 80%, transparent 100%); pointer-events: none; max-width: 100%; }
130
  .medallion .centre > * { pointer-events: auto; }
131
  .medallion .org { font-family: var(--sans); font-weight: 600; font-size: 13px; color: var(--ink-3); margin: 0; }
index.html CHANGED
@@ -47,7 +47,7 @@
47
  <div class="facts">
48
  <article class="fact">
49
  <p class="fact-label">Model size</p>
50
- <h2>318M parameters</h2>
51
  <p>Botanic1-S scores 0.758 on the aggregate benchmark across nine task families. PlantCAD2-L scores 0.756, Carbon-8B 0.727, and Evo&nbsp;2-7B 0.695.</p>
52
  <p class="fact-note">Frozen models · aggregate test score (S<sub>bal</sub><sup>test</sup>)</p>
53
  <a class="text-link" href="leaderboard.html">Scores by task and species <span aria-hidden="true">↗</span></a>
 
47
  <div class="facts">
48
  <article class="fact">
49
  <p class="fact-label">Model size</p>
50
+ <h2>318M to 3.2B parameters</h2>
51
  <p>Botanic1-S scores 0.758 on the aggregate benchmark across nine task families. PlantCAD2-L scores 0.756, Carbon-8B 0.727, and Evo&nbsp;2-7B 0.695.</p>
52
  <p class="fact-note">Frozen models · aggregate test score (S<sub>bal</sub><sup>test</sup>)</p>
53
  <a class="text-link" href="leaderboard.html">Scores by task and species <span aria-hidden="true">↗</span></a>