Skip to content

Commit e17784b

Browse files
ryan-williamsclaude
andcommitted
Fix view-dependent atom occlusion + atom palette + tile-copy labels
Three issues from the same user report: 1. Atoms were sometimes hidden inside their iso spheres depending on camera angle. Tile-copy atoms have transparent material; combined with the (also transparent) iso, Three.js's per-frame z-sort flipped visibility as the camera moved. Atoms now always `depthWrite=true` (the small alpha-cut artifact on transparent atoms is invisible at their size) and `renderOrder=1`, so they consistently sit over iso. 2. Bi/Rh/O all rendered in similar pink/red tones because Bi and Rh were missing from the CPK table and fell through to the default `#ff1493` (deep pink). Added Bi (purple), Rh (teal), and ~30 other transition metals + lanthanides + post-transition elements that show up in MP materials (Pd, Ir, Te, I, Sb, In, Cd, Hg, Tl, Re, Os, Ta, Hf, Y, Nb, Tc, Ru, La, Ce, Pr, ..., Th, U). Now Bi-Rh-O contrast clearly. 3. Atom labels only rendered for atoms with fractional position inside the primary cell, which made periodic copies in the tile/padded region invisible label-wise. Now every drawn copy gets a label, faded with the same `atomOpacity` used for the spheres. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1422b9a commit e17784b

2 files changed

Lines changed: 49 additions & 12 deletions

File tree

pkgs/core/src/components/CrystalStructure.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,11 @@ export function CrystalStructure({ volume, showAtoms, showAtomLabels, showAbcCel
113113

114114
// Group atoms by (element, quantized opacity) for tiled instanced rendering
115115
// When tilePadding > 0, per-atom opacity is computed from fractional-coordinate distance
116-
// Label atoms: only atoms whose fractional center is inside [0,1]³ (with small tolerance for boundary atoms)
116+
// Label atoms: every drawn atom copy gets a label, faded with the same opacity
117117
const { atomGroups, labelAtoms } = useMemo(() => {
118118
const groups = new Map<string, Array<[number, number, number]>>()
119-
const labels: Array<{ element: string; pos: [number, number, number] }> = []
119+
const labels: Array<{ element: string; pos: [number, number, number]; opacity: number }> = []
120120
const tileList = tiles ?? [{ fracOffset: [0, 0, 0] as [number, number, number], cartOffset: [0, 0, 0] as [number, number, number], opacity: 1, isPrimary: true }]
121-
const eps = 0.01
122121

123122
for (const tile of tileList) {
124123
if (tile.opacity <= 0) continue
@@ -144,11 +143,7 @@ export function CrystalStructure({ volume, showAtoms, showAtomLabels, showAbcCel
144143
arr.push(pos)
145144
groups.set(key, arr)
146145

147-
// Collect label candidates: atoms inside or on boundary of primary cell
148-
const inCell = fracPos[0] >= -eps && fracPos[0] <= 1 + eps &&
149-
fracPos[1] >= -eps && fracPos[1] <= 1 + eps &&
150-
fracPos[2] >= -eps && fracPos[2] <= 1 + eps
151-
if (inCell) labels.push({ element: atom.element, pos })
146+
labels.push({ element: atom.element, pos, opacity })
152147
}
153148
}
154149
return { atomGroups: groups, labelAtoms: labels }
@@ -258,12 +253,12 @@ export function CrystalStructure({ volume, showAtoms, showAtomLabels, showAbcCel
258253
<AtomInstances key={key} element={element} positions={positions} opacity={opacity} />
259254
)
260255
})}
261-
{showAtoms && showAtomLabels && labelAtoms.map(({ element, pos }, i) => {
256+
{showAtoms && showAtomLabels && labelAtoms.map(({ element, pos, opacity }, i) => {
262257
const { color, radius } = getElement(element)
263258
const css = `#${color.toString(16).padStart(6, '0')}`
264259
return (
265260
<Billboard key={`label-${i}`} position={[pos[0], pos[1] + radius * 0.4 + 0.15, pos[2]]}>
266-
<Text fontSize={0.2} color={css} anchorY="bottom" fillOpacity={0.85} outlineWidth={0.015} outlineColor="#000000">
261+
<Text fontSize={0.2} color={css} anchorY="bottom" fillOpacity={0.85 * opacity} outlineWidth={0.015} outlineColor="#000000" outlineOpacity={opacity}>
267262
{element}
268263
</Text>
269264
</Billboard>
@@ -358,9 +353,9 @@ function AtomInstances({ element, positions, opacity = 1 }: { element: string; p
358353
}, [positions, dummy])
359354

360355
return (
361-
<instancedMesh ref={meshRef} args={[undefined, undefined, positions.length]}>
356+
<instancedMesh ref={meshRef} args={[undefined, undefined, positions.length]} renderOrder={1}>
362357
<sphereGeometry args={[radius * 0.4, 16, 12]} />
363-
<meshStandardMaterial color={new Color(color)} transparent={opacity < 1} opacity={opacity} depthWrite={opacity >= 1} />
358+
<meshStandardMaterial color={new Color(color)} transparent={opacity < 1} opacity={opacity} depthWrite />
364359
</instancedMesh>
365360
)
366361
}

pkgs/core/src/utils/elements.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ export const ELEMENTS: Record<string, ElementData> = {
1717
N: { color: 0x3050f8, radius: 0.71 },
1818
O: { color: 0xff0d0d, radius: 0.66 },
1919
F: { color: 0x90e050, radius: 0.57 },
20+
Ne: { color: 0xb3e3f5, radius: 0.58 },
2021
Na: { color: 0xab5cf2, radius: 1.66 },
2122
Mg: { color: 0x8aff00, radius: 1.41 },
2223
Al: { color: 0xbfa6a6, radius: 1.21 },
2324
Si: { color: 0xf0c8a0, radius: 1.11 },
2425
P: { color: 0xff8000, radius: 1.07 },
2526
S: { color: 0xffff30, radius: 1.05 },
2627
Cl: { color: 0x1ff01f, radius: 1.02 },
28+
Ar: { color: 0x80d1e3, radius: 1.06 },
2729
K: { color: 0x8f40d4, radius: 2.03 },
2830
Ca: { color: 0x3dff00, radius: 1.76 },
31+
Sc: { color: 0xe6e6e6, radius: 1.70 },
2932
Ti: { color: 0xbfc2c7, radius: 1.60 },
3033
V: { color: 0xa6a6ab, radius: 1.53 },
3134
Cr: { color: 0x8a99c7, radius: 1.39 },
@@ -40,16 +43,55 @@ export const ELEMENTS: Record<string, ElementData> = {
4043
As: { color: 0xbd80e3, radius: 1.19 },
4144
Se: { color: 0xffa100, radius: 1.20 },
4245
Br: { color: 0xa62929, radius: 1.20 },
46+
Kr: { color: 0x5cb8d1, radius: 1.16 },
47+
Rb: { color: 0x702eb0, radius: 2.20 },
4348
Sr: { color: 0x00ff00, radius: 1.95 },
49+
Y: { color: 0x94ffff, radius: 1.90 },
4450
Zr: { color: 0x94e0e0, radius: 1.75 },
51+
Nb: { color: 0x73c2c9, radius: 1.64 },
4552
Mo: { color: 0x54b5b5, radius: 1.54 },
53+
Tc: { color: 0x3b9e9e, radius: 1.47 },
54+
Ru: { color: 0x248f8f, radius: 1.46 },
55+
Rh: { color: 0x0a7d8c, radius: 1.42 },
56+
Pd: { color: 0x006985, radius: 1.39 },
4657
Ag: { color: 0xc0c0c0, radius: 1.45 },
58+
Cd: { color: 0xffd98f, radius: 1.44 },
59+
In: { color: 0xa67573, radius: 1.42 },
4760
Sn: { color: 0x668080, radius: 1.39 },
61+
Sb: { color: 0x9e63b5, radius: 1.39 },
62+
Te: { color: 0xd47a00, radius: 1.38 },
63+
I: { color: 0x940094, radius: 1.39 },
64+
Xe: { color: 0x429eb0, radius: 1.40 },
65+
Cs: { color: 0x57178f, radius: 2.44 },
4866
Ba: { color: 0x00c900, radius: 2.15 },
67+
La: { color: 0x70d4ff, radius: 2.07 },
68+
Ce: { color: 0xffffc7, radius: 2.04 },
69+
Pr: { color: 0xd9ffc7, radius: 2.03 },
70+
Nd: { color: 0xc7ffc7, radius: 2.01 },
71+
Sm: { color: 0x8fffc7, radius: 1.98 },
72+
Eu: { color: 0x61ffc7, radius: 1.98 },
73+
Gd: { color: 0x45ffc7, radius: 1.96 },
74+
Tb: { color: 0x30ffc7, radius: 1.94 },
75+
Dy: { color: 0x1fffc7, radius: 1.92 },
76+
Ho: { color: 0x00ff9c, radius: 1.92 },
77+
Er: { color: 0x00e675, radius: 1.89 },
78+
Tm: { color: 0x00d452, radius: 1.90 },
79+
Yb: { color: 0x00bf38, radius: 1.87 },
80+
Lu: { color: 0x00ab24, radius: 1.87 },
81+
Hf: { color: 0x4dc2ff, radius: 1.75 },
82+
Ta: { color: 0x4da6ff, radius: 1.70 },
4983
W: { color: 0x2194d6, radius: 1.62 },
84+
Re: { color: 0x267dab, radius: 1.51 },
85+
Os: { color: 0x266696, radius: 1.44 },
86+
Ir: { color: 0x175487, radius: 1.41 },
5087
Pt: { color: 0xd0d0e0, radius: 1.36 },
5188
Au: { color: 0xffd123, radius: 1.36 },
89+
Hg: { color: 0xb8b8d0, radius: 1.32 },
90+
Tl: { color: 0xa6544d, radius: 1.45 },
5291
Pb: { color: 0x575961, radius: 1.46 },
92+
Bi: { color: 0x9e4fb5, radius: 1.48 },
93+
Th: { color: 0x00baff, radius: 2.06 },
94+
U: { color: 0x008fff, radius: 1.96 },
5395
}
5496

5597
const DEFAULT_ELEMENT: ElementData = { color: 0xff1493, radius: 1.0 }

0 commit comments

Comments
 (0)