matterviz 0.4.2 → 0.4.3
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/FilePicker.svelte +40 -50
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +15 -0
- package/dist/brillouin/BrillouinZone.svelte +18 -9
- package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
- package/dist/chempot-diagram/async-compute.svelte.js +12 -13
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/composition/BarChart.svelte +7 -9
- package/dist/composition/Composition.svelte +3 -8
- package/dist/composition/Formula.svelte +2 -2
- package/dist/composition/FormulaFilter.svelte +27 -93
- package/dist/composition/format.js +6 -8
- package/dist/composition/parse.js +50 -105
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +6 -17
- package/dist/convex-hull/ConvexHull2D.svelte +0 -2
- package/dist/convex-hull/ConvexHull3D.svelte +19 -47
- package/dist/convex-hull/ConvexHull4D.svelte +65 -95
- package/dist/convex-hull/StructurePopup.svelte +1 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.js +1 -7
- package/dist/convex-hull/thermodynamics.js +19 -27
- package/dist/coordination/CoordinationBarPlot.svelte +12 -21
- package/dist/element/BohrAtom.svelte +0 -1
- package/dist/element/ElementTile.svelte +26 -44
- package/dist/element/data.d.ts +2 -0
- package/dist/element/data.js +5 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +22 -23
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
- package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
- package/dist/fermi-surface/compute.js +22 -55
- package/dist/fermi-surface/parse.js +38 -70
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +1 -1
- package/dist/io/decompress.d.ts +2 -0
- package/dist/io/decompress.js +3 -2
- package/dist/io/export.d.ts +7 -4
- package/dist/io/export.js +70 -18
- package/dist/io/file-drop.js +60 -35
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +28 -16
- package/dist/isosurface/Isosurface.svelte +687 -196
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +564 -85
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.js +1 -1
- package/dist/layout/InfoCard.svelte +1 -3
- package/dist/layout/SettingsSection.svelte +52 -61
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/ViewerChrome.svelte +7 -1
- package/dist/layout/json-tree/JsonNode.svelte +14 -48
- package/dist/layout/json-tree/JsonTree.svelte +48 -95
- package/dist/layout/json-tree/JsonValue.svelte +3 -18
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -179
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +3 -2
- package/dist/math.js +29 -38
- package/dist/overlays/ContextMenu.svelte +15 -32
- package/dist/overlays/DraggablePane.svelte +39 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
- package/dist/overlays/GlassChip.svelte +3 -1
- package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
- package/dist/phase-diagram/build-diagram.js +1 -6
- package/dist/phase-diagram/svg-to-diagram.js +9 -14
- package/dist/plot/bar/BarPlot.svelte +38 -42
- package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
- package/dist/plot/box/BoxPlot.svelte +27 -23
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +1 -1
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +0 -10
- package/dist/plot/core/layout.d.ts +12 -5
- package/dist/plot/core/layout.js +37 -27
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
- package/dist/plot/core/utils/hierarchy-chart.js +12 -0
- package/dist/plot/core/utils/hierarchy-labels.js +3 -2
- package/dist/plot/histogram/Histogram.svelte +21 -26
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
- package/dist/plot/scatter/ScatterPlot.svelte +67 -52
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/sunburst/Sunburst.svelte +20 -20
- package/dist/plot/treemap/Treemap.svelte +175 -60
- package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
- package/dist/plot/treemap/index.d.ts +1 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +4 -2
- package/dist/plot/treemap/treemap.js +5 -2
- package/dist/rdf/RdfPlot.svelte +17 -35
- package/dist/rdf/calc-rdf.js +59 -44
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +84 -26
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.js +159 -482
- package/dist/spectral/Bands.svelte +151 -158
- package/dist/spectral/BrillouinBandsDos.svelte +4 -19
- package/dist/spectral/Dos.svelte +26 -24
- package/dist/spectral/helpers.js +40 -105
- package/dist/structure/AtomLegend.svelte +8 -13
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/CellSelect.svelte +35 -10
- package/dist/structure/Structure.svelte +177 -99
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureCarousel.svelte +10 -3
- package/dist/structure/StructureControls.svelte +8 -26
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +65 -76
- package/dist/structure/StructureScene.svelte +75 -109
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +120 -55
- package/dist/structure/StructureViewport.svelte.d.ts +3 -1
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +4 -3
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +0 -1
- package/dist/structure/bonding.js +16 -23
- package/dist/structure/export.js +50 -74
- package/dist/structure/format-detect.js +5 -7
- package/dist/structure/index.js +6 -12
- package/dist/structure/parse.js +38 -100
- package/dist/structure/partial-occupancy.js +7 -4
- package/dist/structure/pbc.js +8 -10
- package/dist/structure/polyhedra.js +5 -4
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/symmetry/SymmetryStats.svelte +0 -4
- package/dist/symmetry/WyckoffTable.svelte +5 -4
- package/dist/symmetry/cell-transform.js +5 -14
- package/dist/symmetry/index.js +6 -9
- package/dist/table/HeatmapTable.svelte +61 -103
- package/dist/table/ToggleMenu.svelte +166 -44
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +1 -2
- package/dist/theme/themes.mjs +31 -60
- package/dist/tooltip/TooltipContent.svelte +1 -1
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +140 -120
- package/dist/trajectory/TrajectoryError.svelte +1 -66
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +19 -29
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +7 -5
- package/dist/trajectory/frame-reader.js +3 -4
- package/dist/trajectory/helpers.js +16 -49
- package/dist/trajectory/index.js +2 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/index.d.ts +4 -3
- package/dist/trajectory/parse/index.js +12 -17
- package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
- package/dist/trajectory/parse/vaspout-h5.js +1 -4
- package/dist/trajectory/parse/xyz.js +48 -2
- package/dist/trajectory/plotting.js +14 -37
- package/dist/xrd/XrdPlot.svelte +43 -68
- package/dist/xrd/calc-xrd.js +22 -58
- package/dist/xrd/parse.js +13 -22
- package/package.json +40 -8
- package/readme.md +6 -5
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
|
@@ -232,13 +232,9 @@ function extract_boundaries(doc, format, x_scale, y_scale) {
|
|
|
232
232
|
const dy = Math.abs(coords.y2 - coords.y1);
|
|
233
233
|
if (dx < 15 && dy < 15)
|
|
234
234
|
continue;
|
|
235
|
-
// Check stroke-width to distinguish boundaries from axis lines
|
|
235
|
+
// Check stroke-width to distinguish boundaries from axis lines;
|
|
236
|
+
// only include lines with meaningful stroke
|
|
236
237
|
const stroke_width = parse_stroke_width(path_el) || 1;
|
|
237
|
-
// Axis patches (id="patch_*") are axis borders, not phase boundaries
|
|
238
|
-
const parent_id = group.getAttribute(`id`) ?? ``;
|
|
239
|
-
if (parent_id.startsWith(`patch_`))
|
|
240
|
-
continue;
|
|
241
|
-
// Only include lines with meaningful stroke
|
|
242
238
|
if (stroke_width < 1)
|
|
243
239
|
continue;
|
|
244
240
|
add_boundary(boundaries, coords, x_scale, y_scale, epsilon);
|
|
@@ -407,7 +403,13 @@ function extract_matplotlib_labels(doc, labels) {
|
|
|
407
403
|
if (!comment.includes(`+`))
|
|
408
404
|
continue; // skip axis labels (no "+")
|
|
409
405
|
// Clean LaTeX: "La$_2$NiO$_4$ + NiO" -> "La2NiO4 + NiO"
|
|
410
|
-
const text =
|
|
406
|
+
const text = comment
|
|
407
|
+
.trim()
|
|
408
|
+
.replaceAll(/\$_\{(?<digits>[^}]*)\}\$/g, `$1`) // $_{10}$ -> 10
|
|
409
|
+
.replaceAll(/\$_(?<digit>\d)\$/g, `$1`) // $_2$ -> 2
|
|
410
|
+
.replaceAll(`$`, ``) // remove any remaining $
|
|
411
|
+
.replaceAll(/\s+/g, ` `)
|
|
412
|
+
.trim();
|
|
411
413
|
// Get position from transform="translate(x, y)"
|
|
412
414
|
const pos = parse_translate(group.querySelector(`g[transform]`)) ?? parse_translate(group);
|
|
413
415
|
if (!pos)
|
|
@@ -725,13 +727,6 @@ function find_comment_text(group) {
|
|
|
725
727
|
}
|
|
726
728
|
return null;
|
|
727
729
|
}
|
|
728
|
-
// Clean LaTeX subscript notation: "La$_2$NiO$_4$" -> "La2NiO4"
|
|
729
|
-
const clean_latex = (text) => text
|
|
730
|
-
.replaceAll(/\$_\{(?<digits>[^}]*)\}\$/g, `$1`) // $_{10}$ -> 10
|
|
731
|
-
.replaceAll(/\$_(?<digit>\d)\$/g, `$1`) // $_2$ -> 2
|
|
732
|
-
.replaceAll(`$`, ``) // remove any remaining $
|
|
733
|
-
.replaceAll(/\s+/g, ` `)
|
|
734
|
-
.trim();
|
|
735
730
|
// Parse SVG path data into absolute line segments [x1,y1,x2,y2]
|
|
736
731
|
// Handles all SVG path commands (M/L/H/V/C/S/Q/T/A/Z, both absolute and relative)
|
|
737
732
|
// Curves (C/S/Q/T/A) are approximated as straight lines from start to endpoint
|
|
@@ -251,9 +251,9 @@
|
|
|
251
251
|
let ref_lines_by_z = $derived(group_ref_lines_by_z(index_ref_lines(ref_lines)))
|
|
252
252
|
|
|
253
253
|
// === Categorical Normalization (string x values -> integer indices, see ./data) ===
|
|
254
|
-
let
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
let { category_list, internal_series } = $derived(
|
|
255
|
+
normalize_categorical(series, x_axis.categories),
|
|
256
|
+
)
|
|
257
257
|
|
|
258
258
|
let category_indices = $derived(
|
|
259
259
|
category_list.length > 0 ? category_list.map((_, idx) => idx) : null,
|
|
@@ -378,10 +378,13 @@
|
|
|
378
378
|
})
|
|
379
379
|
|
|
380
380
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
let legend_size_revision = $state(0)
|
|
382
|
+
const legend_footprint = $derived.by(() => {
|
|
383
|
+
void legend_size_revision
|
|
384
|
+
return measured_footprint(legend_element, { width: 120, height: 60 })
|
|
385
|
+
})
|
|
384
386
|
const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
|
|
387
|
+
const should_show_legend = $derived(show_legend ?? series.length > 1)
|
|
385
388
|
|
|
386
389
|
// Obstacle field in normalized [0,1] plot coords (y=0 at top). Each bar is modeled as a segment
|
|
387
390
|
// from baseline to its tip so the legend can't hide inside a tall bar. Built from internal_series
|
|
@@ -428,7 +431,7 @@
|
|
|
428
431
|
})
|
|
429
432
|
|
|
430
433
|
// Move the legend to the bottom margin when no interior spot avoids the bars
|
|
431
|
-
const decor = $derived
|
|
434
|
+
const decor = $derived(
|
|
432
435
|
place_decorations({
|
|
433
436
|
base_pad,
|
|
434
437
|
width,
|
|
@@ -437,7 +440,7 @@
|
|
|
437
440
|
// gate on legend_element (the render signal) not legend_data, whose entries can read pad
|
|
438
441
|
legend:
|
|
439
442
|
legend != null &&
|
|
440
|
-
|
|
443
|
+
should_show_legend &&
|
|
441
444
|
legend_element != null &&
|
|
442
445
|
!legend_has_explicit_pos
|
|
443
446
|
? { footprint: legend_footprint, clearance: legend?.axis_clearance }
|
|
@@ -472,9 +475,6 @@
|
|
|
472
475
|
})),
|
|
473
476
|
)
|
|
474
477
|
const marginal_has_axis = $derived(marginal_axis_presence(show_x2, show_y2))
|
|
475
|
-
const legend_auto_outside = $derived(decor.legend_outside)
|
|
476
|
-
const legend_outside_x = $derived(decor.legend_pos.x)
|
|
477
|
-
const legend_outside_y = $derived(decor.legend_pos.y)
|
|
478
478
|
const chart_width = $derived(Math.max(1, width - pad.l - pad.r))
|
|
479
479
|
const chart_height = $derived(Math.max(1, height - pad.t - pad.b))
|
|
480
480
|
|
|
@@ -569,7 +569,6 @@
|
|
|
569
569
|
let tick_label_widths = $derived({
|
|
570
570
|
y_max: measure_max_tick_width(ticks.y, y_axis.format ?? ``),
|
|
571
571
|
y2_max: measure_max_tick_width(ticks.y2, y2_axis.format ?? ``),
|
|
572
|
-
x2_max: measure_max_tick_width(ticks.x2, x2_axis.format ?? ``),
|
|
573
572
|
})
|
|
574
573
|
|
|
575
574
|
// Shared pan/zoom/touch/drag-rect interaction controller
|
|
@@ -577,7 +576,12 @@
|
|
|
577
576
|
ranges: () => ranges.current,
|
|
578
577
|
scale_type: (axis) =>
|
|
579
578
|
({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
|
|
580
|
-
|
|
579
|
+
plot_bounds: () => ({
|
|
580
|
+
x: pad.l,
|
|
581
|
+
y: pad.t,
|
|
582
|
+
width: chart_width,
|
|
583
|
+
height: chart_height,
|
|
584
|
+
}),
|
|
581
585
|
pan: () => pan,
|
|
582
586
|
set_range: (axis, range) => (ranges.current[axis] = range),
|
|
583
587
|
svg: () => svg_element,
|
|
@@ -703,11 +707,10 @@
|
|
|
703
707
|
let hovered_legend_series_idx = $state<number | null>(null)
|
|
704
708
|
|
|
705
709
|
// Calculate best legend placement using continuous grid sampling
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if (!should_show || !width || !height) return null
|
|
710
|
+
const get_legend_placement = () => {
|
|
711
|
+
if (!should_show_legend || !width || !height) return null
|
|
709
712
|
|
|
710
|
-
|
|
713
|
+
return compute_element_placement({
|
|
711
714
|
plot_bounds: { x: pad.l, y: pad.t, width: chart_width, height: chart_height },
|
|
712
715
|
element: legend_element,
|
|
713
716
|
element_size: { width: 120, height: 60 }, // fallback before first render
|
|
@@ -715,17 +718,16 @@
|
|
|
715
718
|
exclude_rects: [],
|
|
716
719
|
points: bar_points_for_placement,
|
|
717
720
|
})
|
|
718
|
-
|
|
719
|
-
return result
|
|
720
|
-
})
|
|
721
|
+
}
|
|
721
722
|
|
|
722
723
|
// Tweened legend coordinates with shared placement stability gating
|
|
723
724
|
const legend_tween = create_placed_tween({
|
|
724
|
-
placement:
|
|
725
|
+
placement: get_legend_placement,
|
|
725
726
|
dims: () => ({ width, height }),
|
|
726
727
|
responsive: () => legend?.responsive ?? false,
|
|
727
728
|
element: () => legend_element,
|
|
728
729
|
tween: () => legend?.tween,
|
|
730
|
+
on_element_resize: () => (legend_size_revision += 1),
|
|
729
731
|
})
|
|
730
732
|
|
|
731
733
|
// Tooltip state
|
|
@@ -804,6 +806,12 @@
|
|
|
804
806
|
on_bar_hover?.({ ...hover_info, event })
|
|
805
807
|
}
|
|
806
808
|
|
|
809
|
+
const clear_hover = () => {
|
|
810
|
+
hover_info = null
|
|
811
|
+
change(null)
|
|
812
|
+
on_bar_hover?.(null)
|
|
813
|
+
}
|
|
814
|
+
|
|
807
815
|
// Stack offsets (only for bar series in stacked mode, grouped by y-axis)
|
|
808
816
|
let stacked_offsets = $derived(compute_stacked_offsets(internal_series, mode))
|
|
809
817
|
|
|
@@ -913,9 +921,7 @@
|
|
|
913
921
|
onkeydown={pan_zoom.on_key_down}
|
|
914
922
|
onmouseleave={() => {
|
|
915
923
|
hovered = false
|
|
916
|
-
|
|
917
|
-
change(null)
|
|
918
|
-
on_bar_hover?.(null)
|
|
924
|
+
clear_hover()
|
|
919
925
|
}}
|
|
920
926
|
onwheel={pan_zoom.on_wheel}
|
|
921
927
|
ontouchstart={pan_zoom.on_touch_start}
|
|
@@ -964,8 +970,7 @@
|
|
|
964
970
|
on_axis_change={(key) => handle_axis_change(`x`, key)}
|
|
965
971
|
/>
|
|
966
972
|
|
|
967
|
-
<!-- X2-axis (Top) -->
|
|
968
|
-
<!-- Note: x2 axis is only supported for vertical orientation -->
|
|
973
|
+
<!-- X2-axis (Top): only rendered in vertical orientation -->
|
|
969
974
|
{#if show_x2}
|
|
970
975
|
<PlotAxis
|
|
971
976
|
side="x2"
|
|
@@ -1009,8 +1014,7 @@
|
|
|
1009
1014
|
on_axis_change={(key) => handle_axis_change(`y`, key)}
|
|
1010
1015
|
/>
|
|
1011
1016
|
|
|
1012
|
-
<!-- Y2-axis (Right) -->
|
|
1013
|
-
<!-- Note: y2 axis is only supported for vertical orientation. Implementing x2 for horizontal mode requires additional complexity. -->
|
|
1017
|
+
<!-- Y2-axis (Right): only rendered in vertical orientation -->
|
|
1014
1018
|
{#if show_y2}
|
|
1015
1019
|
<PlotAxis
|
|
1016
1020
|
side="y2"
|
|
@@ -1138,11 +1142,7 @@
|
|
|
1138
1142
|
change(hover_info)
|
|
1139
1143
|
on_bar_hover?.({ ...hover_info!, event: evt })
|
|
1140
1144
|
}}
|
|
1141
|
-
onmouseleave={
|
|
1142
|
-
change(null)
|
|
1143
|
-
hover_info = null
|
|
1144
|
-
on_bar_hover?.(null)
|
|
1145
|
-
}}
|
|
1145
|
+
onmouseleave={clear_hover}
|
|
1146
1146
|
onclick={(evt) => {
|
|
1147
1147
|
const pt = find_closest_point(evt, points)
|
|
1148
1148
|
if (!pt) return
|
|
@@ -1304,11 +1304,7 @@
|
|
|
1304
1304
|
aria-label={`bar ${bar_idx + 1} of ${srs.label ?? `series`}`}
|
|
1305
1305
|
style:cursor={on_bar_click ? `pointer` : undefined}
|
|
1306
1306
|
onmousemove={handle_bar_hover(series_idx, bar_idx, color)}
|
|
1307
|
-
onmouseleave={
|
|
1308
|
-
hover_info = null
|
|
1309
|
-
change(null)
|
|
1310
|
-
on_bar_hover?.(null)
|
|
1311
|
-
}}
|
|
1307
|
+
onmouseleave={clear_hover}
|
|
1312
1308
|
onclick={(evt) =>
|
|
1313
1309
|
on_bar_click?.({
|
|
1314
1310
|
...get_bar_data(series_idx, bar_idx, color),
|
|
@@ -1375,11 +1371,11 @@
|
|
|
1375
1371
|
</svg>
|
|
1376
1372
|
|
|
1377
1373
|
<!-- Legend -->
|
|
1378
|
-
{#if legend &&
|
|
1374
|
+
{#if legend && should_show_legend}
|
|
1379
1375
|
{@const legend_pos = placed_coords(
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1376
|
+
decor.legend_outside,
|
|
1377
|
+
decor.legend_pos,
|
|
1378
|
+
legend_tween.placed(),
|
|
1383
1379
|
legend_tween.coords.current,
|
|
1384
1380
|
{ x: pad.l + 10, y: pad.t + 10 },
|
|
1385
1381
|
)}
|
|
@@ -34,36 +34,21 @@
|
|
|
34
34
|
data.map(spg.normalize_spacegroup).filter((sg): sg is number => sg !== null),
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// Histogram of space group number counts
|
|
38
38
|
const histogram = $derived.by(() => {
|
|
39
39
|
const hist = new SvelteMap<number, number>()
|
|
40
|
-
|
|
41
|
-
// Count occurrences
|
|
42
|
-
for (const sg of normalized_data) {
|
|
43
|
-
hist.set(sg, (hist.get(sg) ?? 0) + 1)
|
|
44
|
-
}
|
|
45
|
-
|
|
40
|
+
for (const sg of normalized_data) hist.set(sg, (hist.get(sg) ?? 0) + 1)
|
|
46
41
|
return hist
|
|
47
42
|
})
|
|
48
43
|
|
|
49
|
-
//
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
for (const system of symmetry.CRYSTAL_SYSTEMS) {
|
|
54
|
-
stats.set(system, { count: 0, spacegroups: [] })
|
|
55
|
-
}
|
|
56
|
-
|
|
44
|
+
// Total counts per crystal system
|
|
45
|
+
const crystal_system_counts = $derived.by(() => {
|
|
46
|
+
const counts = new SvelteMap<CrystalSystem, number>()
|
|
57
47
|
for (const [sg, count] of histogram) {
|
|
58
48
|
const system = spg.spacegroup_to_crystal_sys(sg)
|
|
59
|
-
if (
|
|
60
|
-
const stat = stats.get(system)
|
|
61
|
-
if (!stat) continue
|
|
62
|
-
stat.count += count
|
|
63
|
-
stat.spacegroups.push(sg)
|
|
49
|
+
if (system) counts.set(system, (counts.get(system) ?? 0) + count)
|
|
64
50
|
}
|
|
65
|
-
|
|
66
|
-
return stats
|
|
51
|
+
return counts
|
|
67
52
|
})
|
|
68
53
|
|
|
69
54
|
// Create sorted list of space groups for x-axis
|
|
@@ -88,28 +73,20 @@
|
|
|
88
73
|
// Group data by crystal system
|
|
89
74
|
for (const sg of sorted_spacegroups) {
|
|
90
75
|
const system = spg.spacegroup_to_crystal_sys(sg)
|
|
91
|
-
if (system)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
series.x.push(sg)
|
|
98
|
-
series.y.push(histogram.get(sg) ?? 0)
|
|
99
|
-
}
|
|
76
|
+
if (!system) continue
|
|
77
|
+
let series = series_by_system.get(system)
|
|
78
|
+
if (!series) series_by_system.set(system, (series = { x: [], y: [] }))
|
|
79
|
+
series.x.push(sg)
|
|
80
|
+
series.y.push(histogram.get(sg) ?? 0)
|
|
100
81
|
}
|
|
101
82
|
|
|
102
83
|
// Convert to BarSeries array, maintaining order of crystal systems
|
|
103
|
-
|
|
104
|
-
for (const system of symmetry.CRYSTAL_SYSTEMS) {
|
|
84
|
+
return symmetry.CRYSTAL_SYSTEMS.flatMap((system) => {
|
|
105
85
|
const system_data = series_by_system.get(system)
|
|
106
|
-
if (system_data)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return result
|
|
86
|
+
if (!system_data) return []
|
|
87
|
+
const color = symmetry.CRYSTAL_SYSTEM_COLORS[system]
|
|
88
|
+
return { ...system_data, color, label: system, bar_width: 0.9, visible: true }
|
|
89
|
+
})
|
|
113
90
|
})
|
|
114
91
|
|
|
115
92
|
// Always show full space group range (1-230)
|
|
@@ -121,8 +98,7 @@
|
|
|
121
98
|
|
|
122
99
|
return symmetry.CRYSTAL_SYSTEMS.map((system) => {
|
|
123
100
|
const [sg_start, sg_end] = symmetry.CRYSTAL_SYSTEM_RANGES[system]
|
|
124
|
-
const
|
|
125
|
-
const count = stats?.count ?? 0
|
|
101
|
+
const count = crystal_system_counts.get(system) ?? 0
|
|
126
102
|
const color = symmetry.CRYSTAL_SYSTEM_COLORS[system]
|
|
127
103
|
return { system, sg_start, sg_end, count, color }
|
|
128
104
|
}).filter(
|
|
@@ -491,9 +491,11 @@
|
|
|
491
491
|
})
|
|
492
492
|
|
|
493
493
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
494
|
+
let legend_size_revision = $state(0)
|
|
495
|
+
const legend_footprint = $derived.by(() => {
|
|
496
|
+
void legend_size_revision
|
|
497
|
+
return measured_footprint(legend_element, { width: 120, height: 60 })
|
|
498
|
+
})
|
|
497
499
|
const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
|
|
498
500
|
|
|
499
501
|
// Obstacle field in normalized [0,1] coords: each box modeled as a whisker-spanning segment
|
|
@@ -531,7 +533,7 @@
|
|
|
531
533
|
})
|
|
532
534
|
|
|
533
535
|
const should_show_legend = $derived(show_legend ?? false)
|
|
534
|
-
const decor = $derived
|
|
536
|
+
const decor = $derived(
|
|
535
537
|
place_decorations({
|
|
536
538
|
base_pad,
|
|
537
539
|
width,
|
|
@@ -570,9 +572,6 @@
|
|
|
570
572
|
}),
|
|
571
573
|
)
|
|
572
574
|
const marginal_has_axis = $derived(marginal_axis_presence(show_x2, show_y2))
|
|
573
|
-
const legend_auto_outside = $derived(decor.legend_outside)
|
|
574
|
-
const legend_outside_x = $derived(decor.legend_pos.x)
|
|
575
|
-
const legend_outside_y = $derived(decor.legend_pos.y)
|
|
576
575
|
const chart_width = $derived(Math.max(1, width - pad.l - pad.r))
|
|
577
576
|
const chart_height = $derived(Math.max(1, height - pad.t - pad.b))
|
|
578
577
|
|
|
@@ -645,7 +644,6 @@
|
|
|
645
644
|
let tick_label_widths = $derived({
|
|
646
645
|
y_max: measure_max_tick_width(ticks.y, y_axis.format ?? ``),
|
|
647
646
|
y2_max: measure_max_tick_width(ticks.y2, y2_axis.format ?? ``),
|
|
648
|
-
x2_max: measure_max_tick_width(ticks.x2, x2_axis.format ?? ``),
|
|
649
647
|
})
|
|
650
648
|
|
|
651
649
|
// Shared pan/zoom/touch/drag-rect interaction controller
|
|
@@ -653,7 +651,12 @@
|
|
|
653
651
|
ranges: () => ranges.current,
|
|
654
652
|
scale_type: (axis) =>
|
|
655
653
|
({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
|
|
656
|
-
|
|
654
|
+
plot_bounds: () => ({
|
|
655
|
+
x: pad.l,
|
|
656
|
+
y: pad.t,
|
|
657
|
+
width: chart_width,
|
|
658
|
+
height: chart_height,
|
|
659
|
+
}),
|
|
657
660
|
pan: () => pan,
|
|
658
661
|
set_range: (axis, range) => (ranges.current[axis] = range),
|
|
659
662
|
svg: () => svg_element,
|
|
@@ -718,7 +721,7 @@
|
|
|
718
721
|
|
|
719
722
|
let hovered_legend_series_idx = $state<number | null>(null)
|
|
720
723
|
|
|
721
|
-
|
|
724
|
+
const get_legend_placement = () => {
|
|
722
725
|
if (!should_show_legend || !width || !height) return null
|
|
723
726
|
return compute_element_placement({
|
|
724
727
|
plot_bounds: { x: pad.l, y: pad.t, width: chart_width, height: chart_height },
|
|
@@ -728,15 +731,16 @@
|
|
|
728
731
|
exclude_rects: [],
|
|
729
732
|
points: box_points_for_placement,
|
|
730
733
|
})
|
|
731
|
-
}
|
|
734
|
+
}
|
|
732
735
|
|
|
733
736
|
// Tweened legend coordinates with shared placement stability gating
|
|
734
737
|
const legend_tween = create_placed_tween({
|
|
735
|
-
placement:
|
|
738
|
+
placement: get_legend_placement,
|
|
736
739
|
dims: () => ({ width, height }),
|
|
737
740
|
responsive: () => legend?.responsive ?? false,
|
|
738
741
|
element: () => legend_element,
|
|
739
742
|
tween: () => legend?.tween,
|
|
743
|
+
on_element_resize: () => (legend_size_revision += 1),
|
|
740
744
|
})
|
|
741
745
|
|
|
742
746
|
// === Tooltip / hover ===
|
|
@@ -788,6 +792,12 @@
|
|
|
788
792
|
on_box_hover?.({ ...hover_info, event })
|
|
789
793
|
}
|
|
790
794
|
|
|
795
|
+
const clear_hover = () => {
|
|
796
|
+
hover_info = null
|
|
797
|
+
change(null)
|
|
798
|
+
on_box_hover?.(null)
|
|
799
|
+
}
|
|
800
|
+
|
|
791
801
|
// Set theme-aware background when entering fullscreen
|
|
792
802
|
$effect(() => set_fullscreen_bg(wrapper, fullscreen, `--boxplot-fullscreen-bg`))
|
|
793
803
|
|
|
@@ -880,9 +890,7 @@
|
|
|
880
890
|
onkeydown={pan_zoom.on_key_down}
|
|
881
891
|
onmouseleave={() => {
|
|
882
892
|
hovered = false
|
|
883
|
-
|
|
884
|
-
change(null)
|
|
885
|
-
on_box_hover?.(null)
|
|
893
|
+
clear_hover()
|
|
886
894
|
}}
|
|
887
895
|
onwheel={pan_zoom.on_wheel}
|
|
888
896
|
ontouchstart={pan_zoom.on_touch_start}
|
|
@@ -1080,11 +1088,7 @@
|
|
|
1080
1088
|
? 0.25
|
|
1081
1089
|
: 1}
|
|
1082
1090
|
onmousemove={handle_box_hover(box_item, color)}
|
|
1083
|
-
onmouseleave={
|
|
1084
|
-
hover_info = null
|
|
1085
|
-
change(null)
|
|
1086
|
-
on_box_hover?.(null)
|
|
1087
|
-
}}
|
|
1091
|
+
onmouseleave={clear_hover}
|
|
1088
1092
|
onclick={(evt) =>
|
|
1089
1093
|
on_box_click?.({ ...get_box_data(box_item, color), event: evt })}
|
|
1090
1094
|
onkeydown={(evt) => {
|
|
@@ -1215,9 +1219,9 @@
|
|
|
1215
1219
|
|
|
1216
1220
|
{#if legend && should_show_legend}
|
|
1217
1221
|
{@const legend_pos = placed_coords(
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1222
|
+
decor.legend_outside,
|
|
1223
|
+
decor.legend_pos,
|
|
1224
|
+
legend_tween.placed(),
|
|
1221
1225
|
legend_tween.coords.current,
|
|
1222
1226
|
{ x: pad.l + 10, y: pad.t + 10 },
|
|
1223
1227
|
)}
|
|
@@ -7,7 +7,7 @@ type Size = {
|
|
|
7
7
|
width: number;
|
|
8
8
|
height: number;
|
|
9
9
|
};
|
|
10
|
-
export declare const placed_coords: (auto_outside: boolean, outside: Pt,
|
|
10
|
+
export declare const placed_coords: (auto_outside: boolean, outside: Pt, placed: boolean, tweened: Pt, fallback: Pt) => Pt;
|
|
11
11
|
export declare const has_explicit_position: (style?: string | null) => boolean;
|
|
12
12
|
export declare const measured_footprint: (el: HTMLElement | null | undefined, fallback: Size) => Size;
|
|
13
13
|
export declare function build_obstacles_norm(series: {
|
|
@@ -4,7 +4,7 @@ import { compute_element_placement, sample_series_obstacle_points, } from './lay
|
|
|
4
4
|
const DECOR_GAP = 8; // px gap between an outside decoration and the plot edge
|
|
5
5
|
// Final on-screen position for an auto-placed decoration: explicit outside placement
|
|
6
6
|
// wins, then the tweened auto placement, else a static padding fallback
|
|
7
|
-
export const placed_coords = (auto_outside, outside,
|
|
7
|
+
export const placed_coords = (auto_outside, outside, placed, tweened, fallback) => (auto_outside ? outside : placed ? tweened : fallback);
|
|
8
8
|
// True when the user pinned a decoration via its style (an edge property or position:absolute),
|
|
9
9
|
// in which case auto-placement must leave it alone.
|
|
10
10
|
export const has_explicit_position = (style) => /(?:^|[;{]\s*)(?:top|bottom|left|right)\s*:|position\s*:\s*absolute/.test(style ?? ``);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { D3SymbolName } from '../../labels';
|
|
2
|
-
import type { DataSeries
|
|
2
|
+
import type { DataSeries } from '..';
|
|
3
3
|
export declare const get_series_color: (series_idx: number) => string;
|
|
4
4
|
export declare const get_series_symbol: (series_idx: number) => D3SymbolName;
|
|
5
5
|
export declare const extract_series_color: (series_data: DataSeries) => string;
|
|
@@ -12,5 +12,4 @@ export declare const prepare_legend_data: (series: DataSeries[]) => {
|
|
|
12
12
|
symbol_color: string;
|
|
13
13
|
};
|
|
14
14
|
}[];
|
|
15
|
-
export declare const create_data_points: (series: DataSeries[], filter_fn?: (series: DataSeries) => boolean) => Point[];
|
|
16
15
|
export declare function process_prop<T>(prop: T[] | T | undefined | null, idx: number): T | undefined;
|
|
@@ -28,16 +28,6 @@ export const prepare_legend_data = (series) => series.map((series_data, series_i
|
|
|
28
28
|
symbol_color: extract_series_color(series_data),
|
|
29
29
|
},
|
|
30
30
|
}));
|
|
31
|
-
// Create data points from series for analysis
|
|
32
|
-
export const create_data_points = (series, filter_fn) => series
|
|
33
|
-
.filter(filter_fn ?? ((srs) => srs.visible ?? true))
|
|
34
|
-
.flatMap(({ x: xs, y: ys }, series_idx) => {
|
|
35
|
-
const length = Math.min(xs.length, ys.length);
|
|
36
|
-
if (xs.length !== ys.length) {
|
|
37
|
-
console.warn(`length mismatch in series ${series_idx}: x.length=${xs.length} vs y.length=${ys.length}`);
|
|
38
|
-
}
|
|
39
|
-
return xs.slice(0, length).map((x, idx) => ({ x, y: ys[idx] }));
|
|
40
|
-
});
|
|
41
31
|
// Process array or scalar properties for indexed access.
|
|
42
32
|
// If prop is an array, returns the element at the given index.
|
|
43
33
|
// If prop is a scalar, returns the scalar (applied to all indices).
|
|
@@ -10,10 +10,15 @@ export declare const DEFAULT_PLOT_PADDING: Required<Sides>;
|
|
|
10
10
|
export declare function y2_axis_label_x(axis: AxisConfig, width: number, pad_r: number, max_tick_width: number): number;
|
|
11
11
|
export declare const filter_padding: (padding: Partial<Sides> | undefined | null, defaults: Required<Sides>) => Required<Sides>;
|
|
12
12
|
export declare function measure_text_width(text: string, font?: string): number;
|
|
13
|
-
|
|
13
|
+
type Size = {
|
|
14
14
|
width: number;
|
|
15
15
|
height: number;
|
|
16
16
|
};
|
|
17
|
+
type ElementFootprint = Size & {
|
|
18
|
+
offset_x: number;
|
|
19
|
+
offset_y: number;
|
|
20
|
+
};
|
|
21
|
+
export declare const full_footprint_or: (el: HTMLElement | null | undefined, fallback: Size) => ElementFootprint;
|
|
17
22
|
export interface AutoPaddingConfig {
|
|
18
23
|
padding: Partial<Sides>;
|
|
19
24
|
default_padding: Required<Sides>;
|
|
@@ -52,10 +57,7 @@ export declare const centered_rect: (center_x: number, top_y: number, width: num
|
|
|
52
57
|
export declare const rect_within_rect: (rect: Rect, bounds: Rect) => boolean;
|
|
53
58
|
export interface ElementPlacementConfig {
|
|
54
59
|
plot_bounds: Rect;
|
|
55
|
-
element_size:
|
|
56
|
-
width: number;
|
|
57
|
-
height: number;
|
|
58
|
-
};
|
|
60
|
+
element_size: Size;
|
|
59
61
|
element?: HTMLElement | null;
|
|
60
62
|
axis_clearance?: number;
|
|
61
63
|
exclude_rects?: Rect[];
|
|
@@ -70,6 +72,10 @@ export interface ElementPlacementResult {
|
|
|
70
72
|
y: number;
|
|
71
73
|
score: number;
|
|
72
74
|
}
|
|
75
|
+
export declare const point_in_rect: (point: {
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
}, rect: Rect) => boolean;
|
|
73
79
|
export declare const rects_overlap: (r1: Rect, r2: Rect) => boolean;
|
|
74
80
|
export declare function sample_series_obstacle_points(pixel_points: {
|
|
75
81
|
x: number;
|
|
@@ -79,3 +85,4 @@ export declare function sample_series_obstacle_points(pixel_points: {
|
|
|
79
85
|
y: number;
|
|
80
86
|
}[];
|
|
81
87
|
export declare function compute_element_placement(config: ElementPlacementConfig): ElementPlacementResult;
|
|
88
|
+
export {};
|