matterviz 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EmptyState.svelte +10 -2
- package/dist/FilePicker.svelte +123 -82
- package/dist/Icon.svelte +18 -12
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -207
- package/dist/brillouin/BrillouinZone.svelte +292 -149
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.js +1 -2
- package/dist/chempot-diagram/compute.d.ts +10 -0
- package/dist/chempot-diagram/compute.js +250 -88
- package/dist/chempot-diagram/index.d.ts +2 -1
- package/dist/chempot-diagram/index.js +2 -1
- package/dist/chempot-diagram/temperature.js +8 -9
- package/dist/chempot-diagram/types.d.ts +3 -0
- package/dist/chempot-diagram/types.js +1 -0
- package/dist/colors/index.d.ts +1 -1
- package/dist/colors/index.js +5 -3
- package/dist/composition/BarChart.svelte +128 -55
- package/dist/composition/BubbleChart.svelte +102 -49
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +665 -537
- package/dist/composition/PieChart.svelte +183 -108
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -40
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +549 -360
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +425 -328
- package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.js +8 -4
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +9 -0
- package/dist/convex-hull/helpers.js +77 -34
- package/dist/convex-hull/thermodynamics.js +61 -56
- package/dist/convex-hull/types.d.ts +9 -14
- package/dist/convex-hull/types.js +0 -17
- package/dist/coordination/CoordinationBarPlot.svelte +227 -154
- package/dist/element/BohrAtom.svelte +55 -12
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/element/data.js +1 -1
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +328 -187
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +24 -14
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
- package/dist/icons.js +47 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +129 -143
- package/dist/io/is-binary.js +2 -3
- package/dist/io/url-drop.js +1 -2
- package/dist/isosurface/Isosurface.svelte +202 -148
- package/dist/isosurface/IsosurfaceControls.svelte +46 -28
- package/dist/isosurface/parse.js +34 -29
- package/dist/isosurface/slice.js +5 -10
- package/dist/isosurface/types.d.ts +2 -1
- package/dist/isosurface/types.js +61 -12
- package/dist/labels.js +11 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +63 -32
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +10 -2
- package/dist/layout/json-tree/JsonNode.svelte +183 -138
- package/dist/layout/json-tree/JsonTree.svelte +499 -413
- package/dist/layout/json-tree/JsonValue.svelte +127 -99
- package/dist/layout/json-tree/utils.js +4 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +13 -17
- package/dist/math.js +133 -67
- package/dist/overlays/ContextMenu.svelte +65 -40
- package/dist/overlays/DraggablePane.svelte +211 -139
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.js +53 -49
- package/dist/phase-diagram/utils.d.ts +1 -0
- package/dist/phase-diagram/utils.js +80 -25
- package/dist/plot/AxisLabel.svelte +28 -3
- package/dist/plot/BarPlot.svelte +1182 -734
- package/dist/plot/BarPlot.svelte.d.ts +2 -2
- package/dist/plot/BarPlotControls.svelte +31 -5
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +479 -329
- package/dist/plot/ColorScaleSelect.svelte +27 -6
- package/dist/plot/ElementScatter.svelte +36 -15
- package/dist/plot/FillArea.svelte +152 -95
- package/dist/plot/Histogram.svelte +934 -571
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +53 -9
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +157 -114
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -147
- package/dist/plot/ReferenceLine.svelte +76 -22
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -121
- package/dist/plot/ScatterPlot.svelte +1681 -1091
- package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +113 -63
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -403
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +65 -25
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +55 -3
- package/dist/plot/ZoomRect.svelte +4 -2
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/layout.d.ts +4 -1
- package/dist/plot/layout.js +33 -14
- package/dist/plot/reference-line.d.ts +2 -2
- package/dist/plot/reference-line.js +7 -5
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +11 -23
- package/dist/plot/types.js +6 -11
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -66
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/rdf/RdfPlot.svelte +143 -91
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +18 -6
- package/dist/settings.js +46 -16
- package/dist/spectral/Bands.svelte +632 -453
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.js +55 -43
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +215 -134
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +72 -45
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1063 -797
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +349 -118
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -237
- package/dist/structure/StructureScene.svelte +879 -443
- package/dist/structure/StructureScene.svelte.d.ts +15 -7
- package/dist/structure/atom-properties.js +8 -8
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +14 -29
- package/dist/structure/ferrox-wasm.js +1 -1
- package/dist/structure/index.d.ts +13 -3
- package/dist/structure/index.js +83 -23
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +113 -141
- package/dist/structure/partial-occupancy.js +7 -10
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +1 -2
- package/dist/symmetry/SymmetryStats.svelte +84 -41
- package/dist/symmetry/WyckoffTable.svelte +26 -6
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.js +8 -7
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +790 -554
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +125 -92
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +758 -558
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/extract.js +10 -26
- package/dist/trajectory/format-detect.js +5 -5
- package/dist/trajectory/frame-reader.d.ts +1 -1
- package/dist/trajectory/frame-reader.js +5 -12
- package/dist/trajectory/helpers.d.ts +0 -1
- package/dist/trajectory/helpers.js +2 -17
- package/dist/trajectory/index.js +14 -12
- package/dist/trajectory/parse/ase.js +5 -4
- package/dist/trajectory/parse/hdf5.js +26 -18
- package/dist/trajectory/parse/index.js +13 -18
- package/dist/trajectory/parse/lammps.js +17 -7
- package/dist/trajectory/parse/vasp.js +5 -2
- package/dist/trajectory/parse/xyz.js +8 -7
- package/dist/trajectory/plotting.js +13 -8
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/xrd/XrdPlot.svelte +337 -247
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -18
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +99 -103
- package/readme.md +1 -1
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
package/dist/colors/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Vec3 } from '../math';
|
|
|
3
3
|
import type { ELEM_SYMBOLS } from '../labels';
|
|
4
4
|
export type D3InterpolateName = keyof typeof d3_sc & `interpolate${string}`;
|
|
5
5
|
export type D3ColorSchemeName = D3InterpolateName extends `interpolate${infer Name}` ? Name : never;
|
|
6
|
-
export declare const get_d3_interpolator: (name: D3InterpolateName) => (t: number) => string;
|
|
6
|
+
export declare const get_d3_interpolator: (name: D3InterpolateName) => ((t: number) => string);
|
|
7
7
|
export declare const COLOR_SCALE_TYPES: readonly ["continuous", "categorical"];
|
|
8
8
|
export type ColorScaleType = (typeof COLOR_SCALE_TYPES)[number];
|
|
9
9
|
export declare const DEFAULT_CATEGORY_COLORS: Record<string, string>;
|
package/dist/colors/index.js
CHANGED
|
@@ -61,10 +61,10 @@ export const is_color = (val) => {
|
|
|
61
61
|
return false;
|
|
62
62
|
// Check for hex colors, rgb/rgba, hsl/hsla, color(), var(), and named colors
|
|
63
63
|
// Exclude incomplete function prefixes like 'rgb', 'hsl', 'var', 'color'
|
|
64
|
-
return /^(#[0-9a-f]{3,8}|rgba?\([^)]+\)|hsla?\([^)]+\)|color\([^)]+\)|var\([^)]+\)|(?!rgb$|hsl$|var$|color$)[a-z]+)$/i
|
|
65
|
-
.test(val.toString().trim());
|
|
64
|
+
return /^(#[0-9a-f]{3,8}|rgba?\([^)]+\)|hsla?\([^)]+\)|color\([^)]+\)|var\([^)]+\)|(?!rgb$|hsl$|var$|color$)[a-z]+)$/i.test(val.trim());
|
|
66
65
|
};
|
|
67
66
|
export const PLOT_COLORS = [
|
|
67
|
+
// Color series for e.g. line plots
|
|
68
68
|
`#63b3ed`,
|
|
69
69
|
`#68d391`,
|
|
70
70
|
`#fbd38d`,
|
|
@@ -133,7 +133,9 @@ export function is_dark_mode() {
|
|
|
133
133
|
if (stored === `dark` || stored === `light`)
|
|
134
134
|
return stored === `dark`;
|
|
135
135
|
}
|
|
136
|
-
catch {
|
|
136
|
+
catch {
|
|
137
|
+
/* localStorage throws in private browsing mode */
|
|
138
|
+
}
|
|
137
139
|
return globalThis.matchMedia?.(`(prefers-color-scheme: dark)`).matches ?? false;
|
|
138
140
|
}
|
|
139
141
|
// Watch for dark mode changes and call callback on each change. Returns cleanup function.
|
|
@@ -1,59 +1,132 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ColorSchemeName } from '../colors'
|
|
3
|
+
import { ELEMENT_COLOR_SCHEMES, pick_contrast_color } from '../colors'
|
|
4
|
+
import type { CompositionType } from './'
|
|
5
|
+
import type { ElementSymbol } from '../element'
|
|
6
|
+
import { format_num } from '../labels'
|
|
7
|
+
import type { Snippet } from 'svelte'
|
|
8
|
+
import type { SVGAttributes } from 'svelte/elements'
|
|
9
|
+
import { type ChartSegmentData, get_chart_font_scale } from './index'
|
|
10
|
+
import { fractional_composition } from './parse'
|
|
11
|
+
|
|
12
|
+
type BarSegmentData = ChartSegmentData & {
|
|
13
|
+
x: number
|
|
14
|
+
width: number
|
|
15
|
+
can_show_label: boolean
|
|
16
|
+
needs_external_label: boolean
|
|
17
|
+
external_label_position: `above` | `below` | null
|
|
18
|
+
label_x: number
|
|
19
|
+
label_y: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
composition,
|
|
24
|
+
size = 200,
|
|
25
|
+
bar_height = 30,
|
|
26
|
+
label_height = 20,
|
|
27
|
+
gap = 2,
|
|
28
|
+
min_segment_size_for_label = 15,
|
|
29
|
+
thin_segment_threshold = 0.2,
|
|
30
|
+
external_label_size_threshold = 5,
|
|
31
|
+
outer_corners_only = true,
|
|
32
|
+
show_labels = true,
|
|
33
|
+
show_percentages = false,
|
|
34
|
+
show_amounts = true,
|
|
35
|
+
color_scheme = `Vesta`,
|
|
36
|
+
segment_content,
|
|
37
|
+
interactive = true,
|
|
38
|
+
svg_node = $bindable(null),
|
|
39
|
+
children,
|
|
40
|
+
...rest
|
|
41
|
+
}: SVGAttributes<SVGSVGElement> & {
|
|
42
|
+
composition: CompositionType
|
|
43
|
+
size?: number
|
|
44
|
+
bar_height?: number
|
|
45
|
+
label_height?: number
|
|
46
|
+
gap?: number
|
|
47
|
+
min_segment_size_for_label?: number
|
|
48
|
+
thin_segment_threshold?: number
|
|
49
|
+
external_label_size_threshold?: number
|
|
50
|
+
outer_corners_only?: boolean
|
|
51
|
+
show_labels?: boolean
|
|
52
|
+
show_percentages?: boolean
|
|
53
|
+
show_amounts?: boolean
|
|
54
|
+
color_scheme?: ColorSchemeName
|
|
55
|
+
segment_content?: Snippet<[BarSegmentData]>
|
|
56
|
+
interactive?: boolean
|
|
57
|
+
svg_node?: SVGSVGElement | null
|
|
58
|
+
children?: Snippet<[{ hovered_element: ElementSymbol | null }]>
|
|
59
|
+
} = $props()
|
|
60
|
+
|
|
61
|
+
let element_colors = $derived(
|
|
62
|
+
ELEMENT_COLOR_SCHEMES[color_scheme] || ELEMENT_COLOR_SCHEMES.Vesta,
|
|
63
|
+
)
|
|
64
|
+
let fractions = $derived(fractional_composition(composition))
|
|
65
|
+
|
|
66
|
+
let svg_height = $derived(label_height + gap + bar_height + gap + label_height)
|
|
67
|
+
let bar_y = $derived(label_height + gap)
|
|
68
|
+
let above_labels_y = $derived(label_height / 2)
|
|
69
|
+
let below_labels_y = $derived(
|
|
70
|
+
label_height + gap + bar_height + gap + label_height / 2,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
let segments = $derived.by(() => {
|
|
74
|
+
const element_entries = Object.entries(composition).filter(([_, amount]) =>
|
|
75
|
+
amount && amount > 0
|
|
76
|
+
) as [ElementSymbol, number][]
|
|
77
|
+
if (element_entries.length === 0) return []
|
|
78
|
+
|
|
79
|
+
let [current_x, above_labels, below_labels] = [0, 0, 0]
|
|
80
|
+
|
|
17
81
|
return element_entries.map(([element, amount]) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
82
|
+
const fraction = fractions[element] || 0
|
|
83
|
+
const color = element_colors[element] || `#cccccc`
|
|
84
|
+
const width = fraction * size
|
|
85
|
+
const x = current_x
|
|
86
|
+
current_x += width
|
|
87
|
+
|
|
88
|
+
const segment_size = Math.min(width, size)
|
|
89
|
+
const base_scale = Math.min(2, Math.max(1, segment_size / 40))
|
|
90
|
+
const label_text = element + (show_amounts ? amount?.toString() ?? `` : ``) +
|
|
91
|
+
(show_percentages ? `${format_num(fraction, `.1~%`)}` : ``)
|
|
92
|
+
const font_scale = get_chart_font_scale(
|
|
93
|
+
base_scale,
|
|
94
|
+
label_text,
|
|
95
|
+
segment_size * 0.9,
|
|
96
|
+
0.6,
|
|
97
|
+
12,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// Label positioning
|
|
101
|
+
const can_show_label = segment_size >= min_segment_size_for_label
|
|
102
|
+
const is_thin = fraction < thin_segment_threshold
|
|
103
|
+
const can_show_external_label = segment_size >= external_label_size_threshold
|
|
104
|
+
const needs_external_label = is_thin && can_show_external_label
|
|
105
|
+
|
|
106
|
+
let external_label_position: `above` | `below` | null = null
|
|
107
|
+
if (needs_external_label) {
|
|
108
|
+
external_label_position = above_labels <= below_labels ? `above` : `below`
|
|
109
|
+
if (external_label_position === `above`) above_labels++
|
|
110
|
+
else below_labels++
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const text_color = pick_contrast_color({ bg_color: color })
|
|
114
|
+
const label_props = {
|
|
115
|
+
font_scale,
|
|
116
|
+
text_color,
|
|
117
|
+
can_show_label,
|
|
118
|
+
needs_external_label,
|
|
119
|
+
external_label_position,
|
|
120
|
+
label_x: x + width / 2,
|
|
121
|
+
label_y: bar_y + bar_height / 2,
|
|
122
|
+
}
|
|
123
|
+
return { element, amount, fraction, color, x, width, ...label_props }
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
let hovered_element: ElementSymbol | null = $state(null)
|
|
128
|
+
// Generate unique ID for clipPath to avoid collisions across BarCharts
|
|
129
|
+
let clip_path_id = $derived(`bar-clip-${crypto.randomUUID()}`)
|
|
57
130
|
</script>
|
|
58
131
|
|
|
59
132
|
{#snippet label_content(segment: BarSegmentData)}
|
|
@@ -1,55 +1,108 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ColorSchemeName } from '../colors'
|
|
3
|
+
import { ELEMENT_COLOR_SCHEMES, pick_contrast_color } from '../colors'
|
|
4
|
+
import type { CompositionType } from './'
|
|
5
|
+
import type { ElementSymbol } from '../element'
|
|
6
|
+
import { hierarchy, pack } from 'd3-hierarchy'
|
|
7
|
+
import type { Snippet } from 'svelte'
|
|
8
|
+
import type { SVGAttributes } from 'svelte/elements'
|
|
9
|
+
import { type ChartSegmentData, get_chart_font_scale } from './index'
|
|
10
|
+
import { count_atoms_in_composition } from './parse'
|
|
11
|
+
|
|
12
|
+
type BubbleSegmentData = ChartSegmentData & { radius: number; x: number; y: number }
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
composition,
|
|
16
|
+
size = 200,
|
|
17
|
+
padding = 0,
|
|
18
|
+
show_labels = true,
|
|
19
|
+
show_amounts = true,
|
|
20
|
+
color_scheme = `Vesta`,
|
|
21
|
+
bubble_content,
|
|
22
|
+
interactive = true,
|
|
23
|
+
svg_node = $bindable(null),
|
|
24
|
+
children,
|
|
25
|
+
...rest
|
|
26
|
+
}: SVGAttributes<SVGSVGElement> & {
|
|
27
|
+
composition: CompositionType
|
|
28
|
+
size?: number
|
|
29
|
+
padding?: number
|
|
30
|
+
show_labels?: boolean
|
|
31
|
+
show_amounts?: boolean
|
|
32
|
+
color_scheme?: ColorSchemeName
|
|
33
|
+
bubble_content?: Snippet<[BubbleSegmentData]>
|
|
34
|
+
interactive?: boolean
|
|
35
|
+
svg_node?: SVGSVGElement | null
|
|
36
|
+
children?: Snippet<[{ hovered_element: ElementSymbol | null }]>
|
|
37
|
+
} = $props()
|
|
38
|
+
|
|
39
|
+
let element_colors = $derived(
|
|
40
|
+
ELEMENT_COLOR_SCHEMES[color_scheme] || ELEMENT_COLOR_SCHEMES.Vesta,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Calculate bubble data with proper circle packing
|
|
44
|
+
let bubbles = $derived.by<BubbleSegmentData[]>(() => {
|
|
45
|
+
const element_entries = Object.entries(composition).filter(
|
|
46
|
+
([_, amount]) => amount && amount > 0,
|
|
47
|
+
)
|
|
48
|
+
if (element_entries.length === 0) return []
|
|
49
|
+
|
|
50
|
+
// Create hierarchy data structure for D3 pack
|
|
51
|
+
type Child = { element: string; amount: number; color: string }
|
|
52
|
+
type HierarchyData = { children: Child[] }
|
|
53
|
+
|
|
54
|
+
const hierarchy_data: HierarchyData = {
|
|
55
|
+
children: element_entries.map(([element, amount]) => ({
|
|
56
|
+
element,
|
|
57
|
+
amount: amount ?? 0,
|
|
58
|
+
color: element_colors[element] || `#cccccc`,
|
|
59
|
+
})),
|
|
60
|
+
}
|
|
61
|
+
|
|
19
62
|
// Use D3's pack layout for proper circle packing
|
|
20
|
-
const pack_layout = pack().size([
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
]).padding(padding * 0.1)
|
|
24
|
-
|
|
63
|
+
const pack_layout = pack<HierarchyData | Child>().size([
|
|
64
|
+
size - 2 * padding,
|
|
65
|
+
size - 2 * padding,
|
|
66
|
+
]).padding(padding * 0.1) // Small padding between circles
|
|
67
|
+
|
|
68
|
+
const root = pack_layout(
|
|
69
|
+
hierarchy<HierarchyData | Child>(hierarchy_data).sum(
|
|
70
|
+
(data) => (`amount` in data ? data.amount : 0),
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
25
74
|
// Get max radius for font scaling
|
|
26
|
-
const max_radius = Math.max(...root.leaves().map((data) => data.r || 0))
|
|
27
|
-
const total_atoms = count_atoms_in_composition(composition)
|
|
75
|
+
const max_radius = Math.max(...root.leaves().map((data) => data.r || 0))
|
|
76
|
+
const total_atoms = count_atoms_in_composition(composition)
|
|
77
|
+
|
|
28
78
|
return root.leaves().map((node) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
79
|
+
const radius = node.r || 0
|
|
80
|
+
const data = node.data as Child
|
|
81
|
+
|
|
82
|
+
// Calculate font scale based on bubble size and smart text fitting
|
|
83
|
+
const [min_font_scale, max_font_scale] = [0.6, 2] as const
|
|
84
|
+
const scale_factor = radius / max_radius
|
|
85
|
+
const base_scale = min_font_scale +
|
|
86
|
+
scale_factor * (max_font_scale - min_font_scale)
|
|
87
|
+
const label_text = data.element + (show_amounts ? data.amount.toString() : ``)
|
|
88
|
+
const available_space = radius * 2 * 0.8 // 80% of bubble diameter for text
|
|
89
|
+
const font_scale = get_chart_font_scale(base_scale, label_text, available_space)
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
element: data.element as ElementSymbol,
|
|
93
|
+
amount: data.amount,
|
|
94
|
+
fraction: total_atoms > 0 ? data.amount / total_atoms : 0,
|
|
95
|
+
radius,
|
|
96
|
+
x: (node.x || 0) + padding, // Offset by padding
|
|
97
|
+
y: (node.y || 0) + padding,
|
|
98
|
+
color: data.color,
|
|
99
|
+
font_scale,
|
|
100
|
+
text_color: pick_contrast_color({ bg_color: data.color }),
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
let hovered_element: ElementSymbol | null = $state(null)
|
|
53
106
|
</script>
|
|
54
107
|
|
|
55
108
|
<svg
|
|
@@ -1,107 +1,128 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ColorSchemeName } from '../colors'
|
|
3
|
+
import type { CompositionType } from './'
|
|
4
|
+
import { untrack } from 'svelte'
|
|
5
|
+
import { ContextMenu } from '../overlays'
|
|
6
|
+
import { export_svg_as_png, export_svg_as_svg } from '../io/export'
|
|
7
|
+
import type { SVGAttributes } from 'svelte/elements'
|
|
8
|
+
import { get_electro_neg_formula } from './format'
|
|
9
|
+
import { BarChart, BubbleChart, PieChart } from './index'
|
|
10
|
+
import { parse_composition } from './parse'
|
|
11
|
+
|
|
12
|
+
type CompositionChartMode = `pie` | `bubble` | `bar`
|
|
13
|
+
let {
|
|
14
|
+
composition,
|
|
15
|
+
mode = `pie`,
|
|
16
|
+
on_composition_change,
|
|
17
|
+
color_scheme = `Vesta`,
|
|
18
|
+
...rest
|
|
19
|
+
}: SVGAttributes<SVGSVGElement> & {
|
|
20
|
+
composition: string | CompositionType
|
|
21
|
+
mode?: CompositionChartMode
|
|
22
|
+
on_composition_change?: (composition: CompositionType) => void
|
|
23
|
+
color_scheme?: ColorSchemeName
|
|
24
|
+
size?: number
|
|
25
|
+
interactive?: boolean
|
|
26
|
+
} = $props()
|
|
27
|
+
|
|
28
|
+
// Using $state with untrack() - initialized from props but mutated by context menu
|
|
29
|
+
let current_color_scheme = $state(untrack(() => color_scheme as ColorSchemeName))
|
|
30
|
+
let current_mode = $state(untrack(() => mode))
|
|
31
|
+
let svg_node = $state<SVGSVGElement | null>(null)
|
|
32
|
+
|
|
33
|
+
let Component = $derived(
|
|
34
|
+
{ pie: PieChart, bubble: BubbleChart, bar: BarChart }[current_mode],
|
|
35
|
+
)
|
|
36
|
+
let parsed: CompositionType = $derived.by(() => {
|
|
14
37
|
try {
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return {};
|
|
38
|
+
return parse_composition(composition)
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`Failed to parse composition:`, error)
|
|
41
|
+
return {}
|
|
20
42
|
}
|
|
21
|
-
})
|
|
22
|
-
// Call the composition change callback in an effect, not in the derived
|
|
23
|
-
$effect(() => on_composition_change?.(parsed))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
context_menu.
|
|
43
|
+
})
|
|
44
|
+
// Call the composition change callback in an effect, not in the derived
|
|
45
|
+
$effect(() => on_composition_change?.(parsed))
|
|
46
|
+
|
|
47
|
+
let context_menu = $state({ open: false, x: 0, y: 0 })
|
|
48
|
+
|
|
49
|
+
function handle_right_click(event: MouseEvent) { // open context menu
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
context_menu.open = false // Close any existing context menu first
|
|
52
|
+
context_menu.x = event.pageX
|
|
53
|
+
context_menu.y = event.pageY
|
|
30
54
|
// Use a small delay to ensure the prev context menu closes happens before opening new one
|
|
31
|
-
setTimeout(() => context_menu.open = true, 0)
|
|
32
|
-
}
|
|
33
|
-
|
|
55
|
+
setTimeout(() => context_menu.open = true, 0)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const mode_options = [
|
|
34
59
|
{ value: `pie`, icon: `Circle`, label: `Pie Chart` },
|
|
35
60
|
{ value: `bubble`, icon: `Circle`, label: `Bubble Chart` },
|
|
36
61
|
{ value: `bar`, icon: `Graph`, label: `Bar Chart` },
|
|
37
|
-
]
|
|
38
|
-
|
|
62
|
+
] as const
|
|
63
|
+
|
|
64
|
+
const color_scheme_options = [
|
|
39
65
|
{ value: `Vesta`, icon: `ColorPalette`, label: `Vesta` },
|
|
40
66
|
{ value: `Jmol`, icon: `ColorPalette`, label: `Jmol` },
|
|
41
67
|
{ value: `Alloy`, icon: `ColorPalette`, label: `Alloy` },
|
|
42
68
|
{ value: `Pastel`, icon: `ColorPalette`, label: `Pastel` },
|
|
43
69
|
{ value: `Muted`, icon: `ColorPalette`, label: `Muted` },
|
|
44
70
|
{ value: `Dark Mode`, icon: `ColorPalette`, label: `Dark Mode` },
|
|
45
|
-
]
|
|
46
|
-
|
|
71
|
+
] as const
|
|
72
|
+
|
|
73
|
+
const export_options = [
|
|
47
74
|
{ value: `copy_formula`, icon: `Copy`, label: `Copy Formula` },
|
|
48
75
|
{ value: `copy_data`, icon: `Copy`, label: `Copy Data` },
|
|
49
76
|
{ value: `export_svg`, icon: `Download`, label: `Export SVG` },
|
|
50
77
|
{ value: `export_png`, icon: `Download`, label: `Export PNG` },
|
|
51
|
-
]
|
|
52
|
-
|
|
78
|
+
] as const
|
|
79
|
+
|
|
80
|
+
const sec_titles = {
|
|
53
81
|
display_mode: `Display Mode`,
|
|
54
82
|
color_scheme: `Color Scheme`,
|
|
55
83
|
export: `Export`,
|
|
56
|
-
}
|
|
57
|
-
|
|
84
|
+
} as const
|
|
85
|
+
|
|
86
|
+
const context_menu_sections = [
|
|
58
87
|
{ title: sec_titles.display_mode, options: mode_options },
|
|
59
88
|
{ title: sec_titles.color_scheme, options: color_scheme_options },
|
|
60
89
|
{ title: sec_titles.export, options: export_options },
|
|
61
|
-
]
|
|
62
|
-
|
|
90
|
+
] as const
|
|
91
|
+
|
|
92
|
+
function handle_context_menu_select(
|
|
93
|
+
section_title: string,
|
|
94
|
+
option: { value: string },
|
|
95
|
+
) {
|
|
63
96
|
if (section_title === sec_titles.display_mode) {
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Handle export actions
|
|
74
|
-
function handle_export(export_type) {
|
|
97
|
+
current_mode = option.value as CompositionChartMode
|
|
98
|
+
} else if (section_title === sec_titles.color_scheme) {
|
|
99
|
+
current_color_scheme = option.value as ColorSchemeName
|
|
100
|
+
} else if (section_title === sec_titles.export) handle_export(option.value)
|
|
101
|
+
context_menu.open = false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Handle export actions
|
|
105
|
+
function handle_export(export_type: string) {
|
|
75
106
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (svg_node)
|
|
94
|
-
export_svg_as_png(svg_node, filename, 150);
|
|
95
|
-
else
|
|
96
|
-
console.warn(`Chart SVG not available for PNG export`);
|
|
97
|
-
}
|
|
98
|
-
else
|
|
99
|
-
console.warn(`Invalid export type:`, export_type);
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
console.error(`Export failed:`, error);
|
|
107
|
+
if (export_type === `copy_formula`) {
|
|
108
|
+
const formula = get_electro_neg_formula(composition)
|
|
109
|
+
navigator.clipboard.writeText(formula)
|
|
110
|
+
} else if (export_type === `copy_data`) {
|
|
111
|
+
const data = JSON.stringify(parsed, null, 2)
|
|
112
|
+
navigator.clipboard.writeText(data)
|
|
113
|
+
} else if (export_type === `export_svg`) {
|
|
114
|
+
const filename = `${get_electro_neg_formula(composition, true, ``)}.svg`
|
|
115
|
+
if (svg_node) export_svg_as_svg(svg_node, filename)
|
|
116
|
+
else console.warn(`Chart SVG not available for SVG export`)
|
|
117
|
+
} else if (export_type === `export_png`) {
|
|
118
|
+
const filename = `${get_electro_neg_formula(composition, true, ``)}.png`
|
|
119
|
+
if (svg_node) export_svg_as_png(svg_node, filename, 150)
|
|
120
|
+
else console.warn(`Chart SVG not available for PNG export`)
|
|
121
|
+
} else console.warn(`Invalid export type:`, export_type)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(`Export failed:`, error)
|
|
103
124
|
}
|
|
104
|
-
}
|
|
125
|
+
}
|
|
105
126
|
</script>
|
|
106
127
|
|
|
107
128
|
<Component
|