@peter.prib/renderengine 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +203 -2
- package/REFERENCE.md +2198 -0
- package/dist/renderengine-workspace.umd.js +46905 -0
- package/dist/renderengine.umd.js +42223 -0
- package/examples/archimate.html +66 -0
- package/examples/diagram-editor.html +51 -0
- package/package.json +65 -4
- package/src/BaseRenderer.js +57 -0
- package/src/CanvasRenderer.js +194 -0
- package/src/Mat4.js +111 -0
- package/src/OrbitCamera.js +57 -0
- package/src/RendererFactory.js +50 -0
- package/src/SVGRenderer.js +439 -0
- package/src/archimate.js +723 -0
- package/src/archimateArchiFormat.js +1693 -0
- package/src/archimateArchiTypes.js +72 -0
- package/src/archimateArchive.js +140 -0
- package/src/archimateCSV.js +127 -0
- package/src/archimateDrawio.js +182 -0
- package/src/archimateExchangeFormat.js +546 -0
- package/src/archimateRelationshipTable.js +93 -0
- package/src/archimateSpecializations.js +161 -0
- package/src/chartCatalog.js +589 -0
- package/src/charting/ArchimateDiagramEditor.js +1624 -0
- package/src/charting/ArchimateSpecializationSelector.js +134 -0
- package/src/charting/Axis.js +68 -0
- package/src/charting/CameraSeeder.js +30 -0
- package/src/charting/CatalogDialog.js +92 -0
- package/src/charting/Chart.js +37 -0
- package/src/charting/Chart3D.js +99 -0
- package/src/charting/ChartBox.js +80 -0
- package/src/charting/ChartData.js +27 -0
- package/src/charting/ChartRegistry.js +76 -0
- package/src/charting/CheckboxTreeSelector.js +304 -0
- package/src/charting/ColorField.js +107 -0
- package/src/charting/ColorPalette.js +88 -0
- package/src/charting/CommandHistory.js +176 -0
- package/src/charting/DataFilter.js +52 -0
- package/src/charting/Diagram.js +36 -0
- package/src/charting/Diagram3D.js +29 -0
- package/src/charting/DiagramClipboard.js +31 -0
- package/src/charting/DiagramEditor.js +3330 -0
- package/src/charting/DiagramEditorCommand.js +110 -0
- package/src/charting/DiagramEditorCommands.js +237 -0
- package/src/charting/DiagramEditorEdgeCommands.js +142 -0
- package/src/charting/DiagramEditorState.js +98 -0
- package/src/charting/DiagramRegistry.js +86 -0
- package/src/charting/DiagramTypeRegistry.js +326 -0
- package/src/charting/DiagramViewPropertiesDialog.js +46 -0
- package/src/charting/Edge.js +113 -0
- package/src/charting/ErrorBars.js +37 -0
- package/src/charting/FilterSystem.js +285 -0
- package/src/charting/FindReplaceDialog.js +139 -0
- package/src/charting/FormDialog.js +385 -0
- package/src/charting/Gauge.js +58 -0
- package/src/charting/Hierarchy.js +175 -0
- package/src/charting/HintPanel.js +86 -0
- package/src/charting/ImageSelector.js +148 -0
- package/src/charting/InteractionManager.js +725 -0
- package/src/charting/Legend.js +58 -0
- package/src/charting/MenuBar.js +62 -0
- package/src/charting/ModelFileFormat.js +115 -0
- package/src/charting/OrbitControls.js +77 -0
- package/src/charting/Palette.js +119 -0
- package/src/charting/PopupMenu.js +117 -0
- package/src/charting/PropertiesPanel.js +155 -0
- package/src/charting/PropertyListEditor.js +96 -0
- package/src/charting/PropertyLog.js +92 -0
- package/src/charting/SettingsCascade.js +93 -0
- package/src/charting/SettingsDialog.js +53 -0
- package/src/charting/SettingsManager.js +179 -0
- package/src/charting/SettingsViewPropertiesDialog.js +58 -0
- package/src/charting/ShapePaths.js +27 -0
- package/src/charting/Statistics.js +134 -0
- package/src/charting/TemplateCatalog.js +59 -0
- package/src/charting/TemplateLibrary.js +103 -0
- package/src/charting/TextStyle.js +168 -0
- package/src/charting/ViewFit.js +55 -0
- package/src/charting/ViewMenu.js +70 -0
- package/src/charting/ViewPropertiesDialog.js +77 -0
- package/src/charting/ViewTabStrip.js +81 -0
- package/src/charting/archimate/ArchiFileFormat.js +32 -0
- package/src/charting/archimate/ArchimateCanvasEditor.js +815 -0
- package/src/charting/archimate/ArchimateCsvFileFormat.js +35 -0
- package/src/charting/archimate/ArchimateEdge.js +137 -0
- package/src/charting/archimate/ArchimateElementTemplate.js +195 -0
- package/src/charting/archimate/ArchimatePalette.js +102 -0
- package/src/charting/archimate/CsvImportDialog.js +105 -0
- package/src/charting/archimate/CustomViewpoints.js +74 -0
- package/src/charting/archimate/DerivedEdge.js +29 -0
- package/src/charting/archimate/DrawioFileFormat.js +24 -0
- package/src/charting/archimate/ElementFilterRule.js +21 -0
- package/src/charting/archimate/ExchangeFileFormat.js +28 -0
- package/src/charting/archimate/ViewGroupTemplate.js +137 -0
- package/src/charting/archimate/ViewLineEdge.js +84 -0
- package/src/charting/archimate/ViewpointRule.js +45 -0
- package/src/charting/archimate/archimateFileFormats.js +12 -0
- package/src/charting/archimate/archimateNames.js +5 -0
- package/src/charting/archimate/csvMapping.js +122 -0
- package/src/charting/archimate/elementMenuItems.js +32 -0
- package/src/charting/archimate/elementProperties.js +146 -0
- package/src/charting/archimate/elementTypeChoices.js +7 -0
- package/src/charting/archimate/elementTypeGroups.js +19 -0
- package/src/charting/archimate/index.js +17 -0
- package/src/charting/archimate/specializationCatalogDialog.js +64 -0
- package/src/charting/archimate/typeGlyph.js +29 -0
- package/src/charting/arrangement/Arrangement.js +112 -0
- package/src/charting/arrangement/ArrangementCommand.js +29 -0
- package/src/charting/arrangement/BackgroundCommand.js +41 -0
- package/src/charting/arrangement/BackgroundGroup.js +18 -0
- package/src/charting/arrangement/GroupCommand.js +46 -0
- package/src/charting/arrangement/ItemGroup.js +62 -0
- package/src/charting/arrangement/LockCommand.js +32 -0
- package/src/charting/arrangement/UngroupCommand.js +27 -0
- package/src/charting/arrangement/alignment.js +88 -0
- package/src/charting/arrangement/arrangeMenuItems.js +25 -0
- package/src/charting/arrangement/index.js +11 -0
- package/src/charting/arrangement/layouts.js +233 -0
- package/src/charting/canvas/ArchiHint.js +78 -0
- package/src/charting/canvas/CanvasBoxKind.js +39 -0
- package/src/charting/canvas/CanvasPalette.js +49 -0
- package/src/charting/canvas/CanvasTemplateLibrary.js +61 -0
- package/src/charting/canvas/CanvasTemplateMenu.js +151 -0
- package/src/charting/canvas/EditHintCommand.js +28 -0
- package/src/charting/canvas/canvasBoxKinds.js +27 -0
- package/src/charting/canvas/canvasBoxMenuItems.js +40 -0
- package/src/charting/canvas/defaultCanvasTemplates.js +186 -0
- package/src/charting/canvas/diagramBoxKinds.js +12 -0
- package/src/charting/canvas/hintMenuItems.js +49 -0
- package/src/charting/charts/ArcGauge.js +55 -0
- package/src/charting/charts/AreaChart.js +71 -0
- package/src/charting/charts/BarChart.js +80 -0
- package/src/charting/charts/BarChart3D.js +56 -0
- package/src/charting/charts/BarChart3DOrbit.js +53 -0
- package/src/charting/charts/BarChartBase.js +70 -0
- package/src/charting/charts/BarGauge.js +42 -0
- package/src/charting/charts/BoxPlotChart.js +41 -0
- package/src/charting/charts/BubbleChart.js +99 -0
- package/src/charting/charts/BubbleChart3D.js +78 -0
- package/src/charting/charts/BulletGauge.js +87 -0
- package/src/charting/charts/CalendarHeatmapChart.js +204 -0
- package/src/charting/charts/CandlestickChart.js +54 -0
- package/src/charting/charts/ChordDiagram.js +130 -0
- package/src/charting/charts/DensityChart.js +73 -0
- package/src/charting/charts/DialGauge.js +65 -0
- package/src/charting/charts/DonutChart.js +15 -0
- package/src/charting/charts/FunnelChart.js +47 -0
- package/src/charting/charts/GanttChart.js +201 -0
- package/src/charting/charts/GaugeChart.js +175 -0
- package/src/charting/charts/GeoMapChart.js +146 -0
- package/src/charting/charts/GroupedBarChart.js +88 -0
- package/src/charting/charts/GroupedDistributionChart.js +70 -0
- package/src/charting/charts/HeatmapChart.js +80 -0
- package/src/charting/charts/HierarchyChart.js +39 -0
- package/src/charting/charts/HistogramChart.js +45 -0
- package/src/charting/charts/KpiTile.js +72 -0
- package/src/charting/charts/LagPlotChart.js +41 -0
- package/src/charting/charts/LineChart.js +124 -0
- package/src/charting/charts/MultiRingGauge.js +54 -0
- package/src/charting/charts/OutlineChart.js +95 -0
- package/src/charting/charts/ParallelCoordinatesChart.js +130 -0
- package/src/charting/charts/PieChart.js +56 -0
- package/src/charting/charts/PipeChart.js +88 -0
- package/src/charting/charts/PopulationPyramidChart.js +106 -0
- package/src/charting/charts/QQPlotChart.js +64 -0
- package/src/charting/charts/RadarChart.js +76 -0
- package/src/charting/charts/RadialBarChart.js +96 -0
- package/src/charting/charts/RingGauge.js +23 -0
- package/src/charting/charts/SankeyDiagram.js +174 -0
- package/src/charting/charts/ScatterChart.js +104 -0
- package/src/charting/charts/StackedAreaChart.js +15 -0
- package/src/charting/charts/StackedBarChart.js +33 -0
- package/src/charting/charts/StackedBarChart3DOrbit.js +58 -0
- package/src/charting/charts/StreamGraphChart.js +131 -0
- package/src/charting/charts/SunburstChart.js +57 -0
- package/src/charting/charts/ThermometerGauge.js +43 -0
- package/src/charting/charts/TreemapChart.js +54 -0
- package/src/charting/charts/ViolinChart.js +37 -0
- package/src/charting/charts/WaterfallChart.js +66 -0
- package/src/charting/charts/WordCloudChart.js +104 -0
- package/src/charting/diagramModel/AddViewCommand.js +33 -0
- package/src/charting/diagramModel/DiagramModel.js +39 -0
- package/src/charting/diagramModel/DiagramModelSearch.js +22 -0
- package/src/charting/diagramModel/DiagramView.js +50 -0
- package/src/charting/diagramModel/ModelHistory.js +43 -0
- package/src/charting/diagramModel/ModelReport.js +153 -0
- package/src/charting/diagramModel/ModelSearch.js +223 -0
- package/src/charting/diagramModel/RemoveViewCommand.js +25 -0
- package/src/charting/diagramModel/RenameViewCommand.js +21 -0
- package/src/charting/diagramModel/ReplaceTextCommand.js +25 -0
- package/src/charting/diagramModel/ViewSetChecker.js +34 -0
- package/src/charting/diagramModel/ViewSetModel.js +243 -0
- package/src/charting/diagramModel/index.js +6 -0
- package/src/charting/diagramModel/nameTransforms.js +29 -0
- package/src/charting/diagrams/ArchimateCommands.js +141 -0
- package/src/charting/diagrams/ArchimateDiagram.js +263 -0
- package/src/charting/diagrams/ArchimateModel.js +1155 -0
- package/src/charting/diagrams/ArchimateModelChecker.js +125 -0
- package/src/charting/diagrams/ArchimateModelReport.js +128 -0
- package/src/charting/diagrams/ArchimateModelSearch.js +41 -0
- package/src/charting/diagrams/ArchimateValidator.js +357 -0
- package/src/charting/diagrams/CanvasView.js +112 -0
- package/src/charting/diagrams/ContextMenu.js +37 -0
- package/src/charting/diagrams/DataModelDiagram.js +86 -0
- package/src/charting/diagrams/DataModelRelationshipStrategy.js +38 -0
- package/src/charting/diagrams/DerivedRelationships.js +102 -0
- package/src/charting/diagrams/EbnfDiagram.js +39 -0
- package/src/charting/diagrams/EdgeGeometry.js +553 -0
- package/src/charting/diagrams/EditViewItemCommand.js +128 -0
- package/src/charting/diagrams/ElementInstance.js +157 -0
- package/src/charting/diagrams/GraphDiagram.js +1340 -0
- package/src/charting/diagrams/GraphDiagram3D.js +152 -0
- package/src/charting/diagrams/GraphMetrics.js +81 -0
- package/src/charting/diagrams/GridOverlay.js +137 -0
- package/src/charting/diagrams/ImportRowsCommand.js +62 -0
- package/src/charting/diagrams/LabelHelper.js +108 -0
- package/src/charting/diagrams/MergeElementsCommand.js +101 -0
- package/src/charting/diagrams/NestInstanceCommand.js +34 -0
- package/src/charting/diagrams/NodeGeometry.js +158 -0
- package/src/charting/diagrams/PlaceItemsCommand.js +77 -0
- package/src/charting/diagrams/RadialLayout.js +101 -0
- package/src/charting/diagrams/RelationshipInstance.js +93 -0
- package/src/charting/diagrams/RemoveGroupFromViewCommand.js +28 -0
- package/src/charting/diagrams/RemoveLineCommand.js +27 -0
- package/src/charting/diagrams/ShowElementCommand.js +43 -0
- package/src/charting/diagrams/SvgCoordinate.js +17 -0
- package/src/charting/diagrams/SvgZoom.js +127 -0
- package/src/charting/diagrams/Tooltip.js +62 -0
- package/src/charting/diagrams/ViewConnectionInstance.js +102 -0
- package/src/charting/diagrams/ViewObjectInstance.js +56 -0
- package/src/charting/diagrams/commands/AddEdgeCommand.js +34 -0
- package/src/charting/diagrams/commands/AddNodeCommand.js +28 -0
- package/src/charting/diagrams/commands/AddWaypointCommand.js +27 -0
- package/src/charting/diagrams/commands/CreateFolderCommand.js +27 -0
- package/src/charting/diagrams/commands/DeleteEdgeCommand.js +30 -0
- package/src/charting/diagrams/commands/DeleteFolderCommand.js +47 -0
- package/src/charting/diagrams/commands/DeleteNodeCommand.js +35 -0
- package/src/charting/diagrams/commands/DeleteWaypointCommand.js +24 -0
- package/src/charting/diagrams/commands/GraphCommand.js +65 -0
- package/src/charting/diagrams/commands/InvertEdgeCommand.js +24 -0
- package/src/charting/diagrams/commands/MoveNodeCommand.js +44 -0
- package/src/charting/diagrams/commands/MoveWaypointCommand.js +27 -0
- package/src/charting/diagrams/commands/RenameEdgeCommand.js +23 -0
- package/src/charting/diagrams/commands/RenameFolderCommand.js +31 -0
- package/src/charting/diagrams/commands/RenameNodeCommand.js +27 -0
- package/src/charting/diagrams/commands/ResizeNodeCommand.js +46 -0
- package/src/charting/diagrams/commands/UpdateEdgeSideCommand.js +40 -0
- package/src/charting/diagrams/commands/index.js +17 -0
- package/src/charting/diagrams/duplicates.js +49 -0
- package/src/charting/diagrams/nesting.js +30 -0
- package/src/charting/diagrams/roadmap.js +77 -0
- package/src/charting/edgeMarkers/ArrowMarker.js +15 -0
- package/src/charting/edgeMarkers/CircleMarker.js +13 -0
- package/src/charting/edgeMarkers/CrowsFootMarker.js +16 -0
- package/src/charting/edgeMarkers/DiamondMarker.js +15 -0
- package/src/charting/edgeMarkers/EdgeMarker.js +34 -0
- package/src/charting/edgeMarkers/HalfArrowMarker.js +8 -0
- package/src/charting/edgeMarkers/HollowDiamondMarker.js +18 -0
- package/src/charting/edgeMarkers/HollowTriangleMarker.js +18 -0
- package/src/charting/edgeMarkers/OpenArrowMarker.js +17 -0
- package/src/charting/edgeMarkers/index.js +27 -0
- package/src/charting/edgeStyle/EdgeStyleCascade.js +22 -0
- package/src/charting/edgeStyle/EdgeStyleDialog.js +51 -0
- package/src/charting/edgeStyle/EdgeStyleSchema.js +43 -0
- package/src/charting/edgeStyle/SetStyleCommand.js +12 -0
- package/src/charting/edgeStyle/index.js +4 -0
- package/src/charting/emphasis/EmphasisRule.js +42 -0
- package/src/charting/emphasis/HighlightRule.js +24 -0
- package/src/charting/gauges/ArcTrack.js +51 -0
- package/src/charting/gauges/GaugeAnimation.js +51 -0
- package/src/charting/gauges/GaugeBands.js +48 -0
- package/src/charting/gauges/GaugeScale.js +87 -0
- package/src/charting/gauges/GaugeTrack.js +44 -0
- package/src/charting/gauges/GaugeValue.js +61 -0
- package/src/charting/gauges/HorizontalTrack.js +14 -0
- package/src/charting/gauges/LinearTrack.js +29 -0
- package/src/charting/gauges/Sparkline.js +42 -0
- package/src/charting/gauges/VerticalTrack.js +17 -0
- package/src/charting/glyphs/ArrowGlyph.js +21 -0
- package/src/charting/glyphs/BatteryGlyph.js +18 -0
- package/src/charting/glyphs/ChevronGlyph.js +18 -0
- package/src/charting/glyphs/CircleGlyph.js +15 -0
- package/src/charting/glyphs/CloudGlyph.js +19 -0
- package/src/charting/glyphs/ComponentGlyph.js +18 -0
- package/src/charting/glyphs/ConstraintGlyph.js +24 -0
- package/src/charting/glyphs/ContractBoxGlyph.js +10 -0
- package/src/charting/glyphs/CubeGlyph.js +19 -0
- package/src/charting/glyphs/CylinderGlyph.js +19 -0
- package/src/charting/glyphs/DashedDoubleArrowGlyph.js +11 -0
- package/src/charting/glyphs/DeviceGlyph.js +17 -0
- package/src/charting/glyphs/DiamondGlyph.js +17 -0
- package/src/charting/glyphs/DocumentGlyph.js +17 -0
- package/src/charting/glyphs/DoubleArrowGlyph.js +23 -0
- package/src/charting/glyphs/DoubleCircleGlyph.js +24 -0
- package/src/charting/glyphs/EllipseGlyph.js +18 -0
- package/src/charting/glyphs/FactoryGlyph.js +20 -0
- package/src/charting/glyphs/FlagGlyph.js +16 -0
- package/src/charting/glyphs/GapCircleGlyph.js +17 -0
- package/src/charting/glyphs/GearGlyph.js +37 -0
- package/src/charting/glyphs/Glyph.js +98 -0
- package/src/charting/glyphs/GroupingBoxGlyph.js +13 -0
- package/src/charting/glyphs/HalfCircleGlyph.js +22 -0
- package/src/charting/glyphs/HexagonGlyph.js +28 -0
- package/src/charting/glyphs/IconGlyph.js +22 -0
- package/src/charting/glyphs/ImageGlyph.js +16 -0
- package/src/charting/glyphs/JunctionGlyph.js +17 -0
- package/src/charting/glyphs/LollipopGlyph.js +22 -0
- package/src/charting/glyphs/MagnifierGlyph.js +17 -0
- package/src/charting/glyphs/NoteGlyph.js +33 -0
- package/src/charting/glyphs/ObjectBoxGlyph.js +21 -0
- package/src/charting/glyphs/OrJunctionGlyph.js +17 -0
- package/src/charting/glyphs/ParallelogramGlyph.js +20 -0
- package/src/charting/glyphs/PersonGlyph.js +23 -0
- package/src/charting/glyphs/PillGlyph.js +23 -0
- package/src/charting/glyphs/PinGlyph.js +18 -0
- package/src/charting/glyphs/PrincipleGlyph.js +23 -0
- package/src/charting/glyphs/ProductBoxGlyph.js +16 -0
- package/src/charting/glyphs/RectGlyph.js +15 -0
- package/src/charting/glyphs/RoundedBoxGlyph.js +13 -0
- package/src/charting/glyphs/StackGlyph.js +14 -0
- package/src/charting/glyphs/StepsGlyph.js +20 -0
- package/src/charting/glyphs/StickFigureGlyph.js +32 -0
- package/src/charting/glyphs/SystemSoftwareBoxGlyph.js +16 -0
- package/src/charting/glyphs/TabbedBoxGlyph.js +37 -0
- package/src/charting/glyphs/TargetGlyph.js +23 -0
- package/src/charting/glyphs/WavyBoxGlyph.js +17 -0
- package/src/charting/glyphs/WheelGlyph.js +28 -0
- package/src/charting/glyphs/drawGlyph.js +28 -0
- package/src/charting/glyphs/glyphSvg.js +55 -0
- package/src/charting/glyphs/index.js +109 -0
- package/src/charting/modelTree/AnalysisPanel.js +159 -0
- package/src/charting/modelTree/DiagramTabs.js +217 -0
- package/src/charting/modelTree/ModelTree.js +164 -0
- package/src/charting/modelTree/ModelTreePanel.js +914 -0
- package/src/charting/modelTree/TreeBuilder.js +238 -0
- package/src/charting/modelTree/TreeModeRegistry.js +51 -0
- package/src/charting/modelTree/TreeViewMode.js +110 -0
- package/src/charting/modelTree/buildFilterTrees.js +138 -0
- package/src/charting/modelTree/modes/FlatTreeMode.js +46 -0
- package/src/charting/modelTree/modes/LayerGroupedTreeMode.js +62 -0
- package/src/charting/modelTree/modes/TypeGroupedTreeMode.js +60 -0
- package/src/charting/modelTree/treeLayers.js +11 -0
- package/src/charting/report/reportHtml.js +82 -0
- package/src/charting/report/reportViewer.js +118 -0
- package/src/charting/shapes/CircleShape.js +19 -0
- package/src/charting/shapes/PathShape.js +17 -0
- package/src/charting/shapes/RectShape.js +20 -0
- package/src/charting/shapes/Shape.js +20 -0
- package/src/charting/shapes/ShapeFactory.js +23 -0
- package/src/charting/storage/LocalJsonStore.js +43 -0
- package/src/charting/templates/ActorTemplate.js +36 -0
- package/src/charting/templates/BoxTemplate.js +27 -0
- package/src/charting/templates/CircleTemplate.js +18 -0
- package/src/charting/templates/ClassTemplate.js +20 -0
- package/src/charting/templates/CloudTemplate.js +43 -0
- package/src/charting/templates/ComponentTemplate.js +19 -0
- package/src/charting/templates/CylinderTemplate.js +30 -0
- package/src/charting/templates/DataStoreTemplate.js +16 -0
- package/src/charting/templates/DiamondTemplate.js +10 -0
- package/src/charting/templates/DocumentTemplate.js +32 -0
- package/src/charting/templates/EllipseTemplate.js +15 -0
- package/src/charting/templates/FinalStateTemplate.js +12 -0
- package/src/charting/templates/GlyphTemplate.js +47 -0
- package/src/charting/templates/HexagonTemplate.js +17 -0
- package/src/charting/templates/LifelineTemplate.js +20 -0
- package/src/charting/templates/ManualInputTemplate.js +12 -0
- package/src/charting/templates/NoteTemplate.js +23 -0
- package/src/charting/templates/OctagonTemplate.js +7 -0
- package/src/charting/templates/PackageTemplate.js +25 -0
- package/src/charting/templates/ParallelogramTemplate.js +11 -0
- package/src/charting/templates/PentagonTemplate.js +5 -0
- package/src/charting/templates/PolygonTemplate.js +18 -0
- package/src/charting/templates/PredefinedProcessTemplate.js +13 -0
- package/src/charting/templates/RectangleTemplate.js +28 -0
- package/src/charting/templates/RegularPolygonTemplate.js +21 -0
- package/src/charting/templates/RoundedRectangleTemplate.js +5 -0
- package/src/charting/templates/StadiumTemplate.js +8 -0
- package/src/charting/templates/StarTemplate.js +17 -0
- package/src/charting/templates/SwimlaneTemplate.js +18 -0
- package/src/charting/templates/Template.js +208 -0
- package/src/charting/templates/TrapezoidTemplate.js +11 -0
- package/src/charting/templates/TriangleTemplate.js +12 -0
- package/src/charting/templates/index.js +31 -0
- package/src/charting/templates/outline.js +59 -0
- package/src/charting/templates/svg.js +19 -0
- package/src/charting/textToShapes.js +61 -0
- package/src/charting/toolbar/ToolbarButton.js +38 -0
- package/src/charting/toolbar/ToolbarCheckbox.js +31 -0
- package/src/charting/toolbar/ToolbarItem.js +19 -0
- package/src/charting/toolbar/ToolbarMenu.js +36 -0
- package/src/charting/toolbar/ToolbarSelect.js +38 -0
- package/src/charting/toolbar/ToolbarSeparator.js +7 -0
- package/src/charting/toolbar/ToolbarText.js +22 -0
- package/src/charting/toolbar/index.js +8 -0
- package/src/charting/toolbar/pickFile.js +14 -0
- package/src/charting/viewDecorations.js +85 -0
- package/src/charting/zOrder.js +57 -0
- package/src/csvTable.js +61 -0
- package/src/datamodel.js +122 -0
- package/src/datawindow/BoundedWindow.js +36 -0
- package/src/datawindow/FlatDataWindow.js +34 -0
- package/src/datawindow/GraphDataWindow.js +40 -0
- package/src/datawindow/GroupedSampleDataWindow.js +16 -0
- package/src/datawindow/SeriesDataWindow.js +42 -0
- package/src/datawindow/index.js +31 -0
- package/src/diagramCatalog.js +30 -0
- package/src/ebnfDiagram.js +57 -0
- package/src/ebnfParser.js +129 -0
- package/src/exportImage.js +233 -0
- package/src/index.js +2636 -0
- package/src/itIcons.js +84 -0
- package/src/kernelExpr.js +243 -0
- package/src/propertiesDialog.js +463 -0
- package/src/railroadDiagram.js +571 -0
- package/src/treeModeCatalog.js +16 -0
- package/src/workspace/ContextBus.js +79 -0
- package/src/workspace/EditorShell.js +67 -0
- package/src/workspace/Part.js +163 -0
- package/src/workspace/PartFactory.js +49 -0
- package/src/workspace/PartRegistry.js +86 -0
- package/src/workspace/Workspace.js +321 -0
- package/src/workspace/archimateServices.js +33 -0
- package/src/workspace/dom.js +120 -0
- package/src/workspace/index.js +75 -0
- package/src/workspace/layout/GridNode.js +280 -0
- package/src/workspace/layout/LayoutNode.js +89 -0
- package/src/workspace/layout/PartNode.js +77 -0
- package/src/workspace/layout/SplitNode.js +114 -0
- package/src/workspace/layout/TabsNode.js +191 -0
- package/src/workspace/layout/index.js +24 -0
- package/src/workspace/parts/AdapterPart.js +33 -0
- package/src/workspace/parts/ArchimateCanvasPart.js +345 -0
- package/src/workspace/parts/ArchimateEditorPart.js +79 -0
- package/src/workspace/parts/ChartPart.js +115 -0
- package/src/workspace/parts/ContentPart.js +34 -0
- package/src/workspace/parts/DetailPart.js +46 -0
- package/src/workspace/parts/DiagramEditorPart.js +135 -0
- package/src/workspace/parts/EditorPart.js +388 -0
- package/src/workspace/parts/HtmlPart.js +25 -0
- package/src/workspace/parts/ListPart.js +57 -0
- package/src/workspace/parts/ModelFile.js +187 -0
- package/src/workspace/parts/ModelsPart.js +135 -0
- package/src/workspace/styles.js +258 -0
- package/src/workspace/themes.js +96 -0
- package/src/workspace/workArea.js +413 -0
- package/src/workspace/workAreaModel.js +197 -0
- package/src/workspace.js +16 -0
- package/src/zip/ZipArchive.js +160 -0
- package/src/zip/crc32.js +25 -0
- package/src/zip/inflate.js +159 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,2636 @@
|
|
|
1
|
+
import { RendererFactory } from './RendererFactory.js';
|
|
2
|
+
import { Mat4 } from './Mat4.js';
|
|
3
|
+
import { OrbitCamera } from './OrbitCamera.js';
|
|
4
|
+
import {
|
|
5
|
+
CHART_CATALOG, getChartCatalogEntry, chartRegistry,
|
|
6
|
+
AXIS_KIND, ENCODING_CHANNEL, COORD_SYSTEM, COLOR_MODE, CAMERA_MODE,
|
|
7
|
+
} from './chartCatalog.js';
|
|
8
|
+
import {
|
|
9
|
+
DIAGRAM_CATALOG, getDiagramCatalogEntry, diagramRegistry,
|
|
10
|
+
} from './diagramCatalog.js';
|
|
11
|
+
import { ChartBox, ChartLabels } from './charting/ChartBox.js';
|
|
12
|
+
import { Axis } from './charting/Axis.js';
|
|
13
|
+
import { Legend } from './charting/Legend.js';
|
|
14
|
+
import { ColorPalette } from './charting/ColorPalette.js';
|
|
15
|
+
import { ShapePaths } from './charting/ShapePaths.js';
|
|
16
|
+
import { BarChart } from './charting/charts/BarChart.js';
|
|
17
|
+
import { GroupedBarChart } from './charting/charts/GroupedBarChart.js';
|
|
18
|
+
import { LineChart } from './charting/charts/LineChart.js';
|
|
19
|
+
import { AreaChart } from './charting/charts/AreaChart.js';
|
|
20
|
+
import { StackedAreaChart } from './charting/charts/StackedAreaChart.js';
|
|
21
|
+
import { PieChart } from './charting/charts/PieChart.js';
|
|
22
|
+
import { DonutChart } from './charting/charts/DonutChart.js';
|
|
23
|
+
import { PipeChart } from './charting/charts/PipeChart.js';
|
|
24
|
+
import { StackedBarChart } from './charting/charts/StackedBarChart.js';
|
|
25
|
+
import { ScatterChart } from './charting/charts/ScatterChart.js';
|
|
26
|
+
import { BubbleChart } from './charting/charts/BubbleChart.js';
|
|
27
|
+
import { HistogramChart } from './charting/charts/HistogramChart.js';
|
|
28
|
+
import { BoxPlotChart } from './charting/charts/BoxPlotChart.js';
|
|
29
|
+
import { DensityChart } from './charting/charts/DensityChart.js';
|
|
30
|
+
import { ViolinChart } from './charting/charts/ViolinChart.js';
|
|
31
|
+
import { QQPlotChart } from './charting/charts/QQPlotChart.js';
|
|
32
|
+
import { LagPlotChart } from './charting/charts/LagPlotChart.js';
|
|
33
|
+
import { TreemapChart } from './charting/charts/TreemapChart.js';
|
|
34
|
+
import { SunburstChart } from './charting/charts/SunburstChart.js';
|
|
35
|
+
import { OutlineChart } from './charting/charts/OutlineChart.js';
|
|
36
|
+
import { RadarChart } from './charting/charts/RadarChart.js';
|
|
37
|
+
import { FunnelChart } from './charting/charts/FunnelChart.js';
|
|
38
|
+
import { WaterfallChart } from './charting/charts/WaterfallChart.js';
|
|
39
|
+
import { BarGauge } from './charting/charts/BarGauge.js';
|
|
40
|
+
import { BulletGauge } from './charting/charts/BulletGauge.js';
|
|
41
|
+
import { DialGauge } from './charting/charts/DialGauge.js';
|
|
42
|
+
import { ArcGauge } from './charting/charts/ArcGauge.js';
|
|
43
|
+
import { RingGauge } from './charting/charts/RingGauge.js';
|
|
44
|
+
import { MultiRingGauge } from './charting/charts/MultiRingGauge.js';
|
|
45
|
+
import { KpiTile } from './charting/charts/KpiTile.js';
|
|
46
|
+
import { ThermometerGauge } from './charting/charts/ThermometerGauge.js';
|
|
47
|
+
import { GaugeAnimation } from './charting/gauges/GaugeAnimation.js';
|
|
48
|
+
import { GaugeValue } from './charting/gauges/GaugeValue.js';
|
|
49
|
+
import { BarChart3D } from './charting/charts/BarChart3D.js';
|
|
50
|
+
import { BubbleChart3D } from './charting/charts/BubbleChart3D.js';
|
|
51
|
+
import { BarChart3DOrbit } from './charting/charts/BarChart3DOrbit.js';
|
|
52
|
+
import { StackedBarChart3DOrbit } from './charting/charts/StackedBarChart3DOrbit.js';
|
|
53
|
+
import { CalendarHeatmapChart } from './charting/charts/CalendarHeatmapChart.js';
|
|
54
|
+
import { GanttChart } from './charting/charts/GanttChart.js';
|
|
55
|
+
import { StreamGraphChart } from './charting/charts/StreamGraphChart.js';
|
|
56
|
+
import { RadialBarChart } from './charting/charts/RadialBarChart.js';
|
|
57
|
+
import { PopulationPyramidChart } from './charting/charts/PopulationPyramidChart.js';
|
|
58
|
+
import { SankeyDiagram } from './charting/charts/SankeyDiagram.js';
|
|
59
|
+
import { ChordDiagram } from './charting/charts/ChordDiagram.js';
|
|
60
|
+
import { WordCloudChart } from './charting/charts/WordCloudChart.js';
|
|
61
|
+
import { ParallelCoordinatesChart } from './charting/charts/ParallelCoordinatesChart.js';
|
|
62
|
+
import { GeoMapChart } from './charting/charts/GeoMapChart.js';
|
|
63
|
+
import { HeatmapChart } from './charting/charts/HeatmapChart.js';
|
|
64
|
+
import { CandlestickChart } from './charting/charts/CandlestickChart.js';
|
|
65
|
+
import { ShapeFactory } from './charting/shapes/ShapeFactory.js';
|
|
66
|
+
import { Statistics } from './charting/Statistics.js';
|
|
67
|
+
import { Chart3D } from './charting/Chart3D.js';
|
|
68
|
+
import { Hierarchy } from './charting/Hierarchy.js';
|
|
69
|
+
import { Gauge } from './charting/Gauge.js';
|
|
70
|
+
import { ChartData } from './charting/ChartData.js';
|
|
71
|
+
import { DataFilter } from './charting/DataFilter.js';
|
|
72
|
+
import { InteractionManager } from './charting/InteractionManager.js';
|
|
73
|
+
import { ebnfAstToDiagram } from './ebnfDiagram.js';
|
|
74
|
+
import { parseEbnf } from './ebnfParser.js';
|
|
75
|
+
import { Command, CompositeCommand, CommandHistory } from './charting/CommandHistory.js';
|
|
76
|
+
import { Diagram } from './charting/Diagram.js';
|
|
77
|
+
import { Diagram3D } from './charting/Diagram3D.js';
|
|
78
|
+
import { EbnfDiagram } from './charting/diagrams/EbnfDiagram.js';
|
|
79
|
+
import { GraphDiagram } from './charting/diagrams/GraphDiagram.js';
|
|
80
|
+
import { GraphDiagram3D } from './charting/diagrams/GraphDiagram3D.js';
|
|
81
|
+
import { GraphMetrics } from './charting/diagrams/GraphMetrics.js';
|
|
82
|
+
import { LabelHelper } from './charting/diagrams/LabelHelper.js';
|
|
83
|
+
import { NodeGeometry } from './charting/diagrams/NodeGeometry.js';
|
|
84
|
+
import { SvgCoordinate } from './charting/diagrams/SvgCoordinate.js';
|
|
85
|
+
import { EdgeGeometry } from './charting/diagrams/EdgeGeometry.js';
|
|
86
|
+
import { RadialLayout } from './charting/diagrams/RadialLayout.js';
|
|
87
|
+
import { DerivedRelationships } from './charting/diagrams/DerivedRelationships.js';
|
|
88
|
+
import { ArchimateDiagram } from './charting/diagrams/ArchimateDiagram.js';
|
|
89
|
+
import { DataModelDiagram } from './charting/diagrams/DataModelDiagram.js';
|
|
90
|
+
import { createDataWindow, GraphDataWindow } from './datawindow/index.js';
|
|
91
|
+
import { compileFormula } from './kernelExpr.js';
|
|
92
|
+
import { ArchimateNotation, ArchimateLayer, ArchimateElementType, ArchimateRelationshipType, ArchimateViewpoints } from './archimate.js';
|
|
93
|
+
import { ITIconNotation, ITElementType } from './itIcons.js';
|
|
94
|
+
import { ArchimateValidator } from './charting/diagrams/ArchimateValidator.js';
|
|
95
|
+
import { ArchimateSpecializationRegistry } from './archimateSpecializations.js';
|
|
96
|
+
import { ArchimateSpecializationSelector } from './charting/ArchimateSpecializationSelector.js';
|
|
97
|
+
import { ImageSelector } from './charting/ImageSelector.js';
|
|
98
|
+
import { CheckboxTreeSelector } from './charting/CheckboxTreeSelector.js';
|
|
99
|
+
import { SettingsManager, settingsManager } from './charting/SettingsManager.js';
|
|
100
|
+
import { showGlobalSettingsDialog } from './charting/SettingsDialog.js';
|
|
101
|
+
import { ArchimateModel, ArchimateView } from './charting/diagrams/ArchimateModel.js';
|
|
102
|
+
import { CanvasView } from './charting/diagrams/CanvasView.js';
|
|
103
|
+
import { PlaceItemsCommand } from './charting/diagrams/PlaceItemsCommand.js';
|
|
104
|
+
import { RemoveLineCommand } from './charting/diagrams/RemoveLineCommand.js';
|
|
105
|
+
import { CanvasBoxKind } from './charting/canvas/CanvasBoxKind.js';
|
|
106
|
+
import { CANVAS_BOX_KINDS, canvasBoxKind } from './charting/canvas/canvasBoxKinds.js';
|
|
107
|
+
import { canvasBoxMenuItems } from './charting/canvas/canvasBoxMenuItems.js';
|
|
108
|
+
import { ArchiHint } from './charting/canvas/ArchiHint.js';
|
|
109
|
+
import { HintPanel } from './charting/HintPanel.js';
|
|
110
|
+
import { EditHintCommand } from './charting/canvas/EditHintCommand.js';
|
|
111
|
+
import { hintMenuItems } from './charting/canvas/hintMenuItems.js';
|
|
112
|
+
import { CanvasPalette, CANVAS_BOX_MIME } from './charting/canvas/CanvasPalette.js';
|
|
113
|
+
import { ViewLineEdge } from './charting/archimate/ViewLineEdge.js';
|
|
114
|
+
import { ElementInstance } from './charting/diagrams/ElementInstance.js';
|
|
115
|
+
import { ViewObjectInstance } from './charting/diagrams/ViewObjectInstance.js';
|
|
116
|
+
import { Glyph, GLYPHS, glyphFor, drawGlyph } from './charting/glyphs/index.js';
|
|
117
|
+
import { glyphElement, glyphToSvg } from './charting/glyphs/glyphSvg.js';
|
|
118
|
+
import { GlyphTemplate } from './charting/templates/GlyphTemplate.js';
|
|
119
|
+
import { ArchimateElementTemplate, ArchimateEdge, ArchimateCanvasEditor, ArchimatePalette, KIND_PREFERENCE, ARCHIMATE_TYPE_MIME } from './charting/archimate/index.js';
|
|
120
|
+
import { RelationshipInstance } from './charting/diagrams/RelationshipInstance.js';
|
|
121
|
+
import { DiagramEditor } from './charting/DiagramEditor.js';
|
|
122
|
+
import { DiagramEditorState } from './charting/DiagramEditorState.js';
|
|
123
|
+
import { Template, PolygonTemplate, RegularPolygonTemplate, RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate, ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate, EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate, OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate, CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate, PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate } from './charting/templates/index.js';
|
|
124
|
+
import { TemplateCatalog } from './charting/TemplateCatalog.js';
|
|
125
|
+
import { DIAGRAM_TYPES, getTemplatesForDiagramType, getAllDiagramTypes, getDiagramTypesByGroup } from './charting/DiagramTypeRegistry.js';
|
|
126
|
+
import { EdgeMarker, ArrowMarker, DiamondMarker, CircleMarker, CrowsFootMarker, HollowDiamondMarker, HollowTriangleMarker, OpenArrowMarker, HalfArrowMarker, EDGE_MARKERS, getEdgeMarker } from './charting/edgeMarkers/index.js';
|
|
127
|
+
import { Palette } from './charting/Palette.js';
|
|
128
|
+
import { MoveElementCommand, BringToFrontCommand, SendToBackCommand, RaiseCommand, LowerCommand, CreateTemplateCommand, EditPropertyCommand, ResizeCommand, RotateCommand, SetPropertiesCommand, ReorderCommand } from './charting/DiagramEditorCommands.js';
|
|
129
|
+
import { Edge } from './charting/Edge.js';
|
|
130
|
+
import { AddEdgeCommand, DeleteEdgeCommand, ReconnectEdgeCommand, MoveWaypointCommand, AddWaypointCommand, DeleteWaypointCommand } from './charting/DiagramEditorEdgeCommands.js';
|
|
131
|
+
import { EDGE_STYLE_PROPERTIES, LINE_ONLY_PROPERTIES, LINE_STYLE_DASH, EdgeStyleCascade, SetStyleCommand, EdgeStyleDialog } from './charting/edgeStyle/index.js';
|
|
132
|
+
import { DiagramModel, DiagramView, ViewSetModel } from './charting/diagramModel/index.js';
|
|
133
|
+
import { ViewMenu } from './charting/ViewMenu.js';
|
|
134
|
+
import { CanvasTemplateLibrary } from './charting/canvas/CanvasTemplateLibrary.js';
|
|
135
|
+
import { CanvasTemplateMenu } from './charting/canvas/CanvasTemplateMenu.js';
|
|
136
|
+
import { TemplateLibrary } from './charting/TemplateLibrary.js';
|
|
137
|
+
import { Arrangement, ItemGroup, BackgroundGroup, GroupCommand, UngroupCommand, LockCommand, BackgroundCommand, arrangeMenuItems } from './charting/arrangement/index.js';
|
|
138
|
+
import { ViewPropertiesDialog } from './charting/ViewPropertiesDialog.js';
|
|
139
|
+
import { SettingsViewPropertiesDialog } from './charting/SettingsViewPropertiesDialog.js';
|
|
140
|
+
import { DiagramViewPropertiesDialog } from './charting/DiagramViewPropertiesDialog.js';
|
|
141
|
+
import { inheritableFields, readInheritableFields } from './charting/SettingsCascade.js';
|
|
142
|
+
import { LocalJsonStore } from './charting/storage/LocalJsonStore.js';
|
|
143
|
+
import { PopupMenu } from './charting/PopupMenu.js';
|
|
144
|
+
import { ViewTabStrip } from './charting/ViewTabStrip.js';
|
|
145
|
+
import { EditorShell } from './workspace/EditorShell.js';
|
|
146
|
+
import { FormDialog } from './charting/FormDialog.js';
|
|
147
|
+
import { ColorField } from './charting/ColorField.js';
|
|
148
|
+
import { TextStyle } from './charting/TextStyle.js';
|
|
149
|
+
import { ArchimateModelChecker } from './charting/diagrams/ArchimateModelChecker.js';
|
|
150
|
+
import { ViewSetChecker } from './charting/diagramModel/ViewSetChecker.js';
|
|
151
|
+
import { ViewFit } from './charting/ViewFit.js';
|
|
152
|
+
import { archiElementType, NEAREST_ARCHI_TYPE } from './archimateArchiTypes.js';
|
|
153
|
+
import { DEFAULT_CANVAS_TEMPLATES, canvasTemplate } from './charting/canvas/defaultCanvasTemplates.js';
|
|
154
|
+
import { EditViewItemCommand, viewItemChanges } from './charting/diagrams/EditViewItemCommand.js';
|
|
155
|
+
import { ToolbarItem, ToolbarButton, ToolbarMenu, ToolbarSelect, ToolbarCheckbox, ToolbarText, ToolbarSeparator, pickFile } from './charting/toolbar/index.js';
|
|
156
|
+
import { reorderedZ, stackingOrder } from './charting/zOrder.js';
|
|
157
|
+
import { PropertiesPanel } from './charting/PropertiesPanel.js';
|
|
158
|
+
import { MenuBar } from './charting/MenuBar.js';
|
|
159
|
+
import { ArchimateDiagramEditor } from './charting/ArchimateDiagramEditor.js';
|
|
160
|
+
import { ModelTree } from './charting/modelTree/ModelTree.js';
|
|
161
|
+
import { TreeModeRegistry } from './charting/modelTree/TreeModeRegistry.js';
|
|
162
|
+
import { TREE_MODE_CATALOG, treeModeRegistry, getTreeModeCatalogEntry } from './treeModeCatalog.js';
|
|
163
|
+
import { buildModelTreePanel } from './charting/modelTree/ModelTreePanel.js';
|
|
164
|
+
import { buildAnalysisPanel, buildAnalysisData } from './charting/modelTree/AnalysisPanel.js';
|
|
165
|
+
import { buildTypeCategoryTree, buildPropertyTree } from './charting/modelTree/buildFilterTrees.js';
|
|
166
|
+
import { DiagramTabs } from './charting/modelTree/DiagramTabs.js';
|
|
167
|
+
import { DataModelNotation, DataModelLevel, NotationStyle } from './datamodel.js';
|
|
168
|
+
import {
|
|
169
|
+
ARCHI_VISUAL_OBJECTS, ARCHI_VIEW_TYPES,
|
|
170
|
+
parseXML, parseArchiModel, toRenderEngineModel, toArchimateModel, toArchiXML, fromArchimateView, toArchiExport,
|
|
171
|
+
} from './archimateArchiFormat.js';
|
|
172
|
+
import { parseExchangeModel, toExchangeXML, EXCHANGE_NAMESPACE, EXCHANGE_TYPE_PROPERTY, EXCHANGE_VIEWPOINTS } from './archimateExchangeFormat.js';
|
|
173
|
+
import { ModelFileFormat, modelFileMenuItems, importFile } from './charting/ModelFileFormat.js';
|
|
174
|
+
import { ArchiFileFormat, ExchangeFileFormat, ArchimateCsvFileFormat, ARCHIMATE_FILE_FORMATS } from './charting/archimate/index.js';
|
|
175
|
+
import { ShowElementCommand } from './charting/diagrams/ShowElementCommand.js';
|
|
176
|
+
import { EmphasisRule, EMPHASIS_OPACITY, EMPHASIS_LEVELS, strongerEmphasis } from './charting/emphasis/EmphasisRule.js';
|
|
177
|
+
import { HighlightRule } from './charting/emphasis/HighlightRule.js';
|
|
178
|
+
import { CatalogDialog } from './charting/CatalogDialog.js';
|
|
179
|
+
import { CustomViewpoints, CUSTOM_VIEWPOINTS_STORE, specializationCatalogDialog, elementTypeChoices } from './charting/archimate/index.js';
|
|
180
|
+
import { ViewpointRule, ElementFilterRule } from './charting/archimate/index.js';
|
|
181
|
+
import { MODEL_ELEMENT_MIME, TREE_LAYERS, treeLayerOf } from './charting/modelTree/ModelTreePanel.js';
|
|
182
|
+
import { parseArchiFile, toArchiFile, ARCHI_MODEL_ENTRY } from './archimateArchive.js';
|
|
183
|
+
import { ZipArchive } from './zip/ZipArchive.js';
|
|
184
|
+
import {
|
|
185
|
+
exportElementsCSV, exportRelationshipsCSV, exportModelCSV,
|
|
186
|
+
importElementsCSV, importRelationshipsCSV, importModelCSV,
|
|
187
|
+
} from './archimateCSV.js';
|
|
188
|
+
// Safe to import here despite being browser-only in effect: every DOM/clipboard
|
|
189
|
+
// reference sits inside a function body, so loading it under node does nothing.
|
|
190
|
+
import {
|
|
191
|
+
copyImage, toPngBlob, downloadPng, downloadSvg, downloadBlob,
|
|
192
|
+
serializeSvg, prepareSvgMarkup, imageSize, imageFileName, findDrawable, MAX_IMAGE_PIXELS,
|
|
193
|
+
} from './exportImage.js';
|
|
194
|
+
// Same reasoning as exportImage: every DOM reference sits inside a function
|
|
195
|
+
// body, so loading this under node does nothing.
|
|
196
|
+
import {
|
|
197
|
+
showPropertiesDialog, showDiagramPropertiesDialog,
|
|
198
|
+
propertyRowsFor, coerceFieldValue, derivedRefreshPlan, LAYER_CHOICES, NODE_SECTIONS,
|
|
199
|
+
} from './propertiesDialog.js';
|
|
200
|
+
|
|
201
|
+
// NOTE: `src/workspace.js` is deliberately NOT re-exported here. It subclasses
|
|
202
|
+
// HTMLElement at module scope, so merely importing it throws outside a browser
|
|
203
|
+
// — and this module is the node entry point the test suite loads. It is a
|
|
204
|
+
// separate browser-only entry: import 'renderengine/src/workspace.js' directly.
|
|
205
|
+
|
|
206
|
+
export {
|
|
207
|
+
Mat4, OrbitCamera, CHART_CATALOG, getChartCatalogEntry, chartRegistry,
|
|
208
|
+
DIAGRAM_CATALOG, getDiagramCatalogEntry, diagramRegistry,
|
|
209
|
+
TREE_MODE_CATALOG, treeModeRegistry, getTreeModeCatalogEntry,
|
|
210
|
+
AXIS_KIND, ENCODING_CHANNEL, COORD_SYSTEM, COLOR_MODE, CAMERA_MODE,
|
|
211
|
+
Command, CompositeCommand, CommandHistory,
|
|
212
|
+
DiagramEditor, DiagramEditorState, Template, PolygonTemplate, RegularPolygonTemplate, RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate, ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate, EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate, OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate, CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate, PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate, TemplateCatalog, Palette, PropertiesPanel, MenuBar, MoveElementCommand, BringToFrontCommand, SendToBackCommand, RaiseCommand, LowerCommand, CreateTemplateCommand, EditPropertyCommand, ResizeCommand, RotateCommand, SetPropertiesCommand, ReorderCommand, DIAGRAM_TYPES, getTemplatesForDiagramType, getAllDiagramTypes, getDiagramTypesByGroup, EdgeMarker, ArrowMarker, DiamondMarker, CircleMarker, CrowsFootMarker, HollowDiamondMarker, HollowTriangleMarker, OpenArrowMarker, HalfArrowMarker, EDGE_MARKERS, getEdgeMarker, Edge, AddEdgeCommand, DeleteEdgeCommand, ReconnectEdgeCommand, EDGE_STYLE_PROPERTIES, LINE_ONLY_PROPERTIES, LINE_STYLE_DASH, EdgeStyleCascade, SetStyleCommand, EdgeStyleDialog, DiagramModel, DiagramView, ViewSetModel, ViewMenu, ViewPropertiesDialog, SettingsViewPropertiesDialog, DiagramViewPropertiesDialog, inheritableFields, readInheritableFields, LocalJsonStore, PopupMenu, ViewTabStrip, EditorShell, FormDialog, ToolbarItem, ToolbarButton, ToolbarMenu, ToolbarSelect, ToolbarCheckbox, ToolbarText, ToolbarSeparator, pickFile, reorderedZ, stackingOrder, MoveWaypointCommand, AddWaypointCommand, DeleteWaypointCommand,
|
|
213
|
+
ebnfAstToDiagram, parseEbnf,
|
|
214
|
+
createDataWindow, GraphDataWindow,
|
|
215
|
+
ArchimateNotation, ArchimateLayer, ArchimateElementType, ArchimateRelationshipType,
|
|
216
|
+
Glyph, GLYPHS, glyphFor, drawGlyph, glyphElement, glyphToSvg, GlyphTemplate, ArchimateElementTemplate, ArchimateEdge, ArchimateCanvasEditor, ArchimatePalette, KIND_PREFERENCE, ARCHIMATE_TYPE_MIME,
|
|
217
|
+
ArchimateModel, ArchimateView, CanvasView, PlaceItemsCommand, RemoveLineCommand, CanvasBoxKind, CANVAS_BOX_KINDS, canvasBoxKind, canvasBoxMenuItems, ArchiHint, HintPanel, EditHintCommand, hintMenuItems, CanvasPalette, CANVAS_BOX_MIME, ViewLineEdge, Arrangement, ItemGroup, BackgroundGroup, GroupCommand, UngroupCommand, LockCommand, BackgroundCommand, arrangeMenuItems, TemplateLibrary, CanvasTemplateLibrary, CanvasTemplateMenu, DEFAULT_CANVAS_TEMPLATES, canvasTemplate, EditViewItemCommand, viewItemChanges, ColorField, TextStyle, ArchimateModelChecker, ViewSetChecker, ViewFit, archiElementType, NEAREST_ARCHI_TYPE, ElementInstance, ViewObjectInstance, RelationshipInstance, ArchimateDiagramEditor,
|
|
218
|
+
ITIconNotation, ITElementType,
|
|
219
|
+
ArchimateValidator, ArchimateSpecializationSelector, ImageSelector, CheckboxTreeSelector, SettingsManager, settingsManager,
|
|
220
|
+
ModelTree, TreeModeRegistry,
|
|
221
|
+
RadialLayout, DerivedRelationships, EdgeGeometry,
|
|
222
|
+
buildModelTreePanel, buildAnalysisPanel, buildAnalysisData, buildTypeCategoryTree, buildPropertyTree, DiagramTabs,
|
|
223
|
+
DataModelNotation, DataModelLevel, NotationStyle,
|
|
224
|
+
exportElementsCSV, exportRelationshipsCSV, exportModelCSV,
|
|
225
|
+
importElementsCSV, importRelationshipsCSV, importModelCSV,
|
|
226
|
+
parseArchiModel, toArchimateModel, fromArchimateView, toArchiExport,
|
|
227
|
+
parseExchangeModel, toExchangeXML, EXCHANGE_NAMESPACE, EXCHANGE_TYPE_PROPERTY, EXCHANGE_VIEWPOINTS,
|
|
228
|
+
ModelFileFormat, modelFileMenuItems, importFile, ArchiFileFormat, ExchangeFileFormat, ArchimateCsvFileFormat, ARCHIMATE_FILE_FORMATS,
|
|
229
|
+
ShowElementCommand, MODEL_ELEMENT_MIME, TREE_LAYERS, treeLayerOf,
|
|
230
|
+
EmphasisRule, EMPHASIS_OPACITY, EMPHASIS_LEVELS, strongerEmphasis, HighlightRule, ViewpointRule, ElementFilterRule,
|
|
231
|
+
CatalogDialog, CustomViewpoints, CUSTOM_VIEWPOINTS_STORE, specializationCatalogDialog, elementTypeChoices,
|
|
232
|
+
parseArchiFile, toArchiFile, ARCHI_MODEL_ENTRY, ZipArchive,
|
|
233
|
+
copyImage, toPngBlob, downloadPng, downloadSvg, downloadBlob,
|
|
234
|
+
serializeSvg, prepareSvgMarkup, imageSize, imageFileName, findDrawable, MAX_IMAGE_PIXELS,
|
|
235
|
+
showPropertiesDialog, showDiagramPropertiesDialog, showGlobalSettingsDialog,
|
|
236
|
+
propertyRowsFor, coerceFieldValue, derivedRefreshPlan, LAYER_CHOICES, NODE_SECTIONS,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @class RenderEngine
|
|
241
|
+
* This class decides which renderer to use and provides a unified drawing API.
|
|
242
|
+
*/
|
|
243
|
+
export class RenderEngine {
|
|
244
|
+
/** Layout constants for graphDiagram's `shape: 'table'` node type. */
|
|
245
|
+
static TABLE_NODE = NodeGeometry.TABLE_NODE;
|
|
246
|
+
|
|
247
|
+
/** ArchiMate standard-alignment validator (relationship validity, layer
|
|
248
|
+
* consistency) — exposed so callers building ArchiMate authoring UIs can
|
|
249
|
+
* filter relationship-kind pickers without duplicating rules. */
|
|
250
|
+
static ArchimateValidator = ArchimateValidator;
|
|
251
|
+
|
|
252
|
+
/** IT / infrastructure icon extension catalog — firewall, laptop, server,
|
|
253
|
+
* database, container, etc. These are not official ArchiMate types but are
|
|
254
|
+
* resolved by `archimateDiagram` and its construction UI as custom types
|
|
255
|
+
* with sensible layer/aspect/icon defaults. */
|
|
256
|
+
static ITIconNotation = ITIconNotation;
|
|
257
|
+
static ITElementType = ITElementType;
|
|
258
|
+
|
|
259
|
+
/** graphDiagram() node fields that are structural, not caller "extra data"
|
|
260
|
+
* — excluded from the auto-generated extra-data tooltip lines (see
|
|
261
|
+
* _nodeExtraDataLines). Table nodes have their own richer set since
|
|
262
|
+
* columns/rowCount/width are structural there, not extra data. */
|
|
263
|
+
static NODE_STRUCTURAL_KEYS = new Set([
|
|
264
|
+
'id', 'name', 'label', 'x', 'y', 'shape', 'color', 'group', 'size', 'radius', 'width', 'height',
|
|
265
|
+
'icon', 'iconUrl', 'iconShape', 'iconPosition', 'iconCorner', 'labelPosition', 'textAlign', 'verticalAlign',
|
|
266
|
+
'resizable', 'resizeCorner', 'cornerStyle', 'strokeColor', 'strokeWidth', 'href', 'properties', 'specialization', 'useDistinctShape',
|
|
267
|
+
// How its text is drawn (TextStyle, in the node's names) and Archi's class for a canvas box: look, not data.
|
|
268
|
+
'textColor', 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'textDecoration', 'archiVisualType',
|
|
269
|
+
]);
|
|
270
|
+
static TABLE_NODE_STRUCTURAL_KEYS = new Set(['id', 'name', 'label', 'x', 'y', 'shape', 'color', 'group', 'columns', 'rowCount', 'width', 'resizable', 'resizeCorner']);
|
|
271
|
+
/** graphDiagram() edge fields that are structural, not caller "extra
|
|
272
|
+
* data" — same idea as NODE_STRUCTURAL_KEYS, for edges (see the
|
|
273
|
+
* Properties-panel example in test/charts.html, which uses this to show
|
|
274
|
+
* only a caller's own custom fields as editable). */
|
|
275
|
+
static EDGE_STRUCTURAL_KEYS = new Set([
|
|
276
|
+
'source', 'target', 'label', 'sourceLabel', 'targetLabel', 'weight', 'color', 'dashed', 'lineStyle',
|
|
277
|
+
'arrowhead', 'arrowheadStart', 'routing', 'sourceSide', 'targetSide', 'waypoints', 'fromCol', 'toCol',
|
|
278
|
+
'kind', 'properties', 'dasharray', 'tooltip', '_archimateWarning',
|
|
279
|
+
]);
|
|
280
|
+
|
|
281
|
+
/** The ArchiMate 3.2 notation registry (layers, the official element
|
|
282
|
+
* type catalog, relationship notations) archimateDiagram is built on —
|
|
283
|
+
* see src/archimate.js, which owns all of it; this is just a
|
|
284
|
+
* convenient static so callers already holding a RenderEngine instance
|
|
285
|
+
* (`engine.constructor.ArchimateNotation`) don't need a second import,
|
|
286
|
+
* the same reason graphDiagram's own NODE_STRUCTURAL_KEYS etc. live
|
|
287
|
+
* here as statics. */
|
|
288
|
+
static ArchimateNotation = ArchimateNotation;
|
|
289
|
+
|
|
290
|
+
/** ArchiMate viewpoints catalog and helpers — see src/archimate.js. */
|
|
291
|
+
static ArchimateViewpoints = ArchimateViewpoints;
|
|
292
|
+
|
|
293
|
+
/** Editable specialization catalog for ArchiMate — user-defined
|
|
294
|
+
* specializations linked to base types with their own icon/colour.
|
|
295
|
+
* See src/archimateSpecializations.js. */
|
|
296
|
+
static ArchimateSpecializationRegistry = ArchimateSpecializationRegistry;
|
|
297
|
+
|
|
298
|
+
/** Model tree view mode registry (byLayer, byType, flat) — exposed so
|
|
299
|
+
* callers building model tree UIs can populate mode pickers from the
|
|
300
|
+
* registry rather than hardcoding options. See src/treeModeCatalog.js. */
|
|
301
|
+
static treeModeRegistry = treeModeRegistry;
|
|
302
|
+
|
|
303
|
+
/** The data-modelling notation registry (levels, notation styles,
|
|
304
|
+
* cardinality vocabulary) dataModelDiagram is built on — see
|
|
305
|
+
* src/datamodel.js. Same convenience-static reasoning as
|
|
306
|
+
* ArchimateNotation above. */
|
|
307
|
+
static DataModelNotation = DataModelNotation;
|
|
308
|
+
|
|
309
|
+
/** outlineChart()'s node fields that are structural, not caller "extra
|
|
310
|
+
* data" — same idea as NODE_STRUCTURAL_KEYS above, for the
|
|
311
|
+
* treemapChart/sunburstChart/outlineChart shared hierarchy shape. */
|
|
312
|
+
static HIERARCHY_STRUCTURAL_KEYS = new Set(['label', 'value', 'children']);
|
|
313
|
+
|
|
314
|
+
/** Archi native `.archimate` XML visual-object type constants. */
|
|
315
|
+
static ARCHI_VISUAL_OBJECTS = ARCHI_VISUAL_OBJECTS;
|
|
316
|
+
static ARCHI_VIEW_TYPES = ARCHI_VIEW_TYPES;
|
|
317
|
+
|
|
318
|
+
/** Parse an Archi native `.archimate` XML string and return renderengine
|
|
319
|
+
* inputs ready for `archimateDiagram()`: `{ elements, relationships, options }`.
|
|
320
|
+
* `viewId` selects a specific view; defaults to the first view. */
|
|
321
|
+
static importArchiXML(xml, viewId) {
|
|
322
|
+
const archiModel = parseArchiModel(xml);
|
|
323
|
+
return toRenderEngineModel(archiModel, viewId);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Serialize renderengine model data back to an Archi native `.archimate`
|
|
327
|
+
* XML string. `model` is `{ id?, name?, version?, elements[], relationships[] }`
|
|
328
|
+
* and `views` is an array of Archi view descriptors. */
|
|
329
|
+
static exportArchiXML(model, views) {
|
|
330
|
+
return toArchiXML(model, views);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Export model elements/relationships to CSV strings for spreadsheet editing.
|
|
334
|
+
* Returns `{ elementsCSV, relationshipsCSV }`. */
|
|
335
|
+
static exportModelCSV(model) {
|
|
336
|
+
return exportModelCSV(model);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Import model elements/relationships from CSV strings. Returns
|
|
340
|
+
* `{ elements, relationships }` ready for `archimateDiagram()`. */
|
|
341
|
+
static importModelCSV(elementsCSV, relationshipsCSV) {
|
|
342
|
+
return importModelCSV(elementsCSV, relationshipsCSV);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Layout constants for outlineChart()'s indented rows. */
|
|
346
|
+
static OUTLINE_ROW_H = 22;
|
|
347
|
+
static OUTLINE_INDENT_W = 18;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* The full list of chart types this engine supports, with a display
|
|
351
|
+
* label and the method to call for each — see chartCatalog.js. Also
|
|
352
|
+
* available as the standalone `CHART_CATALOG` export for consumers that
|
|
353
|
+
* only need the list (no RenderEngine instance) e.g. to populate a type
|
|
354
|
+
* picker before any query has run.
|
|
355
|
+
*/
|
|
356
|
+
static CHART_CATALOG = CHART_CATALOG;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Object-oriented registry of chart types. Consumers can register custom
|
|
360
|
+
* chart types at runtime via `RenderEngine.CHART_REGISTRY.register(entry)`.
|
|
361
|
+
*/
|
|
362
|
+
static CHART_REGISTRY = chartRegistry;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The full list of diagram types this engine supports, with the method to
|
|
366
|
+
* call for each — see diagramCatalog.js. Also available as the standalone
|
|
367
|
+
* `DIAGRAM_CATALOG` export.
|
|
368
|
+
*/
|
|
369
|
+
static DIAGRAM_CATALOG = DIAGRAM_CATALOG;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Object-oriented registry of diagram types. Consumers can register custom
|
|
373
|
+
* diagram types at runtime via `RenderEngine.DIAGRAM_REGISTRY.register(entry)`.
|
|
374
|
+
*/
|
|
375
|
+
static DIAGRAM_REGISTRY = diagramRegistry;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* `get` trap backing the constructor's chained-access Proxy. Only falls
|
|
379
|
+
* through to a container handle when `prop` is neither a real
|
|
380
|
+
* property/method NOR already-registered (via `register()`) — instance
|
|
381
|
+
* properties like `renderer` are conditionally assigned (never set at
|
|
382
|
+
* all in container-less test mode), so an unset *real* property and an
|
|
383
|
+
* unused container id are otherwise indistinguishable from `in target`
|
|
384
|
+
* alone; requiring registry membership avoids that collision, and costs
|
|
385
|
+
* nothing since `addDataPoint`/`addGraphNode`/`refreshChart` already
|
|
386
|
+
* no-op for an id that was never registered anyway.
|
|
387
|
+
* @private
|
|
388
|
+
*/
|
|
389
|
+
static _proxyHandler = {
|
|
390
|
+
get(target, prop, receiver) {
|
|
391
|
+
if (typeof prop === 'string' && !(prop in target) && target.registry && prop in target.registry) {
|
|
392
|
+
return target._containerHandle(prop);
|
|
393
|
+
}
|
|
394
|
+
return Reflect.get(target, prop, receiver);
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Renders `data` as chart type `key` into this engine's container —
|
|
400
|
+
* looks up the method from CHART_CATALOG so a consumer's type-dispatch
|
|
401
|
+
* code doesn't need its own key→method mapping (only the data-shaping,
|
|
402
|
+
* which is inherently consumer-specific — see each entry's `dataShape`).
|
|
403
|
+
* @param {string} key A CHART_CATALOG key, e.g. 'bar', 'heatmap'.
|
|
404
|
+
* @param {*} data Shaped per that entry's `dataShape`.
|
|
405
|
+
* @param {object} [options]
|
|
406
|
+
* @throws {Error} if `key` isn't in CHART_CATALOG.
|
|
407
|
+
*/
|
|
408
|
+
renderChartType(key, data, options = {}) {
|
|
409
|
+
const entry = RenderEngine.CHART_REGISTRY.get(key);
|
|
410
|
+
if (!entry) throw new Error(`Unknown chart type '${key}' — see RenderEngine.CHART_CATALOG for valid keys`);
|
|
411
|
+
return this[entry.method](data, options);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Renders a registered diagram type by key.
|
|
416
|
+
* @param {string} key A DIAGRAM_CATALOG key, e.g. 'graph', 'archimate'.
|
|
417
|
+
* @param {...*} args Passed through to the diagram method in the same order
|
|
418
|
+
* it expects (e.g. `nodes, edges, options` for 'graph').
|
|
419
|
+
* @throws {Error} if `key` isn't in DIAGRAM_CATALOG.
|
|
420
|
+
*/
|
|
421
|
+
renderDiagramType(key, ...args) {
|
|
422
|
+
const entry = RenderEngine.DIAGRAM_REGISTRY.get(key);
|
|
423
|
+
if (!entry) throw new Error(`Unknown diagram type '${key}' — see RenderEngine.DIAGRAM_CATALOG for valid keys`);
|
|
424
|
+
return this[entry.method](...args);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* @param {HTMLElement} container The container element for the chart.
|
|
429
|
+
* @param {object} [options]
|
|
430
|
+
* @param {'canvas' | 'svg'} [options.rendererType='canvas']
|
|
431
|
+
*/
|
|
432
|
+
constructor(container, options = {}) {
|
|
433
|
+
if (container) {
|
|
434
|
+
const { rendererType = 'canvas' } = options;
|
|
435
|
+
const width = container.clientWidth;
|
|
436
|
+
const height = container.clientHeight;
|
|
437
|
+
|
|
438
|
+
this.container = container; // Store container for event listeners
|
|
439
|
+
this.renderer = RendererFactory.create(rendererType, container, width, height);
|
|
440
|
+
} else {
|
|
441
|
+
// Allow initialization without a container for testing
|
|
442
|
+
this.options = options;
|
|
443
|
+
console.log('RenderEngine initialized in test mode.');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (this.container) {
|
|
447
|
+
this._interactions = new InteractionManager(this);
|
|
448
|
+
this._interactions.attach();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
this._containerHandles = new Map();
|
|
452
|
+
// Lets `register`/`refreshChart`/`addDataPoint`/`addGraphNode` be
|
|
453
|
+
// called either the normal way (`engine.addGraphNode(id, ...)`) or
|
|
454
|
+
// scoped to a container id (`engine[id].addGraphNode(...)` — or
|
|
455
|
+
// `engine.someId.addGraphNode(...)` when `id` happens to be a valid
|
|
456
|
+
// identifier with no hyphens). Only property names that aren't
|
|
457
|
+
// already real properties/methods fall through to a container
|
|
458
|
+
// handle, so this never shadows anything on the class itself.
|
|
459
|
+
return new Proxy(this, RenderEngine._proxyHandler);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Returns (creating and caching on first use) the chained-access handle
|
|
464
|
+
* for container `id` — see the constructor's Proxy comment.
|
|
465
|
+
* @private
|
|
466
|
+
*/
|
|
467
|
+
_containerHandle(id) {
|
|
468
|
+
if (!this._containerHandles.has(id)) {
|
|
469
|
+
const engine = this;
|
|
470
|
+
this._containerHandles.set(id, {
|
|
471
|
+
register: (type, options) => engine.register(id, type, options),
|
|
472
|
+
refreshChart: () => engine.refreshChart(id),
|
|
473
|
+
addDataPoint: (pointOrFactory, opts) => engine.addDataPoint(id, pointOrFactory, opts),
|
|
474
|
+
addGraphNode: (node, newEdges, opts) => engine.addGraphNode(id, node, newEdges, opts),
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
return this._containerHandles.get(id);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Convenience getter: exposes the current active diagram's _graph property
|
|
482
|
+
* (GraphDiagram uses this to store node positions, edges, and other state).
|
|
483
|
+
* Returns undefined if no active diagram or if the active diagram doesn't have _graph.
|
|
484
|
+
* @private
|
|
485
|
+
*/
|
|
486
|
+
get _graph() {
|
|
487
|
+
return this._activeDiagram?._graph;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Default plot-area box for the bar/line/stackedBar/bubble/barChart3D
|
|
492
|
+
* family, sized to the actual renderer dimensions instead of a fixed
|
|
493
|
+
* constant. Reserves margin below the plot for the x-axis tick labels
|
|
494
|
+
* (drawn at y+height+20) and axis label (drawn at y+height+45) so they
|
|
495
|
+
* stay inside the container instead of being clipped (Canvas) or
|
|
496
|
+
* overflowing it (SVG) — a fixed height that assumes a specific
|
|
497
|
+
* container size cuts these off in any container it wasn't tuned for.
|
|
498
|
+
* @private
|
|
499
|
+
*/
|
|
500
|
+
_defaultChartBox() {
|
|
501
|
+
return ChartBox.fromRenderer(this.renderer);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Draws a list of shape descriptors or Shape instances. Each shape is
|
|
506
|
+
* dispatched to the renderer through a concrete Shape subclass, so this
|
|
507
|
+
* method no longer switches on `shape.type`.
|
|
508
|
+
* @param {Array<object|Shape>} shapes An array of shape descriptions to draw.
|
|
509
|
+
*/
|
|
510
|
+
draw(shapes) {
|
|
511
|
+
if (!this.renderer) return;
|
|
512
|
+
this.renderer.clear();
|
|
513
|
+
for (const shape of shapes) {
|
|
514
|
+
const s = ShapeFactory.fromObject(shape);
|
|
515
|
+
s.render(this.renderer);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Render a Chart subclass instance and make it the active chart for event
|
|
521
|
+
* dispatch. This replaces the old pattern of switching on `_chartState.type`
|
|
522
|
+
* in the global click listener.
|
|
523
|
+
* @private
|
|
524
|
+
*/
|
|
525
|
+
_renderChart(chart) {
|
|
526
|
+
this._activeChart = chart;
|
|
527
|
+
this._activeDiagram = null;
|
|
528
|
+
chart.render();
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Render a Diagram subclass instance and make it the active diagram for
|
|
533
|
+
* event dispatch.
|
|
534
|
+
* @private
|
|
535
|
+
*/
|
|
536
|
+
_renderDiagram(diagram) {
|
|
537
|
+
this._activeDiagram = diagram;
|
|
538
|
+
this._activeChart = null;
|
|
539
|
+
diagram.render();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Renders a simple bar chart.
|
|
544
|
+
* @param {Array<{label: string, value: number}>} data The data to chart.
|
|
545
|
+
* @param {object} [options] Charting options.
|
|
546
|
+
* @param {number} [options.x=50] The x-coordinate of the chart origin.
|
|
547
|
+
* @param {number} [options.y=50] The y-coordinate of the chart origin.
|
|
548
|
+
* @param {number} [options.width=500] The width of the chart area.
|
|
549
|
+
* @param {number} [options.height=300] The height of the chart area.
|
|
550
|
+
* @param {string} [options.barColor='steelblue'] The color of the bars.
|
|
551
|
+
* @param {string} [options.title] The chart title.
|
|
552
|
+
* @param {string} [options.xAxisLabel] The label for the x-axis.
|
|
553
|
+
* @param {string} [options.yAxisLabel] The label for the y-axis.
|
|
554
|
+
* @param {string[]} [options.colors] Array of colors for the bars.
|
|
555
|
+
* @param {boolean} [options.showLegend=false] Whether to show the legend.
|
|
556
|
+
*/
|
|
557
|
+
barChart(data, options = {}) {
|
|
558
|
+
if (!this.renderer) return;
|
|
559
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
560
|
+
// Store state for re-rendering and interactivity
|
|
561
|
+
this._chartState = { type: 'bar', data, options };
|
|
562
|
+
this._barDataFilter = this._barDataFilter || new DataFilter();
|
|
563
|
+
this._renderChart(new BarChart(this.renderer, data, options, this._barDataFilter));
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Renders a grouped (multi-set) bar chart.
|
|
569
|
+
* @param {Array<{name: string, values: Array<{label: string, value: number}>}>} data The data to chart.
|
|
570
|
+
* @param {object} [options] Charting options.
|
|
571
|
+
* @param {string[]} [options.colors] Series palette.
|
|
572
|
+
* @param {boolean} [options.legend=false] Show a series legend.
|
|
573
|
+
* @param {number} [options.groupPadding=0.2] Gap between category clusters as a fraction of cluster width.
|
|
574
|
+
* @param {number} [options.barPadding=0.05] Gap between bars inside a cluster as a fraction of bar width.
|
|
575
|
+
*/
|
|
576
|
+
groupedBarChart(data, options = {}) {
|
|
577
|
+
if (!this.renderer) return;
|
|
578
|
+
this._chart3D = false;
|
|
579
|
+
this._chartState = { type: 'groupedBar', data, options };
|
|
580
|
+
this._renderChart(new GroupedBarChart(this.renderer, data, options));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Renders a simple line chart.
|
|
585
|
+
* @param {Array<{label: string, value: number}>} data The data to chart.
|
|
586
|
+
* @param {object} [options] Charting options.
|
|
587
|
+
*/
|
|
588
|
+
lineChart(data, options = {}) {
|
|
589
|
+
if (!this.renderer) return;
|
|
590
|
+
this._chart3D = false;
|
|
591
|
+
this._chartState = { type: 'line', data, options };
|
|
592
|
+
this._renderChart(new LineChart(this.renderer, data, options));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Renders a multi-line chart where each line is a tapering filled ribbon
|
|
597
|
+
* ("pipe") instead of a uniform-width stroke — width at each point tracks
|
|
598
|
+
* a metric field. Neither SVG stroke-width nor Canvas lineWidth can vary
|
|
599
|
+
* along a single stroke, so this is drawn as a filled polygon: each
|
|
600
|
+
* point is offset perpendicular to the local line direction by
|
|
601
|
+
* ±width/2, and the upper/lower boundary points are connected into one
|
|
602
|
+
* closed path — the same `renderer.path()`/M...L...Z mechanism used
|
|
603
|
+
* everywhere else in this file.
|
|
604
|
+
* @param {Array<{label:string,value:number,metric?:number}>|Array<{name:string,values:Array<{label,value,metric}>}>} data
|
|
605
|
+
* @param {object} [options]
|
|
606
|
+
* @param {string} [options.metricCol='metric'] Field name driving pipe width.
|
|
607
|
+
* @param {number} [options.minWidth=4]
|
|
608
|
+
* @param {number} [options.maxWidth=28]
|
|
609
|
+
* @param {string[]} [options.colors]
|
|
610
|
+
* @param {boolean} [options.legend=false]
|
|
611
|
+
*/
|
|
612
|
+
pipeChart(data, options = {}) {
|
|
613
|
+
if (!this.renderer) return;
|
|
614
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
615
|
+
this._chartState = { type: 'pipe', data, options };
|
|
616
|
+
this._renderChart(new PipeChart(this.renderer, data, options));
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Renders a stacked bar chart.
|
|
621
|
+
* @param {Array<object>} data The data to chart. e.g. [{ label: 'Jan', values: [10, 20] }]
|
|
622
|
+
* @param {object} [options] Charting options.
|
|
623
|
+
*/
|
|
624
|
+
stackedBarChart(data, options = {}) {
|
|
625
|
+
if (!this.renderer) return;
|
|
626
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
627
|
+
this._chartState = { type: 'stackedBar', data, options };
|
|
628
|
+
this._renderChart(new StackedBarChart(this.renderer, data, options));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// ── Color-scale helpers (heatmap) ───────────────────────────────────────
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Maps a value within [vMin, vMax] to a color along an N-stop scale
|
|
635
|
+
* (linear interpolation between adjacent stops via ColorPalette).
|
|
636
|
+
* @private
|
|
637
|
+
*/
|
|
638
|
+
_scaleColor(stops, vMin, vMax, v) {
|
|
639
|
+
return ColorPalette.scaleColor(stops, vMin, vMax, v);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** Perceptual-ish luma check, used to pick readable text color on a colored cell/slice. @private */
|
|
643
|
+
_isDarkColor(hex) {
|
|
644
|
+
return ColorPalette.isDark(hex);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Renders a heatmap: a grid of cells, one per (x, y) category pair,
|
|
649
|
+
* shaded by `value` along a low→high color scale — the natural chart for
|
|
650
|
+
* a correlation matrix or any "intensity across two dimensions" dataset.
|
|
651
|
+
* @param {Array<{x: string, y: string, value: number}>} data
|
|
652
|
+
* @param {object} [options]
|
|
653
|
+
* @param {string[]} [options.xLabels] explicit x-axis category order (default: order of first appearance in data)
|
|
654
|
+
* @param {string[]} [options.yLabels] explicit y-axis category order (default: order of first appearance in data)
|
|
655
|
+
* @param {string[]} [options.colorScale=['#f7fbff','#4292c6','#08306b']] low-to-high color stops (2+ hex colors)
|
|
656
|
+
* @param {boolean} [options.showValues=false] draw the numeric value inside each cell
|
|
657
|
+
* @param {string} [options.title]
|
|
658
|
+
*/
|
|
659
|
+
heatmap(data, options = {}) {
|
|
660
|
+
if (!this.renderer) return;
|
|
661
|
+
this._chart3D = false;
|
|
662
|
+
this._chartState = { type: 'heatmap', data, options };
|
|
663
|
+
this._renderChart(new HeatmapChart(this.renderer, data, options));
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Renders a calendar heatmap: a year-at-a-glance grid of day cells, each
|
|
668
|
+
* shaded by its value — the classic GitHub-contribution-graph layout.
|
|
669
|
+
* @param {Array<{date: string|Date, value: number}>} data
|
|
670
|
+
* @param {object} [options]
|
|
671
|
+
* @param {number} [options.year] year to display (default current year)
|
|
672
|
+
* @param {string|Date} [options.startDate] explicit inclusive start
|
|
673
|
+
* @param {string|Date} [options.endDate] explicit inclusive end
|
|
674
|
+
* @param {string[]} [options.colorScale=['#ebedf0','#9be9a8','#40c463','#30a14e','#216e39']] low→high stops
|
|
675
|
+
* @param {string} [options.emptyColor='#ebedf0'] color for days with no data
|
|
676
|
+
* @param {number} [options.cellSize] pixel size of one day cell (auto if omitted)
|
|
677
|
+
* @param {number} [options.cellGap=2] gap between cells
|
|
678
|
+
* @param {boolean} [options.showValues=false] draw the numeric value inside each cell
|
|
679
|
+
* @param {boolean} [options.monthLabels=true] show month names above the grid
|
|
680
|
+
* @param {boolean} [options.weekdayLabels=true] show weekday names beside rows
|
|
681
|
+
* @param {0|1} [options.weekStart=0] 0 = Sunday, 1 = Monday
|
|
682
|
+
* @param {string} [options.title]
|
|
683
|
+
*/
|
|
684
|
+
calendarHeatmap(data, options = {}) {
|
|
685
|
+
if (!this.renderer) return;
|
|
686
|
+
this._chart3D = false;
|
|
687
|
+
this._chartState = { type: 'calendarHeatmap', data, options };
|
|
688
|
+
this._renderChart(new CalendarHeatmapChart(this.renderer, data, options));
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Renders a Gantt chart — horizontal bars on a time axis, one row per task.
|
|
693
|
+
* @param {Array<{label:string, start:string|Date|number, end:string|Date|number, progress?:number, category?:string, color?:string}>} data
|
|
694
|
+
* @param {object} [options]
|
|
695
|
+
* @param {number} [options.barHeight=24]
|
|
696
|
+
* @param {number} [options.barGap=12]
|
|
697
|
+
* @param {string[]} [options.colors] per-category palette
|
|
698
|
+
* @param {string} [options.axisLabel]
|
|
699
|
+
* @param {'auto'|'date'|'datetime'|'iso'} [options.dateFormat='auto']
|
|
700
|
+
* @param {boolean} [options.showProgress] default true if any task has progress
|
|
701
|
+
* @param {boolean} [options.showToday=true]
|
|
702
|
+
* @param {string} [options.title]
|
|
703
|
+
*/
|
|
704
|
+
ganttChart(data, options = {}) {
|
|
705
|
+
if (!this.renderer) return;
|
|
706
|
+
this._chart3D = false;
|
|
707
|
+
this._chartState = { type: 'gantt', data, options };
|
|
708
|
+
this._renderChart(new GanttChart(this.renderer, data, options));
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Renders a stream graph — a stacked area chart with a centered baseline,
|
|
713
|
+
* producing a flowing shape across time.
|
|
714
|
+
* @param {Array<{name: string, values: Array<{label: string, value: number}>}>} data
|
|
715
|
+
* @param {object} [options]
|
|
716
|
+
* @param {string[]} [options.colors]
|
|
717
|
+
* @param {boolean} [options.legend=true]
|
|
718
|
+
* @param {boolean} [options.smooth=true] cubic-bezier smoothing
|
|
719
|
+
* @param {number} [options.tension=0.5]
|
|
720
|
+
* @param {string} [options.title]
|
|
721
|
+
* @param {string} [options.xAxisLabel]
|
|
722
|
+
* @param {string} [options.yAxisLabel]
|
|
723
|
+
*/
|
|
724
|
+
streamGraph(data, options = {}) {
|
|
725
|
+
if (!this.renderer) return;
|
|
726
|
+
this._chart3D = false;
|
|
727
|
+
this._chartState = { type: 'streamGraph', data, options };
|
|
728
|
+
this._renderChart(new StreamGraphChart(this.renderer, data, options));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Renders a radial bar chart (circular barplot) — bars radiating from a
|
|
733
|
+
* central point, one angular slice per category.
|
|
734
|
+
* @param {Array<{label:string, value:number}>} data
|
|
735
|
+
* @param {object} [options]
|
|
736
|
+
* @param {number} [options.cx]
|
|
737
|
+
* @param {number} [options.cy]
|
|
738
|
+
* @param {number} [options.radius]
|
|
739
|
+
* @param {number} [options.innerRadius]
|
|
740
|
+
* @param {number} [options.padAngle=0.02]
|
|
741
|
+
* @param {number} [options.startAngle=0]
|
|
742
|
+
* @param {number} [options.endAngle=2π]
|
|
743
|
+
* @param {string[]} [options.colors]
|
|
744
|
+
* @param {boolean} [options.showValues=false]
|
|
745
|
+
* @param {boolean} [options.legend=true]
|
|
746
|
+
* @param {string} [options.title]
|
|
747
|
+
*/
|
|
748
|
+
radialBarChart(data, options = {}) {
|
|
749
|
+
if (!this.renderer) return;
|
|
750
|
+
this._chart3D = false;
|
|
751
|
+
this._chartState = { type: 'radialBar', data, options };
|
|
752
|
+
this._renderChart(new RadialBarChart(this.renderer, data, options));
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Renders a population pyramid — back-to-back horizontal bars comparing
|
|
757
|
+
* two numeric values per category (commonly male/female by age group).
|
|
758
|
+
* @param {Array<{label:string, left:number, right:number}>} data
|
|
759
|
+
* @param {object} [options]
|
|
760
|
+
* @param {string} [options.leftLabel='Left']
|
|
761
|
+
* @param {string} [options.rightLabel='Right']
|
|
762
|
+
* @param {string} [options.leftColor='#4f8ef7']
|
|
763
|
+
* @param {string} [options.rightColor='#e91e63']
|
|
764
|
+
* @param {boolean} [options.showValues=false]
|
|
765
|
+
* @param {string} [options.title]
|
|
766
|
+
* @param {string} [options.xAxisLabel]
|
|
767
|
+
* @param {string} [options.yAxisLabel]
|
|
768
|
+
*/
|
|
769
|
+
populationPyramid(data, options = {}) {
|
|
770
|
+
if (!this.renderer) return;
|
|
771
|
+
this._chart3D = false;
|
|
772
|
+
this._chartState = { type: 'populationPyramid', data, options };
|
|
773
|
+
this._renderChart(new PopulationPyramidChart(this.renderer, data, options));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Renders a Sankey diagram — flow links whose width is proportional to the
|
|
778
|
+
* value they carry, connecting source nodes to target nodes.
|
|
779
|
+
* @param {object} data
|
|
780
|
+
* @param {Array<{id:string, label?:string, color?:string}>} [data.nodes]
|
|
781
|
+
* @param {Array<{source:string, target:string, value:number}>} data.links
|
|
782
|
+
* @param {object} [options]
|
|
783
|
+
* @param {number} [options.nodeWidth=16]
|
|
784
|
+
* @param {number} [options.nodePadding=16]
|
|
785
|
+
* @param {number} [options.linkOpacity=0.45]
|
|
786
|
+
* @param {string[]} [options.colors]
|
|
787
|
+
* @param {string} [options.title]
|
|
788
|
+
*/
|
|
789
|
+
sankeyDiagram(data, options = {}) {
|
|
790
|
+
if (!this.renderer) return;
|
|
791
|
+
this._chart3D = false;
|
|
792
|
+
this._chartState = { type: 'sankey', data, options };
|
|
793
|
+
this._renderChart(new SankeyDiagram(this.renderer, data, options));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Renders a chord diagram — nodes arranged around a circle, with curved
|
|
798
|
+
* links (chords) whose thickness is proportional to their value.
|
|
799
|
+
* @param {object} data
|
|
800
|
+
* @param {Array<{id:string, label?:string, color?:string}>} [data.nodes]
|
|
801
|
+
* @param {Array<{source:string, target:string, value:number}>} data.links
|
|
802
|
+
* @param {object} [options]
|
|
803
|
+
* @param {number} [options.radius]
|
|
804
|
+
* @param {number} [options.innerRadius]
|
|
805
|
+
* @param {number} [options.padAngle=0.04]
|
|
806
|
+
* @param {string[]} [options.colors]
|
|
807
|
+
* @param {string} [options.title]
|
|
808
|
+
*/
|
|
809
|
+
chordDiagram(data, options = {}) {
|
|
810
|
+
if (!this.renderer) return;
|
|
811
|
+
this._chart3D = false;
|
|
812
|
+
this._chartState = { type: 'chord', data, options };
|
|
813
|
+
this._renderChart(new ChordDiagram(this.renderer, data, options));
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Renders a word cloud — words sized by value and laid out with a simple
|
|
818
|
+
* spiral placement that avoids overlaps.
|
|
819
|
+
* @param {Array<{text:string, value:number}>} data
|
|
820
|
+
* @param {object} [options]
|
|
821
|
+
* @param {string} [options.title]
|
|
822
|
+
* @param {string[]} [options.colors]
|
|
823
|
+
* @param {number} [options.minFontSize=12]
|
|
824
|
+
* @param {number} [options.maxFontSize=48]
|
|
825
|
+
* @param {string} [options.fontFamily='sans-serif']
|
|
826
|
+
* @param {number} [options.padding=4]
|
|
827
|
+
*/
|
|
828
|
+
wordCloud(data, options = {}) {
|
|
829
|
+
if (!this.renderer) return;
|
|
830
|
+
this._chart3D = false;
|
|
831
|
+
this._chartState = { type: 'wordCloud', data, options };
|
|
832
|
+
this._renderChart(new WordCloudChart(this.renderer, data, options));
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Renders a parallel coordinates plot — one vertical axis per dimension,
|
|
837
|
+
* with each data row drawn as a polyline connecting its values.
|
|
838
|
+
* @param {Array<object>} data
|
|
839
|
+
* @param {object} [options]
|
|
840
|
+
* @param {string[]} [options.dimensions]
|
|
841
|
+
* @param {string[]} [options.colors]
|
|
842
|
+
* @param {number} [options.lineWidth=1.5]
|
|
843
|
+
* @param {number} [options.lineOpacity=0.6]
|
|
844
|
+
* @param {boolean} [options.showAxes=true]
|
|
845
|
+
* @param {boolean} [options.showLegend]
|
|
846
|
+
* @param {string} [options.title]
|
|
847
|
+
*/
|
|
848
|
+
parallelCoordinates(data, options = {}) {
|
|
849
|
+
if (!this.renderer) return;
|
|
850
|
+
this._chart3D = false;
|
|
851
|
+
this._chartState = { type: 'parallelCoordinates', data, options };
|
|
852
|
+
this._renderChart(new ParallelCoordinatesChart(this.renderer, data, options));
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Renders a simple geospatial map — equirectangular projection of
|
|
857
|
+
* GeoJSON-like Polygon/MultiPolygon features with optional choropleth
|
|
858
|
+
* fills and bubble markers.
|
|
859
|
+
* @param {object} data
|
|
860
|
+
* @param {Array<object>} data.features
|
|
861
|
+
* @param {Array<{lat:number, lon:number, value:number}>} [data.bubbles]
|
|
862
|
+
* @param {object} [options]
|
|
863
|
+
* @param {[number,number]} [options.center]
|
|
864
|
+
* @param {number} [options.scale]
|
|
865
|
+
* @param {string[]} [options.colorScale]
|
|
866
|
+
* @param {number} [options.bubbleMaxRadius=20]
|
|
867
|
+
* @param {string} [options.bubbleColor]
|
|
868
|
+
* @param {string} [options.title]
|
|
869
|
+
*/
|
|
870
|
+
geoMap(data, options = {}) {
|
|
871
|
+
if (!this.renderer) return;
|
|
872
|
+
this._chart3D = false;
|
|
873
|
+
this._chartState = { type: 'geoMap', data, options };
|
|
874
|
+
this._renderChart(new GeoMapChart(this.renderer, data, options));
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Renders a candlestick (OHLC) chart — one wick+body glyph per time
|
|
879
|
+
* step. The body is colored by direction (close >= open -> up-color,
|
|
880
|
+
* else down-color); the wick spans high-low, the body spans open-close.
|
|
881
|
+
* Optional `markers` overlay point-in-time annotations (e.g. dividend
|
|
882
|
+
* ex-dates, earnings dates) as small hover-tooltip circles above the
|
|
883
|
+
* chart — a click-to-expand version of this would need the same global
|
|
884
|
+
* hitTest-based click wiring `graphDiagram`'s `onNodeClick` uses, out of
|
|
885
|
+
* scope for this pass.
|
|
886
|
+
* @param {Array<{label:string, open:number, high:number, low:number, close:number}>} data
|
|
887
|
+
* @param {object} [options]
|
|
888
|
+
* @param {string} [options.upColor='#26a69a']
|
|
889
|
+
* @param {string} [options.downColor='#ef5350']
|
|
890
|
+
* @param {Array<{label:string, tooltip?:string}>} [options.markers]
|
|
891
|
+
* @param {string} [options.title]
|
|
892
|
+
*/
|
|
893
|
+
candlestick(data, options = {}) {
|
|
894
|
+
if (!this.renderer) return;
|
|
895
|
+
this._chart3D = false;
|
|
896
|
+
this._chartState = { type: 'candlestick', data, options };
|
|
897
|
+
this._renderChart(new CandlestickChart(this.renderer, data, options));
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Builds a path `d` string for a pie slice (rInner=0) or a doughnut/
|
|
902
|
+
* annular slice (rInner>0) spanning startAngle..endAngle (radians).
|
|
903
|
+
* @private
|
|
904
|
+
*/
|
|
905
|
+
_donutSlicePath(cx, cy, rOuter, rInner, startAngle, endAngle) {
|
|
906
|
+
return ShapePaths.donutSlicePath(cx, cy, rOuter, rInner, startAngle, endAngle);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Renders a pie chart, or a doughnut chart when `innerRadiusRatio > 0`.
|
|
911
|
+
* @param {Array<{label:string, value:number}>} data
|
|
912
|
+
* @param {object} [options]
|
|
913
|
+
* @param {number} [options.innerRadiusRatio=0] 0 = pie; e.g. 0.6 = doughnut
|
|
914
|
+
* @param {string[]} [options.colors]
|
|
915
|
+
* @param {boolean} [options.legend=true]
|
|
916
|
+
* @param {string} [options.title]
|
|
917
|
+
*/
|
|
918
|
+
pieChart(data, options = {}) {
|
|
919
|
+
if (!this.renderer) return;
|
|
920
|
+
this._chart3D = false;
|
|
921
|
+
this._chartState = { type: 'pie', data, options };
|
|
922
|
+
this._renderChart(new PieChart(this.renderer, data, options));
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Renders a doughnut chart — a pie chart with a default inner radius.
|
|
927
|
+
* @param {Array<{label: string, value: number}>} data The data to chart.
|
|
928
|
+
* @param {object} [options] Same options as pieChart; innerRadiusRatio defaults to 0.5.
|
|
929
|
+
*/
|
|
930
|
+
donutChart(data, options = {}) {
|
|
931
|
+
if (!this.renderer) return;
|
|
932
|
+
this._chart3D = false;
|
|
933
|
+
this._chartState = { type: 'donut', data, options };
|
|
934
|
+
this._renderChart(new DonutChart(this.renderer, data, options));
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Renders an area chart — a line chart with the region below each series
|
|
939
|
+
* filled down to the baseline (or, with `stacked:true`, down to the
|
|
940
|
+
* previous series' band) to emphasize magnitude/cumulative volume rather
|
|
941
|
+
* than just trend shape.
|
|
942
|
+
* @param {Array<{label,value}>|Array<{name,values:Array<{label,value}>}>} data
|
|
943
|
+
* @param {object} [options]
|
|
944
|
+
* @param {boolean} [options.stacked=false]
|
|
945
|
+
* @param {string[]} [options.colors]
|
|
946
|
+
* @param {boolean} [options.legend=false]
|
|
947
|
+
*/
|
|
948
|
+
areaChart(data, options = {}) {
|
|
949
|
+
if (!this.renderer) return;
|
|
950
|
+
this._chart3D = false;
|
|
951
|
+
this._chartState = { type: 'area', data, options };
|
|
952
|
+
this._renderChart(new AreaChart(this.renderer, data, options));
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Renders a stacked area chart. Convenience wrapper around areaChart with
|
|
957
|
+
* `stacked: true`; accepts the same multi-series data shape.
|
|
958
|
+
* @param {Array<{name: string, values: Array<{label: string, value: number}>}>} data
|
|
959
|
+
* @param {object} [options]
|
|
960
|
+
*/
|
|
961
|
+
stackedAreaChart(data, options = {}) {
|
|
962
|
+
if (!this.renderer) return;
|
|
963
|
+
this._chart3D = false;
|
|
964
|
+
this._chartState = { type: 'stackedArea', data, options };
|
|
965
|
+
this._renderChart(new StackedAreaChart(this.renderer, data, options));
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Computes evenly-spaced bin edges/counts for a raw numeric sample —
|
|
970
|
+
* pure data transform, no rendering, DOM-free and unit-testable (same
|
|
971
|
+
* convention as `_tableNodeSize`/`_tableColumnY`).
|
|
972
|
+
* @private
|
|
973
|
+
*/
|
|
974
|
+
_histogramBins(values, binCount) {
|
|
975
|
+
return Statistics.histogramBins(values, binCount);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Renders a histogram — frequency distribution of a raw numeric sample,
|
|
980
|
+
* binned into `binCount` evenly-spaced buckets (default: a Sturges'-rule
|
|
981
|
+
* estimate, ⌈log2(n)+1⌉, clamped to a sane 5-20 range).
|
|
982
|
+
* @param {number[]} values raw numeric sample (not pre-binned)
|
|
983
|
+
* @param {object} [options]
|
|
984
|
+
* @param {number} [options.binCount]
|
|
985
|
+
* @param {string} [options.barColor='steelblue']
|
|
986
|
+
*/
|
|
987
|
+
histogram(values, options = {}) {
|
|
988
|
+
if (!this.renderer) return;
|
|
989
|
+
this._chart3D = false;
|
|
990
|
+
this._chartState = { type: 'histogram', data: values, options };
|
|
991
|
+
this._renderChart(new HistogramChart(this.renderer, values, options));
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* Inverse standard-normal CDF (the probit function) via Peter Acklam's
|
|
996
|
+
* well-known rational approximation (~1.15e-9 relative accuracy) — the
|
|
997
|
+
* "reference-distribution quantile function" qqPlot needs. Pure,
|
|
998
|
+
* DOM-free, unit-testable against known reference values.
|
|
999
|
+
* @private
|
|
1000
|
+
*/
|
|
1001
|
+
_normalQuantile(p) {
|
|
1002
|
+
return Statistics.normalQuantile(p);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Pairs each sorted sample value with a theoretical normal quantile at
|
|
1007
|
+
* the standard `(i - 0.5)/n` plotting position — the data qqPlot
|
|
1008
|
+
* scatters. Pure, DOM-free, unit-testable. Both fields come out
|
|
1009
|
+
* already ascending (sample is sorted; _normalQuantile is monotonic).
|
|
1010
|
+
* @returns {Array<{theoretical: number, sample: number}>}
|
|
1011
|
+
* @private
|
|
1012
|
+
*/
|
|
1013
|
+
_qqPoints(values) {
|
|
1014
|
+
return Statistics.qqPoints(values);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* `[{x: values[i-lag], y: values[i]}]` for i = lag..length-1 — the data
|
|
1019
|
+
* lagPlot scatters, to visually check autocorrelation. Pure, DOM-free,
|
|
1020
|
+
* unit-testable.
|
|
1021
|
+
* @private
|
|
1022
|
+
*/
|
|
1023
|
+
_lagPairs(values, lag = 1) {
|
|
1024
|
+
return Statistics.lagPairs(values, lag);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Linear-interpolation quantile (the common "R-7"/PERCENTILE.INC method)
|
|
1029
|
+
* over an already-sorted numeric array. Pure, DOM-free, unit-testable.
|
|
1030
|
+
* @private
|
|
1031
|
+
*/
|
|
1032
|
+
_quantile(sorted, p) {
|
|
1033
|
+
return Statistics.quantile(sorted, p);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Computes box-and-whisker summary stats for a raw numeric sample:
|
|
1038
|
+
* quartiles, whiskers clamped to the standard 1.5×IQR Tukey fences (and
|
|
1039
|
+
* to the actual data range), and anything beyond the fences as an
|
|
1040
|
+
* outlier. Pure, DOM-free, unit-testable.
|
|
1041
|
+
* @private
|
|
1042
|
+
*/
|
|
1043
|
+
_boxPlotStats(values) {
|
|
1044
|
+
return Statistics.boxPlotStats(values);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/** Population standard deviation. Pure, DOM-free, unit-testable. @private */
|
|
1048
|
+
_stdev(values) {
|
|
1049
|
+
return Statistics.stdev(values);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Gaussian-kernel density estimate — a smoothed distribution curve from
|
|
1054
|
+
* a raw numeric sample, the shared math core behind densityChart and
|
|
1055
|
+
* violinChart. `bandwidth` defaults via Silverman's rule of thumb
|
|
1056
|
+
* (`1.06 * stdev * n^(-1/5)`, the standard heuristic, not bespoke);
|
|
1057
|
+
* `min`/`max` default to 3 bandwidths past the data's own extremes so
|
|
1058
|
+
* the curve tapers to ~0 rather than being cut off mid-slope. Pure,
|
|
1059
|
+
* DOM-free, unit-testable.
|
|
1060
|
+
* @returns {Array<{x: number, density: number}>}
|
|
1061
|
+
* @private
|
|
1062
|
+
*/
|
|
1063
|
+
_kernelDensityEstimate(values, options = {}) {
|
|
1064
|
+
return Statistics.kernelDensityEstimate(values, options);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Renders a box-and-whisker plot — one box per group, summarizing a raw
|
|
1069
|
+
* numeric sample's spread (median, quartiles, whiskers, outliers)
|
|
1070
|
+
* instead of plotting every point.
|
|
1071
|
+
* @param {Array<{label:string, values:number[]}>} data
|
|
1072
|
+
* @param {object} [options]
|
|
1073
|
+
* @param {string} [options.boxColor='#5470c6']
|
|
1074
|
+
*/
|
|
1075
|
+
boxPlot(data, options = {}) {
|
|
1076
|
+
if (!this.renderer) return;
|
|
1077
|
+
this._chart3D = false;
|
|
1078
|
+
this._chartState = { type: 'boxPlot', data, options };
|
|
1079
|
+
this._renderChart(new BoxPlotChart(this.renderer, data, options));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Renders overlaid distribution curves — one Gaussian-KDE curve per
|
|
1084
|
+
* group (_kernelDensityEstimate), sharing one value-axis so shapes are
|
|
1085
|
+
* visually comparable. Same top-level data shape as boxPlot (a caller
|
|
1086
|
+
* can hand the identical dataset to either) and the same multi-series
|
|
1087
|
+
* fill+topline+legend rendering pattern as areaChart, just with the
|
|
1088
|
+
* curve computed from a raw sample instead of literal category/value
|
|
1089
|
+
* points. Y is relative density (curve shape only) — no meaningful
|
|
1090
|
+
* absolute-number axis, so no numeric y-tick labels, same spirit as
|
|
1091
|
+
* radarChart's unlabeled radial scale.
|
|
1092
|
+
* @param {Array<{label:string, values:number[]}>} data
|
|
1093
|
+
* @param {object} [options]
|
|
1094
|
+
* @param {string[]} [options.colors]
|
|
1095
|
+
* @param {boolean} [options.legend=true]
|
|
1096
|
+
*/
|
|
1097
|
+
densityChart(data, options = {}) {
|
|
1098
|
+
if (!this.renderer) return;
|
|
1099
|
+
this._chart3D = false;
|
|
1100
|
+
this._chartState = { type: 'density', data, options };
|
|
1101
|
+
this._renderChart(new DensityChart(this.renderer, data, options));
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Renders a violin plot — one mirrored Gaussian-KDE silhouette per
|
|
1106
|
+
* group in its own x-slot, the same per-group layout math as boxPlot
|
|
1107
|
+
* (same top-level data shape too — a caller can hand the identical
|
|
1108
|
+
* dataset to either). Unlike boxPlot's single flat `boxColor`, each
|
|
1109
|
+
* violin gets its own palette color: a violin's shape is visually
|
|
1110
|
+
* larger/more prominent than a thin box glyph, so per-group color
|
|
1111
|
+
* materially helps distinguish many holdings at a glance (same
|
|
1112
|
+
* reasoning treemapChart/sunburstChart color per top-level group). A
|
|
1113
|
+
* thin median tick (reusing _quantile, like boxPlot's own) is drawn
|
|
1114
|
+
* for reference — no outlier dots, since showing the full distribution
|
|
1115
|
+
* shape including tails IS the point of a violin, unlike box plot's
|
|
1116
|
+
* separate Tukey-fence "outlier" concept.
|
|
1117
|
+
* @param {Array<{label:string, values:number[]}>} data
|
|
1118
|
+
* @param {object} [options]
|
|
1119
|
+
* @param {string[]} [options.colors]
|
|
1120
|
+
*/
|
|
1121
|
+
violinChart(data, options = {}) {
|
|
1122
|
+
if (!this.renderer) return;
|
|
1123
|
+
this._chart3D = false;
|
|
1124
|
+
this._chartState = { type: 'violin', data, options };
|
|
1125
|
+
this._renderChart(new ViolinChart(this.renderer, data, options));
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/** Layout constant for treemapChart's branch-node header band. */
|
|
1129
|
+
static TREEMAP_HEADER_H = 16;
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* A leaf's own value, or a branch's value computed as the sum of its
|
|
1133
|
+
* children (recursively — never required from the caller). Non-finite
|
|
1134
|
+
* or non-positive values can't be allocated area/angle and are treated
|
|
1135
|
+
* as 0 (filtered out by the caller doing the layout, not here). Pure —
|
|
1136
|
+
* directly unit-testable. Shared by treemapChart and sunburstChart —
|
|
1137
|
+
* both consume the same `Array<{label, value?, children?}>` shape.
|
|
1138
|
+
* @private
|
|
1139
|
+
*/
|
|
1140
|
+
_hierarchyValue(node) {
|
|
1141
|
+
return Hierarchy.value(node);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Tree depth: 1 for a leaf, 1 + the deepest child for a branch. Pure.
|
|
1146
|
+
* Used by sunburstChart to size each ring so an arbitrarily deep tree
|
|
1147
|
+
* still fits within one fixed radius, rather than overflowing it.
|
|
1148
|
+
* @private
|
|
1149
|
+
*/
|
|
1150
|
+
_hierarchyDepth(node) {
|
|
1151
|
+
return Hierarchy.depth(node);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* The worst (largest) width:height aspect ratio among `row`'s
|
|
1156
|
+
* rectangles if laid out along a strip of the given `side` length —
|
|
1157
|
+
* the score squarify minimizes one row at a time. Pure. Assumes every
|
|
1158
|
+
* item's `.area` is already computed (see _squarify).
|
|
1159
|
+
* @private
|
|
1160
|
+
*/
|
|
1161
|
+
_squarifyWorstRatio(row, side) {
|
|
1162
|
+
return Hierarchy.squarifyWorstRatio(row, side);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* Squarified treemap (Bruls/Huizing/van Wijk): lays `items` (each
|
|
1167
|
+
* `{value, ref}`, pre-sorted descending by value by the caller) into
|
|
1168
|
+
* the rect `x,y,width,height`, area-proportional to value. Repeatedly
|
|
1169
|
+
* grows a "row" along the shorter side of the remaining space while
|
|
1170
|
+
* the worst aspect ratio in that row keeps improving, then flushes the
|
|
1171
|
+
* row and continues into what's left. Pure — operates on generic
|
|
1172
|
+
* `{value, ref}` pairs, not tied to the tree-node shape, so it's
|
|
1173
|
+
* directly testable with plain numbers.
|
|
1174
|
+
* @returns {Array<{ref: *, x: number, y: number, width: number, height: number}>}
|
|
1175
|
+
* @private
|
|
1176
|
+
*/
|
|
1177
|
+
_squarify(items, x, y, width, height) {
|
|
1178
|
+
return Hierarchy.squarify(items, x, y, width, height);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* Recursive layout over a treemapChart() node tree. Computes each
|
|
1183
|
+
* node's value (_hierarchyValue), squarifies this level (_squarify), and
|
|
1184
|
+
* for a branch reserves a fixed-height header band at the top of its
|
|
1185
|
+
* rect before recursing into the remaining content rect for its
|
|
1186
|
+
* children at depth+1. Returns a FLAT list covering every node in the
|
|
1187
|
+
* tree — a branch's own entry is pushed before its children are
|
|
1188
|
+
* recursed into (pre-order), so a caller painting in array order never
|
|
1189
|
+
* covers a node with its own descendant drawn first. Each entry also
|
|
1190
|
+
* carries `topAncestor` (the depth-0 node this one descends from, or
|
|
1191
|
+
* itself at depth 0) so a renderer can color every descendant off its
|
|
1192
|
+
* top-level group without re-walking the tree. Pure — no DOM/renderer
|
|
1193
|
+
* access.
|
|
1194
|
+
* @returns {Array<{node: object, x: number, y: number, width: number, height: number, depth: number, isLeaf: boolean, topAncestor: object}>}
|
|
1195
|
+
* @private
|
|
1196
|
+
*/
|
|
1197
|
+
_treemapLayout(nodes, x, y, width, height, depth = 0, topAncestors = null) {
|
|
1198
|
+
return Hierarchy.treemapLayout(nodes, x, y, width, height, depth, topAncestors, RenderEngine.TREEMAP_HEADER_H);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Renders a treemap — rectangles nested/sized proportional to value
|
|
1203
|
+
* (squarified layout, see _treemapLayout), the standard chart for
|
|
1204
|
+
* "composition of a whole across a hierarchy" (e.g. disk usage by
|
|
1205
|
+
* folder, headcount by department/team).
|
|
1206
|
+
* @param {Array<{label: string, value?: number, children?: Array}>} data
|
|
1207
|
+
* A leaf has `value` and no `children`; a branch has `children` (its
|
|
1208
|
+
* value is the sum, computed automatically) and can nest to any depth.
|
|
1209
|
+
* @param {object} [options]
|
|
1210
|
+
* @param {string[]} [options.colors] Palette — one color per top-level node, descendants shaded lighter by depth.
|
|
1211
|
+
* @param {string} [options.title]
|
|
1212
|
+
*/
|
|
1213
|
+
treemapChart(data, options = {}) {
|
|
1214
|
+
if (!this.renderer) return;
|
|
1215
|
+
this._chart3D = false;
|
|
1216
|
+
this._chartState = { type: 'treemap', data, options };
|
|
1217
|
+
this._renderChart(new TreemapChart(this.renderer, data, options));
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Recursive angular layout over the same node tree treemapChart()
|
|
1222
|
+
* consumes — mirrors _treemapLayout's shape but partitions ANGLE
|
|
1223
|
+
* instead of area, one ring per depth. Unlike _treemapLayout (which
|
|
1224
|
+
* sorts descending for squarify's aspect-ratio heuristic), input order
|
|
1225
|
+
* is preserved — same convention as pieChart, and there's no
|
|
1226
|
+
* aspect-ratio concept for a polar arc that sorting would improve.
|
|
1227
|
+
* Returns a flat pre-order list, each entry carrying `topAncestor` (the
|
|
1228
|
+
* depth-0 node this one descends from) for render-time color lookup —
|
|
1229
|
+
* same pattern _treemapLayout established. Pure — no DOM/renderer
|
|
1230
|
+
* access.
|
|
1231
|
+
* @returns {Array<{node: object, cx: number, cy: number, rInner: number, rOuter: number, startAngle: number, endAngle: number, depth: number, isLeaf: boolean, topAncestor: object}>}
|
|
1232
|
+
* @private
|
|
1233
|
+
*/
|
|
1234
|
+
_sunburstLayout(nodes, cx, cy, innerRadius, ringWidth, startAngle, endAngle, depth = 0, topAncestors = null) {
|
|
1235
|
+
return Hierarchy.sunburstLayout(nodes, cx, cy, innerRadius, ringWidth, startAngle, endAngle, depth, topAncestors);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Renders a sunburst — the same hierarchical composition treemapChart
|
|
1240
|
+
* shows as nested rectangles, drawn instead as nested rings (one ring
|
|
1241
|
+
* per depth), each ring's arcs sized by value like a pie chart. Shares
|
|
1242
|
+
* treemapChart's exact data contract (_hierarchyValue) and its
|
|
1243
|
+
* top-level-palette/depth-lightened coloring scheme; reuses
|
|
1244
|
+
* _donutSlicePath (built for pieChart's doughnut mode) for the actual
|
|
1245
|
+
* annular-sector geometry, since a sunburst ring IS a doughnut slice.
|
|
1246
|
+
* @param {Array<{label: string, value?: number, children?: Array}>} data
|
|
1247
|
+
* Same shape as treemapChart: a leaf has `value`, a branch has
|
|
1248
|
+
* `children` (value computed as their sum) and can nest to any depth.
|
|
1249
|
+
* @param {object} [options]
|
|
1250
|
+
* @param {number} [options.cx] @param {number} [options.cy] @param {number} [options.radius]
|
|
1251
|
+
* @param {number} [options.innerRadiusRatio=0] Fraction of `radius` left as a center hole, same semantics as pieChart.
|
|
1252
|
+
* @param {string[]} [options.colors] Palette — one color per top-level node, descendants shaded lighter by depth (like treemapChart).
|
|
1253
|
+
* @param {string} [options.title]
|
|
1254
|
+
*/
|
|
1255
|
+
sunburstChart(data, options = {}) {
|
|
1256
|
+
if (!this.renderer) return;
|
|
1257
|
+
this._chart3D = false;
|
|
1258
|
+
this._chartState = { type: 'sunburst', data, options };
|
|
1259
|
+
this._renderChart(new SunburstChart(this.renderer, data, options));
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Flat, pre-order list of *visible* rows for outlineChart() — a branch
|
|
1264
|
+
* present in `collapsed` (a Set of node object references, not ids —
|
|
1265
|
+
* this shape has no id field) contributes its own row but none of its
|
|
1266
|
+
* descendants'. Threads `topAncestor` through the recursion the same
|
|
1267
|
+
* way _treemapLayout/_sunburstLayout do, so the renderer can look up a
|
|
1268
|
+
* row's top-level accent color without re-walking the tree. Pure — no
|
|
1269
|
+
* DOM/renderer access, directly unit-testable.
|
|
1270
|
+
* @returns {Array<{node: object, depth: number, hasChildren: boolean, topAncestor: object}>}
|
|
1271
|
+
* @private
|
|
1272
|
+
*/
|
|
1273
|
+
_outlineLayout(nodes, depth, collapsed, topAncestors = null) {
|
|
1274
|
+
return Hierarchy.outlineLayout(nodes, depth, collapsed, topAncestors);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* A leaf row's trailing descriptive text — every field beyond
|
|
1279
|
+
* label/value/children, values only (no "key: " prefix, unlike
|
|
1280
|
+
* _nodeExtraDataLines' tooltip lines — a plain dash-joined fragment
|
|
1281
|
+
* reads better on an outline row than a labeled list), e.g. a table
|
|
1282
|
+
* row's "3 columns · 3 rows · hybrid" from
|
|
1283
|
+
* `{label:'users', columns:3, rows:3, mode:'hybrid'}`. Pure.
|
|
1284
|
+
* @private
|
|
1285
|
+
*/
|
|
1286
|
+
_outlineExtraText(node) {
|
|
1287
|
+
return Hierarchy.outlineExtraText(node, RenderEngine.HIERARCHY_STRUCTURAL_KEYS);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
/** Re-invokes outlineChart() with its last data/options — same
|
|
1291
|
+
* "re-render after a click-driven state change" pattern
|
|
1292
|
+
* _rerenderGraph() uses for graphDiagram's own +/− badges.
|
|
1293
|
+
* @private */
|
|
1294
|
+
_rerenderOutline() {
|
|
1295
|
+
if (this._chartState?.type === 'outline') this.outlineChart(this._chartState.data, this._chartState.options);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Renders an outline / tree list — the same hierarchical composition
|
|
1300
|
+
* treemapChart/sunburstChart show as nested rectangles/rings, drawn
|
|
1301
|
+
* instead as an indented, collapsible list (a file-explorer or site-menu's
|
|
1302
|
+
* usual shape): one row per node, a branch's own value auto-summed from
|
|
1303
|
+
* its descendants (_hierarchyValue) shown as a trailing count, a leaf's
|
|
1304
|
+
* own extra fields (anything beyond label/value/children) shown as
|
|
1305
|
+
* trailing descriptive text instead. Any branch gets a +/− toggle;
|
|
1306
|
+
* clicking it collapses/expands just that subtree and re-renders —
|
|
1307
|
+
* collapse state (`this._outlineCollapsed`, a Set of node object
|
|
1308
|
+
* references) persists across re-renders of the *same* data array, the
|
|
1309
|
+
* same convention graphDiagram's hierarchical-key +/− badges use for
|
|
1310
|
+
* `_graphCollapsedNodes`. Content taller than the container scales down
|
|
1311
|
+
* to fit (top-anchored, not centered) rather than clipping or scrolling —
|
|
1312
|
+
* same "set viewBox to the content's real size" trick renderDiagram()
|
|
1313
|
+
* uses for a wide railroad diagram. SVG-only: the toggle/click
|
|
1314
|
+
* interactivity needs real per-row DOM elements, same reason
|
|
1315
|
+
* graphDiagram is SVG-only.
|
|
1316
|
+
* @param {Array<{label: string, value?: number, children?: Array}>} data
|
|
1317
|
+
* Same shape as treemapChart/sunburstChart: a leaf has `value` (or its
|
|
1318
|
+
* own extra fields), a branch has `children` (value computed as their
|
|
1319
|
+
* sum) and can nest to any depth. Any field beyond label/value/children
|
|
1320
|
+
* is extra descriptive data, not part of the structural contract.
|
|
1321
|
+
* @param {object} [options]
|
|
1322
|
+
* @param {string[]} [options.colors] Palette — one color per top-level node's row accent, descendants shaded lighter by depth (same scheme as treemapChart/sunburstChart).
|
|
1323
|
+
* @param {string} [options.title]
|
|
1324
|
+
* @param {function} [options.onNodeClick] Fired when a row's label (not its +/− toggle) is clicked.
|
|
1325
|
+
*/
|
|
1326
|
+
outlineChart(data, options = {}) {
|
|
1327
|
+
if (!this.renderer || !this.renderer.supportsSceneGraph) {
|
|
1328
|
+
console.warn('Outline chart is only supported in SVG renderer.');
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
this._chart3D = false;
|
|
1332
|
+
this._chartState = { type: 'outline', data, options };
|
|
1333
|
+
this._outlineCollapsed = this._outlineCollapsed || new Set();
|
|
1334
|
+
this._renderChart(new OutlineChart(this.renderer, data, options, this._outlineCollapsed, () => this._rerenderOutline()));
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Renders a radar/spider chart — one polygon per series across N shared
|
|
1339
|
+
* axes, each axis scaled independently by its own max value across all
|
|
1340
|
+
* series, so differently-scaled metrics (e.g. "latency ms" vs "score
|
|
1341
|
+
* 0-10") share one chart without one axis flattening the others.
|
|
1342
|
+
* @param {Array<{name:string, values:Array<{axis:string, value:number}>}>|Array<{axis:string,value:number}>} data
|
|
1343
|
+
* @param {object} [options]
|
|
1344
|
+
* @param {string[]} [options.colors]
|
|
1345
|
+
* @param {boolean} [options.legend=true]
|
|
1346
|
+
*/
|
|
1347
|
+
radarChart(data, options = {}) {
|
|
1348
|
+
if (!this.renderer) return;
|
|
1349
|
+
this._chart3D = false;
|
|
1350
|
+
this._chartState = { type: 'radar', data, options };
|
|
1351
|
+
this._renderChart(new RadarChart(this.renderer, data, options));
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* Renders a funnel chart — one horizontal trapezoid band per stage,
|
|
1356
|
+
* width proportional to `value` relative to the largest stage, for
|
|
1357
|
+
* visualizing drop-off across a sequential process (e.g. a sales
|
|
1358
|
+
* pipeline). Each stage's bottom edge width comes from the *next*
|
|
1359
|
+
* stage's value, so the taper between adjacent bands is continuous.
|
|
1360
|
+
* @param {Array<{label:string, value:number}>} data ordered top-to-bottom (largest first, by convention)
|
|
1361
|
+
* @param {object} [options]
|
|
1362
|
+
* @param {string[]} [options.colors]
|
|
1363
|
+
* @param {string} [options.title]
|
|
1364
|
+
*/
|
|
1365
|
+
funnelChart(data, options = {}) {
|
|
1366
|
+
if (!this.renderer) return;
|
|
1367
|
+
this._chart3D = false;
|
|
1368
|
+
this._chartState = { type: 'funnel', data, options };
|
|
1369
|
+
this._renderChart(new FunnelChart(this.renderer, data, options));
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* Renders a waterfall chart — running-total bars, each floating from the
|
|
1374
|
+
* previous cumulative value to the new one (a "delta" bar), except
|
|
1375
|
+
* entries marked `type:'total'` which draw full-height from zero (a
|
|
1376
|
+
* subtotal/grand-total checkpoint). Increases/decreases are colored
|
|
1377
|
+
* distinctly; thin connector lines between consecutive bars follow the
|
|
1378
|
+
* classic waterfall styling.
|
|
1379
|
+
* @param {Array<{label:string, value:number, type?:'delta'|'total'}>} data
|
|
1380
|
+
* @param {object} [options]
|
|
1381
|
+
* @param {string} [options.upColor='#26a69a']
|
|
1382
|
+
* @param {string} [options.downColor='#ef5350']
|
|
1383
|
+
* @param {string} [options.totalColor='#5470c6']
|
|
1384
|
+
*/
|
|
1385
|
+
waterfallChart(data, options = {}) {
|
|
1386
|
+
if (!this.renderer) return;
|
|
1387
|
+
this._chart3D = false;
|
|
1388
|
+
this._chartState = { type: 'waterfall', data, options };
|
|
1389
|
+
this._renderChart(new WaterfallChart(this.renderer, data, options));
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* Reads whatever `colorCol` names for one data point — a plain field
|
|
1394
|
+
* name (unchanged behavior: `d[colorCol]`), or a compiled
|
|
1395
|
+
* `{formula, params}` expression evaluated over the point's own fields
|
|
1396
|
+
* (`kernelExpr.js`, the same small grammar sqlengine's compiled UDFs
|
|
1397
|
+
* use — see `documentation`/`tasks.md`). Shared by every chart with a
|
|
1398
|
+
* `colorCol` option (scatterChart/bubbleChart/bubbleChart3D) so a
|
|
1399
|
+
* formula means the same thing wherever it's written, and each distinct
|
|
1400
|
+
* value it returns gets its own palette color exactly as a plain
|
|
1401
|
+
* field's distinct values already do.
|
|
1402
|
+
* @private
|
|
1403
|
+
*/
|
|
1404
|
+
_readColorCol(d, colorCol) {
|
|
1405
|
+
return ChartData.readColorCol(d, colorCol);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/**
|
|
1409
|
+
* Renders a plain 2D scatter plot — x/y points at a fixed marker radius,
|
|
1410
|
+
* no size encoding (see bubbleChart() for that, which requires a `z`
|
|
1411
|
+
* field this doesn't). Optional per-point categorical coloring via
|
|
1412
|
+
* `colorCol`/`category`/`group`, same convention as bubbleChart. Uses a
|
|
1413
|
+
* real [min,max] domain per axis (not a 0-anchored one), so negative
|
|
1414
|
+
* values (e.g. investment returns, normal quantiles) plot correctly —
|
|
1415
|
+
* qqPlot/lagPlot both delegate their entire rendering here and need
|
|
1416
|
+
* exactly that.
|
|
1417
|
+
* @param {Array<{label: string, x: number, y: number}>} data The data to chart.
|
|
1418
|
+
* @param {object} [options] Charting options.
|
|
1419
|
+
* @param {boolean|{from:{x,y},to:{x,y}}} [options.referenceLine] A dashed
|
|
1420
|
+
* reference line: `true` draws identity (y=x) over the domains'
|
|
1421
|
+
* overlap (what lagPlot uses); an explicit `{from,to}` pair (data
|
|
1422
|
+
* space) draws exactly that segment (what qqPlot's fitted Q1/Q3 line
|
|
1423
|
+
* uses — not the same as identity).
|
|
1424
|
+
*/
|
|
1425
|
+
scatterChart(data, options = {}) {
|
|
1426
|
+
if (!this.renderer) return;
|
|
1427
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
1428
|
+
this._chartState = { type: 'scatter', data, options };
|
|
1429
|
+
this._renderChart(new ScatterChart(this.renderer, data, options));
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
/**
|
|
1433
|
+
* Renders a Q-Q (quantile-quantile) plot — tests whether a raw sample
|
|
1434
|
+
* is approximately normally distributed, the standard tail-risk/
|
|
1435
|
+
* fat-tails diagnostic for a return series before trusting a risk
|
|
1436
|
+
* model's normality assumption. Each point is (theoretical normal
|
|
1437
|
+
* quantile, sample quantile) via _qqPoints; a dashed reference line
|
|
1438
|
+
* fit through the sample's own Q1/Q3 (the standard qqline() convention
|
|
1439
|
+
* — not a plain y=x identity) shows what "perfectly normal" would look
|
|
1440
|
+
* like — points hugging the line are normal-ish, systematic curvature
|
|
1441
|
+
* (especially at the ends) signals fat tails/skew. Delegates its
|
|
1442
|
+
* entire rendering (points + reference line) to scatterChart via its
|
|
1443
|
+
* `referenceLine` option — no separate rendering code here.
|
|
1444
|
+
* @param {number[]} values Raw numeric sample.
|
|
1445
|
+
* @param {object} [options] Forwarded to scatterChart (title/axis labels/colors/box).
|
|
1446
|
+
*/
|
|
1447
|
+
qqPlot(values, options = {}) {
|
|
1448
|
+
if (!this.renderer) return;
|
|
1449
|
+
this._chart3D = false;
|
|
1450
|
+
this._chartState = { type: 'qqPlot', data: values, options };
|
|
1451
|
+
this._renderChart(new QQPlotChart(this.renderer, values, options));
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* Renders a lag plot — value[t] against value[t-lag] (default lag=1),
|
|
1456
|
+
* the standard visual autocorrelation check: points scattered with no
|
|
1457
|
+
* structure suggest an uncorrelated (white-noise) series; a clear
|
|
1458
|
+
* diagonal trend (hugging the identity reference line) suggests real
|
|
1459
|
+
* serial dependence — worth checking before treating lagged values as
|
|
1460
|
+
* predictive features (e.g. a KAN model's input engineering). The pure
|
|
1461
|
+
* lag-pairing transform (_lagPairs) is the only genuinely new logic
|
|
1462
|
+
* here — rendering delegates entirely to scatterChart.
|
|
1463
|
+
* @param {number[]} values Raw ordered numeric series.
|
|
1464
|
+
* @param {object} [options] `lag` (default 1) plus everything scatterChart accepts.
|
|
1465
|
+
*/
|
|
1466
|
+
lagPlot(values, options = {}) {
|
|
1467
|
+
if (!this.renderer) return;
|
|
1468
|
+
this._chart3D = false;
|
|
1469
|
+
this._chartState = { type: 'lagPlot', data: values, options };
|
|
1470
|
+
this._renderChart(new LagPlotChart(this.renderer, values, options));
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
/**
|
|
1474
|
+
* Renders a bubble chart.
|
|
1475
|
+
* @param {Array<{label: string, x: number, y: number, z: number}>} data The data to chart.
|
|
1476
|
+
* @param {object} [options] Charting options.
|
|
1477
|
+
*/
|
|
1478
|
+
bubbleChart(data, options = {}) {
|
|
1479
|
+
if (!this.renderer) return;
|
|
1480
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
1481
|
+
this._chartState = { type: 'bubble', data, options };
|
|
1482
|
+
this._renderChart(new BubbleChart(this.renderer, data, options));
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Projects a normalized 3D point (each axis roughly in [-1, 1], centered
|
|
1487
|
+
* on the data's own origin) through a real orbit camera (OrbitCamera.js —
|
|
1488
|
+
* composed Mat4 rotation + a genuine perspective-projection matrix, not
|
|
1489
|
+
* the hand-tuned `distance/(distance+depth*0.6)` heuristic this used to
|
|
1490
|
+
* be) down to a 2D offset plus a depth value for z-sorting. Callers scale
|
|
1491
|
+
* sx/sy by the chart's own pixel radius and offset to its center — same
|
|
1492
|
+
* contract as before, so bubbleChart3D/_drawBox3D's `toScreen` closures
|
|
1493
|
+
* didn't need to change. `camera` is the plain {theta,phi,distance}
|
|
1494
|
+
* state `_camera3D`/`_addOrbit3DListeners` already drive; fovY/near/far
|
|
1495
|
+
* aren't part of that shared state (nothing currently varies them), so
|
|
1496
|
+
* OrbitCamera's own defaults apply. Square aspect (1) — callers already
|
|
1497
|
+
* scale sx and sy by the same single pixelRadius, i.e. they assume
|
|
1498
|
+
* uniform x/y scaling, so this preserves that.
|
|
1499
|
+
* @private
|
|
1500
|
+
*/
|
|
1501
|
+
_project3D(x, y, z, camera) {
|
|
1502
|
+
return Chart3D.project(x, y, z, camera);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* Renders a true 3D scatter/bubble chart — x/y/z are all spatial axes
|
|
1507
|
+
* (unlike bubbleChart(), where z encodes bubble *size*, not position) —
|
|
1508
|
+
* with an orbit camera you can drag to rotate and scroll to zoom (see
|
|
1509
|
+
* _addOrbit3DListeners). Distinct from bubbleChart({zAxis:true})'s fixed
|
|
1510
|
+
* isometric shift: this recomputes a real projection every frame and
|
|
1511
|
+
* z-sorts points back-to-front, so nearer points correctly occlude
|
|
1512
|
+
* farther ones as you rotate.
|
|
1513
|
+
* @param {Array<{label: string, x: number, y: number, z: number, size?: number, category?: string}>} data
|
|
1514
|
+
* @param {object} [options]
|
|
1515
|
+
* @param {string|{formula:string,params:string[]}} [options.colorCol] Field name to color points by
|
|
1516
|
+
* (auto-detects `category`/`group`), or a compiled formula over the point's own fields, e.g.
|
|
1517
|
+
* `{formula: 'value > threshold ? 1 : 0', params: ['value', 'threshold']}` — see `kernelExpr.js`.
|
|
1518
|
+
* @param {string[]} [options.colors] Palette for categorical coloring.
|
|
1519
|
+
* @param {string} [options.title]
|
|
1520
|
+
* @param {number} [options.initialTheta=-0.6] Starting azimuth (radians).
|
|
1521
|
+
* @param {number} [options.initialPhi=0.35] Starting elevation (radians).
|
|
1522
|
+
* @param {number} [options.initialDistance=2.2] Starting camera distance (zoom).
|
|
1523
|
+
*/
|
|
1524
|
+
bubbleChart3D(data, options = {}) {
|
|
1525
|
+
if (!this.renderer) return;
|
|
1526
|
+
|
|
1527
|
+
const {
|
|
1528
|
+
initialTheta = -0.6, initialPhi = 0.35, initialDistance = 2.2,
|
|
1529
|
+
} = options;
|
|
1530
|
+
|
|
1531
|
+
// Camera state persists across redraws (drag/wheel) — only seed it
|
|
1532
|
+
// the first time this exact chart is drawn, not on every redraw.
|
|
1533
|
+
if (!this._camera3D) this._camera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
1534
|
+
this._initialCamera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
1535
|
+
this._chart3D = true;
|
|
1536
|
+
this._chartState = { type: 'bubble3d', data, options };
|
|
1537
|
+
this._chart3DRedraw = () => this.bubbleChart3D(data, { ...options, initialTheta, initialPhi, initialDistance });
|
|
1538
|
+
|
|
1539
|
+
this._renderChart(new BubbleChart3D(this.renderer, data, options, this._camera3D));
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* Draws one 3D box (a bar or one segment of a stacked bar) as three
|
|
1544
|
+
* visible faces — top, plus one X-side and one Z-side wall — through the
|
|
1545
|
+
* orbit camera. Full backface culling/lighting is out of scope here:
|
|
1546
|
+
* instead of testing every face, the X/Z side to draw is chosen by the
|
|
1547
|
+
* sign of the camera's approximate world-space direction
|
|
1548
|
+
* (sin(theta)/cos(theta) for a standard orbit-camera spherical→cartesian
|
|
1549
|
+
* position), which is cheap and looks correct across the normal drag
|
|
1550
|
+
* range even though it isn't exact culling. Bars don't interpenetrate,
|
|
1551
|
+
* so the caller only needs to z-sort *bars* (by center depth), not faces.
|
|
1552
|
+
* @returns {number} the box's average corner depth, for the caller's own
|
|
1553
|
+
* bar-level z-sort.
|
|
1554
|
+
* @private
|
|
1555
|
+
*/
|
|
1556
|
+
_drawBox3D(x0, x1, y0, y1, z0, z1, color, toScreen, camera) {
|
|
1557
|
+
return Chart3D.drawBox(this.renderer, x0, x1, y0, y1, z0, z1, color, toScreen, camera);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* Groups flat {x, z, ...} data into grid indices/labels for the two
|
|
1562
|
+
* orbit-camera bar chart variants — shared so barChart3DOrbit and
|
|
1563
|
+
* stackedBarChart3DOrbit bucket categories identically.
|
|
1564
|
+
* @private
|
|
1565
|
+
*/
|
|
1566
|
+
_bucketGrid3D(data) {
|
|
1567
|
+
return Chart3D.bucketGrid(data);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* Renders a grouped 3D bar chart across two categorical axes (x and z)
|
|
1572
|
+
* with a real orbit camera — drag to rotate, scroll to zoom (see
|
|
1573
|
+
* _addOrbit3DListeners). Distinct from barChart3D's isometric single-axis
|
|
1574
|
+
* bars: this is what "3D bar chart" usually means (a grid of bars, viewed
|
|
1575
|
+
* from any angle), not a fixed-tilt 2D chart with a depth shadow.
|
|
1576
|
+
* @param {Array<{x: string, z: string, value: number}>} data
|
|
1577
|
+
* @param {object} [options]
|
|
1578
|
+
* @param {string[]} [options.colors] Palette, indexed by z-group.
|
|
1579
|
+
* @param {string} [options.title]
|
|
1580
|
+
* @param {number} [options.initialTheta=-0.5] @param {number} [options.initialPhi=0.45] @param {number} [options.initialDistance=2.4]
|
|
1581
|
+
*/
|
|
1582
|
+
barChart3DOrbit(data, options = {}) {
|
|
1583
|
+
if (!this.renderer) return;
|
|
1584
|
+
|
|
1585
|
+
const { initialTheta = -0.5, initialPhi = 0.45, initialDistance = 2.4 } = options;
|
|
1586
|
+
|
|
1587
|
+
if (!this._camera3D) this._camera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
1588
|
+
this._initialCamera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
1589
|
+
this._chart3D = true;
|
|
1590
|
+
this._chartState = { type: 'bar3dOrbit', data, options };
|
|
1591
|
+
this._chart3DRedraw = () => this.barChart3DOrbit(data, { ...options, initialTheta, initialPhi, initialDistance });
|
|
1592
|
+
|
|
1593
|
+
this._renderChart(new BarChart3DOrbit(this.renderer, data, options, this._camera3D));
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Stacked-segment version of barChart3DOrbit: each (x, z) grid cell
|
|
1598
|
+
* holds a stack of `values`, each drawn as its own shorter box on top of
|
|
1599
|
+
* the previous segment (correct occlusion within one bar since segments
|
|
1600
|
+
* share x/z and only differ in y-extent — no special-casing needed
|
|
1601
|
+
* beyond the existing per-bar z-sort).
|
|
1602
|
+
* @param {Array<{x: string, z: string, values: number[]}>} data
|
|
1603
|
+
* @param {object} [options] Same as barChart3DOrbit, plus `colors` is
|
|
1604
|
+
* indexed by *segment index* (stack position) instead of z-group.
|
|
1605
|
+
*/
|
|
1606
|
+
stackedBarChart3DOrbit(data, options = {}) {
|
|
1607
|
+
if (!this.renderer) return;
|
|
1608
|
+
|
|
1609
|
+
const { initialTheta = -0.5, initialPhi = 0.45, initialDistance = 2.4 } = options;
|
|
1610
|
+
|
|
1611
|
+
if (!this._camera3D) this._camera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
1612
|
+
this._initialCamera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
1613
|
+
this._chart3D = true;
|
|
1614
|
+
this._chartState = { type: 'stackedBar3dOrbit', data, options };
|
|
1615
|
+
this._chart3DRedraw = () => this.stackedBarChart3DOrbit(data, { ...options, initialTheta, initialPhi, initialDistance });
|
|
1616
|
+
|
|
1617
|
+
this._renderChart(new StackedBarChart3DOrbit(this.renderer, data, options, this._camera3D));
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/**
|
|
1621
|
+
* Renders a 3D bar chart (isometric projection).
|
|
1622
|
+
* @param {Array<{label: string, value: number, depth?: number}>} data The data to chart.
|
|
1623
|
+
* @param {object} [options] Charting options.
|
|
1624
|
+
*/
|
|
1625
|
+
barChart3D(data, options = {}) {
|
|
1626
|
+
if (!this.renderer) return;
|
|
1627
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
1628
|
+
this._chartState = { type: 'bar3d', data, options };
|
|
1629
|
+
this._renderChart(new BarChart3D(this.renderer, data, options));
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
/**
|
|
1633
|
+
* Helper function to lighten/darken a color for 3D effect.
|
|
1634
|
+
* @private
|
|
1635
|
+
*/
|
|
1636
|
+
_lightenColor(color, amount) {
|
|
1637
|
+
return ColorPalette.lighten(color, amount);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/** @private hex "#rrggbb" -> {r,g,b} */
|
|
1641
|
+
_hexToRgb(hex) {
|
|
1642
|
+
return ColorPalette.hexToRgb(hex);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
/** @private Linear-interpolates between two hex colors at t in [0,1]. */
|
|
1646
|
+
_interpolateColor(c1, c2, t) {
|
|
1647
|
+
return ColorPalette.interpolate(c1, c2, t);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* Resolves a gauge `color` option to a single hex color for the given
|
|
1652
|
+
* 0-100 value. A plain string passes through unchanged. An array is
|
|
1653
|
+
* treated as a heat-map gradient, in either of two forms:
|
|
1654
|
+
* - plain hex strings, evenly spaced across 0-100:
|
|
1655
|
+
* `['#67e043', '#f9d939', '#f23c24']`
|
|
1656
|
+
* - `{stop, color}` pairs at explicit percentages (unevenly spaced
|
|
1657
|
+
* breakpoints, e.g. "green until 50, amber until 80, red above"):
|
|
1658
|
+
* `[{stop:0,color:'#67e043'},{stop:50,color:'#f9d939'},{stop:80,color:'#f23c24'}]`
|
|
1659
|
+
* @private
|
|
1660
|
+
*/
|
|
1661
|
+
_resolveGaugeColor(color, percent) {
|
|
1662
|
+
return Gauge.resolveColor(color, percent);
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Renders a bar gauge.
|
|
1667
|
+
* @param {number|{value:number, target?:number}} value The value to display (0-100 unless options.min/max are given).
|
|
1668
|
+
* @param {object} [options] Charting options.
|
|
1669
|
+
*/
|
|
1670
|
+
barGauge(value, options = {}) {
|
|
1671
|
+
this._renderGauge(BarGauge, 'barGauge', value, options);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
/**
|
|
1675
|
+
* Renders a bullet graph: a primary bar compared against qualitative
|
|
1676
|
+
* performance ranges and a target marker.
|
|
1677
|
+
* @param {number|{value:number, target?:number, ranges?:Array<{max:number, label?, color?}>}} data The actual value, or an object with value/target/ranges.
|
|
1678
|
+
* @param {object} [options] Charting options.
|
|
1679
|
+
*/
|
|
1680
|
+
bulletGauge(data, options = {}) {
|
|
1681
|
+
this._renderGauge(BulletGauge, 'bullet', data, options);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* Renders a dial gauge.
|
|
1686
|
+
* @param {number|{value:number, target?:number}} value The value to display (0-100 unless options.min/max are given).
|
|
1687
|
+
* @param {object} [options] Charting options.
|
|
1688
|
+
*/
|
|
1689
|
+
dialGauge(value, options = {}) {
|
|
1690
|
+
this._renderGauge(DialGauge, 'dialGauge', value, options);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* Renders an arc gauge: a half circle (or any start angle and sweep) filled to the value.
|
|
1695
|
+
* @param {number|{value:number, target?:number}} value The value (0-100 unless options.min/max are given).
|
|
1696
|
+
* @param {object} [options] The gauge options (see REFERENCE.md, Gauges), cx, cy, r, thickness, startAngle, sweep, cap.
|
|
1697
|
+
*/
|
|
1698
|
+
arcGauge(value, options = {}) {
|
|
1699
|
+
this._renderGauge(ArcGauge, 'arcGauge', value, options);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* Renders a progress ring, its value in the middle.
|
|
1704
|
+
* @param {number|{value:number, target?:number}} value The value (0-100 unless options.min/max are given).
|
|
1705
|
+
* @param {object} [options] The gauge options, cx, cy, r, thickness.
|
|
1706
|
+
*/
|
|
1707
|
+
ringGauge(value, options = {}) {
|
|
1708
|
+
this._renderGauge(RingGauge, 'ringGauge', value, options);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/**
|
|
1712
|
+
* Renders several values as rings one inside the other, with a legend.
|
|
1713
|
+
* @param {Array<number|{value:number, label?:string, color?:string, min?:number, max?:number}>} values Outer ring first.
|
|
1714
|
+
* @param {object} [options] cx, cy, r, thickness, gap, colors, legend, min, max, format, units, title.
|
|
1715
|
+
*/
|
|
1716
|
+
multiRingGauge(values, options = {}) {
|
|
1717
|
+
this._renderGauge(MultiRingGauge, 'multiRingGauge', values, options);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* Renders a KPI tile: a figure, its change since the previous value, and a sparkline of its history.
|
|
1722
|
+
* @param {number|{value?:number, previous?:number, history?:number[]}} data
|
|
1723
|
+
* @param {object} [options] x, y, width, height, format, units, change ('percent'|'value'), upIsGood, bands, sparkColor, background, title.
|
|
1724
|
+
*/
|
|
1725
|
+
kpiTile(data, options = {}) {
|
|
1726
|
+
this._renderGauge(KpiTile, 'kpiTile', data, options);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
/**
|
|
1730
|
+
* Renders a thermometer: a tube filled from a bulb up to the value.
|
|
1731
|
+
* @param {number|{value:number, target?:number}} value The value (0-100 unless options.min/max are given).
|
|
1732
|
+
* @param {object} [options] The gauge options, x, y, width, height, bulb.
|
|
1733
|
+
*/
|
|
1734
|
+
thermometerGauge(value, options = {}) {
|
|
1735
|
+
this._renderGauge(ThermometerGauge, 'thermometerGauge', value, options);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
/**
|
|
1739
|
+
* Every gauge method draws through here. With `options.animate` (true:
|
|
1740
|
+
* 400ms, or milliseconds) a gauge of the same type already showing eases
|
|
1741
|
+
* from the value it shows to the new one; a newer call stops an older
|
|
1742
|
+
* animation and starts from where it got to.
|
|
1743
|
+
* @private
|
|
1744
|
+
*/
|
|
1745
|
+
_renderGauge(GaugeType, type, data, options = {}) {
|
|
1746
|
+
if (!this.renderer) return;
|
|
1747
|
+
this._chart3D = false; // leaving 3D mode — clears the SVG-zoom guard added for bubbleChart3D
|
|
1748
|
+
this._chartState = { type, data, options };
|
|
1749
|
+
this._gaugeAnimation?.stop();
|
|
1750
|
+
const shown = this._gaugeShown?.type === type ? this._gaugeShown.data : null;
|
|
1751
|
+
const draw = (frame) => {
|
|
1752
|
+
this._gaugeShown = { type, data: frame };
|
|
1753
|
+
this._renderChart(new GaugeType(this.renderer, frame, options));
|
|
1754
|
+
};
|
|
1755
|
+
if (!options.animate || shown == null) {
|
|
1756
|
+
draw(data);
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
this._gaugeAnimation = new GaugeAnimation({
|
|
1760
|
+
from: GaugeValue.from(shown, options),
|
|
1761
|
+
to: GaugeValue.from(data, options),
|
|
1762
|
+
duration: options.animate === true ? 400 : Number(options.animate),
|
|
1763
|
+
draw,
|
|
1764
|
+
}).start();
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
_arcPath(cx, cy, r, startAngle, endAngle) {
|
|
1768
|
+
return Gauge.arcPath(cx, cy, r, startAngle, endAngle);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
/**
|
|
1772
|
+
* Renders a simple network graph diagram. If any node's `id` is a
|
|
1773
|
+
* Dewey-style hierarchical key (digits joined by `.`, `-`, or `/`, e.g.
|
|
1774
|
+
* `"1.2.3"`, `"1-2-3"`) — see `_inferHierarchyEdges` — the parent-child
|
|
1775
|
+
* relationships implied by those keys replace `edges` entirely (except
|
|
1776
|
+
* "quasi-leaf" edges touching a non-Dewey-key node, which are kept and
|
|
1777
|
+
* attach that plain node as an extra child) and the diagram is laid out
|
|
1778
|
+
* as a top-down tree (`_computeTreeLayout`) instead of force-directed.
|
|
1779
|
+
* In that mode, any node with children gets a +/− badge to collapse or
|
|
1780
|
+
* expand its subtree, and a node's extra fields (beyond id/label/x/y/
|
|
1781
|
+
* shape/color/group/size) are auto-listed in its hover tooltip. Call
|
|
1782
|
+
* `filterGraphNodes(keyword)` at any time to live-filter which nodes are
|
|
1783
|
+
* shown by label/extra-data keyword (ancestors of a match stay visible
|
|
1784
|
+
* in hierarchical mode).
|
|
1785
|
+
* @param {Array<{id: string, label?: string|((node:object)=>string), name?: string, x: number, y: number, size?: number, icon?: string, iconUrl?: string}>} nodes
|
|
1786
|
+
* Array of node objects — `size` (a plain number) scales that node's radius, see `sizeBy` below. `icon`
|
|
1787
|
+
* (a short glyph/emoji string) or `iconUrl` (an image/SVG file or data URI) replaces the plain initials
|
|
1788
|
+
* text centred on the node's shape — `iconUrl` wins if both are set; neither applies to `shape:'person'`
|
|
1789
|
+
* (which already draws its own glyph) or `shape:'table'`. A `shape:'rect'` node's own `width`/`height`
|
|
1790
|
+
* override the usual square-of-radius box (see `options.labelPosition` for putting its label inside it,
|
|
1791
|
+
* word-wrapped and clipped to that box rather than a single overflowing line). Its own `cornerStyle`
|
|
1792
|
+
* (`'sharp'|'rounded'|'chamfered'`, default a plain small rx) picks the corner treatment — `archimateDiagram`
|
|
1793
|
+
* uses this for ArchiMate's own generic box shapes (_archimateCornerStyle), but it's a plain graphDiagram
|
|
1794
|
+
* node field, usable without archimateDiagram at all.
|
|
1795
|
+
* **Label resolution** (see `_resolveNodeLabel`, used everywhere a node's label is shown — the box/
|
|
1796
|
+
* caption text, tooltip, table header, keyword search): `label` may be a plain string (used as-is), a
|
|
1797
|
+
* function `(node) => string` (called at render time — the same "may be a function" convention
|
|
1798
|
+
* `contextMenuItems`' own `label` already uses), or a string containing `{field}` placeholders (each
|
|
1799
|
+
* replaced with that field's own value off the node — plain substitution, not a parser, e.g.
|
|
1800
|
+
* `'{name} ({type})'`). An empty/unset `label` falls back to `name`.
|
|
1801
|
+
* @param {Array<{source: string, target: string, weight?: number}>} edges Array of edge objects linking nodes by id — replaced by the inferred hierarchy if one is detected (quasi-leaf edges excepted, see above). `weight` scales that edge's stroke width, see `minEdgeWidth` below.
|
|
1802
|
+
* @param {object} [options] Rendering options.
|
|
1803
|
+
* @param {'degree'} [options.sizeBy] Size nodes by connection count (in + out edges) instead of requiring the
|
|
1804
|
+
* caller to pre-populate `node.size` — useful when "how connected is this node" IS the value worth showing.
|
|
1805
|
+
* @param {number} [options.minRadius] @param {number} [options.maxRadius] @param {'linear'|'sqrt'} [options.sizeScale]
|
|
1806
|
+
* Range/curve `node.size` (or degree, with `sizeBy:'degree'`) maps into — only takes effect once some node
|
|
1807
|
+
* has a finite size value; `sqrt` compresses outliers where the value scales with an area/count, not a length.
|
|
1808
|
+
* @param {number} [options.minEdgeWidth] @param {number} [options.maxEdgeWidth] @param {'linear'|'sqrt'} [options.edgeWidthScale]
|
|
1809
|
+
* Same shape as node sizing, for `edge.weight` → stroke width; only takes effect once some edge has a finite weight.
|
|
1810
|
+
* @param {'arrow'|'circle'|'tee'|'diamond'|'diamond-hollow'|'triangle-hollow'|'open-arrow'|'crowsfoot-one'|'crowsfoot-one-optional'|'crowsfoot-many'|'crowsfoot-many-mandatory'|'none'} [edge.arrowhead='arrow']
|
|
1811
|
+
* Glyph at the target end. `edge.arrowheadStart` is the same set, independently, for the SOURCE end (e.g.
|
|
1812
|
+
* ArchiMate composition's filled diamond there while the target end carries none — see archimateDiagram;
|
|
1813
|
+
* the `crowsfoot-*` glyphs are ERD cardinality notation, see dataModelDiagram).
|
|
1814
|
+
* @param {string} [edge.sourceLabel] @param {string} [edge.targetLabel] Independent text labels near each
|
|
1815
|
+
* end (e.g. a UML association's multiplicity, '0..1'/'1..*'/...) — distinct from the one midpoint
|
|
1816
|
+
* `edge.label` above (e.g. the relationship's own name); see dataModelDiagram's 'uml' notation.
|
|
1817
|
+
* @param {'solid'|'dashed'|'dotted'} [edge.lineStyle] Generalizes the older boolean `edge.dashed` (still
|
|
1818
|
+
* honored when `lineStyle` is unset) — 'dotted' is the case neither the old flag nor its fixed pattern covered.
|
|
1819
|
+
* @param {'straight'|'curved'|'orthogonal'|'polyline'} [options.edgeRouting='straight'] Default auto-routing
|
|
1820
|
+
* for edges that don't set their own `edge.routing`. `'straight'` is the original point-to-point line.
|
|
1821
|
+
* `'curved'` is a smooth bezier leaving/arriving along each end's own normal. `'orthogonal'` is a
|
|
1822
|
+
* right-angle (Manhattan) connector. `'polyline'` is a straight line that steps around one other node
|
|
1823
|
+
* with a single waypoint if it's directly in the way, otherwise it's the same as `'straight'`. An
|
|
1824
|
+
* individual edge can also carry `weight`/`color`/`label`/`dashed`/`arrowhead` (existing) plus
|
|
1825
|
+
* `routing` (overrides this default) and `sourceSide`/`targetSide` (`'top'|'right'|'bottom'|'left'`,
|
|
1826
|
+
* default `'auto'`) — a pinned side fixes *where on the node* the line attaches (it stays there as the
|
|
1827
|
+
* node moves) instead of always sliding around to face whatever it's connected to; force-directed
|
|
1828
|
+
* organic routing is what `graphDiagram`'s node layout already does by default (nodes/edges settle via
|
|
1829
|
+
* the same spring simulation) — see the class doc comment above for the tree-layout alternative.
|
|
1830
|
+
* When `editable`, any edge can also be hand-routed: right-click it and choose "Add Waypoint Here" to
|
|
1831
|
+
* drop a bend point where you clicked (`_insertEdgeWaypoint`/`_edgeWaypointsWithInsertion`), drag a bend
|
|
1832
|
+
* point to move it, double-click a bend point to remove it. That builds `edge.waypoints` (`[{x,y}, ...]`,
|
|
1833
|
+
* world/SVG coordinates) — set it directly to hand a route in instead, or read it back via
|
|
1834
|
+
* `onEdgeWaypointsChange(edge, waypoints)`. Waypoints beat every auto routing style outright, threading
|
|
1835
|
+
* the line through them in order.
|
|
1836
|
+
* @param {boolean} [options.resizable=false] Diagram-wide default for whether a node shows a resize
|
|
1837
|
+
* handle on hover — an individual node's own `node.resizable` (true/false) overrides this default
|
|
1838
|
+
* either way. Dragging the handle sets `node.radius` (a hard override, beating `sizeBy`/domain
|
|
1839
|
+
* sizing the same way an explicit `node.x`/`node.y` already beats computed layout) or, for a
|
|
1840
|
+
* `shape:'table'` node, `node.width` — see `onNodeResize(node)`.
|
|
1841
|
+
* @param {'top-left'|'top-right'|'bottom-left'|'bottom-right'} [options.resizeCorner='top-right']
|
|
1842
|
+
* Which corner the resize handle sits at, diagram-wide. `resizeCornerByShape: {shape: corner}`
|
|
1843
|
+
* overrides it per node shape, and a node's own `node.resizeCorner` overrides both.
|
|
1844
|
+
* @param {boolean} [options.edgesOnTop=false] Draw lines over boxes rather than under them (ArchiMate
|
|
1845
|
+
* tools do: a line into an element drawn inside another stays visible). Selection handles stay on top.
|
|
1846
|
+
* @param {'center'|'corner'} [options.iconPosition='center'] Where a node's `icon`/`iconUrl` (see the
|
|
1847
|
+
* nodes param above) sits — centred on the shape (replacing the initials) or a small badge at a corner
|
|
1848
|
+
* (`iconCorner`/`iconCornerByShape`/node's own `node.iconCorner` — same precedence as `resizeCorner`,
|
|
1849
|
+
* independently configurable). A node's own `node.iconPosition` overrides this default.
|
|
1850
|
+
* @param {'below'|'inside'} [options.labelPosition='below'] `'below'` is the original caption-under-the-
|
|
1851
|
+
* shape convention; `'inside'` left-aligns the label inside the box instead — meant for a `shape:'rect'`
|
|
1852
|
+
* node with its own `width`/`height` (a non-square override, same idea as a table node's `node.width`).
|
|
1853
|
+
* A node's own `node.labelPosition` overrides this default. See `archimateDiagram`, which sets both.
|
|
1854
|
+
* @param {Array<object>} [options.contextMenuItems] Replaces the built-in node right-click menu
|
|
1855
|
+
* (Pin/Unpin, Rename [editable only], Show Details, Hide, Delete). Each entry is either
|
|
1856
|
+
* `{separator: true}`, `{label, action(node, engine)}` (`label` may be a `label(node)`
|
|
1857
|
+
* function), or `{label, items: [...]}` for a hover pulldown/submenu of the same shape —
|
|
1858
|
+
* useful for a family of related actions (e.g. several layout choices) under one row.
|
|
1859
|
+
* @param {Array<object>} [options.edgeContextMenuItems] Same shape, replacing the built-in edge
|
|
1860
|
+
* menu (Rename/Delete) — only shown when `editable` is true.
|
|
1861
|
+
* @param {Array<object>} [options.canvasContextMenuItems] Same shape, replacing the built-in
|
|
1862
|
+
* canvas (empty-space) menu (Add Node). Unlike the edge menu, supplying this is enough on its
|
|
1863
|
+
* own to show a canvas-level menu even with `editable` left off — the built-in Add Node default
|
|
1864
|
+
* is genuinely an editing action so *that* stays editable-gated, but a caller providing their
|
|
1865
|
+
* own canvas items (e.g. re-arranging nodes — a view action, not an edit) clearly wants a menu
|
|
1866
|
+
* regardless. Every context-menu action — built-in or one of these — receives `(subject, engine,
|
|
1867
|
+
* point)`, `point` being the right-click location in SVG/world coordinates (`{x,y}`).
|
|
1868
|
+
* @param {(node:object)=>void} [options.onNodeDoubleClick] Double-click on a node calls this instead of
|
|
1869
|
+
* doing anything else — nothing built-in happens on its own (no default dialog), so a caller wanting
|
|
1870
|
+
* "double-click to edit" wires this to open their own properties UI (see the Properties dialog in
|
|
1871
|
+
* `test/charts.html`, opened this way there).
|
|
1872
|
+
* @param {(edge:object)=>void} [options.onEdgeDoubleClick] Same, for double-clicking an edge's line.
|
|
1873
|
+
* Double-clicking an existing waypoint handle still removes it regardless (a distinct, precise gesture
|
|
1874
|
+
* on a small routing control point, not "the edge" in general) — this fires for the line itself.
|
|
1875
|
+
* @param {(point:{x:number,y:number})=>void} [options.onCanvasDoubleClick] Double-clicking empty canvas
|
|
1876
|
+
* (no node/edge/waypoint under the cursor) calls this with the click point in SVG/world coordinates —
|
|
1877
|
+
* e.g. to open a diagram/view-level settings dialog, since there's no single node/edge object to edit.
|
|
1878
|
+
*/ graphDiagram(nodes, edges, options = {}) {
|
|
1879
|
+
const { folders = [], folderLayout = {}, elementFolders = [] } = options;
|
|
1880
|
+
|
|
1881
|
+
// Build element-to-folder Map for quick lookup
|
|
1882
|
+
const elementFolderMap = new Map();
|
|
1883
|
+
for (const [elementId, folderId] of elementFolders) {
|
|
1884
|
+
elementFolderMap.set(elementId, folderId);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// Pass elementFolderMap in options for rendering
|
|
1888
|
+
const optionsWithMap = { ...options, elementFolderMap };
|
|
1889
|
+
|
|
1890
|
+
this._chartState = { type: 'graph', nodes, edges, folders, options: optionsWithMap };
|
|
1891
|
+
this._chart3D = false;
|
|
1892
|
+
if (!this.renderer || !this.renderer.supportsSceneGraph) {
|
|
1893
|
+
console.warn('Advanced graph diagram is only supported in SVG renderer.');
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
this.renderer.clear();
|
|
1897
|
+
const diagram = new GraphDiagram(this.renderer, nodes, edges, optionsWithMap, this, folders);
|
|
1898
|
+
this._graphDiagram = diagram;
|
|
1899
|
+
this._renderDiagram(diagram);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
/**
|
|
1903
|
+
* ArchiMate-notation diagram — a thin transform over graphDiagram(),
|
|
1904
|
+
* *not* a separate renderer: it builds plain rect nodes/edges with
|
|
1905
|
+
* ArchiMate's own visual rules and calls graphDiagram() with them, so
|
|
1906
|
+
* every graphDiagram capability (drag, resize, routing, right-click
|
|
1907
|
+
* menus, editable add/rename/delete, ...) applies to the result exactly
|
|
1908
|
+
* as it would to any other graphDiagram.
|
|
1909
|
+
*
|
|
1910
|
+
* The actual notation data — layers, the official ~55-name element type
|
|
1911
|
+
* catalog, the 11 relationship types' line/arrowhead notation — lives in
|
|
1912
|
+
* `src/archimate.js` as real objects (ArchimateLayer/ArchimateElementType/
|
|
1913
|
+
* ArchimateRelationshipType), queried through the `ArchimateNotation`
|
|
1914
|
+
* registry (also `RenderEngine.ArchimateNotation`) rather than this
|
|
1915
|
+
* class's own flat lookup tables; an element's TYPE carries a genuine
|
|
1916
|
+
* reference to its LAYER object, not a name in one map and the same name
|
|
1917
|
+
* in a second one that could drift apart — see `ArchimateNotation.
|
|
1918
|
+
* typesInLayer(layer)` for the query that relationship supports directly
|
|
1919
|
+
* (e.g. a "type" dropdown that must narrow to match a chosen "layer" —
|
|
1920
|
+
* see the Properties/Add Element dialogs in test/charts.html).
|
|
1921
|
+
*
|
|
1922
|
+
* Matches Archi's (archimatetool.com) visual conventions structurally —
|
|
1923
|
+
* per-layer pastel fill, the label inside the box, a small type-icon
|
|
1924
|
+
* badge in a corner, official box corner shapes (chamfered/rounded/sharp
|
|
1925
|
+
* by layer and aspect), and the 11 official relationship line/arrowhead
|
|
1926
|
+
* notations (composition's filled diamond, aggregation's hollow diamond,
|
|
1927
|
+
* realization/specialization's hollow triangle, serving/access/
|
|
1928
|
+
* influence's open arrow, ...). Icons are representative glyphs echoing
|
|
1929
|
+
* each official pictogram's own idea, not a redraw of the real vector
|
|
1930
|
+
* artwork (ArchimateElementType#icon) — there's still no true per-type
|
|
1931
|
+
* icon CATALOG the way, say, a UML tool's stencil palette would have one.
|
|
1932
|
+
*
|
|
1933
|
+
* @param {Array<{id, label, layer?, type?, aspect?, icon?, iconUrl?, x?, y?, width?, height?, color?, cornerStyle?}>} elements
|
|
1934
|
+
* `layer` picks the fill colour (extendable/overridable via `options.layerColors`); if omitted,
|
|
1935
|
+
* derived from `type` (_archimateElementLayer/ArchimateNotation.elementLayer) — first checking
|
|
1936
|
+
* whether `type` is one of the ~55 official ArchiMate element names (most of these do NOT start
|
|
1937
|
+
* with their own layer's name — e.g. `type:'goal'` implies `layer:'motivation'`, `type:'node'`
|
|
1938
|
+
* implies `layer:'technology'` — so a plain prefix guess alone would get them wrong), then falling
|
|
1939
|
+
* back to the prefix of `type` before its first '-' for anything not in that catalog (so a custom
|
|
1940
|
+
* `type:'business-my-widget'` still implies `layer:'business'` with no extra config — this works
|
|
1941
|
+
* for ANY '<layer>-whatever' string, official name or not); `element.layer`/`type` both missing
|
|
1942
|
+
* falls back to 'other' (neutral grey). An explicit `element.color` always wins over the derived
|
|
1943
|
+
* layer colour, same as plain graphDiagram. The resolved `layer` (explicit or derived) is always
|
|
1944
|
+
* written back onto the built node — so it's present and editable even for an element that only
|
|
1945
|
+
* ever set `type`, not `layer` itself. An element's own `icon`/`iconUrl` always wins for its corner
|
|
1946
|
+
* badge; otherwise an official `type`'s own representative icon is used, then just a generic
|
|
1947
|
+
* per-layer one — either can be overridden diagram-wide via `options.defaultIcons`.
|
|
1948
|
+
* **Box corner style** (`_archimateCornerStyle`, matching the official notation's own generic
|
|
1949
|
+
* shapes): every Motivation-layer element is chamfered (all 4 corners cut) regardless of anything
|
|
1950
|
+
* else; `'behavior'` aspect (Process/Function/Service/Event/Interaction-like elements) gets rounded
|
|
1951
|
+
* corners — an explicit `aspect` wins, else an official `type`'s own aspect (so `type:'business-
|
|
1952
|
+
* process'` is rounded with zero extra config, same "official name already knows" idea as layer);
|
|
1953
|
+
* everything else is sharp (correct for active/passive-structure elements, the common case). An
|
|
1954
|
+
* element's own `cornerStyle` (`'sharp'|'rounded'|'chamfered'`) overrides all of that directly.
|
|
1955
|
+
* @param {Array<{source, target, kind?, label?}>} relationships
|
|
1956
|
+
* `kind` — one of composition/aggregation/assignment/realization/specialization/serving/
|
|
1957
|
+
* access/influence/triggering/flow/association (the 11 official ArchiMate relationship
|
|
1958
|
+
* types), or your own — picks the line style/arrowhead(s), extendable/overridable via
|
|
1959
|
+
* `options.relationshipStyles`; an explicit `lineStyle`/`dashed`/`arrowhead`/`arrowheadStart`
|
|
1960
|
+
* on any one relationship still overrides its kind's default, same as a plain graphDiagram
|
|
1961
|
+
* edge. An unrecognised/missing `kind` falls back to a plain solid association line rather
|
|
1962
|
+
* than being rejected.
|
|
1963
|
+
* @param {object} [options] Everything graphDiagram accepts (editable, resizable, onNodeClick,
|
|
1964
|
+
* contextMenuItems, ...), plus:
|
|
1965
|
+
* @param {object} [options.layerColors] Extra/overriding `layer -> color` entries.
|
|
1966
|
+
* @param {object} [options.relationshipStyles] Extra/overriding `kind -> {lineStyle,arrowhead,arrowheadStart}` entries.
|
|
1967
|
+
* @param {object} [options.defaultIcons] Extra/overriding `layer -> glyph` entries — wins even over an
|
|
1968
|
+
* official type's own icon (a deliberate diagram-wide override), used ONLY for an element that sets
|
|
1969
|
+
* neither `icon` nor `iconUrl` itself, so every element still shows some corner badge out of the box.
|
|
1970
|
+
* @param {number} [options.elementWidth=120] @param {number} [options.elementHeight=55]
|
|
1971
|
+
* Default box size for an element that doesn't set its own `width`/`height`.
|
|
1972
|
+
* @param {'3.2'|'4'} [options.archimateVersion='4'] Which ArchiMate version's element catalog/notation
|
|
1973
|
+
* applies (`ArchimateNotation.versions()`) — defaults to '4', the current standard. Under '4' (the
|
|
1974
|
+
* default), every element is first run through `ArchimateNotation.migrateElementToV4` (a no-op for
|
|
1975
|
+
* anything not one of the 19 retired/consolidated v3.2 types — see src/archimate.js's own doc
|
|
1976
|
+
* comment), so 3.2-authored data (e.g. `type:'business-process'`) still renders correctly, auto-
|
|
1977
|
+
* upgraded to its v4 equivalent (`type:'process'`) and tagged with its own migration provenance
|
|
1978
|
+
* (`ArchiMate4_Domain`/`MigratedFrom`, or `LegacyType`/`Type`+`MigratedFrom` for a retired type) —
|
|
1979
|
+
* both plain extra data, visible in the hover tooltip/Properties dialog like any other custom field.
|
|
1980
|
+
* Under '3.2', no migration happens and the element/relationship catalogs are the original ones.
|
|
1981
|
+
* @param {boolean} [options.useDistinctShapes=false] Diagram-wide default for whether an element with a
|
|
1982
|
+
* real ArchiMate notation shape (`ArchimateElementType#notationShape` — arrow for Process, chevron for
|
|
1983
|
+
* Function, cylinder for Role, cube for Node, ... — cross-checked against the spec's own Appendix A;
|
|
1984
|
+
* not every type has one) renders as that shape instead of the plain box + corner icon — an individual
|
|
1985
|
+
* element's own `useDistinctShape` (true/false) overrides this default either way. A type with no
|
|
1986
|
+
* implemented notationShape always renders as the plain box regardless.
|
|
1987
|
+
*/
|
|
1988
|
+
archimateDiagram(elements, relationships, options = {}) {
|
|
1989
|
+
this._renderDiagram(new ArchimateDiagram(this.renderer, elements, relationships, options, this));
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* Live diagram-wide setting: which choice in each element type's own
|
|
1994
|
+
* `iconChoices` (src/archimate.js — choice 0 is always the ArchiMate-
|
|
1995
|
+
* standard glyph) is used as the DEFAULT icon for a node that doesn't
|
|
1996
|
+
* carry its own explicit `icon`/`iconUrl` — see archimateDiagram's own
|
|
1997
|
+
* `iconChoiceIndex` option, which only sets this once at build time.
|
|
1998
|
+
* Changing it here re-derives the icon for every node that hasn't been
|
|
1999
|
+
* individually customised (its current icon still matches what
|
|
2000
|
+
* auto-derivation gave it under the OLD index — the same "was it ever
|
|
2001
|
+
* customised" test the Properties dialog's own layer/type-change
|
|
2002
|
+
* handler uses in test/charts.html), leaving any node whose icon was
|
|
2003
|
+
* deliberately picked (via that dialog, or the original `elements` data)
|
|
2004
|
+
* untouched. No-op if this isn't (or is no longer) an ArchiMate diagram.
|
|
2005
|
+
*/
|
|
2006
|
+
_archimateSetIconChoiceIndex(newIndex) {
|
|
2007
|
+
ArchimateDiagram.setIconChoiceIndex(this, newIndex);
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
/** Pure. Maps a raw column/attribute type string (any casing/precision,
|
|
2011
|
+
* e.g. 'VARCHAR(255)', 'bigint', 'TIMESTAMPTZ') to one of the `shape:
|
|
2012
|
+
* 'table'` node's own colour-coded groups (see _buildTableNodeContent's
|
|
2013
|
+
* TYPE_COLORS) — 'UNKNOWN' (a neutral grey dot) for anything
|
|
2014
|
+
* unrecognised or unset, same fallback a real sqlengine-style table
|
|
2015
|
+
* node already uses for a column with no known type. */
|
|
2016
|
+
_dataModelTypeGroup(type) {
|
|
2017
|
+
return DataModelDiagram.typeGroup(type);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
/**
|
|
2021
|
+
* Pure. Builds one dataModelDiagram node from a raw `entity` at a given
|
|
2022
|
+
* level/notation — reusable directly, the same "the diagram only
|
|
2023
|
+
* enriches its own up-front input" reasoning as _archimateBuildNode/
|
|
2024
|
+
* _archimateBuildEdge (see their own comments) applies here too, for a
|
|
2025
|
+
* caller's own "add entity" UI. `entity.name`/`description` are shared
|
|
2026
|
+
* across every level (the entity's own identity); `entity[level]`
|
|
2027
|
+
* supplies that level's own `attributes` (and physical's own
|
|
2028
|
+
* `tableName`) — an entity with none at the current level (typical for
|
|
2029
|
+
* 'conceptual') always renders as a plain box, regardless of notation.
|
|
2030
|
+
* @param {object} entity {id, name, description?, x?, y?, color?,
|
|
2031
|
+
* conceptual?, logical?, physical?}
|
|
2032
|
+
* @param {{level: string, notation: NotationStyle}} opts
|
|
2033
|
+
*/
|
|
2034
|
+
_dataModelBuildNode(entity, { level, notation }) {
|
|
2035
|
+
return DataModelDiagram.buildNode(entity, { level, notation });
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
/**
|
|
2039
|
+
* Pure. Builds one dataModelDiagram edge from a raw `relationship` —
|
|
2040
|
+
* ONLY for a notation whose `relationshipAsNode` is false (Chen's "the
|
|
2041
|
+
* relationship IS its own diamond node, with two plain edges instead of
|
|
2042
|
+
* one" is handled directly in dataModelDiagram, since it touches both
|
|
2043
|
+
* the nodes and edges arrays, not just one edge). `relationship.
|
|
2044
|
+
* identifying === false` renders as a dashed line (a non-identifying
|
|
2045
|
+
* relationship, in real ERD terms) — unset/true stays solid.
|
|
2046
|
+
* @param {object} relationship {source, target, name?,
|
|
2047
|
+
* sourceCardinality?, targetCardinality?, identifying?}
|
|
2048
|
+
* @param {NotationStyle} notation
|
|
2049
|
+
*/
|
|
2050
|
+
_dataModelBuildEdge(relationship, notation) {
|
|
2051
|
+
return DataModelDiagram.buildEdge(relationship, notation);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* A conceptual/logical/physical data (entity-relationship) diagram — a
|
|
2056
|
+
* thin transform over graphDiagram, the same idea as archimateDiagram:
|
|
2057
|
+
* builds enriched nodes/edges, then calls graphDiagram with them. See
|
|
2058
|
+
* src/datamodel.js's own doc comment for the full reasoning; in short:
|
|
2059
|
+
*
|
|
2060
|
+
* **Three levels, one model — the cross-mapping.** `options.level`
|
|
2061
|
+
* (`'conceptual'|'logical'|'physical'`, default `'logical'`) picks which
|
|
2062
|
+
* of each entity's own `entity.conceptual`/`.logical`/`.physical`
|
|
2063
|
+
* sub-object supplies its attributes — `entity.name`/`.description` stay
|
|
2064
|
+
* shared across all three (the same object/visual split archimateDiagram
|
|
2065
|
+
* draws between an element and its diagram, just spanning "levels"
|
|
2066
|
+
* instead of "diagrams"). Re-calling this with a different `level` over
|
|
2067
|
+
* the SAME `entities`/`relationships` re-renders the same model at
|
|
2068
|
+
* different fidelity, so a conceptual entity, its logical attributes,
|
|
2069
|
+
* and its physical column types can never drift out of correspondence —
|
|
2070
|
+
* there's only ever one model, never three separately-maintained
|
|
2071
|
+
* diagrams.
|
|
2072
|
+
*
|
|
2073
|
+
* **Three notations.** `options.notation` (`'chen'|'crowsfoot'|'uml'`,
|
|
2074
|
+
* default `'crowsfoot'`) — Chen renders each relationship as its own
|
|
2075
|
+
* diamond-shaped node between two plain, text-labelled edges; Crow's
|
|
2076
|
+
* Foot renders cardinality as an arrowhead-style glyph at each entity
|
|
2077
|
+
* end; UML shows the cardinality (as a multiplicity string) as a text
|
|
2078
|
+
* label near each end instead (`edge.sourceLabel`/`targetLabel`).
|
|
2079
|
+
*
|
|
2080
|
+
* @param {Array<object>} entities See _dataModelBuildNode.
|
|
2081
|
+
* @param {Array<object>} relationships {source, target, name?,
|
|
2082
|
+
* sourceCardinality?, targetCardinality?, identifying?} — cardinality
|
|
2083
|
+
* is a UML multiplicity string (`DataModelNotation.cardinalities()`).
|
|
2084
|
+
* @param {object} [options] Everything graphDiagram accepts, plus:
|
|
2085
|
+
* @param {'conceptual'|'logical'|'physical'} [options.level='logical']
|
|
2086
|
+
* @param {'chen'|'crowsfoot'|'uml'} [options.notation='crowsfoot']
|
|
2087
|
+
*/
|
|
2088
|
+
dataModelDiagram(entities, relationships, options = {}) {
|
|
2089
|
+
this._renderDiagram(new DataModelDiagram(this.renderer, entities, relationships, options, this));
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
/**
|
|
2093
|
+
* A real 3D node-graph — nodes carry (or get simulated) x/y/z positions,
|
|
2094
|
+
* viewed through the same orbit camera as bubbleChart3D (drag to rotate,
|
|
2095
|
+
* scroll to zoom, double-click to reset — see _addOrbit3DListeners),
|
|
2096
|
+
* projected via OrbitCamera/Mat4 rather than 2D graphDiagram()'s SVG
|
|
2097
|
+
* viewBox pan/zoom. Nodes are simple point+label (matching bubbleChart3D's
|
|
2098
|
+
* dot style) — there is no per-node dragging in 3D (only camera orbit);
|
|
2099
|
+
* dragging a point in 2D screen space doesn't have a single obvious 3D
|
|
2100
|
+
* interpretation, so it's deliberately out of scope here.
|
|
2101
|
+
* @param {Array<{id:string, label?:string, x?:number, y?:number, z?:number, color?:string, group?:string, size?:number}>} nodes
|
|
2102
|
+
* @param {Array<{source:string, target:string, color?:string, weight?:number}>} edges
|
|
2103
|
+
* @param {object} [options]
|
|
2104
|
+
* @param {(node:object)=>void} [options.onNodeClick]
|
|
2105
|
+
* @param {number} [options.nodeRadius=8]
|
|
2106
|
+
* @param {'degree'} [options.sizeBy] Size nodes by connection count instead of `node.size`.
|
|
2107
|
+
* @param {number} [options.minRadius] @param {number} [options.maxRadius] @param {'linear'|'sqrt'} [options.sizeScale]
|
|
2108
|
+
* @param {number} [options.minEdgeWidth] @param {number} [options.maxEdgeWidth] @param {'linear'|'sqrt'} [options.edgeWidthScale]
|
|
2109
|
+
* Edge thickness from `edge.weight`, same scaling shape as node sizing.
|
|
2110
|
+
* @param {number} [options.initialTheta=-0.6] @param {number} [options.initialPhi=0.35] @param {number} [options.initialDistance=2.2]
|
|
2111
|
+
*/
|
|
2112
|
+
graphDiagram3D(nodes, edges, options = {}) {
|
|
2113
|
+
const { initialTheta = -0.6, initialPhi = 0.35, initialDistance = 2.2 } = options;
|
|
2114
|
+
if (!this._camera3D) this._camera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
2115
|
+
this._initialCamera3D = { theta: initialTheta, phi: initialPhi, distance: initialDistance };
|
|
2116
|
+
this._chart3D = true;
|
|
2117
|
+
this._chart3DRedraw = () => this.graphDiagram3D(nodes, edges, options);
|
|
2118
|
+
this._renderDiagram(new GraphDiagram3D(this.renderer, nodes, edges, options, this._camera3D, this));
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
/** Pure. Resolves a node's display label — delegates to LabelHelper. */
|
|
2122
|
+
_resolveNodeLabel(node) {
|
|
2123
|
+
return LabelHelper.resolveNodeLabel(node);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/** Pure. Word-wraps label text — delegates to LabelHelper. */
|
|
2127
|
+
_wrapLabelLines(text, maxWidth, fontSize, maxHeight, lineHeight) {
|
|
2128
|
+
return LabelHelper.wrapLabelLines(text, maxWidth, fontSize, maxHeight, lineHeight);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
/** Pure. "key: value" lines for caller-supplied extra data — delegates to
|
|
2132
|
+
* LabelHelper. */
|
|
2133
|
+
_nodeExtraDataLines(node, knownKeys) {
|
|
2134
|
+
return LabelHelper.nodeExtraDataLines(node, knownKeys);
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
/** Pure. Which ArchiMate layer an element belongs to — delegates to
|
|
2138
|
+
* ArchimateDiagram.elementLayer so the real logic lives with the diagram. */
|
|
2139
|
+
_archimateElementLayer(element, version) {
|
|
2140
|
+
return ArchimateDiagram.elementLayer(element, version);
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
/** Pure. Fill colour for an ArchiMate `layer` name — delegates to
|
|
2144
|
+
* ArchimateDiagram.layerColor. */
|
|
2145
|
+
_archimateLayerColor(layer, layerColors, ownColor) {
|
|
2146
|
+
return ArchimateDiagram.layerColor(layer, layerColors, ownColor);
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
/** Pure. Default corner-badge glyph for an ArchiMate element — delegates
|
|
2150
|
+
* to ArchimateDiagram.defaultIcon. */
|
|
2151
|
+
_archimateDefaultIcon(element, layer, defaultIcons, iconChoiceIndex = 0, version) {
|
|
2152
|
+
return ArchimateDiagram.defaultIcon(element, layer, defaultIcons, iconChoiceIndex, version);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
/** Pure. `{lineStyle, arrowhead, arrowheadStart}` notation for a
|
|
2156
|
+
* relationship `kind` — delegates to ArchimateDiagram.relationshipStyle. */
|
|
2157
|
+
_archimateRelationshipStyle(kind, relationshipStyles) {
|
|
2158
|
+
return ArchimateDiagram.relationshipStyle(kind, relationshipStyles);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
/** Builds one archimateDiagram node from a raw `element` — delegates to
|
|
2162
|
+
* ArchimateDiagram.buildNode so the diagram class owns the transform. */
|
|
2163
|
+
_archimateBuildNode(element, options = {}) {
|
|
2164
|
+
return ArchimateDiagram.buildNode(element, options);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
/** Pure. Builds one archimateDiagram edge from a raw `relationship` —
|
|
2168
|
+
* delegates to ArchimateDiagram.buildEdge. */
|
|
2169
|
+
_archimateBuildEdge(relationship, options = {}) {
|
|
2170
|
+
return ArchimateDiagram.buildEdge(relationship, options);
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
/** Pure. Which corner style an ArchiMate `shape:'rect'` node renders —
|
|
2174
|
+
* delegates to ArchimateDiagram.cornerStyle. */
|
|
2175
|
+
_archimateCornerStyle(element, layer, version) {
|
|
2176
|
+
return ArchimateDiagram.cornerStyle(element, layer, version);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
/** Pure. SVG path `d` for a chamfered rectangle — delegates to NodeGeometry. */
|
|
2180
|
+
_chamferedRectPath(w, h, chamfer) {
|
|
2181
|
+
return NodeGeometry.chamferedRectPath(w, h, chamfer);
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
/** Build the SVG element for a node's shape — delegates to NodeGeometry. */
|
|
2185
|
+
_nodeShapeEl(shape, R, color, size, cornerStyle, strokeColor = '#fff', strokeWidth = 2, iconUrl = null, icon = null) {
|
|
2186
|
+
return NodeGeometry.nodeShapeEl(this.renderer, shape, R, color, size, cornerStyle, strokeColor, strokeWidth, iconUrl, icon);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
/** Bounding box a node occupies — delegates to NodeGeometry.nodeSize. */
|
|
2190
|
+
_nodeSize(node, R, sizeDomain, sizeOptions) {
|
|
2191
|
+
return NodeGeometry.nodeSize(node, R, sizeDomain, sizeOptions);
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
/** Pure. What a node's own size VALUE is, before it's mapped to a radius —
|
|
2195
|
+
* delegates to GraphMetrics.nodeSizeValue. */
|
|
2196
|
+
_nodeSizeValue(node, sizeBy, degreeMap) {
|
|
2197
|
+
return GraphMetrics.nodeSizeValue(node, sizeBy, degreeMap);
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
/** Pure — no DOM/renderer access. {min,max} of every node's size VALUE —
|
|
2201
|
+
* delegates to GraphMetrics.computeSizeDomain. */
|
|
2202
|
+
_computeSizeDomain(nodes, { sizeBy, degreeMap } = {}) {
|
|
2203
|
+
return GraphMetrics.computeSizeDomain(nodes, { sizeBy, degreeMap });
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
/** Pure. Maps a node's size value into [minRadius,maxRadius] — delegates to
|
|
2207
|
+
* GraphMetrics.effectiveNodeRadius. */
|
|
2208
|
+
_effectiveNodeRadius(node, R, sizeDomain, sizeOptions = {}) {
|
|
2209
|
+
return GraphMetrics.effectiveNodeRadius(node, R, sizeDomain, sizeOptions);
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
/** Pure. Corner point of a box — delegates to NodeGeometry.cornerPoint. */
|
|
2213
|
+
_cornerPoint(pos, size, corner) {
|
|
2214
|
+
return NodeGeometry.cornerPoint(pos, size, corner);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
/** Pure. Generic 3-tier corner resolution — delegates to NodeGeometry. */
|
|
2218
|
+
_resolveNodeCorner(node, nodeKey, byShape, defaultCorner) {
|
|
2219
|
+
return NodeGeometry.resolveNodeCorner(node, nodeKey, byShape, defaultCorner);
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
/** Pure. Resize-handle corner — delegates to NodeGeometry.nodeResizeCorner. */
|
|
2223
|
+
_nodeResizeCorner(node, resizeCornerByShape, defaultCorner = 'top-right') {
|
|
2224
|
+
return NodeGeometry.nodeResizeCorner(node, resizeCornerByShape, defaultCorner);
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
/** Pure. Icon-badge corner — delegates to NodeGeometry.nodeIconCorner. */
|
|
2228
|
+
_nodeIconCorner(node, iconCornerByShape, defaultCorner = 'top-right') {
|
|
2229
|
+
return NodeGeometry.nodeIconCorner(node, iconCornerByShape, defaultCorner);
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
/** Pure. Resize-handle drag result — delegates to NodeGeometry.resizedNodeValue. */
|
|
2233
|
+
_resizedNodeValue(node, corner, center, mousePos) {
|
|
2234
|
+
return NodeGeometry.resizedNodeValue(node, corner, center, mousePos);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
/** Pure — no DOM/renderer access. Map<nodeId, connectionCount> — delegates
|
|
2238
|
+
* to GraphMetrics.computeNodeDegrees. */
|
|
2239
|
+
_computeNodeDegrees(nodes, edges) {
|
|
2240
|
+
return GraphMetrics.computeNodeDegrees(nodes, edges);
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
/** Pure. {min,max} of edge.weight across all edges — delegates to
|
|
2244
|
+
* GraphMetrics.computeEdgeWidthDomain. */
|
|
2245
|
+
_computeEdgeWidthDomain(edges) {
|
|
2246
|
+
return GraphMetrics.computeEdgeWidthDomain(edges);
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
/** Pure. Maps edge.weight into [minEdgeWidth,maxEdgeWidth] — delegates to
|
|
2250
|
+
* GraphMetrics.effectiveEdgeWidth. */
|
|
2251
|
+
_effectiveEdgeWidth(edge, defaultWidth, widthDomain, widthOptions = {}) {
|
|
2252
|
+
return GraphMetrics.effectiveEdgeWidth(edge, defaultWidth, widthDomain, widthOptions);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
/** Pure — no DOM/renderer access. Table-node size — delegates to NodeGeometry. */
|
|
2256
|
+
_tableNodeSize(node) {
|
|
2257
|
+
return NodeGeometry.tableNodeSize(node);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
/** World-space Y of a named column's row within a table node — delegates to
|
|
2261
|
+
* NodeGeometry.tableColumnY. */
|
|
2262
|
+
_tableColumnY(node, posEntry, size, colName) {
|
|
2263
|
+
return NodeGeometry.tableColumnY(node, posEntry, size, colName);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
// Edge geometry helpers — delegated to EdgeGeometry.
|
|
2267
|
+
_edgeAnchor(pos, size, side, ux, uy, rectBoundary) {
|
|
2268
|
+
return EdgeGeometry.edgeAnchor(pos, size, side, ux, uy, rectBoundary);
|
|
2269
|
+
}
|
|
2270
|
+
_edgeSideOrientation(side) {
|
|
2271
|
+
return EdgeGeometry.edgeSideOrientation(side);
|
|
2272
|
+
}
|
|
2273
|
+
_curvedEdgePath(a1, a2) {
|
|
2274
|
+
return EdgeGeometry.curvedEdgePath(a1, a2);
|
|
2275
|
+
}
|
|
2276
|
+
_orthogonalEdgePath(a1, a2, sourceSide, targetSide) {
|
|
2277
|
+
return EdgeGeometry.orthogonalEdgePath(a1, a2, sourceSide, targetSide);
|
|
2278
|
+
}
|
|
2279
|
+
_segmentIntersectsCircle(a1, a2, circle) {
|
|
2280
|
+
return EdgeGeometry.segmentIntersectsCircle(a1, a2, circle);
|
|
2281
|
+
}
|
|
2282
|
+
_polylineEdgePath(a1, a2, obstacles = []) {
|
|
2283
|
+
return EdgeGeometry.polylineEdgePath(a1, a2, obstacles);
|
|
2284
|
+
}
|
|
2285
|
+
_nearestSegmentInsertIndex(points, x, y) {
|
|
2286
|
+
return EdgeGeometry.nearestSegmentInsertIndex(points, x, y);
|
|
2287
|
+
}
|
|
2288
|
+
_edgeWaypointsWithInsertion(edge, sourcePos, targetPos, point) {
|
|
2289
|
+
return EdgeGeometry.edgeWaypointsWithInsertion(edge, sourcePos, targetPos, point);
|
|
2290
|
+
}
|
|
2291
|
+
_pointToSegmentDist2(a, b, x, y) {
|
|
2292
|
+
return EdgeGeometry.pointToSegmentDist2(a, b, x, y);
|
|
2293
|
+
}
|
|
2294
|
+
_computeEdgePath(sourceInfo, sourcePos, sourceSize, targetInfo, targetPos, targetSize, edge, defaultRouting = 'straight', obstacles = []) {
|
|
2295
|
+
return EdgeGeometry.computeEdgePath(sourceInfo, sourcePos, sourceSize, targetInfo, targetPos, targetSize, edge, defaultRouting, obstacles);
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
/** Draws a table-card node's header + one row per column into wrapper `g`. */
|
|
2299
|
+
_buildTableNodeContent(g, node) {
|
|
2300
|
+
return NodeGeometry.buildTableNodeContent(this.renderer, g, node);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* Re-render the current graph diagram, if one is active. Exposed as a
|
|
2305
|
+
* convenience so consumer callbacks and demo dialogs can trigger a redraw
|
|
2306
|
+
* without reaching into `engine._graphDiagram` directly.
|
|
2307
|
+
*
|
|
2308
|
+
* Consumer code (e.g. test/charts.html's add-note/add-image dialogs) updates
|
|
2309
|
+
* `engine._chartState.nodes/edges/options` and then calls this method. The
|
|
2310
|
+
* active GraphDiagram instance keeps its own `_chartState`, so we push the
|
|
2311
|
+
* engine state into the diagram before re-rendering and pull the resulting
|
|
2312
|
+
* state back out, keeping the two references in sync.
|
|
2313
|
+
*/
|
|
2314
|
+
_rerenderGraph() {
|
|
2315
|
+
if (!this._graphDiagram) return;
|
|
2316
|
+
this._graphDiagram._chartState = this._chartState;
|
|
2317
|
+
this._graphDiagram._rerenderGraph();
|
|
2318
|
+
this._chartState = this._graphDiagram._chartState;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
/**
|
|
2322
|
+
* Generate a unique node id from a label, using the current graph diagram's
|
|
2323
|
+
* node list (or the static helper if no diagram is active yet). Mirrors
|
|
2324
|
+
* GraphDiagram._generateNodeId so add-note / add-image dialogs don't need
|
|
2325
|
+
* to reach into `engine._graphDiagram`.
|
|
2326
|
+
*/
|
|
2327
|
+
_generateNodeId(label) {
|
|
2328
|
+
if (this._graphDiagram) return this._graphDiagram._generateNodeId(label);
|
|
2329
|
+
return GraphDiagram.generateNodeId(label, (this._chartState?.nodes ?? []).map(n => n.id));
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
/**
|
|
2333
|
+
* Renders a railroad diagram.
|
|
2334
|
+
* @param {Diagram} diagram A diagram object from railroadDiagram.js
|
|
2335
|
+
*/
|
|
2336
|
+
railroadDiagram(diagram) {
|
|
2337
|
+
if (this.renderer) {
|
|
2338
|
+
this.renderer.clear();
|
|
2339
|
+
this.renderer.renderDiagram(diagram);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
/**
|
|
2344
|
+
* Renders an EBNF grammar rule as a railroad (syntax) diagram. Accepts
|
|
2345
|
+
* either form:
|
|
2346
|
+
* - a raw EBNF string (e.g. `"DOUBLE ::= [0-9]+ ('.' [0-9]*)?"`) —
|
|
2347
|
+
* parsed via `parseEbnf` (ebnfParser.js); the verbatim input text is
|
|
2348
|
+
* also drawn in a caption band under the diagram.
|
|
2349
|
+
* - an already-parsed AST node — the bnfengine-shaped tree
|
|
2350
|
+
* (`{type:'Terminal'|'NonTerminal'|'Sequence'|'Choice'|'Optional'|
|
|
2351
|
+
* 'Repetition'|'Group', ...}` — see ebnfDiagram.js's own header)
|
|
2352
|
+
* converted via `ebnfAstToDiagram` before delegating to
|
|
2353
|
+
* `railroadDiagram()`. No caption is drawn for this form — there's no
|
|
2354
|
+
* source text to show.
|
|
2355
|
+
* @param {string|object} rule EBNF text, or a bnfengine-shaped AST node.
|
|
2356
|
+
* @param {object} [options]
|
|
2357
|
+
* @param {string} [options.captionBackground='#fdf6e3'] Caption band fill (text form only).
|
|
2358
|
+
*/
|
|
2359
|
+
ebnfDiagram(rule, options = {}) {
|
|
2360
|
+
if (!this.renderer) return;
|
|
2361
|
+
this._renderDiagram(new EbnfDiagram(this.renderer, rule, options));
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
/**
|
|
2365
|
+
* Performs a hit test at the given coordinates.
|
|
2366
|
+
* @param {number} x The x-coordinate.
|
|
2367
|
+
* @param {number} y The y-coordinate.
|
|
2368
|
+
* @returns {object | null} The shape object that was hit, or null.
|
|
2369
|
+
*/
|
|
2370
|
+
hitTest(x, y) {
|
|
2371
|
+
const direct = this.renderer?.hitTest ? this.renderer.hitTest(x, y) : null;
|
|
2372
|
+
if (direct) return direct;
|
|
2373
|
+
// Pixel-exact SVG hitTest misses thin or angled edges. Fall back to a
|
|
2374
|
+
// distance-based search over the active graph diagram's edge paths.
|
|
2375
|
+
const svgPoint = this._clientToSvgPoint?.(x, y);
|
|
2376
|
+
if (!svgPoint || !this._graphDiagram) return null;
|
|
2377
|
+
const tolerance = this._chartState?.options?.edgeHitTolerance ?? 14;
|
|
2378
|
+
const edge = this._graphDiagram.findNearestEdge(svgPoint.x, svgPoint.y, tolerance);
|
|
2379
|
+
return edge ? { isEdge: true, edge } : null;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
/**
|
|
2383
|
+
* Live-filter the current graph diagram by keyword. Nodes whose label or
|
|
2384
|
+
* extra data does not match are hidden; edges touching hidden nodes are
|
|
2385
|
+
* hidden too. In hierarchical mode ancestors of a match stay visible.
|
|
2386
|
+
* Pass an empty string to clear the filter.
|
|
2387
|
+
* @param {string} keyword
|
|
2388
|
+
*/
|
|
2389
|
+
filterGraphNodes(keyword) {
|
|
2390
|
+
this._graphDiagram?.filterGraphNodes?.(keyword);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
/** Screen (clientX/clientY) → SVG user-space coordinates, honoring the
|
|
2394
|
+
* current pan/zoom viewBox (_addZoomListeners). Shared by drag,
|
|
2395
|
+
* add-node, and edge-creation — all of which need a graph-space point,
|
|
2396
|
+
* not a raw screen pixel. */
|
|
2397
|
+
_clientToSvgPoint(clientX, clientY) {
|
|
2398
|
+
return SvgCoordinate.clientToSvgPoint(this.renderer?.svg, clientX, clientY);
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
/**
|
|
2402
|
+
* Applies a named state to a shape.
|
|
2403
|
+
* @param {object} shape The shape object to apply the state to.
|
|
2404
|
+
* @param {string} stateName The name of the state (e.g., 'hover').
|
|
2405
|
+
*/
|
|
2406
|
+
enterState(shape, stateName) {
|
|
2407
|
+
if (this.renderer?.enterState) {
|
|
2408
|
+
this.renderer.enterState(shape, stateName);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
/**
|
|
2413
|
+
* Removes a named state from a shape.
|
|
2414
|
+
* @param {object} shape The shape object to remove the state from.
|
|
2415
|
+
* @param {string} stateName The name of the state (e.g., 'hover').
|
|
2416
|
+
*/
|
|
2417
|
+
leaveState(shape, stateName) {
|
|
2418
|
+
if (this.renderer?.leaveState) {
|
|
2419
|
+
this.renderer.leaveState(shape, stateName);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
2424
|
+
// SVGEngine Compatibility Methods & Utilities
|
|
2425
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
2426
|
+
|
|
2427
|
+
/**
|
|
2428
|
+
* Resolves a dot-notation path in the global data object.
|
|
2429
|
+
* Compatible with SVGEngine's _resolvePath pattern.
|
|
2430
|
+
* @param {string} path Dot-notation path (e.g., 'user.name', 'data.items.0.value')
|
|
2431
|
+
* @param {Function} [mapper] Optional function to transform the resolved value
|
|
2432
|
+
* @returns {*} The resolved value or undefined
|
|
2433
|
+
*/
|
|
2434
|
+
resolvePath(path, mapper, globalData = this.globalData) {
|
|
2435
|
+
if (!path) return undefined;
|
|
2436
|
+
let val = path.split('.').reduce((p, c) => {
|
|
2437
|
+
if (p && typeof p === 'object' && c in p) {
|
|
2438
|
+
return p[c];
|
|
2439
|
+
} else if (Array.isArray(p) && !isNaN(c)) {
|
|
2440
|
+
return p[parseInt(c, 10)];
|
|
2441
|
+
}
|
|
2442
|
+
return undefined;
|
|
2443
|
+
}, globalData);
|
|
2444
|
+
return (mapper && val !== undefined) ? mapper(val, globalData) : val;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* Sets global data that can be referenced in chart options via paths.
|
|
2449
|
+
* @param {object} data The global data object
|
|
2450
|
+
*/
|
|
2451
|
+
setGlobalData(data) {
|
|
2452
|
+
this.globalData = data;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
/**
|
|
2456
|
+
* Generates nice round tick values for axis labels.
|
|
2457
|
+
* Ensures readable, evenly-spaced values on axes.
|
|
2458
|
+
* @param {number} maxVal The maximum value
|
|
2459
|
+
* @param {number} [count=4] Desired number of ticks
|
|
2460
|
+
* @returns {number[]} Array of tick values
|
|
2461
|
+
*/
|
|
2462
|
+
niceTicks(maxVal, count = 4) {
|
|
2463
|
+
return Axis.niceTicks(maxVal, count);
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
/**
|
|
2467
|
+
* Formats a value for display with appropriate units (M, k, etc.).
|
|
2468
|
+
* @param {number} v The value to format
|
|
2469
|
+
* @returns {string} Formatted value string
|
|
2470
|
+
*/
|
|
2471
|
+
formatValue(v) {
|
|
2472
|
+
return Axis.formatValue(v);
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
/**
|
|
2476
|
+
* Draws numeric y-axis tick marks + value labels (via niceTicks/
|
|
2477
|
+
* formatValue) along the left edge of a cartesian plot area, plus a
|
|
2478
|
+
* light horizontal gridline per tick. Shared by every chart method with
|
|
2479
|
+
* a numeric, zero-based y-axis — call it once, before drawing the
|
|
2480
|
+
* chart's own marks, so gridlines sit behind them.
|
|
2481
|
+
*
|
|
2482
|
+
* niceTicks rounds its top tick UP past the raw data max (e.g. a max
|
|
2483
|
+
* value of 82 becomes a top tick of 100) — so this returns that
|
|
2484
|
+
* "nice" max rather than the raw one passed in, and the caller MUST
|
|
2485
|
+
* scale its own marks (bar heights, line points, ...) against the
|
|
2486
|
+
* returned value, not the raw max, or the tallest mark won't reach the
|
|
2487
|
+
* axis's own top tick.
|
|
2488
|
+
* @param {number} x Left edge of the plot area (data-space origin).
|
|
2489
|
+
* @param {number} y Top edge of the plot area.
|
|
2490
|
+
* @param {number} width Plot area width.
|
|
2491
|
+
* @param {number} height Plot area height.
|
|
2492
|
+
* @param {number} rawMax The raw (un-rounded) data maximum.
|
|
2493
|
+
* @returns {number} The "nice" domain max the drawn ticks actually span.
|
|
2494
|
+
*/
|
|
2495
|
+
_drawYAxisTicks(x, y, width, height, rawMax) {
|
|
2496
|
+
return new Axis().drawYAxis(this.renderer, { x, y, width, height }, rawMax);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* Binds a tooltip to an element (for Canvas context).
|
|
2501
|
+
* Can be extended for use with interactive features.
|
|
2502
|
+
* @param {object} element The element or shape to bind tooltip to
|
|
2503
|
+
* @param {string} text The tooltip text
|
|
2504
|
+
*/
|
|
2505
|
+
bindTooltip(element, text) {
|
|
2506
|
+
if (!text || !element) return;
|
|
2507
|
+
if (element.tooltip) {
|
|
2508
|
+
element.tooltip = text;
|
|
2509
|
+
} else if (element.setAttribute && typeof element.setAttribute === 'function') {
|
|
2510
|
+
element.setAttribute('title', text);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
/**
|
|
2515
|
+
* Binds a rich tooltip with multiple rows to an element.
|
|
2516
|
+
* @param {object} element The element to bind tooltip to
|
|
2517
|
+
* @param {object} data Object with { title, rows: [{k, v}] }
|
|
2518
|
+
*/
|
|
2519
|
+
bindRichTooltip(element, data) {
|
|
2520
|
+
if (!data || !element) return;
|
|
2521
|
+
const { title, rows = [] } = data;
|
|
2522
|
+
const text = `${title}\n${rows.map(r => `${r.k}: ${r.v}`).join('\n')}`;
|
|
2523
|
+
this.bindTooltip(element, text);
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
/**
|
|
2527
|
+
* Lightens or darkens a color by a factor for shading effects.
|
|
2528
|
+
* Used internally for 3D effects.
|
|
2529
|
+
* @param {string} color Hex color string
|
|
2530
|
+
* @param {number} amount Amount to lighten (-1 to 1)
|
|
2531
|
+
* @returns {string} Modified hex color
|
|
2532
|
+
*/
|
|
2533
|
+
adjustColor(color, amount) {
|
|
2534
|
+
return this._lightenColor(color, amount);
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
/**
|
|
2538
|
+
* Registers a chart or diagram type for lazy rendering.
|
|
2539
|
+
* Compatible with SVGEngine's registry pattern. For a graph type
|
|
2540
|
+
* (`graphDiagram`/`graphDiagram3D`), pass `{nodes, edges, ...}` in
|
|
2541
|
+
* `options` instead of `{data}` — `refreshChart`/`addGraphNode` detect
|
|
2542
|
+
* the graph shape via `options.nodes`.
|
|
2543
|
+
* @param {string} id Chart container ID
|
|
2544
|
+
* @param {string} type A CHART_CATALOG key (e.g. 'bar', 'line'), a
|
|
2545
|
+
* DIAGRAM_CATALOG key (e.g. 'graph', 'archimate'), or — for
|
|
2546
|
+
* railroadDiagram, which isn't a registry entry — the real method name.
|
|
2547
|
+
* @param {object} [options] Chart/diagram options
|
|
2548
|
+
*/
|
|
2549
|
+
register(id, type, options = {}) {
|
|
2550
|
+
if (!this.registry) this.registry = {};
|
|
2551
|
+
this.registry[id] = { type, options };
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
/**
|
|
2555
|
+
* Refreshes a registered chart, re-resolving its data and re-invoking
|
|
2556
|
+
* its render method.
|
|
2557
|
+
* @param {string} id Chart container ID
|
|
2558
|
+
*/
|
|
2559
|
+
refreshChart(id) {
|
|
2560
|
+
if (!this.registry || !this.registry[id]) return;
|
|
2561
|
+
const config = this.registry[id];
|
|
2562
|
+
const container = document.getElementById(id);
|
|
2563
|
+
if (!container) return;
|
|
2564
|
+
|
|
2565
|
+
// Catalog keys ('bar', 'heatmap', 'graph', 'archimate', ...) resolve
|
|
2566
|
+
// through the registries to their real method name; railroadDiagram
|
|
2567
|
+
// isn't a registry entry, so `type` already IS the method name there.
|
|
2568
|
+
const chartEntry = RenderEngine.CHART_REGISTRY.get(config.type);
|
|
2569
|
+
const diagramEntry = RenderEngine.DIAGRAM_REGISTRY.get(config.type);
|
|
2570
|
+
const methodName = chartEntry?.method ?? diagramEntry?.method ?? config.type;
|
|
2571
|
+
if (typeof this[methodName] !== 'function') return;
|
|
2572
|
+
|
|
2573
|
+
if (config.options.nodes !== undefined) {
|
|
2574
|
+
// Graph-shaped registration: two positional data args, not one.
|
|
2575
|
+
this[methodName](config.options.nodes, config.options.edges || [], config.options);
|
|
2576
|
+
} else {
|
|
2577
|
+
const data = config.options.data || this.resolvePath(config.options.dataPath, config.options.dataMapper);
|
|
2578
|
+
this[methodName](data, config.options);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
/**
|
|
2583
|
+
* Appends one data point to a registered chart's data and re-renders it,
|
|
2584
|
+
* keeping at most `maxPoints` (oldest dropped first) — see
|
|
2585
|
+
* REFERENCE.md's "Dynamically adding data points in a bounded window".
|
|
2586
|
+
* Delegates the actual shape-aware append/trim to a `datawindow/`
|
|
2587
|
+
* class, selected via the chart's `CHART_CATALOG` `streamShape`; the
|
|
2588
|
+
* window instance is cached per `id` so repeated calls keep growing the
|
|
2589
|
+
* same bounded collection instead of re-deriving it from scratch.
|
|
2590
|
+
* @param {string} id A container id already passed to `register()`.
|
|
2591
|
+
* @param {*|function} pointOrFactory A literal point, or a factory (see
|
|
2592
|
+
* the `datawindow/` classes for the exact signature each streamShape
|
|
2593
|
+
* calls it with).
|
|
2594
|
+
* @param {{maxPoints?: number}} [options] `0` (default) = unbounded.
|
|
2595
|
+
*/
|
|
2596
|
+
addDataPoint(id, pointOrFactory, { maxPoints = 0 } = {}) {
|
|
2597
|
+
const config = this.registry?.[id];
|
|
2598
|
+
const entry = config && RenderEngine.CHART_REGISTRY.get(config.type);
|
|
2599
|
+
if (!entry || entry.streamShape === 'none') return; // not registered, or a fixed-shape type (heatmap/radar/gauges/3D-orbit grids)
|
|
2600
|
+
|
|
2601
|
+
this._windows = this._windows || {};
|
|
2602
|
+
if (!this._windows[id]) {
|
|
2603
|
+
const current = config.options.data || this.resolvePath(config.options.dataPath, config.options.dataMapper);
|
|
2604
|
+
this._windows[id] = createDataWindow(entry.streamShape, current, { max: maxPoints });
|
|
2605
|
+
}
|
|
2606
|
+
this._windows[id].max = maxPoints; // let the cap change between calls, e.g. a live-editable "Max points" field
|
|
2607
|
+
config.options.data = this._windows[id].push(pointOrFactory);
|
|
2608
|
+
this.refreshChart(id);
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
/**
|
|
2612
|
+
* Appends one node (and its edges) to a registered graph diagram and
|
|
2613
|
+
* re-renders it, keeping at most `maxNodes` — oldest nodes dropped
|
|
2614
|
+
* first, along with any edge left dangling off a removed node. See
|
|
2615
|
+
* `GraphDataWindow` (src/datawindow/GraphDataWindow.js).
|
|
2616
|
+
* @param {string} id A container id already `register()`ed with
|
|
2617
|
+
* `{nodes, edges}` in its options.
|
|
2618
|
+
* @param {object} node The new node, e.g. `{id, label}`.
|
|
2619
|
+
* @param {Array<object>} [newEdges] Edges connecting the new node, e.g. `[{source, target}]`.
|
|
2620
|
+
* @param {{maxNodes?: number}} [options] `0` (default) = unbounded.
|
|
2621
|
+
*/
|
|
2622
|
+
addGraphNode(id, node, newEdges = [], { maxNodes = 0 } = {}) {
|
|
2623
|
+
const config = this.registry?.[id];
|
|
2624
|
+
if (!config || config.options.nodes === undefined) return; // not registered, or not registered graph-shaped
|
|
2625
|
+
|
|
2626
|
+
this._windows = this._windows || {};
|
|
2627
|
+
if (!this._windows[id]) {
|
|
2628
|
+
this._windows[id] = new GraphDataWindow({ nodes: config.options.nodes, edges: config.options.edges || [] }, { max: maxNodes });
|
|
2629
|
+
}
|
|
2630
|
+
this._windows[id].max = maxNodes;
|
|
2631
|
+
this._windows[id].push(node, newEdges);
|
|
2632
|
+
config.options.nodes = this._windows[id].nodes;
|
|
2633
|
+
config.options.edges = this._windows[id].edges;
|
|
2634
|
+
this.refreshChart(id);
|
|
2635
|
+
}
|
|
2636
|
+
}
|