Skip to content

Commit

Permalink
Add Lattice.svelte (#41)
Browse files Browse the repository at this point in the history
* replace iconify with HTML entities

* extract Lattice.svelte from StructureScene

* add 2 MP P1 structures

* sort JSON structures by MP ID number

* /structure demo make grid wider

* fix tests + update deps

* install-cmd: npm install --force

* revert accidental line stage

* only render Lattice if available
  • Loading branch information
janosh authored Jul 8, 2023
1 parent f43dd31 commit 9d4e29e
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 97 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ on:
jobs:
build:
uses: janosh/workflows/.github/workflows/nodejs-gh-pages.yml@main
with:
install-cmd: npm install --force
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ on:
jobs:
tests:
uses: janosh/workflows/.github/workflows/npm-test-release.yml@main
with:
install-cmd: npm install --force
34 changes: 17 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"dependencies": {
"@iconify/svelte": "^3.1.4",
"@sveltejs/kit": "^1.20.5",
"@sveltejs/kit": "^1.22.1",
"d3": "^7.8.5",
"d3-array": "^3.2.4",
"d3-color": "^3.1.0",
Expand All @@ -34,47 +34,47 @@
"d3-scale-chromatic": "^3.0.0",
"d3-shape": "^3.2.0",
"highlight.js": "^11.8.0",
"svelte": "4.0.0",
"svelte-multiselect": "^9.0.0",
"three": "^0.153.0"
"svelte": "4.0.5",
"svelte-multiselect": "^10.0.0",
"three": "^0.154.0"
},
"devDependencies": {
"@playwright/test": "^1.35.1",
"@sveltejs/adapter-static": "2.0.2",
"@sveltejs/package": "^2.1.0",
"@threlte/core": "6.0.0-next.9",
"@threlte/extras": "5.0.0-next.13",
"@threlte/core": "6.0.0-next.11",
"@threlte/extras": "5.0.0-next.16",
"@types/d3-array": "^3.0.5",
"@types/d3-color": "^3.1.0",
"@types/d3-interpolate-path": "^2.0.0",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"@types/d3-shape": "^3.1.1",
"@types/three": "^0.152.1",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"@vitest/coverage-c8": "^0.32.2",
"eslint": "^8.43.0",
"@types/three": "^0.153.0",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"@vitest/coverage-c8": "^0.33.0",
"eslint": "^8.44.0",
"eslint-plugin-svelte": "^2.32.2",
"hastscript": "^7.2.0",
"jsdom": "^22.1.0",
"mdsvex": "^0.11.0",
"mdsvexamples": "^0.3.3",
"prettier": "^2.8.8",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^2.10.1",
"rehype-autolink-headings": "^6.1.1",
"rehype-katex-svelte": "^1.2.0",
"rehype-slug": "^5.1.0",
"remark-math": "3.0.0",
"sharp": "^0.32.1",
"svelte-check": "^3.4.4",
"svelte-check": "^3.4.5",
"svelte-preprocess": "^5.0.4",
"svelte-toc": "^0.5.5",
"svelte-zoo": "^0.4.8",
"svelte2tsx": "^0.6.16",
"typescript": "5.1.5",
"vite": "^4.3.9",
"vitest": "^0.32.2"
"svelte2tsx": "^0.6.19",
"typescript": "5.1.6",
"vite": "^4.4.2",
"vitest": "^0.33.0"
},
"keywords": [
"svelte",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/material/MaterialCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
{
title: `Database IDs`,
value: material.database_IDs.icsd,
condition: material.database_IDs?.icsd?.length,
condition: material.database_IDs?.icsd?.length ?? false,
},
].filter((itm) => itm?.condition ?? true)
</script>
Expand Down
75 changes: 75 additions & 0 deletions src/lib/structure/Lattice.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script lang="ts">
import { add, type Vector } from '$lib'
import { T } from '@threlte/core'
import { BufferGeometry, Vector3 } from 'three'
export let matrix: [Vector, Vector, Vector]
export let show_cell: 'surface' | 'wireframe' | null = `wireframe`
// thickness of the wireframe lines that indicate the lattice's unit cell
// due to limitations of OpenGL with WebGL renderer, on most platforms linewidth will be 1 regardless of set value
// see https://threejs.org/docs/#api/en/materials/MeshBasicMaterial.wireframe
export let cell_color: string = `white`
export let cell_line_width: number = 1
// cell opacity
export let cell_opacity: number | undefined = undefined
// whether to show the lattice vectors
export let show_vectors: boolean = true
// lattice vector colors
export let vector_colors: [string, string, string] = [`red`, `green`, `blue`]
// lattice vector origin (all arrows start from this point)
export let vector_origin: Vector = [-1, -1, -1]
$: [vec_a, vec_b, vec_c] = matrix
const origin = [0, 0, 0]
$: points = [
origin,
add(origin, vec_a),
origin,
add(origin, vec_b),
origin,
add(origin, vec_c),
add(origin, vec_a),
add(origin, vec_a, vec_b),
add(origin, vec_a),
add(origin, vec_a, vec_c),
add(origin, vec_b),
add(origin, vec_b, vec_a),
add(origin, vec_b),
add(origin, vec_b, vec_c),
add(origin, vec_c),
add(origin, vec_c, vec_a),
add(origin, vec_c),
add(origin, vec_c, vec_b),
add(origin, vec_a, vec_b),
add(origin, vec_a, vec_b, vec_c),
add(origin, vec_a, vec_c),
add(origin, vec_a, vec_b, vec_c),
add(origin, vec_b, vec_c),
add(origin, vec_a, vec_b, vec_c),
]
</script>

{#if show_cell}
<T.LineSegments
geometry={new BufferGeometry().setFromPoints(points.map((p) => new Vector3(...p)))}
>
<T.LineBasicMaterial
color={cell_color}
opacity={cell_opacity}
linewidth={cell_line_width}
/>
</T.LineSegments>
{/if}

{#if show_vectors}
{#each matrix as vec, idx}
{@const [x, y, z] = vec ?? []}
<T.Group position={vector_origin}>
<T.ArrowHelper
args={[{ x, y, z }, { x: 0, y: 0, z: 0 }, 6, vector_colors[idx], 1, 1, 1]}
/>
</T.Group>
{/each}
{/if}
13 changes: 4 additions & 9 deletions src/lib/structure/Structure.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<script lang="ts">
import { element_color_schemes } from '$lib/colors'
import { element_colors } from '$lib/stores'
import Iconify from '@iconify/svelte'
import { Canvas } from '@threlte/core'
import { Tooltip } from 'svelte-zoo'
import type { ElementSymbol } from '..'
import Icon from '../Icon.svelte'
import { download } from '../api'
import type { PymatgenStructure } from '../structure'
import {
Expand Down Expand Up @@ -88,7 +86,7 @@
$: {
// set camera position based on structure size
const scale = initial_zoom ?? 1000 / Math.min(width, height)
camera_position = [scale * a, 0.5 * scale * b, scale * c]
camera_position = [scale * a, scale * b, scale * c]
}
const on_window_click =
(node: (HTMLElement | null)[], cb: () => void) => (event: MouseEvent) => {
Expand Down Expand Up @@ -167,9 +165,7 @@
</button>
{#if enable_tips}
<button class="info-icon" on:click={() => tips_modal?.showModal()}>
<slot name="tips-icon">
<Iconify icon="mdi:information" inline />
</slot>
<slot name="tips-icon">&#9432;</slot>
</button>
{/if}
</section>
Expand Down Expand Up @@ -294,8 +290,7 @@
</select>
</label>
<button type="button" on:click={download_json} title={save_json_btn_text}>
<Icon icon="mdi:download" />
{save_json_btn_text}
⬇ {save_json_btn_text}
</button>
</dialog>

Expand Down Expand Up @@ -389,7 +384,7 @@
box-sizing: border-box;
top: var(--struct-controls-top, 30pt);
right: var(--struct-controls-right, 6pt);
background: var(--struct-controls-bg, rgba(0, 0, 0, 0.7));
background: var(--struct-controls-bg, rgba(0, 0, 0, 0.8));
padding: var(--struct-controls-padding, 6pt 9pt);
border-radius: var(--struct-controls-border-radius, 3pt);
width: var(--struct-controls-width, 20em);
Expand Down
50 changes: 11 additions & 39 deletions src/lib/structure/StructureScene.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { PymatgenStructure, Site, Vector } from '$lib'
import { Bond, atomic_radii, euclidean_dist, pretty_num } from '$lib'
import { Bond, Lattice, add, atomic_radii, euclidean_dist, pretty_num } from '$lib'
import { element_colors } from '$lib/stores'
import { T } from '@threlte/core'
import {
Expand Down Expand Up @@ -33,19 +33,6 @@
// whether to show the structure's lattice cell as a wireframe
export let show_atoms: boolean = true
export let show_bonds: boolean = true
export let show_cell: 'surface' | 'wireframe' | null = `wireframe`
// thickness of the wireframe lines that indicate the lattice's unit cell
// due to limitations of OpenGL with WebGL renderer, on most platforms linewidth will be 1 regardless of set value
// see https://threejs.org/docs/#api/en/materials/MeshBasicMaterial.wireframe
export let cell_line_width: number = 1
// cell opacity
export let cell_opacity: number | undefined = undefined
// whether to show the lattice vectors
export let show_vectors: boolean = true
// lattice vector colors
export let vector_colors: [string, string, string] = [`red`, `green`, `blue`]
// lattice vector origin (all arrows start from this point)
export let vector_origin: { x: number; y: number; z: number } = { x: -1, y: -1, z: -1 }
export let hovered_idx: number | null = null
export let active_idx: number | null = null
export let hovered_site: Site | null = null
Expand All @@ -64,20 +51,21 @@
opacity: 0.5,
}
$: ({ a, b, c } = structure?.lattice ?? { a: 0, b: 0, c: 0 })
$: hovered_site = structure?.sites?.[hovered_idx ?? -1] ?? null
$: active_site = structure?.sites?.[active_idx ?? -1] ?? null
interactivity()
// naive max dist bond strategy
let bond_pairs: [Vector, Vector][] = []
$: if (structure?.sites && show_bonds) {
bond_pairs = []
for (const { xyz } of structure.sites) {
for (const { xyz: other_xyz } of structure.sites) {
const dist = euclidean_dist(xyz, other_xyz)
if (dist < max_bond_dist) bond_pairs.push([xyz, other_xyz])
for (const { xyz, species } of structure.sites) {
for (const { xyz: xyz_2, species: species_2 } of structure.sites) {
const dist = euclidean_dist(xyz, xyz_2)
if (dist < max_bond_dist) {
const [elem, elem_2] = [species[0].element, species_2[0].element]
bond_pairs.push([xyz, xyz_2, elem, elem_2, dist])
}
}
}
}
Expand All @@ -92,7 +80,7 @@
zoomSpeed={zoom_speed}
enablePan={pan_speed > 0}
panSpeed={pan_speed}
target={[a / 2, b / 2, c / 2]}
target={add(...(structure?.lattice?.matrix ?? [])).map((x) => x / 2)}
maxZoom={max_zoom}
minZoom={min_zoom}
bind:this={orbit_controls}
Expand Down Expand Up @@ -187,24 +175,8 @@
</HTML>
{/if}

{#if show_cell}
<T.Mesh position={[a / 2, b / 2, c / 2]}>
<T.BoxGeometry args={[a, b, c]} />
<T.MeshBasicMaterial
transparent
opacity={cell_opacity ?? (show_cell == `surface` ? 0.2 : 1)}
wireframe={show_cell == `wireframe`}
wireframeLinewidth={cell_line_width}
/>
</T.Mesh>
{/if}

{#if show_vectors}
{#each structure?.lattice?.matrix ?? [] as [a, b, c], idx}
<T.ArrowHelper
args={[{ x: a, y: b, z: c }, vector_origin, 4, vector_colors[idx], 1, 0.3]}
/>
{/each}
{#if structure?.lattice}
<Lattice {...structure?.lattice} {...$$restProps} />
{/if}

<style>
Expand Down
17 changes: 15 additions & 2 deletions src/lib/structure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { pretty_num } from '$lib'
import element_data from '$lib/element/data'

export { default as Bond } from './Bond.svelte'
export { default as Lattice } from './Lattice.svelte'
export { default as Structure } from './Structure.svelte'
export { default as StructureCard } from './StructureCard.svelte'
export { default as StructureLegend } from './StructureLegend.svelte'
Expand All @@ -16,6 +17,7 @@ export type Species = {
}

export type Vector = [number, number, number]
export type NdVector = number[]

export type Site = {
species: Species[]
Expand All @@ -25,7 +27,7 @@ export type Site = {
properties: Record<string, unknown>
}

export type Lattice = {
export type PymatgenLattice = {
matrix: [Vector, Vector, Vector]
pbc: boolean[]
a: number
Expand All @@ -38,7 +40,7 @@ export type Lattice = {
}

export type PymatgenStructure = {
lattice: Lattice
lattice: PymatgenLattice
sites: Site[]
charge: number
id?: string
Expand Down Expand Up @@ -177,3 +179,14 @@ export function symmetrize_structure(

return symmetrized_structure
}

export function add(...vecs: NdVector[]): NdVector {
// add up any number of same-length vectors
const result = vecs[0].slice()
for (const vec of vecs.slice(1)) {
for (const [idx, val] of vec.entries()) {
result[idx] += val
}
}
return result
}
4 changes: 3 additions & 1 deletion src/routes/(demos)/structure/+page.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,13 @@
<style>
ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1em;
list-style: none;
padding: 0;
text-align: center;
width: 90vw;
margin-left: calc(50cqw - 45vw);
}
</style>
```
17 changes: 12 additions & 5 deletions src/site/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ export const structures = Object.entries(
eager: true,
import: `default`,
}) as Record<string, IdStructure>,
).map(([path, structure]) => {
const id = path.split(`/`).at(-1)?.split(`.`)[0] as string
structure.id = id
return structure
})
)
.map(([path, structure]) => {
const id = path.split(`/`).at(-1)?.split(`.`)[0] as string
structure.id = id
return structure
})
.sort((struct1, struct2) => {
const [n1, n2] = [struct1, struct2].map((struct) =>
struct.id.split(`-`)[1].padStart(6, `0`),
)
return n1.localeCompare(n2)
})
1 change: 1 addition & 0 deletions src/site/structures/mp-1229155.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"@module": "pymatgen.core.structure", "@class": "Structure", "charge": 0, "lattice": {"matrix": [[4.538843, 0.0, 0.0], [0.013561999999999736, 6.980989, 0.0], [0.011766000000000597, 0.062471999999996426, 13.284895000000004]], "pbc": [true, true, true], "a": 4.538843, "b": 6.981002173468005, "c": 13.285047096287055, "alpha": 89.73047155796912, "beta": 89.94925556341678, "gamma": 89.88869136103648, "volume": 420.9400424559625}, "sites": [{"species": [{"element": "Ag", "oxidation_state": 1, "occu": 1}], "abc": [0.012024000000000035, 0.7900990000000001, 0.457361], "xyz": [0.07067168039600022, 5.544244684303, 6.075992862095002], "label": "Ag+", "properties": {}}, {"species": [{"element": "Ag", "oxidation_state": 1, "occu": 1}], "abc": [0.011587999999999932, 0.21110200000000012, 0.544301], "xyz": [0.06186332357399996, 1.5077043119499989, 7.230981633395003], "label": "Ag+", "properties": {}}, {"species": [{"element": "Ag", "oxidation_state": 1, "occu": 1}], "abc": [0.987252, 0.29174500000000003, 0.043733], "xyz": [4.485453037604, 2.039400723781, 0.5809883130350002], "label": "Ag+", "properties": {}}, {"species": [{"element": "Ag", "oxidation_state": 1, "occu": 1}], "abc": [0.987558, 0.7051530000000001, 0.949779], "xyz": [4.503109100094001, 4.9819999300049975, 12.617714288205004], "label": "Ag+", "properties": {}}, {"species": [{"element": "Hg", "oxidation_state": 2, "occu": 1}], "abc": [0.501077, 0.6275310000000001, 0.244061], "xyz": [2.285692031059, 4.396033986950999, 3.242324758595001], "label": "Hg2+", "properties": {}}, {"species": [{"element": "Hg", "oxidation_state": 2, "occu": 1}], "abc": [0.504748, 0.3705710000000001, 0.757356], "xyz": [2.3049086611620004, 2.634265618750998, 10.061394937620003], "label": "Hg2+", "properties": {}}, {"species": [{"element": "Hg", "oxidation_state": 2, "occu": 1}], "abc": [0.495424, 0.12873400000000002, 0.262097], "xyz": [2.2534814782419996, 0.9150643617099992, 3.4819311248150013], "label": "Hg2+", "properties": {}}, {"species": [{"element": "Hg", "oxidation_state": 2, "occu": 1}], "abc": [0.49655800000000005, 0.871745, 0.739496], "xyz": [2.2743223180200007, 6.131840049916998, 9.824126712920004], "label": "Hg2+", "properties": {}}, {"species": [{"element": "S", "oxidation_state": -2, "occu": 1}], "abc": [0.509305, 0.861151, 0.378621], "xyz": [2.327789218663, 6.035338869450999, 5.0299402297950015], "label": "S2-", "properties": {}}, {"species": [{"element": "S", "oxidation_state": -2, "occu": 1}], "abc": [0.509056, 0.13759900000000014, 0.623569], "xyz": [2.3197282927000002, 0.9995327079789987, 8.284048690255004], "label": "S2-", "properties": {}}, {"species": [{"element": "S", "oxidation_state": -2, "occu": 1}], "abc": [0.489144, 0.36278900000000003, 0.127331], "xyz": [2.2265661413560003, 2.540580640553, 1.6915789652450006], "label": "S2-", "properties": {}}, {"species": [{"element": "S", "oxidation_state": -2, "occu": 1}], "abc": [0.491765, 0.6355380000000002, 0.872781], "xyz": [2.2509324354970004, 4.491208161713998, 11.594803942995004], "label": "S2-", "properties": {}}, {"species": [{"element": "Br", "oxidation_state": -1, "occu": 1}], "abc": [0.0048160000000000425, 0.874179, 0.127765], "xyz": [0.03521796647600004, 6.110615718111, 1.6973446096750004], "label": "Br-", "properties": {}}, {"species": [{"element": "Cl", "oxidation_state": -1, "occu": 1}], "abc": [5.999999999994898e-05, 0.13324700000000012, 0.87003], "xyz": [0.012316199374000254, 0.9845483554429977, 11.558257196850002], "label": "Cl-", "properties": {}}, {"species": [{"element": "Cl", "oxidation_state": -1, "occu": 1}], "abc": [0.999898, 0.3710000000000001, 0.372063], "xyz": [4.547789233272, 2.6131904387359994, 4.942817888385001], "label": "Cl-", "properties": {}}, {"species": [{"element": "Cl", "oxidation_state": -1, "occu": 1}], "abc": [0.999726, 0.6278170000000001, 0.629655], "xyz": [4.553522331902, 4.422119378172998, 8.364900561225003], "label": "Cl-", "properties": {}}]}
Loading

0 comments on commit 9d4e29e

Please sign in to comment.