react-cheminfo 0.10.0 → 0.11.0
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/lib/chrome/ui/NavLink.d.ts.map +1 -1
- package/lib/chrome/ui/NavLink.js +4 -3
- package/lib/chrome/ui/NavLink.js.map +1 -1
- package/lib/chrome/ui/SiteFooter.d.ts +8 -0
- package/lib/chrome/ui/SiteFooter.d.ts.map +1 -1
- package/lib/chrome/ui/SiteFooter.js +7 -2
- package/lib/chrome/ui/SiteFooter.js.map +1 -1
- package/lib/chrome/ui/SiteHeader.d.ts +11 -0
- package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
- package/lib/chrome/ui/SiteHeader.js +7 -2
- package/lib/chrome/ui/SiteHeader.js.map +1 -1
- package/lib/chrome/ui/navItem.d.ts +12 -0
- package/lib/chrome/ui/navItem.d.ts.map +1 -1
- package/lib/chrome/ui/navItem.js.map +1 -1
- package/lib/core.d.ts +1 -0
- package/lib/core.d.ts.map +1 -1
- package/lib/core.js +1 -0
- package/lib/core.js.map +1 -1
- package/lib/download/core/figureLegend.d.ts +90 -0
- package/lib/download/core/figureLegend.d.ts.map +1 -0
- package/lib/download/core/figureLegend.js +77 -0
- package/lib/download/core/figureLegend.js.map +1 -0
- package/lib/download/core/figureSvg.d.ts.map +1 -1
- package/lib/download/core/figureSvg.js +71 -1
- package/lib/download/core/figureSvg.js.map +1 -1
- package/lib/download/core/figureSvgDocument.d.ts +9 -2
- package/lib/download/core/figureSvgDocument.d.ts.map +1 -1
- package/lib/download/core/figureSvgDocument.js +4 -1
- package/lib/download/core/figureSvgDocument.js.map +1 -1
- package/lib/download/core/figureTarget.d.ts +11 -0
- package/lib/download/core/figureTarget.d.ts.map +1 -1
- package/lib/download/core/figureTarget.js +19 -0
- package/lib/download/core/figureTarget.js.map +1 -1
- package/lib/download/core/index.d.ts +3 -1
- package/lib/download/core/index.d.ts.map +1 -1
- package/lib/download/core/index.js +2 -1
- package/lib/download/core/index.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +13 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +5 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/overlay/ui/OverlayLegend.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayLegend.js +1 -1
- package/lib/overlay/ui/OverlayLegend.js.map +1 -1
- package/lib/projection/core/projectionCopy.d.ts +4 -0
- package/lib/projection/core/projectionCopy.d.ts.map +1 -1
- package/lib/projection/core/projectionCopy.js +3 -0
- package/lib/projection/core/projectionCopy.js.map +1 -1
- package/lib/projection/core/projectionOptions.d.ts +12 -0
- package/lib/projection/core/projectionOptions.d.ts.map +1 -1
- package/lib/projection/core/projectionOptions.js +27 -1
- package/lib/projection/core/projectionOptions.js.map +1 -1
- package/lib/projection/core/projectionStrings.d.ts +4 -0
- package/lib/projection/core/projectionStrings.d.ts.map +1 -1
- package/lib/projection/core/projectionStrings.js +14 -0
- package/lib/projection/core/projectionStrings.js.map +1 -1
- package/lib/projection/core/projectionTabDefaults.d.ts.map +1 -1
- package/lib/projection/core/projectionTabDefaults.js +12 -0
- package/lib/projection/core/projectionTabDefaults.js.map +1 -1
- package/lib/projection/core/projectionTabs.d.ts +1 -1
- package/lib/projection/core/projectionTabs.d.ts.map +1 -1
- package/lib/projection/core/projectionTabs.js +9 -0
- package/lib/projection/core/projectionTabs.js.map +1 -1
- package/lib/projection/ui/ProjectionMapTab.d.ts +6 -0
- package/lib/projection/ui/ProjectionMapTab.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionMapTab.js +2 -1
- package/lib/projection/ui/ProjectionMapTab.js.map +1 -1
- package/lib/projection/ui/ProjectionPanel.d.ts +6 -0
- package/lib/projection/ui/ProjectionPanel.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionPanel.js +15 -1
- package/lib/projection/ui/ProjectionPanel.js.map +1 -1
- package/lib/projection/ui/ProjectionReadout.d.ts +7 -0
- package/lib/projection/ui/ProjectionReadout.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionReadout.js +5 -4
- package/lib/projection/ui/ProjectionReadout.js.map +1 -1
- package/lib/projection/ui/ProjectionSpaceControls.d.ts +51 -0
- package/lib/projection/ui/ProjectionSpaceControls.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionSpaceControls.js +71 -0
- package/lib/projection/ui/ProjectionSpaceControls.js.map +1 -0
- package/lib/projection/ui/ProjectionSpaceMore.d.ts +32 -0
- package/lib/projection/ui/ProjectionSpaceMore.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionSpaceMore.js +57 -0
- package/lib/projection/ui/ProjectionSpaceMore.js.map +1 -0
- package/lib/projection/ui/ProjectionSpaceTab.d.ts +98 -0
- package/lib/projection/ui/ProjectionSpaceTab.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionSpaceTab.js +66 -0
- package/lib/projection/ui/ProjectionSpaceTab.js.map +1 -0
- package/lib/projection/ui/ProjectionViewer.d.ts +1 -0
- package/lib/projection/ui/ProjectionViewer.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionViewer.js +2 -1
- package/lib/projection/ui/ProjectionViewer.js.map +1 -1
- package/lib/projection/ui/index.d.ts +1 -0
- package/lib/projection/ui/index.d.ts.map +1 -1
- package/lib/projection/ui/index.js.map +1 -1
- package/lib/projection/ui/projectionBarModel.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarModel.js +3 -2
- package/lib/projection/ui/projectionBarModel.js.map +1 -1
- package/lib/projection/ui/projectionBarReadings.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarReadings.js +17 -0
- package/lib/projection/ui/projectionBarReadings.js.map +1 -1
- package/lib/projection/ui/projectionBarSlots.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarSlots.js +17 -0
- package/lib/projection/ui/projectionBarSlots.js.map +1 -1
- package/lib/projection/ui/projectionCloudChoices.d.ts +18 -0
- package/lib/projection/ui/projectionCloudChoices.d.ts.map +1 -0
- package/lib/projection/ui/projectionCloudChoices.js +19 -0
- package/lib/projection/ui/projectionCloudChoices.js.map +1 -0
- package/lib/projection/ui/projectionEllipsoid.d.ts +38 -0
- package/lib/projection/ui/projectionEllipsoid.d.ts.map +1 -0
- package/lib/projection/ui/projectionEllipsoid.js +71 -0
- package/lib/projection/ui/projectionEllipsoid.js.map +1 -0
- package/lib/projection/ui/projectionMapChrome.d.ts +8 -0
- package/lib/projection/ui/projectionMapChrome.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapChrome.js +10 -6
- package/lib/projection/ui/projectionMapChrome.js.map +1 -1
- package/lib/projection/ui/projectionMapView.d.ts +13 -0
- package/lib/projection/ui/projectionMapView.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapView.js +26 -0
- package/lib/projection/ui/projectionMapView.js.map +1 -1
- package/lib/projection/ui/projectionSpaceModel.d.ts +14 -0
- package/lib/projection/ui/projectionSpaceModel.d.ts.map +1 -0
- package/lib/projection/ui/projectionSpaceModel.js +14 -0
- package/lib/projection/ui/projectionSpaceModel.js.map +1 -0
- package/lib/projection/ui/projectionViewerProps.d.ts +34 -0
- package/lib/projection/ui/projectionViewerProps.d.ts.map +1 -1
- package/lib/scatter/ui/ScatterPlot.d.ts +1 -1
- package/lib/scatter/ui/ScatterPlot.d.ts.map +1 -1
- package/lib/scatter/ui/ScatterPlot.js +18 -2
- package/lib/scatter/ui/ScatterPlot.js.map +1 -1
- package/lib/scatter/ui/lassoGesture.d.ts +14 -2
- package/lib/scatter/ui/lassoGesture.d.ts.map +1 -1
- package/lib/scatter/ui/lassoGesture.js +4 -1
- package/lib/scatter/ui/lassoGesture.js.map +1 -1
- package/lib/scatter/ui/scatterPlotProps.d.ts +34 -0
- package/lib/scatter/ui/scatterPlotProps.d.ts.map +1 -1
- package/lib/scatter/ui/useScatterInteraction.d.ts +9 -0
- package/lib/scatter/ui/useScatterInteraction.d.ts.map +1 -1
- package/lib/scatter/ui/useScatterInteraction.js +9 -1
- package/lib/scatter/ui/useScatterInteraction.js.map +1 -1
- package/lib/scatter3d/core/cloudGesture.d.ts +17 -0
- package/lib/scatter3d/core/cloudGesture.d.ts.map +1 -0
- package/lib/scatter3d/core/cloudGesture.js +2 -0
- package/lib/scatter3d/core/cloudGesture.js.map +1 -0
- package/lib/scatter3d/core/confidenceEllipsoid.d.ts +63 -0
- package/lib/scatter3d/core/confidenceEllipsoid.d.ts.map +1 -0
- package/lib/scatter3d/core/confidenceEllipsoid.js +128 -0
- package/lib/scatter3d/core/confidenceEllipsoid.js.map +1 -0
- package/lib/scatter3d/core/cubeFrame.d.ts +40 -0
- package/lib/scatter3d/core/cubeFrame.d.ts.map +1 -0
- package/lib/scatter3d/core/cubeFrame.js +58 -0
- package/lib/scatter3d/core/cubeFrame.js.map +1 -0
- package/lib/scatter3d/core/cubePoints.d.ts +35 -0
- package/lib/scatter3d/core/cubePoints.d.ts.map +1 -0
- package/lib/scatter3d/core/cubePoints.js +50 -0
- package/lib/scatter3d/core/cubePoints.js.map +1 -0
- package/lib/scatter3d/core/ellipsoidCoverage.d.ts +32 -0
- package/lib/scatter3d/core/ellipsoidCoverage.d.ts.map +1 -0
- package/lib/scatter3d/core/ellipsoidCoverage.js +83 -0
- package/lib/scatter3d/core/ellipsoidCoverage.js.map +1 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.d.ts +42 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.d.ts.map +1 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.js +52 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.js.map +1 -0
- package/lib/scatter3d/core/index.d.ts +29 -0
- package/lib/scatter3d/core/index.d.ts.map +1 -0
- package/lib/scatter3d/core/index.js +22 -0
- package/lib/scatter3d/core/index.js.map +1 -0
- package/lib/scatter3d/core/orbitCamera.d.ts +113 -0
- package/lib/scatter3d/core/orbitCamera.d.ts.map +1 -0
- package/lib/scatter3d/core/orbitCamera.js +104 -0
- package/lib/scatter3d/core/orbitCamera.js.map +1 -0
- package/lib/scatter3d/core/symmetricEigen.d.ts +47 -0
- package/lib/scatter3d/core/symmetricEigen.d.ts.map +1 -0
- package/lib/scatter3d/core/symmetricEigen.js +219 -0
- package/lib/scatter3d/core/symmetricEigen.js.map +1 -0
- package/lib/scatter3d/ui/CloudFrameLayer.d.ts +33 -0
- package/lib/scatter3d/ui/CloudFrameLayer.d.ts.map +1 -0
- package/lib/scatter3d/ui/CloudFrameLayer.js +37 -0
- package/lib/scatter3d/ui/CloudFrameLayer.js.map +1 -0
- package/lib/scatter3d/ui/CloudPointLayer.d.ts +62 -0
- package/lib/scatter3d/ui/CloudPointLayer.d.ts.map +1 -0
- package/lib/scatter3d/ui/CloudPointLayer.js +67 -0
- package/lib/scatter3d/ui/CloudPointLayer.js.map +1 -0
- package/lib/scatter3d/ui/CloudShellLayer.d.ts +52 -0
- package/lib/scatter3d/ui/CloudShellLayer.d.ts.map +1 -0
- package/lib/scatter3d/ui/CloudShellLayer.js +40 -0
- package/lib/scatter3d/ui/CloudShellLayer.js.map +1 -0
- package/lib/scatter3d/ui/ScatterCloud.d.ts +24 -0
- package/lib/scatter3d/ui/ScatterCloud.d.ts.map +1 -0
- package/lib/scatter3d/ui/ScatterCloud.js +189 -0
- package/lib/scatter3d/ui/ScatterCloud.js.map +1 -0
- package/lib/scatter3d/ui/cloudGeometry.d.ts +108 -0
- package/lib/scatter3d/ui/cloudGeometry.d.ts.map +1 -0
- package/lib/scatter3d/ui/cloudGeometry.js +122 -0
- package/lib/scatter3d/ui/cloudGeometry.js.map +1 -0
- package/lib/scatter3d/ui/cloudShellStyles.d.ts +36 -0
- package/lib/scatter3d/ui/cloudShellStyles.d.ts.map +1 -0
- package/lib/scatter3d/ui/cloudShellStyles.js +62 -0
- package/lib/scatter3d/ui/cloudShellStyles.js.map +1 -0
- package/lib/scatter3d/ui/cloudZoom.d.ts +21 -0
- package/lib/scatter3d/ui/cloudZoom.d.ts.map +1 -0
- package/lib/scatter3d/ui/cloudZoom.js +22 -0
- package/lib/scatter3d/ui/cloudZoom.js.map +1 -0
- package/lib/scatter3d/ui/index.d.ts +29 -0
- package/lib/scatter3d/ui/index.d.ts.map +1 -0
- package/lib/scatter3d/ui/index.js +21 -0
- package/lib/scatter3d/ui/index.js.map +1 -0
- package/lib/scatter3d/ui/scatterCloudModel.d.ts +60 -0
- package/lib/scatter3d/ui/scatterCloudModel.d.ts.map +1 -0
- package/lib/scatter3d/ui/scatterCloudModel.js +66 -0
- package/lib/scatter3d/ui/scatterCloudModel.js.map +1 -0
- package/lib/scatter3d/ui/scatterCloudProps.d.ts +185 -0
- package/lib/scatter3d/ui/scatterCloudProps.d.ts.map +1 -0
- package/lib/scatter3d/ui/scatterCloudProps.js +9 -0
- package/lib/scatter3d/ui/scatterCloudProps.js.map +1 -0
- package/lib/scatter3d/ui/useOrbitDrag.d.ts +60 -0
- package/lib/scatter3d/ui/useOrbitDrag.d.ts.map +1 -0
- package/lib/scatter3d/ui/useOrbitDrag.js +129 -0
- package/lib/scatter3d/ui/useOrbitDrag.js.map +1 -0
- package/lib/ui.d.ts +1 -0
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +1 -0
- package/lib/ui.js.map +1 -1
- package/package.json +1 -1
- package/src/chrome/ui/NavLink.tsx +5 -3
- package/src/chrome/ui/SiteFooter.tsx +16 -1
- package/src/chrome/ui/SiteHeader.tsx +19 -1
- package/src/chrome/ui/navItem.ts +12 -0
- package/src/core.ts +1 -0
- package/src/download/core/figureLegend.ts +167 -0
- package/src/download/core/figureSvg.ts +81 -1
- package/src/download/core/figureSvgDocument.ts +13 -3
- package/src/download/core/figureTarget.ts +21 -0
- package/src/download/core/index.ts +8 -0
- package/src/ecosystem/core/sites.ts +14 -0
- package/src/ecosystem/ui/glyphs.tsx +26 -0
- package/src/overlay/ui/OverlayLegend.tsx +7 -1
- package/src/projection/core/projectionCopy.ts +9 -0
- package/src/projection/core/projectionOptions.ts +48 -3
- package/src/projection/core/projectionStrings.ts +16 -0
- package/src/projection/core/projectionTabDefaults.ts +12 -0
- package/src/projection/core/projectionTabs.ts +10 -1
- package/src/projection/ui/ProjectionMapTab.tsx +8 -0
- package/src/projection/ui/ProjectionPanel.tsx +63 -0
- package/src/projection/ui/ProjectionReadout.tsx +16 -6
- package/src/projection/ui/ProjectionSpaceControls.tsx +219 -0
- package/src/projection/ui/ProjectionSpaceMore.tsx +169 -0
- package/src/projection/ui/ProjectionSpaceTab.tsx +217 -0
- package/src/projection/ui/ProjectionViewer.tsx +3 -0
- package/src/projection/ui/index.ts +1 -0
- package/src/projection/ui/projectionBarModel.ts +3 -2
- package/src/projection/ui/projectionBarReadings.ts +18 -0
- package/src/projection/ui/projectionBarSlots.tsx +19 -0
- package/src/projection/ui/projectionCloudChoices.ts +23 -0
- package/src/projection/ui/projectionEllipsoid.ts +78 -0
- package/src/projection/ui/projectionMapChrome.ts +26 -5
- package/src/projection/ui/projectionMapView.ts +45 -0
- package/src/projection/ui/projectionSpaceModel.ts +14 -0
- package/src/projection/ui/projectionViewerProps.ts +35 -0
- package/src/scatter/ui/ScatterPlot.tsx +21 -2
- package/src/scatter/ui/lassoGesture.ts +15 -2
- package/src/scatter/ui/scatterPlotProps.ts +35 -0
- package/src/scatter/ui/useScatterInteraction.ts +22 -1
- package/src/scatter3d/core/cloudGesture.ts +16 -0
- package/src/scatter3d/core/confidenceEllipsoid.ts +177 -0
- package/src/scatter3d/core/cubeFrame.ts +72 -0
- package/src/scatter3d/core/cubePoints.ts +75 -0
- package/src/scatter3d/core/ellipsoidCoverage.ts +86 -0
- package/src/scatter3d/core/ellipsoidSilhouette.ts +76 -0
- package/src/scatter3d/core/index.ts +52 -0
- package/src/scatter3d/core/orbitCamera.ts +159 -0
- package/src/scatter3d/core/symmetricEigen.ts +273 -0
- package/src/scatter3d/ui/CloudFrameLayer.tsx +97 -0
- package/src/scatter3d/ui/CloudPointLayer.tsx +132 -0
- package/src/scatter3d/ui/CloudShellLayer.tsx +107 -0
- package/src/scatter3d/ui/ScatterCloud.tsx +312 -0
- package/src/scatter3d/ui/cloudGeometry.ts +200 -0
- package/src/scatter3d/ui/cloudShellStyles.ts +74 -0
- package/src/scatter3d/ui/cloudZoom.ts +22 -0
- package/src/scatter3d/ui/index.ts +41 -0
- package/src/scatter3d/ui/scatterCloudModel.ts +98 -0
- package/src/scatter3d/ui/scatterCloudProps.ts +191 -0
- package/src/scatter3d/ui/useOrbitDrag.ts +220 -0
- package/src/ui.ts +1 -0
- package/styles/chrome.css +29 -4
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { depthFraction } from "../core/orbitCamera.js";
|
|
4
|
+
/**
|
|
5
|
+
* Every sample as one dot, painted from the back of the box forwards.
|
|
6
|
+
*
|
|
7
|
+
* Two things carry the depth, and neither costs an event handler: the order,
|
|
8
|
+
* so a dot in front covers one behind it, and the size, so a dot at the front
|
|
9
|
+
* of the box is a fifth larger than the same dot at the back. Both are read
|
|
10
|
+
* from the projection rather than from the data, so a turn changes them and a
|
|
11
|
+
* filter does not.
|
|
12
|
+
*
|
|
13
|
+
* Like the flat cloud's own layer, this one carries no handler and no closure
|
|
14
|
+
* per dot: every gesture lands on one transparent rectangle above it and is
|
|
15
|
+
* answered from a coordinate array.
|
|
16
|
+
* @param props - See {@link CloudPointLayerProps}.
|
|
17
|
+
* @returns The cloud.
|
|
18
|
+
*/
|
|
19
|
+
export const CloudPointLayer = memo(function CloudPointLayer(props) {
|
|
20
|
+
const { points, depths, order, groupOf, colors, opacities } = props;
|
|
21
|
+
const { fallbackColor = 'var(--text-faint)', radius = 3.5 } = props;
|
|
22
|
+
const { outlinedFrom } = props;
|
|
23
|
+
const xs = points.x;
|
|
24
|
+
const ys = points.y;
|
|
25
|
+
const count = Math.min(xs.length, ys.length, order.length);
|
|
26
|
+
const hollowFrom = outlineStart(outlinedFrom, count);
|
|
27
|
+
const dots = [];
|
|
28
|
+
for (let step = 0; step < count; step++) {
|
|
29
|
+
const index = order[step];
|
|
30
|
+
const x = xs[index];
|
|
31
|
+
const y = ys[index];
|
|
32
|
+
if (x === undefined || y === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
35
|
+
continue;
|
|
36
|
+
const group = groupOf?.[index] ?? -1;
|
|
37
|
+
const color = colors?.[group] ?? fallbackColor;
|
|
38
|
+
const alpha = opacities?.[group] ?? 1;
|
|
39
|
+
const hollow = index >= hollowFrom;
|
|
40
|
+
dots.push(_jsx("circle", { cx: round(x), cy: round(y), r: round(radius * depthScale(depths[index])), fill: hollow ? 'none' : color, stroke: hollow ? color : undefined, strokeWidth: hollow ? OUTLINE_WIDTH : undefined, opacity: alpha === 1 ? undefined : alpha }, index));
|
|
41
|
+
}
|
|
42
|
+
return _jsx("g", { "data-layer": "points", children: dots });
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* How much larger a dot is drawn for sitting near the front of the box.
|
|
46
|
+
*
|
|
47
|
+
* A fifth from back to front. Enough that the eye reads it as depth when it is
|
|
48
|
+
* looking for depth, and little enough that nobody mistakes it for a value:
|
|
49
|
+
* a dot size that carried a measurement would be a fourth dimension nothing
|
|
50
|
+
* labelled.
|
|
51
|
+
* @param depth - How far towards the reader the sample is, in cube units.
|
|
52
|
+
* @returns The factor to draw its radius at.
|
|
53
|
+
*/
|
|
54
|
+
function depthScale(depth) {
|
|
55
|
+
return 0.9 + DEPTH_RANGE * depthFraction(depth);
|
|
56
|
+
}
|
|
57
|
+
function outlineStart(outlinedFrom, count) {
|
|
58
|
+
if (outlinedFrom === undefined || !Number.isFinite(outlinedFrom)) {
|
|
59
|
+
return count;
|
|
60
|
+
}
|
|
61
|
+
return Math.max(0, Math.floor(outlinedFrom));
|
|
62
|
+
}
|
|
63
|
+
const DEPTH_RANGE = 0.4;
|
|
64
|
+
/** The same weight the flat cloud draws a hollow dot at, so the two read alike. */
|
|
65
|
+
const OUTLINE_WIDTH = 1.5;
|
|
66
|
+
const round = (value) => Math.round(value * 100) / 100;
|
|
67
|
+
//# sourceMappingURL=CloudPointLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudPointLayer.js","sourceRoot":"","sources":["../../../src/scatter3d/ui/CloudPointLayer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { ConfidenceEllipsoid } from '../core/confidenceEllipsoid.ts';
|
|
3
|
+
import type { OrbitCamera, OrbitViewport } from '../core/orbitCamera.ts';
|
|
4
|
+
/** One group's shell, fitted once and ready to be turned. */
|
|
5
|
+
export interface CloudShell {
|
|
6
|
+
/** A stable key, so a re-render does not rebuild every shell. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Its colour, which is its group's. */
|
|
9
|
+
color: string;
|
|
10
|
+
/** The shape it summarises, in cube units. */
|
|
11
|
+
ellipsoid: ConfidenceEllipsoid;
|
|
12
|
+
/**
|
|
13
|
+
* How strongly it is drawn, from a legend entry switching its group off.
|
|
14
|
+
* @default 1
|
|
15
|
+
*/
|
|
16
|
+
opacity?: number;
|
|
17
|
+
}
|
|
18
|
+
/** What {@link CloudShellLayer} draws. */
|
|
19
|
+
export interface CloudShellLayerProps {
|
|
20
|
+
/** The shells, one per group that has one. */
|
|
21
|
+
shells: readonly CloudShell[];
|
|
22
|
+
/** Where the reader is standing. */
|
|
23
|
+
camera: OrbitCamera;
|
|
24
|
+
/** Where the middle of the cube is, and how large it is drawn. */
|
|
25
|
+
viewport: OrbitViewport;
|
|
26
|
+
/**
|
|
27
|
+
* How solid one wall of the glass is. Turn it down when many groups overlap,
|
|
28
|
+
* since two shells crossing compound to twice this.
|
|
29
|
+
* @default 0.1
|
|
30
|
+
*/
|
|
31
|
+
fillOpacity?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The group shells, as translucent glass.
|
|
35
|
+
*
|
|
36
|
+
* Each one is drawn as a single ellipse, because that is exactly what a shell
|
|
37
|
+
* looks like: an orthographic camera sends a sphere carried by three axes to
|
|
38
|
+
* an ellipse, whatever it is turned to. So the outline is a curve rather than
|
|
39
|
+
* a ring of facets, it stays a curve at any zoom, and there are no seams
|
|
40
|
+
* between neighbouring faces to read as a wireframe.
|
|
41
|
+
*
|
|
42
|
+
* What the faces were for is kept in the fill. A ray through a shell crosses
|
|
43
|
+
* it twice — the near wall and the far one — so the glass is darkest where the
|
|
44
|
+
* reader looks through both walls at a glancing angle, which is the rim, and
|
|
45
|
+
* lightest through the middle where the far wall is turned away. That is one
|
|
46
|
+
* gradient, and it says the shell is a solid the same way a hundred and sixty
|
|
47
|
+
* stacked polygons did.
|
|
48
|
+
* @param props - See {@link CloudShellLayerProps}.
|
|
49
|
+
* @returns The shells.
|
|
50
|
+
*/
|
|
51
|
+
export declare const CloudShellLayer: import("react").MemoExoticComponent<(props: CloudShellLayerProps) => ReactElement>;
|
|
52
|
+
//# sourceMappingURL=CloudShellLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudShellLayer.d.ts","sourceRoot":"","sources":["../../../src/scatter3d/ui/CloudShellLayer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAKzE,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,SAAS,EAAE,mBAAmB,CAAC;IAC/B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,kEAAkE;IAClE,QAAQ,EAAE,aAAa,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,eAAe,8CACnB,oBAAoB,KAC1B,YAAY,CA8Cb,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useId } from 'react';
|
|
3
|
+
import { projectShellOutline } from "./cloudGeometry.js";
|
|
4
|
+
import { SHELL_FILL_OPACITY, shellGlassStops } from "./cloudShellStyles.js";
|
|
5
|
+
/**
|
|
6
|
+
* The group shells, as translucent glass.
|
|
7
|
+
*
|
|
8
|
+
* Each one is drawn as a single ellipse, because that is exactly what a shell
|
|
9
|
+
* looks like: an orthographic camera sends a sphere carried by three axes to
|
|
10
|
+
* an ellipse, whatever it is turned to. So the outline is a curve rather than
|
|
11
|
+
* a ring of facets, it stays a curve at any zoom, and there are no seams
|
|
12
|
+
* between neighbouring faces to read as a wireframe.
|
|
13
|
+
*
|
|
14
|
+
* What the faces were for is kept in the fill. A ray through a shell crosses
|
|
15
|
+
* it twice — the near wall and the far one — so the glass is darkest where the
|
|
16
|
+
* reader looks through both walls at a glancing angle, which is the rim, and
|
|
17
|
+
* lightest through the middle where the far wall is turned away. That is one
|
|
18
|
+
* gradient, and it says the shell is a solid the same way a hundred and sixty
|
|
19
|
+
* stacked polygons did.
|
|
20
|
+
* @param props - See {@link CloudShellLayerProps}.
|
|
21
|
+
* @returns The shells.
|
|
22
|
+
*/
|
|
23
|
+
export const CloudShellLayer = memo(function CloudShellLayer(props) {
|
|
24
|
+
const { shells, camera, viewport, fillOpacity = SHELL_FILL_OPACITY } = props;
|
|
25
|
+
const prefix = useId();
|
|
26
|
+
const drawn = [];
|
|
27
|
+
for (const shell of shells) {
|
|
28
|
+
const outline = projectShellOutline(shell.ellipsoid, camera, viewport);
|
|
29
|
+
if (outline === null)
|
|
30
|
+
continue;
|
|
31
|
+
drawn.push({ shell, outline });
|
|
32
|
+
}
|
|
33
|
+
// Furthest first, so a shell in front of another is drawn over it.
|
|
34
|
+
drawn.sort((a, b) => a.outline.depth - b.outline.depth);
|
|
35
|
+
return (_jsx("g", { "data-layer": "shells", pointerEvents: "none", children: drawn.map(({ shell, outline }) => {
|
|
36
|
+
const paint = `${prefix}${shell.id}`;
|
|
37
|
+
return (_jsxs("g", { "data-shell": shell.id, children: [_jsx("defs", { children: _jsx("radialGradient", { id: paint, children: shellGlassStops((shell.opacity ?? 1) * fillOpacity).map((stop) => (_jsx("stop", { offset: stop.offset, stopColor: shell.color, stopOpacity: stop.opacity }, stop.offset))) }) }), _jsx("ellipse", { cx: outline.cx, cy: outline.cy, rx: outline.rx, ry: outline.ry, transform: `rotate(${outline.angle} ${outline.cx} ${outline.cy})`, fill: `url(#${paint})` })] }, shell.id));
|
|
38
|
+
}) }));
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=CloudShellLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudShellLayer.js","sourceRoot":"","sources":["../../../src/scatter3d/ui/CloudShellLayer.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAKpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAiC5E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,eAAe,CAC1D,KAA2B;IAE3B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,kBAAkB,EAAE,GAAG,KAAK,CAAC;IAC7E,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC;IAEvB,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACvE,IAAI,OAAO,KAAK,IAAI;YAAE,SAAS;QAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACjC,CAAC;IACD,mEAAmE;IACnE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAExD,OAAO,CACL,0BAAc,QAAQ,EAAC,aAAa,EAAC,MAAM,YACxC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;YAChC,MAAM,KAAK,GAAG,GAAG,MAAM,GAAG,KAAK,CAAC,EAAE,EAAE,CAAC;YACrC,OAAO,CACL,2BAA8B,KAAK,CAAC,EAAE,aACpC,yBACE,yBAAgB,EAAE,EAAE,KAAK,YACtB,eAAe,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CACtD,CAAC,IAAI,EAAE,EAAE,CAAC,CACR,eAEE,MAAM,EAAE,IAAI,CAAC,MAAM,EACnB,SAAS,EAAE,KAAK,CAAC,KAAK,EACtB,WAAW,EAAE,IAAI,CAAC,OAAO,IAHpB,IAAI,CAAC,MAAM,CAIhB,CACH,CACF,GACc,GACZ,EACP,kBACE,EAAE,EAAE,OAAO,CAAC,EAAE,EACd,EAAE,EAAE,OAAO,CAAC,EAAE,EACd,EAAE,EAAE,OAAO,CAAC,EAAE,EACd,EAAE,EAAE,OAAO,CAAC,EAAE,EACd,SAAS,EAAE,UAAU,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,EAAE,GAAG,EACjE,IAAI,EAAE,QAAQ,KAAK,GAAG,GACtB,KAtBI,KAAK,CAAC,EAAE,CAuBZ,CACL,CAAC;QACJ,CAAC,CAAC,GACA,CACL,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { ScatterCloudProps } from './scatterCloudProps.ts';
|
|
3
|
+
export type { CloudGesture, ScatterCloudProps } from './scatterCloudProps.ts';
|
|
4
|
+
/**
|
|
5
|
+
* A cloud of samples in a box the reader can turn, with a glass shell around
|
|
6
|
+
* each group.
|
|
7
|
+
*
|
|
8
|
+
* It is the flat scatter's twin and shares with it everything it can — the
|
|
9
|
+
* selection, the lasso, the hover card, the labels and the group colours are
|
|
10
|
+
* the same hooks and the same helpers — because a reader who has learned the
|
|
11
|
+
* map must not have to learn this separately. What it cannot share is the
|
|
12
|
+
* frame: three axes seen from an angle have no room for three sets of tick
|
|
13
|
+
* labels, so the axes are all stretched to one cube, the frame names them, and
|
|
14
|
+
* the numbers stay in the card the pointer raises.
|
|
15
|
+
*
|
|
16
|
+
* It draws in SVG rather than in WebGL, and that is a decision about saving
|
|
17
|
+
* rather than about drawing. Every other figure in this package is saved from
|
|
18
|
+
* the bar above it as an SVG, and a canvas laid over the same picture would
|
|
19
|
+
* leave a hole in that file exactly where the shells are.
|
|
20
|
+
* @param props - See {@link ScatterCloudProps}.
|
|
21
|
+
* @returns The cloud.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ScatterCloud(props: ScatterCloudProps): ReactElement;
|
|
24
|
+
//# sourceMappingURL=ScatterCloud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScatterCloud.d.ts","sourceRoot":"","sources":["../../../src/scatter3d/ui/ScatterCloud.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiC,YAAY,EAAE,MAAM,OAAO,CAAC;AAqCzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CA2OnE"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { CHART_WHEEL_DWELL, useWheelZoom, } from "../../chart/ui/useWheelZoom.js";
|
|
4
|
+
import { nearestPointIndex } from "../../scatter/core/screenPoints.js";
|
|
5
|
+
import { ScatterLabelLayer } from "../../scatter/ui/ScatterLabelLayer.js";
|
|
6
|
+
import { ScatterMarkLayer } from "../../scatter/ui/ScatterMarkLayer.js";
|
|
7
|
+
import { surfacePosition } from "../../scatter/ui/lassoGesture.js";
|
|
8
|
+
import { SCATTER_GROUP_LABEL_SIZE, SCATTER_LABEL_SIZE, } from "../../scatter/ui/scatterLabelBoxes.js";
|
|
9
|
+
import { placeScatterLabels } from "../../scatter/ui/scatterLabelPlacement.js";
|
|
10
|
+
import { scatterGroupLabels, scatterPointLabels, } from "../../scatter/ui/scatterLabels.js";
|
|
11
|
+
import { LASSO_STYLE, scatterGroupInk, } from "../../scatter/ui/scatterPlotModel.js";
|
|
12
|
+
import { useScatterInteraction } from "../../scatter/ui/useScatterInteraction.js";
|
|
13
|
+
import { cubePoints } from "../core/cubePoints.js";
|
|
14
|
+
import { DEFAULT_ORBIT_CAMERA } from "../core/orbitCamera.js";
|
|
15
|
+
import { CloudFrameLayer } from "./CloudFrameLayer.js";
|
|
16
|
+
import { CloudPointLayer } from "./CloudPointLayer.js";
|
|
17
|
+
import { CloudShellLayer } from "./CloudShellLayer.js";
|
|
18
|
+
import { cloudView, projectCloud } from "./cloudGeometry.js";
|
|
19
|
+
import { clampCloudZoom } from "./cloudZoom.js";
|
|
20
|
+
import { cloudLabel, cloudShells } from "./scatterCloudModel.js";
|
|
21
|
+
import { useOrbitDrag } from "./useOrbitDrag.js";
|
|
22
|
+
/**
|
|
23
|
+
* A cloud of samples in a box the reader can turn, with a glass shell around
|
|
24
|
+
* each group.
|
|
25
|
+
*
|
|
26
|
+
* It is the flat scatter's twin and shares with it everything it can — the
|
|
27
|
+
* selection, the lasso, the hover card, the labels and the group colours are
|
|
28
|
+
* the same hooks and the same helpers — because a reader who has learned the
|
|
29
|
+
* map must not have to learn this separately. What it cannot share is the
|
|
30
|
+
* frame: three axes seen from an angle have no room for three sets of tick
|
|
31
|
+
* labels, so the axes are all stretched to one cube, the frame names them, and
|
|
32
|
+
* the numbers stay in the card the pointer raises.
|
|
33
|
+
*
|
|
34
|
+
* It draws in SVG rather than in WebGL, and that is a decision about saving
|
|
35
|
+
* rather than about drawing. Every other figure in this package is saved from
|
|
36
|
+
* the bar above it as an SVG, and a canvas laid over the same picture would
|
|
37
|
+
* leave a hole in that file exactly where the shells are.
|
|
38
|
+
* @param props - See {@link ScatterCloudProps}.
|
|
39
|
+
* @returns The cloud.
|
|
40
|
+
*/
|
|
41
|
+
export function ScatterCloud(props) {
|
|
42
|
+
const { x, y, z, width, height, xLabel, yLabel, zLabel } = props;
|
|
43
|
+
const { groupOf, groups, mutedGroups, ellipsoid = null } = props;
|
|
44
|
+
const { ellipsoidMinimumPoints, ellipsoidFillOpacity } = props;
|
|
45
|
+
const { showGroupLabels = false, pointLabels, pointRadius = 3.5 } = props;
|
|
46
|
+
const { outlinedFrom, selected, defaultSelected, onSelectionChange } = props;
|
|
47
|
+
const { selectMode = 'replace', onHoverChange, overlay } = props;
|
|
48
|
+
const { onPointDoubleClick } = props;
|
|
49
|
+
const { gesture = 'turn', wheelZoom = true, label, testId } = props;
|
|
50
|
+
const [ownCamera, setOwnCamera] = useState(DEFAULT_ORBIT_CAMERA);
|
|
51
|
+
const [ownZoom, setOwnZoom] = useState(1);
|
|
52
|
+
const camera = props.camera ?? ownCamera;
|
|
53
|
+
const zoom = props.zoom ?? ownZoom;
|
|
54
|
+
const selecting = gesture === 'select';
|
|
55
|
+
const view = useMemo(() => cloudView(width, height, zoom), [height, width, zoom]);
|
|
56
|
+
const cube = useMemo(() => cubePoints(x, y, z), [x, y, z]);
|
|
57
|
+
const cloud = useMemo(() => projectCloud(cube, camera, view.viewport), [camera, cube, view.viewport]);
|
|
58
|
+
const ink = useMemo(() => scatterGroupInk(groups, mutedGroups), [groups, mutedGroups]);
|
|
59
|
+
const shells = useMemo(() => ellipsoid === null || groupOf === undefined || groups === undefined
|
|
60
|
+
? NO_SHELLS
|
|
61
|
+
: cloudShells({
|
|
62
|
+
cube,
|
|
63
|
+
groupOf,
|
|
64
|
+
groups,
|
|
65
|
+
ink,
|
|
66
|
+
size: ellipsoid,
|
|
67
|
+
minimumPoints: ellipsoidMinimumPoints,
|
|
68
|
+
}), [cube, ellipsoid, ellipsoidMinimumPoints, groupOf, groups, ink]);
|
|
69
|
+
const interaction = useScatterInteraction({
|
|
70
|
+
points: cloud.points,
|
|
71
|
+
selected,
|
|
72
|
+
defaultSelected,
|
|
73
|
+
onSelectionChange,
|
|
74
|
+
selectMode,
|
|
75
|
+
enabled: selecting,
|
|
76
|
+
touchLasso: selecting,
|
|
77
|
+
hoverRadius: pointRadius + HOVER_SLACK,
|
|
78
|
+
onHoverChange,
|
|
79
|
+
});
|
|
80
|
+
// Everything a gesture callback reads is taken from here rather than from
|
|
81
|
+
// the closure, so a caller that owns the camera and answers with an inline
|
|
82
|
+
// arrow — which is how every caller writes it — does not rebuild the drag
|
|
83
|
+
// sixty times during its own turn.
|
|
84
|
+
const live = useRef({ camera, zoom, points: cloud.points, props });
|
|
85
|
+
useLayoutEffect(() => {
|
|
86
|
+
live.current = { camera, zoom, points: cloud.points, props };
|
|
87
|
+
});
|
|
88
|
+
const { select, mask } = interaction.selection;
|
|
89
|
+
const { moveTo, leave, pin, unpin, hovered } = interaction.hover;
|
|
90
|
+
const onOrbit = useCallback((turn) => {
|
|
91
|
+
const next = turn(live.current.camera);
|
|
92
|
+
// Written back so the frames of one drag compose, rather than each of them
|
|
93
|
+
// turning the camera React has not re-rendered with yet.
|
|
94
|
+
live.current.camera = next;
|
|
95
|
+
if (live.current.props.camera === undefined)
|
|
96
|
+
setOwnCamera(next);
|
|
97
|
+
live.current.props.onCameraChange?.(next);
|
|
98
|
+
}, []);
|
|
99
|
+
const onTap = useCallback((tapX, tapY, mode) => {
|
|
100
|
+
const radius = (live.current.props.pointRadius ?? 3.5) + HOVER_SLACK;
|
|
101
|
+
const index = nearestPointIndex(live.current.points, tapX, tapY, radius);
|
|
102
|
+
if (index === -1) {
|
|
103
|
+
unpin();
|
|
104
|
+
if (mode === 'replace')
|
|
105
|
+
interaction.selection.clear();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
select([index], mode, 'point');
|
|
109
|
+
pin(index);
|
|
110
|
+
}, [interaction.selection, pin, select, unpin]);
|
|
111
|
+
const onWheel = useCallback((_x, _y, factor) => {
|
|
112
|
+
// A factor above one widens a frame, which on a box means standing back.
|
|
113
|
+
const next = clampCloudZoom(live.current.zoom / factor);
|
|
114
|
+
live.current.zoom = next;
|
|
115
|
+
if (live.current.props.zoom === undefined)
|
|
116
|
+
setOwnZoom(next);
|
|
117
|
+
live.current.props.onZoomChange?.(next);
|
|
118
|
+
}, []);
|
|
119
|
+
function handleDoubleClick(event) {
|
|
120
|
+
if (onPointDoubleClick === undefined)
|
|
121
|
+
return;
|
|
122
|
+
const at = surfacePosition(event, 0, 0);
|
|
123
|
+
const radius = (live.current.props.pointRadius ?? 3.5) + HOVER_SLACK;
|
|
124
|
+
const index = nearestPointIndex(live.current.points, at.x, at.y, radius);
|
|
125
|
+
if (index === -1)
|
|
126
|
+
return;
|
|
127
|
+
onPointDoubleClick({
|
|
128
|
+
index,
|
|
129
|
+
x: at.x,
|
|
130
|
+
y: at.y,
|
|
131
|
+
clientX: event.clientX,
|
|
132
|
+
clientY: event.clientY,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const orbit = useOrbitDrag({
|
|
136
|
+
enabled: !selecting,
|
|
137
|
+
onOrbit,
|
|
138
|
+
onTap,
|
|
139
|
+
onMove: moveTo,
|
|
140
|
+
onLeave: leave,
|
|
141
|
+
mode: selectMode,
|
|
142
|
+
});
|
|
143
|
+
const { ref: wheelSurface } = useWheelZoom({
|
|
144
|
+
enabled: wheelZoom,
|
|
145
|
+
delay: CHART_WHEEL_DWELL,
|
|
146
|
+
onZoom: onWheel,
|
|
147
|
+
});
|
|
148
|
+
const named = useMemo(() => pointLabels === undefined
|
|
149
|
+
? undefined
|
|
150
|
+
: placeScatterLabels(scatterPointLabels(cloud.points, pointLabels, {
|
|
151
|
+
groupOf,
|
|
152
|
+
colors: ink.colors,
|
|
153
|
+
}), {
|
|
154
|
+
fontSize: SCATTER_LABEL_SIZE,
|
|
155
|
+
radius: pointRadius,
|
|
156
|
+
bounds: view.rect,
|
|
157
|
+
}), [cloud.points, groupOf, ink.colors, pointLabels, pointRadius, view.rect]);
|
|
158
|
+
const crowds = useMemo(() => showGroupLabels && groups !== undefined
|
|
159
|
+
? placeScatterLabels(scatterGroupLabels(cloud.points, groups.map((group) => group.label), { groupOf, colors: ink.colors }), {
|
|
160
|
+
fontSize: SCATTER_GROUP_LABEL_SIZE,
|
|
161
|
+
bold: true,
|
|
162
|
+
centered: true,
|
|
163
|
+
radius: pointRadius,
|
|
164
|
+
bounds: view.rect,
|
|
165
|
+
})
|
|
166
|
+
: undefined, [
|
|
167
|
+
cloud.points,
|
|
168
|
+
groupOf,
|
|
169
|
+
groups,
|
|
170
|
+
ink.colors,
|
|
171
|
+
pointRadius,
|
|
172
|
+
showGroupLabels,
|
|
173
|
+
view.rect,
|
|
174
|
+
]);
|
|
175
|
+
return (_jsxs("div", { "data-testid": testId, style: { position: 'relative', width, height }, role: "img", "aria-label": label ?? cloudLabel(xLabel, yLabel, zLabel, cube.length), children: [_jsxs("svg", { width: width, height: height, style: SVG_STYLE, children: [_jsx(CloudFrameLayer, { camera: camera, viewport: view.viewport, xLabel: xLabel, yLabel: yLabel, zLabel: zLabel }), _jsx(CloudShellLayer, { shells: shells, camera: camera, viewport: view.viewport, fillOpacity: ellipsoidFillOpacity }), _jsx(CloudPointLayer, { points: cloud.points, depths: cloud.depths, order: cloud.order, groupOf: groupOf, colors: ink.colors, opacities: ink.opacities, radius: pointRadius, outlinedFrom: outlinedFrom }), _jsx(ScatterMarkLayer, { points: cloud.points, selected: mask, hovered: hovered, radius: pointRadius }), named === undefined ? null : (_jsx(ScatterLabelLayer, { labels: named, radius: pointRadius })), crowds === undefined ? null : (_jsx(ScatterLabelLayer, { labels: crowds, radius: pointRadius, strong: true })), interaction.lasso.pathData === '' ? null : (_jsx("path", { d: interaction.lasso.pathData, ...LASSO_STYLE })), _jsx("rect", { x: 0, y: 0, width: Math.max(0, width), height: Math.max(0, height), ref: wheelSurface, fill: "transparent", onDoubleClick: handleDoubleClick, ...(selecting ? interaction.surface : orbit.surface) })] }), overlay] }));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* How much further than a dot's own radius the pointer may be and still catch
|
|
179
|
+
* it.
|
|
180
|
+
*
|
|
181
|
+
* More slack than the map allows, because a dot in a cloud is a smaller target
|
|
182
|
+
* than the same dot on a map: the box is turned, so the dots are packed into a
|
|
183
|
+
* hexagon rather than spread over a rectangle, and the ones at the back are
|
|
184
|
+
* drawn smaller still.
|
|
185
|
+
*/
|
|
186
|
+
const HOVER_SLACK = 8.5;
|
|
187
|
+
const NO_SHELLS = [];
|
|
188
|
+
const SVG_STYLE = { display: 'block', touchAction: 'none' };
|
|
189
|
+
//# sourceMappingURL=ScatterCloud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScatterCloud.js","sourceRoot":"","sources":["../../../src/scatter3d/ui/ScatterCloud.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the cube, the cloud and the shells land on screen for one camera.
|
|
3
|
+
*
|
|
4
|
+
* Everything a cloud draws is cut once, in cube units, and re-projected on
|
|
5
|
+
* every turn — so an orbit costs one multiply-add per vertex and nothing else.
|
|
6
|
+
* That is what keeps a drag smooth on a figure holding a dozen shells of a
|
|
7
|
+
* hundred and sixty faces each, and it is why the cutting and the projecting
|
|
8
|
+
* are two steps rather than one.
|
|
9
|
+
*/
|
|
10
|
+
import type { ScreenPoints } from '../../scatter/core/screenPoints.ts';
|
|
11
|
+
import type { ConfidenceEllipsoid } from '../core/confidenceEllipsoid.ts';
|
|
12
|
+
import type { OrbitCamera, OrbitViewport, Vector3 } from '../core/orbitCamera.ts';
|
|
13
|
+
/** Room kept between the figure's edge and the cube, in pixels. */
|
|
14
|
+
export declare const CLOUD_MARGIN = 16;
|
|
15
|
+
/**
|
|
16
|
+
* How much of the shorter side the cube spans before the reader zooms.
|
|
17
|
+
*
|
|
18
|
+
* Well short of the whole: the cube is drawn turned, so its silhouette is a
|
|
19
|
+
* hexagon as wide as its body diagonal, and a cube sized to the box edge-on
|
|
20
|
+
* would push two of its corners past the frame the moment it was turned.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CLOUD_FILL = 0.62;
|
|
23
|
+
/** The rectangle the cloud is drawn in, and how the cube maps into it. */
|
|
24
|
+
export interface CloudView {
|
|
25
|
+
/** The drawing area, ready to spread onto a `<rect>`. */
|
|
26
|
+
rect: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
/** Where the middle of the cube sits, and how large it is drawn. */
|
|
33
|
+
viewport: OrbitViewport;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The drawing area and the cube's place in it.
|
|
37
|
+
* @param width - Total width of the figure, in pixels.
|
|
38
|
+
* @param height - Total height.
|
|
39
|
+
* @param zoom - What the reader has zoomed to; `1` is the resting size.
|
|
40
|
+
* @returns See {@link CloudView}.
|
|
41
|
+
*/
|
|
42
|
+
export declare function cloudView(width: number, height: number, zoom: number): CloudView;
|
|
43
|
+
/** Where every sample landed, and in what order they are painted. */
|
|
44
|
+
export interface CloudCloud {
|
|
45
|
+
/** The positions, in the same space a gesture arrives in. */
|
|
46
|
+
points: ScreenPoints;
|
|
47
|
+
/** How far towards the reader each one is, in cube units. */
|
|
48
|
+
depths: Float64Array;
|
|
49
|
+
/** The rows, furthest first, which is the order they are drawn in. */
|
|
50
|
+
order: Uint32Array;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Every sample, projected and ordered back to front.
|
|
54
|
+
*
|
|
55
|
+
* The order is what makes the cloud read as a solid: a dot at the front drawn
|
|
56
|
+
* over a dot at the back says which is which, where the same two dots in row
|
|
57
|
+
* order say nothing at all. It is a separate array rather than a sorted copy
|
|
58
|
+
* of the points because the hit test, the labels and the selection all index
|
|
59
|
+
* by row and must not be reordered with the painting.
|
|
60
|
+
* @param cube - Every sample, in cube units.
|
|
61
|
+
* @param camera - Where the reader is standing.
|
|
62
|
+
* @param viewport - Where the middle of the cube is, and how large it is drawn.
|
|
63
|
+
* @returns See {@link CloudCloud}.
|
|
64
|
+
*/
|
|
65
|
+
export declare function projectCloud(cube: readonly Vector3[], camera: OrbitCamera, viewport: OrbitViewport): CloudCloud;
|
|
66
|
+
/** One shell's outline, ready to be filled. */
|
|
67
|
+
export interface CloudOutline {
|
|
68
|
+
/** Where its middle landed, in pixels from the left. */
|
|
69
|
+
cx: number;
|
|
70
|
+
/** And from the top. */
|
|
71
|
+
cy: number;
|
|
72
|
+
/** Half its longest width, in pixels. */
|
|
73
|
+
rx: number;
|
|
74
|
+
/** Half its width across that, in pixels. */
|
|
75
|
+
ry: number;
|
|
76
|
+
/** How far the long axis is turned from the horizontal, in degrees. */
|
|
77
|
+
angle: number;
|
|
78
|
+
/** How far towards the reader its middle is, in cube units. */
|
|
79
|
+
depth: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Where one shell's outline lands on screen.
|
|
83
|
+
* @param ellipsoid - The shell, in cube units.
|
|
84
|
+
* @param camera - Where the reader is standing.
|
|
85
|
+
* @param viewport - Where the middle of the cube is, and how large it is drawn.
|
|
86
|
+
* @returns See {@link CloudOutline}, or `null` when the camera sends the shell nowhere a browser can draw.
|
|
87
|
+
*/
|
|
88
|
+
export declare function projectShellOutline(ellipsoid: ConfidenceEllipsoid, camera: OrbitCamera, viewport: OrbitViewport): CloudOutline | null;
|
|
89
|
+
/** A segment of the frame, in pixels. */
|
|
90
|
+
export interface CloudSegment {
|
|
91
|
+
/** Where it starts. */
|
|
92
|
+
x1: number;
|
|
93
|
+
/** Where it starts. */
|
|
94
|
+
y1: number;
|
|
95
|
+
/** Where it ends. */
|
|
96
|
+
x2: number;
|
|
97
|
+
/** Where it ends. */
|
|
98
|
+
y2: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A run of cube-space segments, projected.
|
|
102
|
+
* @param segments - The segments, as pairs of corners in cube units.
|
|
103
|
+
* @param camera - Where the reader is standing.
|
|
104
|
+
* @param viewport - Where the middle of the cube is, and how large it is drawn.
|
|
105
|
+
* @returns The segments, in pixels, in the order they were given.
|
|
106
|
+
*/
|
|
107
|
+
export declare function projectSegments(segments: ReadonlyArray<readonly [Vector3, Vector3]>, camera: OrbitCamera, viewport: OrbitViewport): CloudSegment[];
|
|
108
|
+
//# sourceMappingURL=cloudGeometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudGeometry.d.ts","sourceRoot":"","sources":["../../../src/scatter3d/ui/cloudGeometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,OAAO,EACR,MAAM,wBAAwB,CAAC;AAGhC,mEAAmE;AACnE,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,oEAAoE;IACpE,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,SAAS,CAYX;AAED,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;IACrB,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;IACrB,sEAAsE;IACtE,KAAK,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,aAAa,GACtB,UAAU,CAmBZ;AAED,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,mBAAmB,EAC9B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,aAAa,GACtB,YAAY,GAAG,IAAI,CAerB;AAED,yCAAyC;AACzC,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,EACpD,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,aAAa,GACtB,YAAY,EAAE,CAahB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the cube, the cloud and the shells land on screen for one camera.
|
|
3
|
+
*
|
|
4
|
+
* Everything a cloud draws is cut once, in cube units, and re-projected on
|
|
5
|
+
* every turn — so an orbit costs one multiply-add per vertex and nothing else.
|
|
6
|
+
* That is what keeps a drag smooth on a figure holding a dozen shells of a
|
|
7
|
+
* hundred and sixty faces each, and it is why the cutting and the projecting
|
|
8
|
+
* are two steps rather than one.
|
|
9
|
+
*/
|
|
10
|
+
import { ellipsoidSilhouette } from "../core/ellipsoidSilhouette.js";
|
|
11
|
+
import { projectPoint } from "../core/orbitCamera.js";
|
|
12
|
+
/** Room kept between the figure's edge and the cube, in pixels. */
|
|
13
|
+
export const CLOUD_MARGIN = 16;
|
|
14
|
+
/**
|
|
15
|
+
* How much of the shorter side the cube spans before the reader zooms.
|
|
16
|
+
*
|
|
17
|
+
* Well short of the whole: the cube is drawn turned, so its silhouette is a
|
|
18
|
+
* hexagon as wide as its body diagonal, and a cube sized to the box edge-on
|
|
19
|
+
* would push two of its corners past the frame the moment it was turned.
|
|
20
|
+
*/
|
|
21
|
+
export const CLOUD_FILL = 0.62;
|
|
22
|
+
/**
|
|
23
|
+
* The drawing area and the cube's place in it.
|
|
24
|
+
* @param width - Total width of the figure, in pixels.
|
|
25
|
+
* @param height - Total height.
|
|
26
|
+
* @param zoom - What the reader has zoomed to; `1` is the resting size.
|
|
27
|
+
* @returns See {@link CloudView}.
|
|
28
|
+
*/
|
|
29
|
+
export function cloudView(width, height, zoom) {
|
|
30
|
+
const inner = Math.max(0, width - CLOUD_MARGIN * 2);
|
|
31
|
+
const tall = Math.max(0, height - CLOUD_MARGIN * 2);
|
|
32
|
+
const factor = Number.isFinite(zoom) && zoom > 0 ? zoom : 1;
|
|
33
|
+
return {
|
|
34
|
+
rect: { x: CLOUD_MARGIN, y: CLOUD_MARGIN, width: inner, height: tall },
|
|
35
|
+
viewport: {
|
|
36
|
+
scale: (Math.min(inner, tall) / 2) * CLOUD_FILL * factor,
|
|
37
|
+
centerX: CLOUD_MARGIN + inner / 2,
|
|
38
|
+
centerY: CLOUD_MARGIN + tall / 2,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Every sample, projected and ordered back to front.
|
|
44
|
+
*
|
|
45
|
+
* The order is what makes the cloud read as a solid: a dot at the front drawn
|
|
46
|
+
* over a dot at the back says which is which, where the same two dots in row
|
|
47
|
+
* order say nothing at all. It is a separate array rather than a sorted copy
|
|
48
|
+
* of the points because the hit test, the labels and the selection all index
|
|
49
|
+
* by row and must not be reordered with the painting.
|
|
50
|
+
* @param cube - Every sample, in cube units.
|
|
51
|
+
* @param camera - Where the reader is standing.
|
|
52
|
+
* @param viewport - Where the middle of the cube is, and how large it is drawn.
|
|
53
|
+
* @returns See {@link CloudCloud}.
|
|
54
|
+
*/
|
|
55
|
+
export function projectCloud(cube, camera, viewport) {
|
|
56
|
+
const count = cube.length;
|
|
57
|
+
const x = new Float64Array(count);
|
|
58
|
+
const y = new Float64Array(count);
|
|
59
|
+
const depths = new Float64Array(count);
|
|
60
|
+
for (let index = 0; index < count; index++) {
|
|
61
|
+
const at = projectPoint(cube[index], camera, viewport);
|
|
62
|
+
x[index] = at.x;
|
|
63
|
+
y[index] = at.y;
|
|
64
|
+
depths[index] = at.depth;
|
|
65
|
+
}
|
|
66
|
+
const rows = new Uint32Array(count);
|
|
67
|
+
for (let index = 0; index < count; index++)
|
|
68
|
+
rows[index] = index;
|
|
69
|
+
const order = rows.toSorted((a, b) => depths[a] - depths[b]);
|
|
70
|
+
return { points: { x, y }, depths, order };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Where one shell's outline lands on screen.
|
|
74
|
+
* @param ellipsoid - The shell, in cube units.
|
|
75
|
+
* @param camera - Where the reader is standing.
|
|
76
|
+
* @param viewport - Where the middle of the cube is, and how large it is drawn.
|
|
77
|
+
* @returns See {@link CloudOutline}, or `null` when the camera sends the shell nowhere a browser can draw.
|
|
78
|
+
*/
|
|
79
|
+
export function projectShellOutline(ellipsoid, camera, viewport) {
|
|
80
|
+
const at = projectPoint(ellipsoid.center, camera, viewport);
|
|
81
|
+
const { rx, ry, angle } = ellipsoidSilhouette(ellipsoid, camera);
|
|
82
|
+
const outline = {
|
|
83
|
+
cx: round(at.x),
|
|
84
|
+
cy: round(at.y),
|
|
85
|
+
rx: round(rx * viewport.scale),
|
|
86
|
+
ry: round(ry * viewport.scale),
|
|
87
|
+
angle: round((angle * 180) / Math.PI),
|
|
88
|
+
depth: at.depth,
|
|
89
|
+
};
|
|
90
|
+
for (const value of [outline.cx, outline.cy, outline.rx, outline.ry]) {
|
|
91
|
+
if (!Number.isFinite(value))
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return outline;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A run of cube-space segments, projected.
|
|
98
|
+
* @param segments - The segments, as pairs of corners in cube units.
|
|
99
|
+
* @param camera - Where the reader is standing.
|
|
100
|
+
* @param viewport - Where the middle of the cube is, and how large it is drawn.
|
|
101
|
+
* @returns The segments, in pixels, in the order they were given.
|
|
102
|
+
*/
|
|
103
|
+
export function projectSegments(segments, camera, viewport) {
|
|
104
|
+
const lines = [];
|
|
105
|
+
for (const [from, to] of segments) {
|
|
106
|
+
const start = projectPoint(from, camera, viewport);
|
|
107
|
+
const end = projectPoint(to, camera, viewport);
|
|
108
|
+
lines.push({
|
|
109
|
+
x1: round(start.x),
|
|
110
|
+
y1: round(start.y),
|
|
111
|
+
x2: round(end.x),
|
|
112
|
+
y2: round(end.y),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return lines;
|
|
116
|
+
}
|
|
117
|
+
/*
|
|
118
|
+
* Two decimals, which is under a tenth of a device pixel at any zoom a browser
|
|
119
|
+
* offers and keeps the markup short enough to read in a failing test.
|
|
120
|
+
*/
|
|
121
|
+
const round = (value) => Math.round(value * 100) / 100;
|
|
122
|
+
//# sourceMappingURL=cloudGeometry.js.map
|