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.
Files changed (288) hide show
  1. package/lib/chrome/ui/NavLink.d.ts.map +1 -1
  2. package/lib/chrome/ui/NavLink.js +4 -3
  3. package/lib/chrome/ui/NavLink.js.map +1 -1
  4. package/lib/chrome/ui/SiteFooter.d.ts +8 -0
  5. package/lib/chrome/ui/SiteFooter.d.ts.map +1 -1
  6. package/lib/chrome/ui/SiteFooter.js +7 -2
  7. package/lib/chrome/ui/SiteFooter.js.map +1 -1
  8. package/lib/chrome/ui/SiteHeader.d.ts +11 -0
  9. package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
  10. package/lib/chrome/ui/SiteHeader.js +7 -2
  11. package/lib/chrome/ui/SiteHeader.js.map +1 -1
  12. package/lib/chrome/ui/navItem.d.ts +12 -0
  13. package/lib/chrome/ui/navItem.d.ts.map +1 -1
  14. package/lib/chrome/ui/navItem.js.map +1 -1
  15. package/lib/core.d.ts +1 -0
  16. package/lib/core.d.ts.map +1 -1
  17. package/lib/core.js +1 -0
  18. package/lib/core.js.map +1 -1
  19. package/lib/download/core/figureLegend.d.ts +90 -0
  20. package/lib/download/core/figureLegend.d.ts.map +1 -0
  21. package/lib/download/core/figureLegend.js +77 -0
  22. package/lib/download/core/figureLegend.js.map +1 -0
  23. package/lib/download/core/figureSvg.d.ts.map +1 -1
  24. package/lib/download/core/figureSvg.js +71 -1
  25. package/lib/download/core/figureSvg.js.map +1 -1
  26. package/lib/download/core/figureSvgDocument.d.ts +9 -2
  27. package/lib/download/core/figureSvgDocument.d.ts.map +1 -1
  28. package/lib/download/core/figureSvgDocument.js +4 -1
  29. package/lib/download/core/figureSvgDocument.js.map +1 -1
  30. package/lib/download/core/figureTarget.d.ts +11 -0
  31. package/lib/download/core/figureTarget.d.ts.map +1 -1
  32. package/lib/download/core/figureTarget.js +19 -0
  33. package/lib/download/core/figureTarget.js.map +1 -1
  34. package/lib/download/core/index.d.ts +3 -1
  35. package/lib/download/core/index.d.ts.map +1 -1
  36. package/lib/download/core/index.js +2 -1
  37. package/lib/download/core/index.js.map +1 -1
  38. package/lib/ecosystem/core/sites.d.ts +1 -1
  39. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  40. package/lib/ecosystem/core/sites.js +13 -0
  41. package/lib/ecosystem/core/sites.js.map +1 -1
  42. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  43. package/lib/ecosystem/ui/glyphs.js +5 -0
  44. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  45. package/lib/overlay/ui/OverlayLegend.d.ts.map +1 -1
  46. package/lib/overlay/ui/OverlayLegend.js +1 -1
  47. package/lib/overlay/ui/OverlayLegend.js.map +1 -1
  48. package/lib/projection/core/projectionCopy.d.ts +4 -0
  49. package/lib/projection/core/projectionCopy.d.ts.map +1 -1
  50. package/lib/projection/core/projectionCopy.js +3 -0
  51. package/lib/projection/core/projectionCopy.js.map +1 -1
  52. package/lib/projection/core/projectionOptions.d.ts +12 -0
  53. package/lib/projection/core/projectionOptions.d.ts.map +1 -1
  54. package/lib/projection/core/projectionOptions.js +27 -1
  55. package/lib/projection/core/projectionOptions.js.map +1 -1
  56. package/lib/projection/core/projectionStrings.d.ts +4 -0
  57. package/lib/projection/core/projectionStrings.d.ts.map +1 -1
  58. package/lib/projection/core/projectionStrings.js +14 -0
  59. package/lib/projection/core/projectionStrings.js.map +1 -1
  60. package/lib/projection/core/projectionTabDefaults.d.ts.map +1 -1
  61. package/lib/projection/core/projectionTabDefaults.js +12 -0
  62. package/lib/projection/core/projectionTabDefaults.js.map +1 -1
  63. package/lib/projection/core/projectionTabs.d.ts +1 -1
  64. package/lib/projection/core/projectionTabs.d.ts.map +1 -1
  65. package/lib/projection/core/projectionTabs.js +9 -0
  66. package/lib/projection/core/projectionTabs.js.map +1 -1
  67. package/lib/projection/ui/ProjectionMapTab.d.ts +6 -0
  68. package/lib/projection/ui/ProjectionMapTab.d.ts.map +1 -1
  69. package/lib/projection/ui/ProjectionMapTab.js +2 -1
  70. package/lib/projection/ui/ProjectionMapTab.js.map +1 -1
  71. package/lib/projection/ui/ProjectionPanel.d.ts +6 -0
  72. package/lib/projection/ui/ProjectionPanel.d.ts.map +1 -1
  73. package/lib/projection/ui/ProjectionPanel.js +15 -1
  74. package/lib/projection/ui/ProjectionPanel.js.map +1 -1
  75. package/lib/projection/ui/ProjectionReadout.d.ts +7 -0
  76. package/lib/projection/ui/ProjectionReadout.d.ts.map +1 -1
  77. package/lib/projection/ui/ProjectionReadout.js +5 -4
  78. package/lib/projection/ui/ProjectionReadout.js.map +1 -1
  79. package/lib/projection/ui/ProjectionSpaceControls.d.ts +51 -0
  80. package/lib/projection/ui/ProjectionSpaceControls.d.ts.map +1 -0
  81. package/lib/projection/ui/ProjectionSpaceControls.js +71 -0
  82. package/lib/projection/ui/ProjectionSpaceControls.js.map +1 -0
  83. package/lib/projection/ui/ProjectionSpaceMore.d.ts +32 -0
  84. package/lib/projection/ui/ProjectionSpaceMore.d.ts.map +1 -0
  85. package/lib/projection/ui/ProjectionSpaceMore.js +57 -0
  86. package/lib/projection/ui/ProjectionSpaceMore.js.map +1 -0
  87. package/lib/projection/ui/ProjectionSpaceTab.d.ts +98 -0
  88. package/lib/projection/ui/ProjectionSpaceTab.d.ts.map +1 -0
  89. package/lib/projection/ui/ProjectionSpaceTab.js +66 -0
  90. package/lib/projection/ui/ProjectionSpaceTab.js.map +1 -0
  91. package/lib/projection/ui/ProjectionViewer.d.ts +1 -0
  92. package/lib/projection/ui/ProjectionViewer.d.ts.map +1 -1
  93. package/lib/projection/ui/ProjectionViewer.js +2 -1
  94. package/lib/projection/ui/ProjectionViewer.js.map +1 -1
  95. package/lib/projection/ui/index.d.ts +1 -0
  96. package/lib/projection/ui/index.d.ts.map +1 -1
  97. package/lib/projection/ui/index.js.map +1 -1
  98. package/lib/projection/ui/projectionBarModel.d.ts.map +1 -1
  99. package/lib/projection/ui/projectionBarModel.js +3 -2
  100. package/lib/projection/ui/projectionBarModel.js.map +1 -1
  101. package/lib/projection/ui/projectionBarReadings.d.ts.map +1 -1
  102. package/lib/projection/ui/projectionBarReadings.js +17 -0
  103. package/lib/projection/ui/projectionBarReadings.js.map +1 -1
  104. package/lib/projection/ui/projectionBarSlots.d.ts.map +1 -1
  105. package/lib/projection/ui/projectionBarSlots.js +17 -0
  106. package/lib/projection/ui/projectionBarSlots.js.map +1 -1
  107. package/lib/projection/ui/projectionCloudChoices.d.ts +18 -0
  108. package/lib/projection/ui/projectionCloudChoices.d.ts.map +1 -0
  109. package/lib/projection/ui/projectionCloudChoices.js +19 -0
  110. package/lib/projection/ui/projectionCloudChoices.js.map +1 -0
  111. package/lib/projection/ui/projectionEllipsoid.d.ts +38 -0
  112. package/lib/projection/ui/projectionEllipsoid.d.ts.map +1 -0
  113. package/lib/projection/ui/projectionEllipsoid.js +71 -0
  114. package/lib/projection/ui/projectionEllipsoid.js.map +1 -0
  115. package/lib/projection/ui/projectionMapChrome.d.ts +8 -0
  116. package/lib/projection/ui/projectionMapChrome.d.ts.map +1 -1
  117. package/lib/projection/ui/projectionMapChrome.js +10 -6
  118. package/lib/projection/ui/projectionMapChrome.js.map +1 -1
  119. package/lib/projection/ui/projectionMapView.d.ts +13 -0
  120. package/lib/projection/ui/projectionMapView.d.ts.map +1 -1
  121. package/lib/projection/ui/projectionMapView.js +26 -0
  122. package/lib/projection/ui/projectionMapView.js.map +1 -1
  123. package/lib/projection/ui/projectionSpaceModel.d.ts +14 -0
  124. package/lib/projection/ui/projectionSpaceModel.d.ts.map +1 -0
  125. package/lib/projection/ui/projectionSpaceModel.js +14 -0
  126. package/lib/projection/ui/projectionSpaceModel.js.map +1 -0
  127. package/lib/projection/ui/projectionViewerProps.d.ts +34 -0
  128. package/lib/projection/ui/projectionViewerProps.d.ts.map +1 -1
  129. package/lib/scatter/ui/ScatterPlot.d.ts +1 -1
  130. package/lib/scatter/ui/ScatterPlot.d.ts.map +1 -1
  131. package/lib/scatter/ui/ScatterPlot.js +18 -2
  132. package/lib/scatter/ui/ScatterPlot.js.map +1 -1
  133. package/lib/scatter/ui/lassoGesture.d.ts +14 -2
  134. package/lib/scatter/ui/lassoGesture.d.ts.map +1 -1
  135. package/lib/scatter/ui/lassoGesture.js +4 -1
  136. package/lib/scatter/ui/lassoGesture.js.map +1 -1
  137. package/lib/scatter/ui/scatterPlotProps.d.ts +34 -0
  138. package/lib/scatter/ui/scatterPlotProps.d.ts.map +1 -1
  139. package/lib/scatter/ui/useScatterInteraction.d.ts +9 -0
  140. package/lib/scatter/ui/useScatterInteraction.d.ts.map +1 -1
  141. package/lib/scatter/ui/useScatterInteraction.js +9 -1
  142. package/lib/scatter/ui/useScatterInteraction.js.map +1 -1
  143. package/lib/scatter3d/core/cloudGesture.d.ts +17 -0
  144. package/lib/scatter3d/core/cloudGesture.d.ts.map +1 -0
  145. package/lib/scatter3d/core/cloudGesture.js +2 -0
  146. package/lib/scatter3d/core/cloudGesture.js.map +1 -0
  147. package/lib/scatter3d/core/confidenceEllipsoid.d.ts +63 -0
  148. package/lib/scatter3d/core/confidenceEllipsoid.d.ts.map +1 -0
  149. package/lib/scatter3d/core/confidenceEllipsoid.js +128 -0
  150. package/lib/scatter3d/core/confidenceEllipsoid.js.map +1 -0
  151. package/lib/scatter3d/core/cubeFrame.d.ts +40 -0
  152. package/lib/scatter3d/core/cubeFrame.d.ts.map +1 -0
  153. package/lib/scatter3d/core/cubeFrame.js +58 -0
  154. package/lib/scatter3d/core/cubeFrame.js.map +1 -0
  155. package/lib/scatter3d/core/cubePoints.d.ts +35 -0
  156. package/lib/scatter3d/core/cubePoints.d.ts.map +1 -0
  157. package/lib/scatter3d/core/cubePoints.js +50 -0
  158. package/lib/scatter3d/core/cubePoints.js.map +1 -0
  159. package/lib/scatter3d/core/ellipsoidCoverage.d.ts +32 -0
  160. package/lib/scatter3d/core/ellipsoidCoverage.d.ts.map +1 -0
  161. package/lib/scatter3d/core/ellipsoidCoverage.js +83 -0
  162. package/lib/scatter3d/core/ellipsoidCoverage.js.map +1 -0
  163. package/lib/scatter3d/core/ellipsoidSilhouette.d.ts +42 -0
  164. package/lib/scatter3d/core/ellipsoidSilhouette.d.ts.map +1 -0
  165. package/lib/scatter3d/core/ellipsoidSilhouette.js +52 -0
  166. package/lib/scatter3d/core/ellipsoidSilhouette.js.map +1 -0
  167. package/lib/scatter3d/core/index.d.ts +29 -0
  168. package/lib/scatter3d/core/index.d.ts.map +1 -0
  169. package/lib/scatter3d/core/index.js +22 -0
  170. package/lib/scatter3d/core/index.js.map +1 -0
  171. package/lib/scatter3d/core/orbitCamera.d.ts +113 -0
  172. package/lib/scatter3d/core/orbitCamera.d.ts.map +1 -0
  173. package/lib/scatter3d/core/orbitCamera.js +104 -0
  174. package/lib/scatter3d/core/orbitCamera.js.map +1 -0
  175. package/lib/scatter3d/core/symmetricEigen.d.ts +47 -0
  176. package/lib/scatter3d/core/symmetricEigen.d.ts.map +1 -0
  177. package/lib/scatter3d/core/symmetricEigen.js +219 -0
  178. package/lib/scatter3d/core/symmetricEigen.js.map +1 -0
  179. package/lib/scatter3d/ui/CloudFrameLayer.d.ts +33 -0
  180. package/lib/scatter3d/ui/CloudFrameLayer.d.ts.map +1 -0
  181. package/lib/scatter3d/ui/CloudFrameLayer.js +37 -0
  182. package/lib/scatter3d/ui/CloudFrameLayer.js.map +1 -0
  183. package/lib/scatter3d/ui/CloudPointLayer.d.ts +62 -0
  184. package/lib/scatter3d/ui/CloudPointLayer.d.ts.map +1 -0
  185. package/lib/scatter3d/ui/CloudPointLayer.js +67 -0
  186. package/lib/scatter3d/ui/CloudPointLayer.js.map +1 -0
  187. package/lib/scatter3d/ui/CloudShellLayer.d.ts +52 -0
  188. package/lib/scatter3d/ui/CloudShellLayer.d.ts.map +1 -0
  189. package/lib/scatter3d/ui/CloudShellLayer.js +40 -0
  190. package/lib/scatter3d/ui/CloudShellLayer.js.map +1 -0
  191. package/lib/scatter3d/ui/ScatterCloud.d.ts +24 -0
  192. package/lib/scatter3d/ui/ScatterCloud.d.ts.map +1 -0
  193. package/lib/scatter3d/ui/ScatterCloud.js +189 -0
  194. package/lib/scatter3d/ui/ScatterCloud.js.map +1 -0
  195. package/lib/scatter3d/ui/cloudGeometry.d.ts +108 -0
  196. package/lib/scatter3d/ui/cloudGeometry.d.ts.map +1 -0
  197. package/lib/scatter3d/ui/cloudGeometry.js +122 -0
  198. package/lib/scatter3d/ui/cloudGeometry.js.map +1 -0
  199. package/lib/scatter3d/ui/cloudShellStyles.d.ts +36 -0
  200. package/lib/scatter3d/ui/cloudShellStyles.d.ts.map +1 -0
  201. package/lib/scatter3d/ui/cloudShellStyles.js +62 -0
  202. package/lib/scatter3d/ui/cloudShellStyles.js.map +1 -0
  203. package/lib/scatter3d/ui/cloudZoom.d.ts +21 -0
  204. package/lib/scatter3d/ui/cloudZoom.d.ts.map +1 -0
  205. package/lib/scatter3d/ui/cloudZoom.js +22 -0
  206. package/lib/scatter3d/ui/cloudZoom.js.map +1 -0
  207. package/lib/scatter3d/ui/index.d.ts +29 -0
  208. package/lib/scatter3d/ui/index.d.ts.map +1 -0
  209. package/lib/scatter3d/ui/index.js +21 -0
  210. package/lib/scatter3d/ui/index.js.map +1 -0
  211. package/lib/scatter3d/ui/scatterCloudModel.d.ts +60 -0
  212. package/lib/scatter3d/ui/scatterCloudModel.d.ts.map +1 -0
  213. package/lib/scatter3d/ui/scatterCloudModel.js +66 -0
  214. package/lib/scatter3d/ui/scatterCloudModel.js.map +1 -0
  215. package/lib/scatter3d/ui/scatterCloudProps.d.ts +185 -0
  216. package/lib/scatter3d/ui/scatterCloudProps.d.ts.map +1 -0
  217. package/lib/scatter3d/ui/scatterCloudProps.js +9 -0
  218. package/lib/scatter3d/ui/scatterCloudProps.js.map +1 -0
  219. package/lib/scatter3d/ui/useOrbitDrag.d.ts +60 -0
  220. package/lib/scatter3d/ui/useOrbitDrag.d.ts.map +1 -0
  221. package/lib/scatter3d/ui/useOrbitDrag.js +129 -0
  222. package/lib/scatter3d/ui/useOrbitDrag.js.map +1 -0
  223. package/lib/ui.d.ts +1 -0
  224. package/lib/ui.d.ts.map +1 -1
  225. package/lib/ui.js +1 -0
  226. package/lib/ui.js.map +1 -1
  227. package/package.json +1 -1
  228. package/src/chrome/ui/NavLink.tsx +5 -3
  229. package/src/chrome/ui/SiteFooter.tsx +16 -1
  230. package/src/chrome/ui/SiteHeader.tsx +19 -1
  231. package/src/chrome/ui/navItem.ts +12 -0
  232. package/src/core.ts +1 -0
  233. package/src/download/core/figureLegend.ts +167 -0
  234. package/src/download/core/figureSvg.ts +81 -1
  235. package/src/download/core/figureSvgDocument.ts +13 -3
  236. package/src/download/core/figureTarget.ts +21 -0
  237. package/src/download/core/index.ts +8 -0
  238. package/src/ecosystem/core/sites.ts +14 -0
  239. package/src/ecosystem/ui/glyphs.tsx +26 -0
  240. package/src/overlay/ui/OverlayLegend.tsx +7 -1
  241. package/src/projection/core/projectionCopy.ts +9 -0
  242. package/src/projection/core/projectionOptions.ts +48 -3
  243. package/src/projection/core/projectionStrings.ts +16 -0
  244. package/src/projection/core/projectionTabDefaults.ts +12 -0
  245. package/src/projection/core/projectionTabs.ts +10 -1
  246. package/src/projection/ui/ProjectionMapTab.tsx +8 -0
  247. package/src/projection/ui/ProjectionPanel.tsx +63 -0
  248. package/src/projection/ui/ProjectionReadout.tsx +16 -6
  249. package/src/projection/ui/ProjectionSpaceControls.tsx +219 -0
  250. package/src/projection/ui/ProjectionSpaceMore.tsx +169 -0
  251. package/src/projection/ui/ProjectionSpaceTab.tsx +217 -0
  252. package/src/projection/ui/ProjectionViewer.tsx +3 -0
  253. package/src/projection/ui/index.ts +1 -0
  254. package/src/projection/ui/projectionBarModel.ts +3 -2
  255. package/src/projection/ui/projectionBarReadings.ts +18 -0
  256. package/src/projection/ui/projectionBarSlots.tsx +19 -0
  257. package/src/projection/ui/projectionCloudChoices.ts +23 -0
  258. package/src/projection/ui/projectionEllipsoid.ts +78 -0
  259. package/src/projection/ui/projectionMapChrome.ts +26 -5
  260. package/src/projection/ui/projectionMapView.ts +45 -0
  261. package/src/projection/ui/projectionSpaceModel.ts +14 -0
  262. package/src/projection/ui/projectionViewerProps.ts +35 -0
  263. package/src/scatter/ui/ScatterPlot.tsx +21 -2
  264. package/src/scatter/ui/lassoGesture.ts +15 -2
  265. package/src/scatter/ui/scatterPlotProps.ts +35 -0
  266. package/src/scatter/ui/useScatterInteraction.ts +22 -1
  267. package/src/scatter3d/core/cloudGesture.ts +16 -0
  268. package/src/scatter3d/core/confidenceEllipsoid.ts +177 -0
  269. package/src/scatter3d/core/cubeFrame.ts +72 -0
  270. package/src/scatter3d/core/cubePoints.ts +75 -0
  271. package/src/scatter3d/core/ellipsoidCoverage.ts +86 -0
  272. package/src/scatter3d/core/ellipsoidSilhouette.ts +76 -0
  273. package/src/scatter3d/core/index.ts +52 -0
  274. package/src/scatter3d/core/orbitCamera.ts +159 -0
  275. package/src/scatter3d/core/symmetricEigen.ts +273 -0
  276. package/src/scatter3d/ui/CloudFrameLayer.tsx +97 -0
  277. package/src/scatter3d/ui/CloudPointLayer.tsx +132 -0
  278. package/src/scatter3d/ui/CloudShellLayer.tsx +107 -0
  279. package/src/scatter3d/ui/ScatterCloud.tsx +312 -0
  280. package/src/scatter3d/ui/cloudGeometry.ts +200 -0
  281. package/src/scatter3d/ui/cloudShellStyles.ts +74 -0
  282. package/src/scatter3d/ui/cloudZoom.ts +22 -0
  283. package/src/scatter3d/ui/index.ts +41 -0
  284. package/src/scatter3d/ui/scatterCloudModel.ts +98 -0
  285. package/src/scatter3d/ui/scatterCloudProps.ts +191 -0
  286. package/src/scatter3d/ui/useOrbitDrag.ts +220 -0
  287. package/src/ui.ts +1 -0
  288. 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