@sapui5/sap.ui.vk 1.138.0 → 1.139.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/package.json +1 -1
  2. package/src/sap/ui/vk/.library +9 -1
  3. package/src/sap/ui/vk/AnimationPlayback.js +1 -1
  4. package/src/sap/ui/vk/AnimationPlayer.js +1 -1
  5. package/src/sap/ui/vk/AnimationSequence.js +1 -1
  6. package/src/sap/ui/vk/AnimationTimeSlider.js +1 -1
  7. package/src/sap/ui/vk/AnimationTrack.js +1 -1
  8. package/src/sap/ui/vk/Annotation.js +1 -1
  9. package/src/sap/ui/vk/BaseNodeProxy.js +1 -1
  10. package/src/sap/ui/vk/Camera.js +1 -1
  11. package/src/sap/ui/vk/ContentConnector.js +149 -95
  12. package/src/sap/ui/vk/ContentManager.js +1 -1
  13. package/src/sap/ui/vk/ContentResource.js +26 -7
  14. package/src/sap/ui/vk/ContentType.js +41 -0
  15. package/src/sap/ui/vk/Core.js +1 -1
  16. package/src/sap/ui/vk/DownloadManager.js +1 -1
  17. package/src/sap/ui/vk/DrawerToolbar.js +46 -19
  18. package/src/sap/ui/vk/DvlException.js +1 -1
  19. package/src/sap/ui/vk/FlexibleControl.js +1 -1
  20. package/src/sap/ui/vk/FlexibleControlLayoutData.js +1 -1
  21. package/src/sap/ui/vk/Highlight.js +1 -1
  22. package/src/sap/ui/vk/ImageContentManager.js +1 -1
  23. package/src/sap/ui/vk/JointUtils.js +1 -1
  24. package/src/sap/ui/vk/LayerProxy.js +1 -1
  25. package/src/sap/ui/vk/Loco.js +1 -1
  26. package/src/sap/ui/vk/Material.js +1 -1
  27. package/src/sap/ui/vk/NativeViewport.js +1 -1
  28. package/src/sap/ui/vk/NodeHierarchy.js +1 -1
  29. package/src/sap/ui/vk/NodeProxy.js +1 -1
  30. package/src/sap/ui/vk/NodeUtils.js +1 -1
  31. package/src/sap/ui/vk/Notifications.js +1 -1
  32. package/src/sap/ui/vk/OrthographicCamera.js +1 -1
  33. package/src/sap/ui/vk/PerspectiveCamera.js +1 -1
  34. package/src/sap/ui/vk/ProgressIndicator.js +1 -1
  35. package/src/sap/ui/vk/RedlineCollaboration.js +1 -1
  36. package/src/sap/ui/vk/RedlineConversation.js +1 -1
  37. package/src/sap/ui/vk/RedlineDesign.js +1 -1
  38. package/src/sap/ui/vk/RedlineElement.js +1 -1
  39. package/src/sap/ui/vk/RedlineElementComment.js +1 -1
  40. package/src/sap/ui/vk/RedlineElementEllipse.js +1 -1
  41. package/src/sap/ui/vk/RedlineElementFreehand.js +1 -1
  42. package/src/sap/ui/vk/RedlineElementLine.js +1 -1
  43. package/src/sap/ui/vk/RedlineElementRectangle.js +1 -1
  44. package/src/sap/ui/vk/RedlineElementText.js +1 -1
  45. package/src/sap/ui/vk/RedlineSurface.js +1 -1
  46. package/src/sap/ui/vk/SafeArea.js +1 -1
  47. package/src/sap/ui/vk/Scene.js +5 -1
  48. package/src/sap/ui/vk/SceneTree.js +20 -3
  49. package/src/sap/ui/vk/StepNavigation.js +1 -1
  50. package/src/sap/ui/vk/Texture.js +1 -1
  51. package/src/sap/ui/vk/ToggleMenuButton.js +1 -1
  52. package/src/sap/ui/vk/ToggleMenuItem.js +1 -1
  53. package/src/sap/ui/vk/Toolbar.js +1 -1
  54. package/src/sap/ui/vk/View.js +1 -1
  55. package/src/sap/ui/vk/ViewGallery.js +1 -1
  56. package/src/sap/ui/vk/ViewGalleryThumbnail.js +1 -1
  57. package/src/sap/ui/vk/ViewGroup.js +1 -1
  58. package/src/sap/ui/vk/ViewManager.js +1 -1
  59. package/src/sap/ui/vk/ViewStateManager.js +3 -10
  60. package/src/sap/ui/vk/ViewStateManagerBase.js +2 -2
  61. package/src/sap/ui/vk/Viewer.js +2 -2
  62. package/src/sap/ui/vk/Viewport.js +1 -1
  63. package/src/sap/ui/vk/ViewportBase.js +24 -70
  64. package/src/sap/ui/vk/dvl/BaseNodeProxy.js +1 -1
  65. package/src/sap/ui/vk/dvl/ContentManager.js +1 -1
  66. package/src/sap/ui/vk/dvl/GraphicsCore.js +1 -1
  67. package/src/sap/ui/vk/dvl/LayerProxy.js +1 -1
  68. package/src/sap/ui/vk/dvl/NodeHierarchy.js +1 -1
  69. package/src/sap/ui/vk/dvl/NodeProxy.js +1 -1
  70. package/src/sap/ui/vk/dvl/Scene.js +1 -1
  71. package/src/sap/ui/vk/dvl/ViewStateManager.js +1 -1
  72. package/src/sap/ui/vk/dvl/Viewport.js +1 -1
  73. package/src/sap/ui/vk/ecad/ElementsPanel.js +14 -41
  74. package/src/sap/ui/vk/ecad/LayersPanel.js +8 -25
  75. package/src/sap/ui/vk/i18n/messagebundle.properties +46 -44
  76. package/src/sap/ui/vk/i18n/messagebundle_ar.properties +25 -12
  77. package/src/sap/ui/vk/i18n/messagebundle_bg.properties +25 -12
  78. package/src/sap/ui/vk/i18n/messagebundle_ca.properties +25 -12
  79. package/src/sap/ui/vk/i18n/messagebundle_cnr.properties +25 -12
  80. package/src/sap/ui/vk/i18n/messagebundle_cs.properties +25 -12
  81. package/src/sap/ui/vk/i18n/messagebundle_cy.properties +25 -12
  82. package/src/sap/ui/vk/i18n/messagebundle_da.properties +25 -12
  83. package/src/sap/ui/vk/i18n/messagebundle_de.properties +26 -13
  84. package/src/sap/ui/vk/i18n/messagebundle_el.properties +25 -12
  85. package/src/sap/ui/vk/i18n/messagebundle_en.properties +25 -326
  86. package/src/sap/ui/vk/i18n/messagebundle_en_GB.properties +25 -12
  87. package/src/sap/ui/vk/i18n/messagebundle_en_US_saprigi.properties +26 -12
  88. package/src/sap/ui/vk/i18n/messagebundle_es.properties +27 -14
  89. package/src/sap/ui/vk/i18n/messagebundle_es_MX.properties +25 -12
  90. package/src/sap/ui/vk/i18n/messagebundle_et.properties +25 -12
  91. package/src/sap/ui/vk/i18n/messagebundle_fi.properties +25 -12
  92. package/src/sap/ui/vk/i18n/messagebundle_fr.properties +25 -12
  93. package/src/sap/ui/vk/i18n/messagebundle_fr_CA.properties +25 -12
  94. package/src/sap/ui/vk/i18n/messagebundle_hi.properties +25 -12
  95. package/src/sap/ui/vk/i18n/messagebundle_hr.properties +25 -12
  96. package/src/sap/ui/vk/i18n/messagebundle_hu.properties +25 -12
  97. package/src/sap/ui/vk/i18n/messagebundle_id.properties +26 -13
  98. package/src/sap/ui/vk/i18n/messagebundle_it.properties +25 -12
  99. package/src/sap/ui/vk/i18n/messagebundle_iw.properties +25 -12
  100. package/src/sap/ui/vk/i18n/messagebundle_ja.properties +25 -12
  101. package/src/sap/ui/vk/i18n/messagebundle_kk.properties +37 -24
  102. package/src/sap/ui/vk/i18n/messagebundle_ko.properties +22 -9
  103. package/src/sap/ui/vk/i18n/messagebundle_lt.properties +25 -12
  104. package/src/sap/ui/vk/i18n/messagebundle_lv.properties +25 -12
  105. package/src/sap/ui/vk/i18n/messagebundle_mk.properties +25 -12
  106. package/src/sap/ui/vk/i18n/messagebundle_ms.properties +25 -12
  107. package/src/sap/ui/vk/i18n/messagebundle_nl.properties +25 -12
  108. package/src/sap/ui/vk/i18n/messagebundle_no.properties +25 -12
  109. package/src/sap/ui/vk/i18n/messagebundle_pl.properties +25 -12
  110. package/src/sap/ui/vk/i18n/messagebundle_pt.properties +25 -12
  111. package/src/sap/ui/vk/i18n/messagebundle_pt_PT.properties +25 -12
  112. package/src/sap/ui/vk/i18n/messagebundle_ro.properties +25 -12
  113. package/src/sap/ui/vk/i18n/messagebundle_ru.properties +25 -12
  114. package/src/sap/ui/vk/i18n/messagebundle_sh.properties +25 -12
  115. package/src/sap/ui/vk/i18n/messagebundle_sk.properties +25 -12
  116. package/src/sap/ui/vk/i18n/messagebundle_sl.properties +25 -12
  117. package/src/sap/ui/vk/i18n/messagebundle_sr.properties +25 -12
  118. package/src/sap/ui/vk/i18n/messagebundle_sv.properties +25 -12
  119. package/src/sap/ui/vk/i18n/messagebundle_th.properties +25 -12
  120. package/src/sap/ui/vk/i18n/messagebundle_tr.properties +25 -12
  121. package/src/sap/ui/vk/i18n/messagebundle_uk.properties +25 -12
  122. package/src/sap/ui/vk/i18n/messagebundle_vi.properties +25 -12
  123. package/src/sap/ui/vk/i18n/messagebundle_zh_CN.properties +25 -12
  124. package/src/sap/ui/vk/i18n/messagebundle_zh_TW.properties +25 -12
  125. package/src/sap/ui/vk/library.js +5 -5
  126. package/src/sap/ui/vk/measurements/Angle.js +1 -1
  127. package/src/sap/ui/vk/measurements/Area.js +1 -1
  128. package/src/sap/ui/vk/measurements/Distance.js +1 -1
  129. package/src/sap/ui/vk/measurements/Edge.js +1 -1
  130. package/src/sap/ui/vk/measurements/Face.js +1 -1
  131. package/src/sap/ui/vk/measurements/Feature.js +1 -1
  132. package/src/sap/ui/vk/measurements/Vertex.js +1 -1
  133. package/src/sap/ui/vk/pdf/ContentManager.js +1 -1
  134. package/src/sap/ui/vk/pdf/Page.js +9 -10
  135. package/src/sap/ui/vk/pdf/Utils.js +4 -2
  136. package/src/sap/ui/vk/pdf/Viewport.js +1 -1
  137. package/src/sap/ui/vk/svg/BaseNodeProxy.js +1 -1
  138. package/src/sap/ui/vk/svg/ContentDeliveryService.js +1 -1
  139. package/src/sap/ui/vk/svg/ContentManager.js +4 -5
  140. package/src/sap/ui/vk/svg/NodeHierarchy.js +1 -1
  141. package/src/sap/ui/vk/svg/NodeProxy.js +1 -1
  142. package/src/sap/ui/vk/svg/OrthographicCamera.js +1 -1
  143. package/src/sap/ui/vk/svg/Scene.js +1 -1
  144. package/src/sap/ui/vk/svg/SceneBuilder.js +1 -1
  145. package/src/sap/ui/vk/svg/ViewStateManager.js +1 -1
  146. package/src/sap/ui/vk/svg/Viewport.js +11 -18
  147. package/src/sap/ui/vk/thirdparty/pdf.worker.js +1 -1
  148. package/src/sap/ui/vk/thirdparty/three.js +31455 -31473
  149. package/src/sap/ui/vk/threejs/AnimationHelper.js +1 -1
  150. package/src/sap/ui/vk/threejs/BaseNodeProxy.js +1 -1
  151. package/src/sap/ui/vk/threejs/Billboard.js +23 -11
  152. package/src/sap/ui/vk/threejs/Callout.js +1 -1
  153. package/src/sap/ui/vk/threejs/ContentDeliveryService.js +1 -1
  154. package/src/sap/ui/vk/threejs/ContentManager.js +2 -2
  155. package/src/sap/ui/vk/threejs/DetailView.js +1 -1
  156. package/src/sap/ui/vk/threejs/HighlightPlayer.js +14 -22
  157. package/src/sap/ui/vk/threejs/Material.js +1 -1
  158. package/src/sap/ui/vk/threejs/NodeHierarchy.js +1 -1
  159. package/src/sap/ui/vk/threejs/NodeProxy.js +9 -11
  160. package/src/sap/ui/vk/threejs/OrthographicCamera.js +1 -1
  161. package/src/sap/ui/vk/threejs/OutlineRenderer.js +4 -10
  162. package/src/sap/ui/vk/threejs/PerspectiveCamera.js +1 -1
  163. package/src/sap/ui/vk/threejs/PointCloudGroup.js +1 -1
  164. package/src/sap/ui/vk/threejs/Scene.js +1 -15
  165. package/src/sap/ui/vk/threejs/SceneBuilder.js +1 -4
  166. package/src/sap/ui/vk/threejs/Texture.js +1 -1
  167. package/src/sap/ui/vk/threejs/ThreeExtensions.js +10 -174
  168. package/src/sap/ui/vk/threejs/ThreeUtils.js +7 -18
  169. package/src/sap/ui/vk/threejs/Thrustline.js +1 -1
  170. package/src/sap/ui/vk/threejs/ViewStateManager.js +751 -446
  171. package/src/sap/ui/vk/threejs/Viewport.js +41 -31
  172. package/src/sap/ui/vk/tools/AnchorPointTool.js +1 -1
  173. package/src/sap/ui/vk/tools/AnchorPointToolGizmo.js +1 -1
  174. package/src/sap/ui/vk/tools/AxisAngleRotationTool.js +1 -1
  175. package/src/sap/ui/vk/tools/AxisAngleRotationToolGizmo.js +1 -1
  176. package/src/sap/ui/vk/tools/CreateEllipseTool.js +1 -1
  177. package/src/sap/ui/vk/tools/CreateEllipseToolGizmo.js +1 -1
  178. package/src/sap/ui/vk/tools/CreateParametricGizmo.js +1 -1
  179. package/src/sap/ui/vk/tools/CreatePathTool.js +1 -1
  180. package/src/sap/ui/vk/tools/CreatePathToolGizmo.js +1 -1
  181. package/src/sap/ui/vk/tools/CreateRectangleTool.js +1 -1
  182. package/src/sap/ui/vk/tools/CreateRectangleToolGizmo.js +1 -1
  183. package/src/sap/ui/vk/tools/CreateTextTool.js +1 -1
  184. package/src/sap/ui/vk/tools/CreateTextToolGizmo.js +1 -1
  185. package/src/sap/ui/vk/tools/CrossSectionTool.js +1 -1
  186. package/src/sap/ui/vk/tools/CrossSectionToolGizmo.js +1 -1
  187. package/src/sap/ui/vk/tools/DuplicateSvgElementTool.js +1 -1
  188. package/src/sap/ui/vk/tools/DuplicateSvgElementToolGizmo.js +1 -1
  189. package/src/sap/ui/vk/tools/ExplodeTool.js +1 -1
  190. package/src/sap/ui/vk/tools/ExplodeToolGizmo.js +1 -1
  191. package/src/sap/ui/vk/tools/Gizmo.js +1 -1
  192. package/src/sap/ui/vk/tools/HitTestTool.js +1 -1
  193. package/src/sap/ui/vk/tools/MoveTool.js +1 -1
  194. package/src/sap/ui/vk/tools/MoveToolGizmo.js +1 -1
  195. package/src/sap/ui/vk/tools/PointCloudSelectionTool.js +1 -1
  196. package/src/sap/ui/vk/tools/PointCloudSelectionToolGizmo.js +1 -1
  197. package/src/sap/ui/vk/tools/RectSelectTool.js +1 -1
  198. package/src/sap/ui/vk/tools/RedlineTool.js +1 -1
  199. package/src/sap/ui/vk/tools/RedlineToolGizmo.js +1 -1
  200. package/src/sap/ui/vk/tools/RotateOrbitTool.js +1 -1
  201. package/src/sap/ui/vk/tools/RotateTool.js +1 -1
  202. package/src/sap/ui/vk/tools/RotateToolGizmo.js +1 -1
  203. package/src/sap/ui/vk/tools/RotateTurntableTool.js +1 -1
  204. package/src/sap/ui/vk/tools/ScaleTool.js +1 -1
  205. package/src/sap/ui/vk/tools/ScaleToolGizmo.js +1 -1
  206. package/src/sap/ui/vk/tools/SceneOrientationTool.js +1 -1
  207. package/src/sap/ui/vk/tools/SceneOrientationToolGizmo.js +1 -1
  208. package/src/sap/ui/vk/tools/Tool.js +1 -1
  209. package/src/sap/ui/vk/tools/TooltipTool.js +1 -1
  210. package/src/sap/ui/vk/tools/TooltipToolGizmo.js +1 -1
  211. package/src/sap/ui/vk/tools/TooltipToolHandler.js +5 -0
  212. package/src/sap/ui/vk/tools/TransformSvgElementTool.js +1 -1
  213. package/src/sap/ui/vk/tools/TransformSvgElementToolGizmo.js +1 -1
  214. package/src/sap/ui/vk/totara/TotaraLoader.js +2 -2
  215. package/src/sap/ui/vk/totara/TotaraLoaderWorker.js +18 -21
  216. package/ui5.yaml +5 -3
  217. package/src/sap/ui/vk/threejs/v2/ViewStateManager.js +0 -1445
@@ -7,6 +7,7 @@
7
7
 
8
8
  // Provides the ViewStateManager class.
9
9
  sap.ui.define([
10
+ "sap/base/assert",
10
11
  "../Core",
11
12
  "../ViewStateManagerBase",
12
13
  "../thirdparty/three",
@@ -17,7 +18,6 @@ sap.ui.define([
17
18
  "./Scene",
18
19
  "../ObjectType",
19
20
  "../RotationType",
20
- "./OutlineRenderer",
21
21
  "./HighlightPlayer",
22
22
  "../HighlightDisplayState",
23
23
  "../Highlight",
@@ -30,6 +30,7 @@ sap.ui.define([
30
30
  "../TransformationMatrix",
31
31
  "sap/ui/core/Element"
32
32
  ], function(
33
+ assert,
33
34
  vkCore,
34
35
  ViewStateManagerBase,
35
36
  THREE,
@@ -40,7 +41,6 @@ sap.ui.define([
40
41
  Scene,
41
42
  ObjectType,
42
43
  RotationType,
43
- OutlineRenderer,
44
44
  HighlightPlayer,
45
45
  HighlightDisplayState,
46
46
  Highlight,
@@ -57,17 +57,32 @@ sap.ui.define([
57
57
 
58
58
  var VisibilityTracker;
59
59
 
60
+ class MaterialCache {
61
+ cloneMaterial(material, nodeState) {
62
+ // TODO: a proper implementation required.
63
+ const materialClone = material.clone();
64
+ materialClone.userData.prototypeMaterial = material;
65
+ return materialClone;
66
+ }
67
+
68
+ releaseMaterial(material) {
69
+ // TODO: a proper implementation required.
70
+ ThreeUtils.disposeMaterial(material);
71
+ return this;
72
+ }
73
+ }
74
+
60
75
  /**
61
76
  * Constructor for a new ViewStateManager.
62
77
  *
63
78
  * @class
64
- * Manages the visibility and selection states of nodes in the scene.
79
+ * Manages the visibility, selection, opacity and tint color states of nodes in the scene.
65
80
  *
66
81
  * @param {string} [sId] ID for the new ViewStateManager object. Generated automatically if no ID is given.
67
82
  * @param {object} [mSettings] Initial settings for the new ViewStateManager object.
68
83
  * @public
69
84
  * @author SAP SE
70
- * @version 1.138.0
85
+ * @version 1.139.0
71
86
  * @extends sap.ui.vk.ViewStateManagerBase
72
87
  * @alias sap.ui.vk.threejs.ViewStateManager
73
88
  * @since 1.32.0
@@ -81,26 +96,62 @@ sap.ui.define([
81
96
  var basePrototype = ViewStateManager.getMetadata().getParent().getClass().prototype;
82
97
 
83
98
  ViewStateManager.prototype.init = function() {
84
- if (basePrototype.init) {
85
- basePrototype.init.call(this);
86
- }
99
+ basePrototype.init?.call(this);
87
100
 
88
101
  this._nodeHierarchy = null;
102
+
103
+ // A map where `key` is `nodeRef` and `value` is a structure:
104
+ //
105
+ // type NodeState {
106
+ // visible?: bool
107
+ // selected: bool
108
+ // ancestorSelected: bool
109
+ // opacity?: number // floating number in range [0, 1]
110
+ // ancestorOverridesOpacity: bool
111
+ // tintColor?: int // tint color in ABGR format
112
+ // ancestorTintColor?: int // tint color of ancestor in ABGR format
113
+ // highlightColor?: float[] // highlight color in [r, g, b, a] format (this is an animated highlight, do not confuse it with selection highlight)
114
+ // ancestorHighlightColor?: float[] // highlight color of ancestor in [r, g, b, a] format
115
+ // boundingBoxNode?: THREE.Box3Helper // assigned if selected; ancestorSelected does not affect this property
116
+ // material?: THREE.MeshPhongMaterial // if selected or ancestorSelected or opacity != null or
117
+ // // tintColor != null or ancestorTintColor != null or
118
+ // // highlightColor != null or ancestorHighlightColor != null
119
+ // material2?: THREE.MeshPhongMaterial // if opacity != null or tintColor != null or ancestorTintColor != null or
120
+ // // highlightColor != null or ancestorHighlightColor != null
121
+ // }
122
+ //
123
+ // If a NodeState property is `null`, `undefined` or missing then the property original
124
+ // value is taken from the node itself.
125
+ //
126
+ // The `selected` cannot have value `null` as it is a non-persistent runtime property.
89
127
  this._nodeStates = new Map();
90
- this._selectedNodes = new Set(); // a collection of selected nodes for quick access,
128
+
129
+ // A collection of selected nodes for quick access, usually there are not many selected objects, so it is OK to
130
+ // store them in a collection.
131
+ this._selectedNodes = new Set();
132
+
133
+ // A set of nodes which needs to update material
134
+ this._needsMaterialUpdate = new Set();
135
+
91
136
  // usually there are not many selected objects,
92
137
  // so it is OK to store them in a collection.
93
- this._outlineRenderer = new OutlineRenderer(1.0);
94
138
  this._outlinedNodes = new Set();
95
139
  this.setOutlineColor("rgba(255, 0, 255, 1.0)");
96
140
  this.setOutlineWidth(1.0);
97
141
 
98
- this._visibilityTracker = new VisibilityTracker();
142
+ this._materialCache = new MaterialCache();
99
143
 
100
144
  this._showSelectionBoundingBox = true;
145
+
146
+ this._visibilityTracker = new VisibilityTracker();
147
+
148
+ // This scene owns and renders boxHelper objects for selected objects. Though the scene owns
149
+ // boxHelpers, the `parent` properties of the boxHelpers are set to the corresponding nodes
150
+ // rather than to this scene as those nodes are used to calculate the world matrices of theOpa
151
+ // boxHelpers.
101
152
  this._boundingBoxesScene = new THREE.Scene();
102
153
  this._selectedNodesBoundingBox = new THREE.Box3();
103
- this._selectionColor = new THREE.Color(0xC0C000);
154
+ this._boundingBoxColor = new THREE.Color(0xC0C000);
104
155
 
105
156
  this.setHighlightColor("rgba(255, 0, 0, 1.0)");
106
157
 
@@ -121,26 +172,53 @@ sap.ui.define([
121
172
  ThreeUtils.getAllTHREENodes([this._boundingBoxesScene], all3DNodes, allGroupNodes);
122
173
  all3DNodes.forEach(function(n3d) {
123
174
  ThreeUtils.disposeObject(n3d);
175
+ // TODO: does not seem to be necessary as bounding boxes are owned by
176
+ // `_boundingBoxesScene` and they are not in `n3d.parent.children` arrays.
124
177
  n3d.parent.remove(n3d);
125
178
  });
126
179
 
127
180
  allGroupNodes.forEach(function(g3d) {
181
+ // TODO: see the comment above.
128
182
  g3d.parent.remove(g3d);
129
183
  });
130
184
  };
131
185
 
186
+ ViewStateManager.prototype._clearNodeStates = function() {
187
+ var nodeStates = this._nodeStates;
188
+
189
+ nodeStates.forEach(function(state, nodeRef) {
190
+ if (state.material != null) {
191
+ ThreeUtils.disposeMaterial(state.material);
192
+ }
193
+
194
+ if (state.material2 != null) {
195
+ ThreeUtils.disposeMaterial(state.material2);
196
+ }
197
+
198
+ if (state.boundingBoxNode) {
199
+ ThreeUtils.disposeObject(state.boundingBoxNode);
200
+ }
201
+ }, this);
202
+
203
+ nodeStates.clear();
204
+ };
205
+
132
206
  ViewStateManager.prototype.exit = function() {
133
207
  vkCore.getEventBus().unsubscribe("sap.ui.vk", "activateView", this._onActivateView, this);
134
208
 
135
- if (this._outlineRenderer) {
136
- this._outlineRenderer.dispose();
137
- this._outlineRenderer = null;
138
- }
209
+ this._clearNodeStates();
210
+ this._selectedNodes.clear();
211
+
212
+ this._outlinedNodes.clear();
139
213
 
140
214
  if (this._boundingBoxesScene) {
141
215
  this._clearBoundingBoxScene();
142
216
  this._boundingBoxesScene = null;
143
217
  }
218
+
219
+ this._nodeHierarchy = null;
220
+
221
+ basePrototype.exit?.call(this);
144
222
  };
145
223
 
146
224
  ////////////////////////////////////////////////////////////////////////
@@ -176,11 +254,17 @@ sap.ui.define([
176
254
  // Node hierarchy handling begins.
177
255
 
178
256
  ViewStateManager.prototype._setScene = function(scene) {
257
+ this._clearNodeStates();
258
+
259
+ // TODO(VSM): Move this to _clearNodeStates.
179
260
  if (this._boundingBoxesScene) {
180
261
  this._clearBoundingBoxScene();
181
262
  }
263
+ // TODO(VSM): Move this to _setNodeHierarchy?
182
264
  this._boundingBoxesScene = new THREE.Scene();
183
265
  this._setNodeHierarchy(scene ? scene.getDefaultNodeHierarchy() : null);
266
+
267
+ // TODO(VSM): WTF?! Remove this!
184
268
  if (scene) {
185
269
  scene.setViewStateManager(this);
186
270
  }
@@ -195,12 +279,16 @@ sap.ui.define([
195
279
  return this;
196
280
  };
197
281
 
282
+ // TODO(VSM): Move the body to _setScene?
198
283
  ViewStateManager.prototype._setNodeHierarchy = function(nodeHierarchy) {
199
284
  var oldNodeHierarchy = this._nodeHierarchy;
200
285
 
201
286
  if (this._nodeHierarchy) {
287
+ this._nodeHierarchy.detachNodeReplaced(this._handleNodeReplaced, this);
288
+ this._nodeHierarchy.detachNodeUpdated(this._handleNodeUpdated, this);
289
+ this._nodeHierarchy.detachNodeRemoving(this._handleNodeRemoving, this);
202
290
  this._nodeHierarchy = null;
203
- this._nodeStates.clear();
291
+ this._clearNodeStates();
204
292
  this._selectedNodes.clear();
205
293
  this._outlinedNodes.clear();
206
294
  this._visibilityTracker.clear();
@@ -213,17 +301,15 @@ sap.ui.define([
213
301
  this._nodeHierarchy.attachNodeUpdated(this._handleNodeUpdated, this);
214
302
  this._nodeHierarchy.attachNodeRemoving(this._handleNodeRemoving, this);
215
303
 
216
- this._initialState = { visible: [], hidden: [] };
217
- var that = this;
218
-
219
- var allNodeRefs = nodeHierarchy.findNodesByName();
220
- allNodeRefs.forEach(function(nodeRef) {
221
- that._initialState[nodeRef.visible ? "visible" : "hidden"].push(nodeRef);
304
+ const visible = [];
305
+ const hidden = [];
306
+ nodeHierarchy.getSceneRef().traverse(function(nodeRef) {
307
+ (nodeRef.visible ? visible : hidden).push(nodeRef);
222
308
  });
223
309
 
224
310
  this.fireVisibilityChanged({
225
- visible: this._initialState.visible,
226
- hidden: this._initialState.hidden
311
+ visible: visible,
312
+ hidden: hidden
227
313
  });
228
314
  }
229
315
 
@@ -292,12 +378,6 @@ sap.ui.define([
292
378
  }
293
379
  };
294
380
 
295
- ViewStateManager.prototype._renderOutline = function(renderer, scene, camera) {
296
- var c = abgrToColor(this._outlineColorABGR);
297
- var color = new THREE.Color(c.red / 255.0, c.green / 255.0, c.blue / 255.0);
298
- this._outlineRenderer.render(renderer, scene, camera, Array.from(this._outlinedNodes), color, this._jointCollection);
299
- };
300
-
301
381
  // Node hierarchy handling ends.
302
382
  ////////////////////////////////////////////////////////////////////////
303
383
 
@@ -337,12 +417,7 @@ sap.ui.define([
337
417
  * @public
338
418
  */
339
419
  ViewStateManager.prototype.resetNodeProperty = function(nodeRef, property) {
340
- var currentView = this.getCurrentView();
341
- if (!currentView) {
342
- return;
343
- }
344
-
345
- var nodeInfo = currentView.getNodeInfos();
420
+ const nodeInfo = this.getCurrentView()?.getNodeInfos();
346
421
 
347
422
  if (nodeInfo) {
348
423
 
@@ -406,7 +481,7 @@ sap.ui.define([
406
481
  this.setTransformation(transforms.nodeRefs, transforms.positions);
407
482
  }
408
483
  } else {
409
- nodeRef._vkSetOpacity(node.opacity, this._jointCollection);
484
+ this.setOpacity(nodeRef, node.opacity);
410
485
  var eventParameters = {
411
486
  changed: nodeRef,
412
487
  opacity: nodeInfo.opacity
@@ -414,7 +489,7 @@ sap.ui.define([
414
489
 
415
490
  this.fireOpacityChanged(eventParameters);
416
491
  }
417
- }.bind(this));
492
+ }, this);
418
493
  }
419
494
  };
420
495
 
@@ -440,16 +515,28 @@ sap.ui.define([
440
515
  }
441
516
  }, this);
442
517
 
443
- return {
444
- visible: visible,
445
- hidden: hidden
446
- };
518
+ return { visible, hidden };
447
519
  };
448
520
 
449
521
  ViewStateManager.prototype.resetVisibility = function() {
450
- this.setVisibilityState(this._initialState.visible, true, false);
451
- this.setVisibilityState(this._initialState.hidden, false, false);
522
+ const toShow = [];
523
+ const toHide = [];
524
+ this._nodeStates.forEach(function(state, nodeRef) {
525
+ const visible = state.visible;
526
+ if (visible === true) {
527
+ toHide.push(nodeRef);
528
+ } else if (visible === false) {
529
+ toShow.push(nodeRef);
530
+ }
531
+ state.visible = null;
532
+ });
533
+ this._deleteUnusedNodeStates();
534
+
452
535
  this._visibilityTracker.clear();
536
+ this.fireVisibilityChanged({
537
+ visible: toShow,
538
+ hidden: toHide
539
+ });
453
540
  return this;
454
541
  };
455
542
 
@@ -465,21 +552,21 @@ sap.ui.define([
465
552
  */
466
553
  ViewStateManager.prototype.getVisibilityState = function(nodeRefs) {
467
554
  if (Array.isArray(nodeRefs)) {
468
- return nodeRefs.map(function(nodeRef) {
469
- return nodeRef ? nodeRef.visible : false;
470
- });
555
+ return nodeRefs.map(nodeRef => effectiveVisibility(nodeRef, this._nodeStates.get(nodeRef)));
556
+ } else {
557
+ return effectiveVisibility(nodeRefs, this._nodeStates.get(nodeRefs));
471
558
  }
472
-
473
- return nodeRefs ? nodeRefs.visible : false; // NB: The nodeRefs argument is a single nodeRef.
474
559
  };
475
560
 
476
561
  /**
477
562
  * Sets the visibility state of the nodes.
478
- * @param {any|any[]} nodeRefs The node reference or the array of node references.
479
- * @param {boolean|boolean[]} visible The new visibility state or array of states of the nodes.
480
- * @param {boolean} recursive The flags indicates if the change needs to propagate recursively to child nodes.
481
- * @param {boolean} force If a node is made visible but its parent is hidden then it will still be hidden in Viewport. This flag will force node to be visible regardless of parent's state.
482
- * @returns {this} <code>this</code> to allow method chaining.
563
+ * @param {any|any[]} nodeRefs The node reference or the array of node references.
564
+ * @param {boolean|boolean[]} visible The new visibility state or array of states of the nodes.
565
+ * @param {boolean} recursive The flags indicates if the change needs to propagate recursively to child nodes.
566
+ * @param {boolean} force If a node is made visible but its parent is hidden then it will still be
567
+ * hidden in Viewport. This flag will force node to be visible regardless of
568
+ * parent's state.
569
+ * @returns {this} Returns <code>this</code> to allow method chaining
483
570
  * @public
484
571
  */
485
572
  ViewStateManager.prototype.setVisibilityState = function(nodeRefs, visible, recursive, force) {
@@ -487,82 +574,89 @@ sap.ui.define([
487
574
  if (!Array.isArray(nodeRefs)) {
488
575
  nodeRefs = [nodeRefs];
489
576
  }
577
+ // console.log("setVisibilityState", nodeRefs.map(n => n.id + ":" + (n.name || ("<" + n.parent?.name))), visible, recursive, force);
490
578
 
491
579
  // check if we have got an array of booleans as visibility change
492
580
  var isBulkChange = Array.isArray(visible);
493
581
 
494
- var recursiveVisibility = [];
582
+ var recursiveVisibilities = [];
495
583
  var allNodeRefs = nodeRefs;
496
584
 
497
585
  if (recursive) {
498
586
  allNodeRefs = [];
499
- nodeRefs.forEach(function(nodeRef, idx) {
587
+ nodeRefs.forEach(function(nodeRef, index) {
500
588
  var collected = this._collectNodesRecursively(nodeRef);
501
589
  allNodeRefs = allNodeRefs.concat(collected);
502
590
 
503
- var length = recursiveVisibility.length;
504
- recursiveVisibility.length = length + collected.length;
505
- recursiveVisibility.fill(isBulkChange ? visible[idx] : visible, length);
591
+ var length = recursiveVisibilities.length;
592
+ recursiveVisibilities.length += collected.length;
593
+ recursiveVisibilities.fill(isBulkChange ? visible[index] : visible, length);
506
594
  }, this);
507
595
  } else if (!isBulkChange) {
508
596
  // not recursive, visible is a scalar
509
- recursiveVisibility.length = allNodeRefs.length;
510
- recursiveVisibility.fill(visible);
597
+ recursiveVisibilities.length = allNodeRefs.length;
598
+ recursiveVisibilities.fill(visible);
511
599
  } else {
512
600
  // not recursive, visible is an array
513
- recursiveVisibility = visible;
601
+ recursiveVisibilities = visible;
602
+ }
603
+
604
+ if (force) {
605
+ // We use `force` when we un-hide the parents of the un-hidden node recursively up the tree. Extend the
606
+ // array of changed nodes with these ancestors. If they are already visible or there are duplicates they
607
+ // will be filtered out below.
608
+ var additionalNodeRefs = [];
609
+ allNodeRefs.forEach(function(nodeRef, index) {
610
+ var newVisibility = recursiveVisibilities[index];
611
+ if (newVisibility) {
612
+ for (var node = nodeRef; node && !node.isScene; node = node.parent) {
613
+ additionalNodeRefs.push(node);
614
+ }
615
+ }
616
+ });
617
+ allNodeRefs = allNodeRefs.concat(additionalNodeRefs);
618
+ var length = recursiveVisibilities.length;
619
+ recursiveVisibilities.length += additionalNodeRefs.length;
620
+ recursiveVisibilities.fill(true, length);
514
621
  }
515
622
 
516
623
  // filter out unchanged visibility and duplicate nodes
517
624
  var changedVisibility = [];
518
625
  var usedNodeRefs = new Set();
519
- var changed = allNodeRefs.filter(function(nodeRef, index) {
520
- if (nodeRef == null || (nodeRef.userData.skipIt && nodeRef.visible) || usedNodeRefs.has(nodeRef)) {
626
+ var changedNodeRefs = allNodeRefs.filter(function(nodeRef, index) {
627
+ if (nodeRef == null || nodeRef.userData.skipIt || usedNodeRefs.has(nodeRef)) {
521
628
  return false;
522
629
  }
523
630
 
524
631
  usedNodeRefs.add(nodeRef);
525
632
 
526
- var changed = nodeRef ? nodeRef.visible != recursiveVisibility[index] : false;
633
+ const state = this._nodeStates.get(nodeRef);
634
+ var oldVisibility = effectiveVisibility(nodeRef, state);
635
+ var newVisibility = recursiveVisibilities[index];
636
+
637
+ var changed = oldVisibility !== newVisibility;
527
638
  if (changed) {
528
- changedVisibility.push(recursiveVisibility[index]);
639
+ changedVisibility.push(newVisibility);
529
640
  }
530
641
 
531
642
  return changed;
532
643
  }, this);
533
644
 
534
- if (changed.length > 0) {
645
+ if (changedNodeRefs.length > 0) {
646
+ this._applyVisibilityNodeState(changedNodeRefs, changedVisibility);
647
+ this._deleteUnusedNodeStates();
535
648
 
536
649
  var eventParameters = {
537
650
  visible: [],
538
651
  hidden: []
539
652
  };
540
653
 
541
- changed.forEach(function(nodeRef, idx) {
542
- nodeRef.visible = changedVisibility[idx];
543
- var children = nodeRef.children;
544
- if (children.length === 1 && children[0].userData.skipIt) {
545
- // This node is marked as non-visualisable. See comments in method
546
- // SceneContext.prototype.getPartialTreeNodes.
547
- //
548
- // Copy the state from the parent node.
549
- children[0].visible = nodeRef.visible;
550
- }
551
-
552
- eventParameters[nodeRef.visible ? "visible" : "hidden"].push(nodeRef);
553
-
554
- if (force && nodeRef.visible) {
555
- // Force visibility by traversing ancestor nodes and make them visible
556
- var node = nodeRef.parent;
557
- while (node) {
558
- node.visible = true;
559
- node = node.parent;
560
- }
561
- }
562
- }, this);
654
+ changedNodeRefs.forEach(function(nodeRef, index) {
655
+ eventParameters[changedVisibility[index] ? "visible" : "hidden"].push(nodeRef);
656
+ });
563
657
 
564
658
  if (this.getShouldTrackVisibilityChanges()) {
565
- changed.forEach(this._visibilityTracker.trackNodeRef, this._visibilityTracker);
659
+ changedNodeRefs.forEach(this._visibilityTracker.trackNodeRef, this._visibilityTracker);
566
660
  }
567
661
 
568
662
  this.fireVisibilityChanged(eventParameters);
@@ -570,12 +664,11 @@ sap.ui.define([
570
664
  return this;
571
665
  };
572
666
 
573
-
574
667
  /**
575
668
  * Enumerates IDs of the selected nodes.
576
669
  *
577
670
  * @param {function} callback A function to call when the selected nodes are enumerated. The function takes one parameter of type <code>string</code>.
578
- * @returns {this} <code>this</code> to allow method chaining.
671
+ * @returns {this} Returns <code>this</code> to allow method chaining
579
672
  * @public
580
673
  */
581
674
  ViewStateManager.prototype.enumerateSelection = function(callback) {
@@ -587,7 +680,7 @@ sap.ui.define([
587
680
  * Enumerates IDs of the outlined nodes.
588
681
  *
589
682
  * @param {function} callback A function to call when the outlined nodes are enumerated. The function takes one parameter of type <code>string</code>.
590
- * @returns {this} <code>this</code> to allow method chaining.
683
+ * @returns {this} Returns <code>this</code> to allow method chaining
591
684
  * @public
592
685
  */
593
686
  ViewStateManager.prototype.enumerateOutlinedNodes = function(callback) {
@@ -606,13 +699,9 @@ sap.ui.define([
606
699
  * @public
607
700
  */
608
701
  ViewStateManager.prototype.getSelectionState = function(nodeRefs) {
609
- var selectionSet = this._selectedNodes;
610
- function isSelected(nodeRef) {
611
- return selectionSet.has(nodeRef);
612
- }
702
+ const selected = this._selectedNodes.has.bind(this._selectedNodes);
613
703
 
614
- return Array.isArray(nodeRefs) ?
615
- nodeRefs.map(isSelected) : isSelected(nodeRefs); // NB: The nodeRefs argument is a single nodeRef.
704
+ return Array.isArray(nodeRefs) ? nodeRefs.map(selected) : selected(nodeRefs); // NB: The nodeRefs argument is a single nodeRef.
616
705
  };
617
706
 
618
707
  ViewStateManager.prototype._getSelectionComplete = function() {
@@ -647,22 +736,11 @@ sap.ui.define([
647
736
  };
648
737
  };
649
738
 
650
- ViewStateManager.prototype._isAChild = function(childNodeRef, nodeRefs) {
651
- var ancestor = childNodeRef.parent;
652
- while (ancestor) {
653
- if (nodeRefs.has(ancestor)) {
654
- return true;
655
- }
656
- ancestor = ancestor.parent;
657
- }
658
- return false;
659
- };
660
-
661
- ViewStateManager.prototype._AddBoundingBox = function(nodeRef) {
739
+ ViewStateManager.prototype._addBoundingBox = function(nodeRef) {
662
740
  nodeRef._vkCalculateObjectOrientedBoundingBox();
663
741
  };
664
742
 
665
- ViewStateManager.prototype._RemoveBoundingBox = function(nodeRef) {
743
+ ViewStateManager.prototype._removeBoundingBox = function(nodeRef) {
666
744
  delete nodeRef.userData.boundingBox;
667
745
  };
668
746
 
@@ -763,7 +841,7 @@ sap.ui.define([
763
841
  if (createObject) {
764
842
  if (!mtl) {
765
843
  mtl = new THREE.LineBasicMaterial({
766
- color: this._selectionColor,
844
+ color: this._boundingBoxColor,
767
845
  onBeforeCompile: function(shader) {
768
846
  shader.vertexShader = ("attribute mat4 itm;\n" + shader.vertexShader).replace(
769
847
  "#include <begin_vertex>",
@@ -791,16 +869,7 @@ sap.ui.define([
791
869
  */
792
870
  ViewStateManager.prototype.setShowSelectionBoundingBox = function(val) {
793
871
  this._showSelectionBoundingBox = val;
794
- if (this._showSelectionBoundingBox) {
795
- this._selectedNodes.forEach(function(node) { this._AddBoundingBox(node); }.bind(this));
796
- } else {
797
- this._selectedNodes.forEach(function(node) { this._RemoveBoundingBox(node); }.bind(this));
798
- }
799
-
800
- this.fireSelectionChanged({
801
- selected: this._selectedNodes,
802
- unselected: []
803
- });
872
+ this._selectedNodes.forEach(val ? this._addBoundingBox : this._removeBoundingBox, this);
804
873
  };
805
874
 
806
875
  /**
@@ -813,86 +882,58 @@ sap.ui.define([
813
882
  return this._showSelectionBoundingBox;
814
883
  };
815
884
 
816
- ViewStateManager.prototype._isAncestorSelected = function(nodeRef) {
817
- nodeRef = nodeRef.parent;
818
- while (nodeRef) {
819
- if (this._selectedNodes.has(nodeRef)) {
820
- return true;
821
- }
822
-
823
- nodeRef = nodeRef.parent;
824
- }
825
-
826
- return false;
827
- };
828
-
829
- ViewStateManager.prototype._updateHighlightColor = function(nodeRef, parentSelected) {
830
- var selected = parentSelected || this._selectedNodes.has(nodeRef);
831
- var nodeContentType = nodeRef._vkGetNodeContentType();
832
- if (nodeContentType === NodeContentType.Background
833
- || nodeContentType === NodeContentType.Symbol) {
834
- return;
835
- } else {
836
- nodeRef.userData.highlightColor = selected ? this._highlightColorABGR : undefined;
837
- nodeRef._vkUpdateMaterialColorAndOpacity();
838
- var children = nodeRef.children;
839
- for (var i = 0, l = children.length; i < l; i++) {
840
- var userData = children[i].userData;
841
- if (userData && userData.objectType === ObjectType.Hotspot) {
842
- continue;
843
- }
844
- this._updateHighlightColor(children[i], selected);
845
- }
846
- }
847
- };
848
-
849
885
  /**
850
886
  * Sets the selection state of the nodes.
851
887
  * @param {any|any[]} nodeRefs The node reference or the array of node references.
852
888
  * @param {boolean} selected The new selection state of the nodes.
853
889
  * @param {boolean} recursive The flags indicates if the change needs to propagate recursively to child nodes.
854
890
  * @param {boolean} blockNotification The flag to suppress selectionChanged event.
855
- * @returns {this} <code>this</code> to allow method chaining.
891
+ * @returns {this} Returns <code>this</code> to allow method chaining
856
892
  * @deprecated Since version 1.56.3. Use {@link sap.ui.vk.threejs.ViewStateManager#setSelectionStates} instead.
857
893
  * @public
858
894
  */
859
895
  ViewStateManager.prototype.setSelectionState = function(nodeRefs, selected, recursive, blockNotification) {
860
- if (!nodeRefs) {
861
- return this;
862
- }
863
-
864
896
  if (!Array.isArray(nodeRefs)) {
865
897
  nodeRefs = [nodeRefs];
866
898
  }
867
899
 
868
- nodeRefs = (recursive || this.getRecursiveSelection() ? this._collectNodesRecursively(nodeRefs) : nodeRefs).filter(function(value, index, self) {
869
- return self.indexOf(value) === index;
870
- });
900
+ // First, extend `nodeRefs` with descendant nodes based on parameter `recursive` or property `recursiveSelection`.
901
+ nodeRefs = (recursive || this.getRecursiveSelection() ? this._collectNodesRecursively(nodeRefs) : nodeRefs)
902
+ .filter(function(value, index, array) {
903
+ return array.indexOf(value) === index;
904
+ });
871
905
 
906
+ // Then, extend `nodeRefs` with ancestors of nodes being unselected if the
907
+ // `recursiveSelection` property (but not necessarily the `recursive` parameter) is `true`.
872
908
  if (this.getRecursiveSelection() && !selected) {
909
+ // E.g. if we deselect node D1 while the `recursiveSelection` property is `true` we
910
+ // deselect its ancestors C and B recursively. Nodes E and F are unselected previously,
911
+ // see the code above.
912
+ //
913
+ // The siblings stay as is.
914
+ //
915
+ // [ ] A [ ] A
916
+ // [x] B [ ] B
917
+ // [X] C -> [ ] C
918
+ // [X] *D1* [ ] *D1*
919
+ // [X] E [ ] E
920
+ // [X] F [ ] F
921
+ // [X] D2 [X] D2
922
+ // [X] D3 [X] D3
873
923
  nodeRefs = this._nodeHierarchy._appendAncestors(nodeRefs);
874
924
  }
875
925
 
876
- var changed = nodeRefs.filter(function(nodeRef) {
877
- return this._selectedNodes.has(nodeRef) !== selected;
878
- }, this);
926
+ const selectedNodes = this._selectedNodes;
927
+
928
+ // These are the nodes whose selection state changed.
929
+ const changed = nodeRefs.filter(nodeRef => selectedNodes.has(nodeRef) !== selected);
879
930
 
880
931
  if (changed.length > 0) {
881
- changed.forEach(function(nodeRef) {
882
- if (nodeRef) {
883
- this._selectedNodes[selected ? "add" : "delete"](nodeRef);
884
- if (this._showSelectionBoundingBox) {
885
- this[selected ? "_AddBoundingBox" : "_RemoveBoundingBox"](nodeRef);
886
- }
887
- }
888
- }, this);
932
+ this._applySelectionNodeState(changed, selected);
889
933
 
890
- // we need to update this._selectedNodes before updating nodes highlight color
891
- changed.forEach(function(nodeRef) {
892
- if (nodeRef) {
893
- this._updateHighlightColor(nodeRef, selected || this._isAncestorSelected(nodeRef));
894
- }
895
- }, this);
934
+ if (!selected) {
935
+ this._deleteUnusedNodeStates();
936
+ }
896
937
 
897
938
  if (!blockNotification) {
898
939
  this.fireSelectionChanged({
@@ -911,7 +952,7 @@ sap.ui.define([
911
952
  * @param {any|any[]} unselectedNodeRefs The node reference or the array of node references of unselected nodes.
912
953
  * @param {boolean} recursive The flags indicates if the change needs to propagate recursively to child nodes.
913
954
  * @param {boolean} blockNotification The flag to suppress selectionChanged event.
914
- * @returns {this} <code>this</code> to allow method chaining.
955
+ * @returns {this} Returns <code>this</code> to allow method chaining
915
956
  * @public
916
957
  */
917
958
  ViewStateManager.prototype.setSelectionStates = function(selectedNodeRefs, unselectedNodeRefs, recursive, blockNotification) {
@@ -939,25 +980,12 @@ sap.ui.define([
939
980
  }, this);
940
981
 
941
982
  if (selected.length > 0 || unselected.length > 0) {
942
- selected.forEach(function(nodeRef) {
943
- this._selectedNodes.add(nodeRef);
944
- this._updateHighlightColor(nodeRef, true);
945
- if (this._showSelectionBoundingBox) {
946
- this._AddBoundingBox(nodeRef);
947
- }
948
- }, this);
983
+ this._applySelectionNodeState(selected, true);
984
+ this._applySelectionNodeState(unselected, false);
949
985
 
950
- unselected.forEach(function(nodeRef) {
951
- this._selectedNodes.delete(nodeRef);
952
- if (this._showSelectionBoundingBox) {
953
- this._RemoveBoundingBox(nodeRef);
954
- }
955
- }, this);
956
-
957
- // we need to remove all unselected nodes from this._selectedNodes before updating unselected nodes highlight color
958
- unselected.forEach(function(nodeRef) {
959
- this._updateHighlightColor(nodeRef, this._isAncestorSelected(nodeRef));
960
- }, this);
986
+ if (unselected.length > 0) {
987
+ this._deleteUnusedNodeStates();
988
+ }
961
989
 
962
990
  if (!blockNotification) {
963
991
  this.fireSelectionChanged({
@@ -975,7 +1003,7 @@ sap.ui.define([
975
1003
  * @param {sap.ui.core.CSSColor|string|int} color The new outline color. The value can be defined as a string
976
1004
  * in the CSS color format or as an integer in the ABGR format. If <code>null</code>
977
1005
  * is passed then the tint color is reset and the node's own tint color should be used.
978
- * @returns {this} <code>this</code> to allow method chaining.
1006
+ * @returns {this} Returns <code>this</code> to allow method chaining
979
1007
  * @public
980
1008
  */
981
1009
  ViewStateManager.prototype.setOutlineColor = function(color) {
@@ -1001,7 +1029,6 @@ sap.ui.define([
1001
1029
  return this;
1002
1030
  };
1003
1031
 
1004
-
1005
1032
  /**
1006
1033
  * Gets the outline color
1007
1034
  *
@@ -1016,7 +1043,6 @@ sap.ui.define([
1016
1043
  return inABGRFormat ? this._outlineColorABGR : colorToCSSColor(abgrToColor(this._outlineColorABGR));
1017
1044
  };
1018
1045
 
1019
-
1020
1046
  /**
1021
1047
  * Gets the outlining state of the node.
1022
1048
  *
@@ -1037,14 +1063,13 @@ sap.ui.define([
1037
1063
  nodeRefs.map(isOutlined) : isOutlined(nodeRefs); // NB: The nodeRefs argument is a single no
1038
1064
  };
1039
1065
 
1040
-
1041
1066
  /**
1042
1067
  * Sets or resets the outlining state of the nodes.
1043
1068
  * @param {any|any[]} outlinedNodeRefs The node reference or the array of node references of outlined nodes.
1044
1069
  * @param {any|any[]} unoutlinedNodeRefs The node reference or the array of node references of un-outlined nodes.
1045
1070
  * @param {boolean} recursive The flags indicates if the change needs to propagate recursively to child nodes.
1046
1071
  * @param {boolean} blockNotification The flag to suppress outlineChanged event.
1047
- * @returns {this} <code>this</code> to allow method chaining.
1072
+ * @returns {this} Returns <code>this</code> to allow method chaining
1048
1073
  * @public
1049
1074
  */
1050
1075
  ViewStateManager.prototype.setOutliningStates = function(outlinedNodeRefs, unoutlinedNodeRefs, recursive, blockNotification) {
@@ -1099,7 +1124,6 @@ sap.ui.define([
1099
1124
  */
1100
1125
  ViewStateManager.prototype.setOutlineWidth = function(width) {
1101
1126
  this._outlineWidth = width;
1102
- this._outlineRenderer.setOutlineWidth(width);
1103
1127
  this.fireOutlineWidthChanged({
1104
1128
  width: width
1105
1129
  });
@@ -1136,7 +1160,8 @@ sap.ui.define([
1136
1160
  * @private
1137
1161
  */
1138
1162
  ViewStateManager.prototype._getOpacity = function(nodeRef) {
1139
- return nodeRef.userData.opacity !== undefined ? nodeRef.userData.opacity : null;
1163
+ const state = this._nodeStates.get(nodeRef);
1164
+ return state?.opacity ?? null;
1140
1165
  };
1141
1166
 
1142
1167
  /**
@@ -1145,8 +1170,8 @@ sap.ui.define([
1145
1170
  * If a single node is passed to the method then a single value is returned.<br/>
1146
1171
  * If an array of nodes is passed to the method then an array of values is returned.
1147
1172
  *
1148
- * @param {any|any[]} nodeRefs The node reference or the array of node references.
1149
- * @returns {float|float[]} A single value or an array of values. Value <code>null</code> means that the node's own opacity should be used.
1173
+ * @param {any|any[]} nodeRefs The node reference or the array of node references.
1174
+ * @returns {float|float[]|null|null[]} A single value or an array of values. Value <code>null</code> means that the node's own opacity should be used.
1150
1175
  * @public
1151
1176
  */
1152
1177
  ViewStateManager.prototype.getOpacity = function(nodeRefs) {
@@ -1164,7 +1189,7 @@ sap.ui.define([
1164
1189
  * @param {float|float[]|null} opacity The new opacity of the nodes. If <code>null</code> is passed then the opacity is reset
1165
1190
  * and the node's own opacity should be used.
1166
1191
  * @param {boolean} [recursive=false] This flag is not used, as opacity is always recursively applied to the offspring nodes by multiplication
1167
- * @returns {this} <code>this</code> to allow method chaining.
1192
+ * @returns {this} Returns <code>this</code> to allow method chaining
1168
1193
  * @public
1169
1194
  */
1170
1195
  ViewStateManager.prototype.setOpacity = function(nodeRefs, opacity, recursive) {
@@ -1176,54 +1201,55 @@ sap.ui.define([
1176
1201
  // check if we got an array as opacity
1177
1202
  var isBulkChange = Array.isArray(opacity);
1178
1203
 
1179
- if (opacity == null) {
1180
- opacity = undefined;
1181
- } else if (isBulkChange) {
1182
- opacity.forEach(function(value, idx) {
1183
- if (value == null) {
1184
- opacity[idx] = undefined;
1185
- }
1186
- });
1187
- }
1188
-
1189
- var recursiveOpacity = [];
1204
+ var recursiveOpacities = [];
1190
1205
  var allNodeRefs = nodeRefs;
1191
1206
 
1192
- if (!isBulkChange) {
1207
+ if (recursive) {
1208
+ allNodeRefs = [];
1209
+ nodeRefs.forEach(function(nodeRef, index) {
1210
+ var collected = this._collectNodesRecursively(nodeRef);
1211
+ allNodeRefs = allNodeRefs.concat(collected);
1212
+
1213
+ var length = recursiveOpacities.length;
1214
+ recursiveOpacities.length += collected.length;
1215
+ recursiveOpacities.fill(isBulkChange ? opacity[index] : opacity, length);
1216
+ }, this);
1217
+ } else if (!isBulkChange) {
1193
1218
  // not recursive, opacity is a scalar
1194
- recursiveOpacity.length = allNodeRefs.length;
1195
- recursiveOpacity.fill(opacity);
1219
+ recursiveOpacities.length = allNodeRefs.length;
1220
+ recursiveOpacities.fill(opacity);
1196
1221
  } else {
1197
1222
  // not recursive, opacity is an array
1198
- recursiveOpacity = opacity;
1223
+ recursiveOpacities = opacity;
1199
1224
  }
1200
1225
 
1201
1226
  // filter out unchanged opacity and duplicate nodes
1202
- var changedOpacity = [];
1227
+ var changedOpacities = [];
1203
1228
  var usedNodeRefs = new Set();
1204
- var changed = allNodeRefs.filter(function(nodeRef, index) {
1229
+ var changedNodeRefs = allNodeRefs.filter(function(nodeRef, index) {
1205
1230
  if (usedNodeRefs.has(nodeRef)) {
1206
1231
  return false;
1207
1232
  }
1208
1233
 
1209
1234
  usedNodeRefs.add(nodeRef);
1210
1235
 
1211
- var changed = nodeRef ? nodeRef.userData.opacity !== recursiveOpacity[index] : false;
1236
+ const state = this._nodeStates.get(nodeRef);
1237
+ var opacity = recursiveOpacities[index];
1238
+ var changed = state == null && opacity != null || state != null && state.opacity !== opacity;
1212
1239
  if (changed) {
1213
- changedOpacity.push(recursiveOpacity[index]);
1240
+ changedOpacities.push(opacity);
1214
1241
  }
1215
1242
 
1216
1243
  return changed;
1217
1244
  }, this);
1218
1245
 
1219
- if (changed.length > 0) {
1220
- changed.forEach(function(nodeRef, idx) {
1221
- nodeRef._vkSetOpacity(changedOpacity[idx], this._jointCollection);
1222
- }, this);
1246
+ if (changedNodeRefs.length > 0) {
1247
+ this._applyOpacityNodeState(changedNodeRefs, changedOpacities);
1248
+ this._deleteUnusedNodeStates();
1223
1249
 
1224
1250
  var eventParameters = {
1225
- changed: changed,
1226
- opacity: isBulkChange ? changedOpacity : changedOpacity[0]
1251
+ changed: changedNodeRefs,
1252
+ opacity: isBulkChange ? changedOpacities : changedOpacities[0]
1227
1253
  };
1228
1254
 
1229
1255
  this.fireOpacityChanged(eventParameters);
@@ -1242,7 +1268,8 @@ sap.ui.define([
1242
1268
  * @private
1243
1269
  */
1244
1270
  ViewStateManager.prototype._getTintColorABGR = function(nodeRef) {
1245
- return nodeRef.userData.tintColor !== undefined ? nodeRef.userData.tintColor : null;
1271
+ const state = this._nodeStates.get(nodeRef);
1272
+ return state?.tintColor;
1246
1273
  };
1247
1274
 
1248
1275
  /**
@@ -1255,8 +1282,8 @@ sap.ui.define([
1255
1282
  * @private
1256
1283
  */
1257
1284
  ViewStateManager.prototype._getTintColor = function(nodeRef) {
1258
- return nodeRef.userData.tintColor !== undefined ?
1259
- colorToCSSColor(abgrToColor(nodeRef.userData.tintColor)) : null;
1285
+ const tintColorABGR = this._getTintColorABGR(nodeRef);
1286
+ return tintColorABGR != null ? colorToCSSColor(abgrToColor(tintColorABGR)) : null;
1260
1287
  };
1261
1288
 
1262
1289
  /**
@@ -1274,23 +1301,38 @@ sap.ui.define([
1274
1301
  * @public
1275
1302
  */
1276
1303
  ViewStateManager.prototype.getTintColor = function(nodeRefs, inABGRFormat) {
1277
- var getTintColorMethodName = inABGRFormat ? "_getTintColorABGR" : "_getTintColor";
1304
+ var getTintColorMethod = inABGRFormat ? this._getTintColorABGR : this._getTintColor;
1278
1305
  if (Array.isArray(nodeRefs)) {
1279
- return nodeRefs.map(this[getTintColorMethodName], this);
1306
+ return nodeRefs.map(getTintColorMethod, this);
1280
1307
  } else {
1281
- return this[getTintColorMethodName](nodeRefs); // NB: The nodeRefs argument is a single nodeRef.
1308
+ return getTintColorMethod.call(this, nodeRefs); // NB: The nodeRefs argument is a single nodeRef.
1282
1309
  }
1283
1310
  };
1284
1311
 
1312
+ function toABGR(color) {
1313
+ switch (typeof color) {
1314
+ case "number":
1315
+ return color;
1316
+ case "string":
1317
+ const CSSColor = DataType.getType("sap.ui.core.CSSColor");
1318
+ return CSSColor.isValid(color) ? colorToABGR(cssColorToColor(color)) : null;
1319
+ default:
1320
+ return null; // The color is invalid, reset it to null.
1321
+ }
1322
+ }
1323
+
1285
1324
  /**
1286
1325
  * Sets the tint color of the nodes.
1287
1326
  * @param {any|any[]} nodeRefs The node reference or the array of node references.
1288
- * @param {sap.ui.core.CSSColor|int|sap.ui.core.CSSColor[]|int[]|null} tintColor The new tint color of the nodes.
1289
- * The value can be defined as a string in the CSS color format or as an integer in the ABGR format or
1290
- * it could be array of these values. If <code>null</code>
1291
- * is passed then the tint color is reset and the node's own tint color should be used.
1292
- * @param {boolean} [recursive=false] This flag indicates if the change needs to propagate recursively to child nodes.
1293
- * @returns {this} <code>this</code> to allow method chaining.
1327
+ * @param {sap.ui.core.CSSColor|int|sap.ui.core.CSSColor[]|int[]|null} tintColor The new tint color of the nodes. The
1328
+ * value can be defined as a string in the CSS color format
1329
+ * or as an integer in the ABGR format or it could be an
1330
+ * array of these values. If <code>null</code> is passed then
1331
+ * the tint color is reset and the node's own tint color
1332
+ * should be used.
1333
+ * @param {boolean} [recursive=false] This flag indicates if the change needs to propagate
1334
+ * recursively to child nodes.
1335
+ * @returns {this} Returns <code>this</code> to allow method chaining
1294
1336
  * @public
1295
1337
  */
1296
1338
  ViewStateManager.prototype.setTintColor = function(nodeRefs, tintColor, recursive) {
@@ -1298,80 +1340,62 @@ sap.ui.define([
1298
1340
  nodeRefs = [nodeRefs];
1299
1341
  }
1300
1342
 
1301
- var toABGR = function(color) {
1302
- var result = null;
1303
- switch (typeof color) {
1304
- case "number":
1305
- result = color;
1306
- break;
1307
- case "string":
1308
- const CSSColor = DataType.getType("sap.ui.core.CSSColor");
1309
- if (CSSColor.isValid(color)) {
1310
- result = colorToABGR(cssColorToColor(color));
1311
- }
1312
- break;
1313
- default:
1314
- result = undefined; // The color is invalid, reset it to null.
1315
- break;
1316
- }
1317
-
1318
- return result;
1319
- };
1320
-
1321
1343
  // check if we got an array as tint color
1322
1344
  var isBulkChange = Array.isArray(tintColor);
1323
1345
 
1324
- var recursiveColor = [];
1346
+ var recursiveColors = [];
1325
1347
  var allNodeRefs = nodeRefs;
1326
1348
 
1327
1349
  if (recursive) {
1328
1350
  allNodeRefs = [];
1329
- nodeRefs.forEach(function(nodeRef, idx) {
1351
+ nodeRefs.forEach(function(nodeRef, index) {
1330
1352
  var collected = this._collectNodesRecursively(nodeRef);
1331
1353
  allNodeRefs = allNodeRefs.concat(collected);
1332
1354
 
1333
- var length = recursiveColor.length;
1334
- recursiveColor.length = length + collected.length;
1335
- recursiveColor.fill(isBulkChange ? tintColor[idx] : tintColor, length);
1355
+ var length = recursiveColors.length;
1356
+ recursiveColors.length += collected.length;
1357
+ recursiveColors.fill(isBulkChange ? tintColor[index] : tintColor, length);
1336
1358
  }, this);
1337
1359
  } else if (!isBulkChange) {
1338
- // not recursive, opacity is a scalar
1339
- recursiveColor.length = allNodeRefs.length;
1340
- recursiveColor.fill(tintColor);
1360
+ // not recursive, tintColor is a scalar
1361
+ recursiveColors.length = allNodeRefs.length;
1362
+ recursiveColors.fill(tintColor);
1341
1363
  } else {
1342
- // not recursive, opacity is an array
1343
- recursiveColor = tintColor;
1364
+ // not recursive, tintColor is an array
1365
+ recursiveColors = tintColor;
1344
1366
  }
1345
1367
 
1346
- // filter out unchanged opacity and duplicate nodes
1347
- var changedColor = [];
1368
+ // filter out unchanged tintColor and duplicate nodes
1369
+ var changedColors = [];
1370
+ var changedColorsABGR = [];
1348
1371
  var usedNodeRefs = new Set();
1349
- var changed = allNodeRefs.filter(function(nodeRef, index) {
1372
+ var changedNodeRefs = allNodeRefs.filter(function(nodeRef, index) {
1350
1373
  if (usedNodeRefs.has(nodeRef)) {
1351
1374
  return false;
1352
1375
  }
1353
1376
 
1354
1377
  usedNodeRefs.add(nodeRef);
1355
- var changed = nodeRef ? nodeRef.userData.tintColor !== toABGR(recursiveColor[index]) : false;
1378
+
1379
+ const state = this._nodeStates.get(nodeRef);
1380
+ var tintColor = recursiveColors[index];
1381
+ var tintColorABGR = toABGR(recursiveColors[index]);
1382
+ var changed = state == null && tintColor != null || state != null && state.tintColor !== tintColorABGR;
1356
1383
  if (changed) {
1357
- changedColor.push(recursiveColor[index]);
1384
+ changedColors.push(tintColor);
1385
+ changedColorsABGR.push(tintColorABGR);
1358
1386
  }
1359
1387
 
1360
1388
  return changed;
1361
1389
  }, this);
1362
1390
 
1363
- if (changed.length > 0) {
1364
- var changedABGR = [];
1365
- changed.forEach(function(nodeRef, idx) {
1366
- var color = toABGR(changedColor[idx]);
1367
- nodeRef._vkSetTintColor(color);
1368
- changedABGR.push(color);
1369
- }, this);
1391
+ if (changedNodeRefs.length > 0) {
1392
+ this._applyTintColorNodeState(changedNodeRefs, changedColorsABGR);
1393
+ this._deleteUnusedNodeStates();
1370
1394
 
1371
1395
  var eventParameters = {
1372
- changed: changed,
1373
- tintColor: isBulkChange ? changedColor : changedColor[0],
1374
- tintColorABGR: isBulkChange ? changedABGR : changedABGR[0]
1396
+ changed: changedNodeRefs,
1397
+ tintColor: isBulkChange ? changedColors : changedColors[0],
1398
+ tintColorABGR: isBulkChange ? changedColorsABGR : changedColorsABGR[0]
1375
1399
  };
1376
1400
 
1377
1401
  this.fireTintColorChanged(eventParameters);
@@ -1381,11 +1405,48 @@ sap.ui.define([
1381
1405
  };
1382
1406
 
1383
1407
  /**
1384
- * Sets the default highlighting color
1385
- * @param {sap.ui.core.CSSColor|string|int} color The new highlighting color. The value can be defined as a string
1386
- * in the CSS color format or as an integer in the ABGR format. If <code>null</code>
1387
- * is passed then the tint color is reset and the node's own tint color should be used.
1388
- * @returns {this} <code>this</code> to allow method chaining.
1408
+ * Sets the highlight color of the nodes.
1409
+ * @param {any|any[]} nodeRefs The node reference or the array of node references.
1410
+ * @param {float[]|null} highlightColor The new highlight color of the nodes. The
1411
+ * value should be defined as an array of floats in the RGBA format.
1412
+ * If <code>null</code> is passed then the highlight color is reset
1413
+ * and the node's own highlight color should be used.
1414
+ * @param {boolean} [recursive=false] This flag indicates if the change needs to propagate
1415
+ * recursively to child nodes.
1416
+ * @returns {this} Returns <code>this</code> to allow method chaining
1417
+ * @public
1418
+ */
1419
+ ViewStateManager.prototype.setNodesHighlightColor = function(nodeRefs, highlightColor, recursive) {
1420
+ if (!Array.isArray(nodeRefs)) {
1421
+ nodeRefs = [nodeRefs];
1422
+ }
1423
+
1424
+ const allNodeRefs = recursive ? this._collectNodesRecursively(nodeRefs) : nodeRefs;
1425
+ const changedNodeRefs = Array.from(new Set(allNodeRefs)); // filter out duplicate nodes
1426
+
1427
+ if (changedNodeRefs.length > 0) {
1428
+ changedNodeRefs.forEach(function(nodeRef) {
1429
+ const state = this._getNodeState(nodeRef, highlightColor != null);
1430
+ if (state != null) {
1431
+ state.highlightColor = highlightColor;
1432
+ this._setNeedsMaterialUpdate(nodeRef);
1433
+ nodeRef.children.forEach(this._setAncestorHighlightColorRecursively.bind(this,
1434
+ highlightColor != null ? highlightColor : state.ancestorHighlightColor), this);
1435
+ }
1436
+ }, this);
1437
+ this._deleteUnusedNodeStates();
1438
+ }
1439
+
1440
+ return this;
1441
+ };
1442
+
1443
+ /**
1444
+ * Sets the default selection color
1445
+ * @param {sap.ui.core.CSSColor|string|int} color The new selection color. The value can be defined as a string in
1446
+ * the CSS color format or as an integer in the ABGR format. If
1447
+ * <code>null</code> is passed then the tint color is reset and the
1448
+ * node's own tint color should be used.
1449
+ * @returns {this} Returns <code>this</code> to allow method chaining
1389
1450
  * @public
1390
1451
  */
1391
1452
  ViewStateManager.prototype.setHighlightColor = function(color) {
@@ -1405,8 +1466,12 @@ sap.ui.define([
1405
1466
  }
1406
1467
 
1407
1468
  if (this._selectedNodes.size > 0) {
1408
- this._selectedNodes.forEach(function(nodeRef) {
1409
- this._updateHighlightColor(nodeRef, true);
1469
+ // NB: above we check if there are any nodes selected and below we traverse `_nodeStates` because unselected
1470
+ // descendants of selected nodes are not in `_selectedNodes` and we need to update both classes of nodes.
1471
+ this._nodeStates.forEach(function(state, nodeRef) {
1472
+ if (state.selected || state.ancestorSelected) {
1473
+ this._setNeedsMaterialUpdate(nodeRef);
1474
+ }
1410
1475
  }, this);
1411
1476
  }
1412
1477
 
@@ -1418,7 +1483,6 @@ sap.ui.define([
1418
1483
  return this;
1419
1484
  };
1420
1485
 
1421
-
1422
1486
  /**
1423
1487
  * Gets the default highlighting color
1424
1488
  *
@@ -2075,13 +2139,14 @@ sap.ui.define([
2075
2139
  joints.push(joint);
2076
2140
  });
2077
2141
 
2142
+ const that = this;
2078
2143
  var applyOpacity = function(joint, opacity) {
2079
- if (joint.opacity != null && joint.node && joint.node.userData) {
2080
- joint.node.userData.opacity = joint.opacity * opacity;
2081
-
2144
+ if (joint.opacity != null && joint.node) {
2145
+ let nodeOpacity = joint.opacity * opacity;
2082
2146
  if (joint.node.userData.offsetOpacity != null) {
2083
- joint.node.userData.opacity *= joint.node.userData.offsetOpacity;
2147
+ nodeOpacity *= joint.node.userData.offsetOpacity;
2084
2148
  }
2149
+ that.setOpacity(joint.node, nodeOpacity);
2085
2150
  }
2086
2151
  };
2087
2152
 
@@ -2097,29 +2162,6 @@ sap.ui.define([
2097
2162
  return this;
2098
2163
  };
2099
2164
 
2100
- /////////////////////////////////////////////////////////////////////////////////////////////////////////
2101
- //
2102
- // Moved from Viewport class: view activation - related
2103
- //
2104
- /////////////////////////////////////////////////////////////////////////////////////////////////////////
2105
- ViewStateManager.prototype._updateMaterialInNode = function(nodeInfo) {
2106
-
2107
- var node = nodeInfo.target;
2108
-
2109
- for (var i = 0, l = node.children.length; i < l; i++) {
2110
- var child = node.children[i];
2111
- if (child.userData.animatedColor) {
2112
- child._vkUpdateMaterialColorAndOpacity();
2113
- }
2114
- }
2115
-
2116
- var materialId = nodeInfo.materialId;
2117
- if (node.userData.materialId !== materialId) {
2118
- node.userData.materialId = materialId;
2119
- this._scene._setNodeMaterial(node, materialId);
2120
- }
2121
- };
2122
-
2123
2165
  /**
2124
2166
  * Get the Symbol node from nodeId,
2125
2167
  * if nodeId is not set, returns a collection of all Symbol nodes
@@ -2310,37 +2352,11 @@ sap.ui.define([
2310
2352
  };
2311
2353
 
2312
2354
  ViewStateManager.prototype._resetNodesMaterialAndOpacityByCurrentView = function(view) {
2313
-
2314
- if (!view) {
2315
- return;
2316
- }
2317
-
2318
- var nodeInfo = view.getNodeInfos();
2319
-
2320
- if (nodeInfo) { // for totaraLoader
2321
- nodeInfo.forEach(function(node) {
2322
- if (!node.target) {
2323
- return;
2324
- }
2325
-
2326
- this._updateMaterialInNode(node);
2327
- }.bind(this));
2328
-
2329
- nodeInfo.forEach(function(node) {
2330
- if (!node.target || !node.target.userData) {
2331
- return;
2332
- }
2333
-
2334
- node.target.userData.opacity = node.opacity;
2335
- });
2336
-
2337
- var nativeScene = this._scene.getSceneRef();
2338
- nativeScene._vkSetOpacity(undefined, this._jointCollection);
2339
- }
2340
-
2341
- this._selectedNodes.forEach(function(nodeRef) {
2342
- this._updateHighlightColor(nodeRef);
2343
- }.bind(this));
2355
+ view?.getNodeInfos()?.forEach(function(node) {
2356
+ if (node.target) {
2357
+ this.setOpacity(node.target, node.opacity);
2358
+ }
2359
+ }, this);
2344
2360
  };
2345
2361
 
2346
2362
  ViewStateManager.prototype._onActivateView = function(channel, eventId, event) {
@@ -2439,8 +2455,6 @@ sap.ui.define([
2439
2455
  return;
2440
2456
  }
2441
2457
 
2442
- // console.log("_prepareTransition", nodeInfos);
2443
-
2444
2458
  var visibleNodes = new Set();
2445
2459
  var hiddenNodes = new Set();
2446
2460
  nodeInfos.forEach(function(info) {
@@ -2454,7 +2468,7 @@ sap.ui.define([
2454
2468
  var collectTransitionMeshes = function(nodeRef, newVisible, oldVisible) {
2455
2469
  if (!nodeRef.userData.skipIt) {
2456
2470
  newVisible = newVisible && visibleNodes.has(nodeRef);
2457
- oldVisible = oldVisible && nodeRef.visible;
2471
+ oldVisible = oldVisible && this.getVisibilityState(nodeRef);
2458
2472
  }
2459
2473
 
2460
2474
  if (nodeRef.geometry && newVisible !== oldVisible) {
@@ -2498,8 +2512,8 @@ sap.ui.define([
2498
2512
  });
2499
2513
  }
2500
2514
 
2501
- // console.log("+", Array.from(visibleNodes), fadeInNodes);
2502
- // console.log("-", Array.from(hiddenNodes), fadeOutNodes);
2515
+ // console.log("+", Array.from(visibleNodes).map(n => n.name), fadeInNodes.map(n => n.name || ("<" + n.parent?.name)));
2516
+ // console.log("-", Array.from(hiddenNodes).map(n => n.name), fadeOutNodes.map(n => n.name || ("<" + n.parent?.name)));
2503
2517
  // console.log(this._fadeOutBackground, "->", this._fadeInBackground);
2504
2518
  };
2505
2519
 
@@ -2576,20 +2590,12 @@ sap.ui.define([
2576
2590
  return this;
2577
2591
  }
2578
2592
 
2579
- var nodeInfo = currentView.getNodeInfos();
2580
-
2581
- if (nodeInfo) {
2582
-
2583
- nodeInfo.forEach(function(node) {
2584
-
2585
- if (node.target !== nodeRef) {
2586
- return;
2587
- }
2588
-
2593
+ currentView.getNodeInfos()?.forEach(function(nodeInfo) {
2594
+ if (nodeInfo.target === nodeRef) {
2589
2595
  nodeRef.updateMatrix();
2590
- node.transform = nodeRef.matrix.elements.slice();
2591
- });
2592
- }
2596
+ nodeInfo.transform = nodeRef.matrix.elements.slice();
2597
+ }
2598
+ });
2593
2599
 
2594
2600
  if (this._jointCollection && this._jointCollection.length > 0) {
2595
2601
  this._jointCollection.forEach(function(joint) {
@@ -2656,9 +2662,7 @@ sap.ui.define([
2656
2662
  }
2657
2663
 
2658
2664
  var nodeInfo = currentView.getNodeInfos();
2659
-
2660
2665
  if (nodeInfo) {
2661
-
2662
2666
  nodeInfo.forEach(function(node) {
2663
2667
 
2664
2668
  if (node.target !== nodeRef) {
@@ -2718,9 +2722,7 @@ sap.ui.define([
2718
2722
  }
2719
2723
 
2720
2724
  var nodeInfo = currentView.getNodeInfos();
2721
-
2722
2725
  if (nodeInfo) {
2723
-
2724
2726
  nodeInfo.forEach(function(node) {
2725
2727
 
2726
2728
  if (node.target !== nodeRef) {
@@ -2830,16 +2832,10 @@ sap.ui.define([
2830
2832
  * @private
2831
2833
  */
2832
2834
  ViewStateManager.prototype.getRestOpacity = function(nodeRef) {
2833
- var nodeInfo;
2834
- var currentView = this.getCurrentView();
2835
- if (currentView) {
2836
- nodeInfo = currentView.getNodeInfos();
2837
- }
2838
-
2835
+ const nodeInfo = this.getCurrentView()?.getNodeInfos();
2839
2836
  var result = 1;
2840
2837
 
2841
- if (nodeInfo && nodeInfo.length) {
2842
-
2838
+ if (nodeInfo?.length) {
2843
2839
  for (var i = 0; i < nodeInfo.length; i++) {
2844
2840
  var node = nodeInfo[i];
2845
2841
  if (node.target !== nodeRef) {
@@ -2852,6 +2848,7 @@ sap.ui.define([
2852
2848
  break;
2853
2849
  }
2854
2850
  }
2851
+
2855
2852
  return result;
2856
2853
  };
2857
2854
 
@@ -2864,22 +2861,14 @@ sap.ui.define([
2864
2861
  * @private
2865
2862
  */
2866
2863
  ViewStateManager.prototype.setRestOpacity = function(nodeRef, opacity) {
2867
- var nodeInfo;
2868
- var currentView = this.getCurrentView();
2869
- if (currentView) {
2870
- nodeInfo = currentView.getNodeInfos();
2871
- }
2872
-
2873
- if (nodeInfo) {
2874
-
2875
- nodeInfo.forEach(function(node) {
2864
+ const nodeInfos = this.getCurrentView()?.getNodeInfos();
2865
+ nodeInfos?.forEach(function(node) {
2866
+ if (node.target !== nodeRef) {
2867
+ return;
2868
+ }
2869
+ node.opacity = opacity;
2870
+ });
2876
2871
 
2877
- if (node.target !== nodeRef) {
2878
- return;
2879
- }
2880
- node.opacity = opacity;
2881
- });
2882
- }
2883
2872
  return this;
2884
2873
  };
2885
2874
 
@@ -2891,14 +2880,8 @@ sap.ui.define([
2891
2880
  * @private
2892
2881
  */
2893
2882
  ViewStateManager.prototype.restoreRestOpacity = function(nodeRef) {
2894
- var nodeInfo;
2895
- var currentView = this.getCurrentView();
2896
- if (currentView) {
2897
- nodeInfo = currentView.getNodeInfos();
2898
- }
2899
-
2883
+ const nodeInfo = this.getCurrentView()?.getNodeInfos();
2900
2884
  if (nodeInfo) {
2901
-
2902
2885
  nodeInfo.forEach(function(node) {
2903
2886
 
2904
2887
  if (node.target !== nodeRef) {
@@ -2967,28 +2950,23 @@ sap.ui.define([
2967
2950
  * @private
2968
2951
  */
2969
2952
  ViewStateManager.prototype.updateRestOpacity = function(nodeRef) {
2970
- var nodeInfo;
2971
- var currentView = this.getCurrentView();
2972
- if (currentView) {
2973
- nodeInfo = currentView.getNodeInfos();
2953
+ const currentView = this.getCurrentView();
2954
+ if (!currentView) {
2955
+ return this;
2974
2956
  }
2975
2957
 
2976
- if (nodeInfo) {
2977
-
2978
- nodeInfo.forEach(function(node) {
2979
-
2980
- if (node.target !== nodeRef) {
2981
- return;
2982
- }
2983
- if (nodeRef.userData && nodeRef.userData.opacity !== undefined && nodeRef.userData.opacity !== null) {
2984
- node.opacity = nodeRef.userData.opacity;
2958
+ currentView.getNodeInfos()?.forEach(function(nodeInfo) {
2959
+ if (nodeInfo.target === nodeRef) {
2960
+ const opacity = this._getOpacity(nodeRef);
2961
+ if (opacity != null) {
2962
+ nodeInfo.opacity = opacity;
2985
2963
  } else {
2986
- delete node.opacity;
2964
+ delete nodeInfo.opacity;
2987
2965
  }
2988
- });
2989
- }
2966
+ }
2967
+ }, this);
2990
2968
 
2991
- if (this._jointCollection && this._jointCollection.length > 0) {
2969
+ if (this._jointCollection?.length > 0) {
2992
2970
  this._jointCollection.forEach(function(joint) {
2993
2971
  if (!joint.node || !joint.parent) {
2994
2972
  return;
@@ -3031,11 +3009,7 @@ sap.ui.define([
3031
3009
  * @private
3032
3010
  */
3033
3011
  ViewStateManager.prototype.getRestTransformationWorld = function(nodeRef) {
3034
- var nodeInfo;
3035
- var currentView = this.getCurrentView();
3036
- if (currentView) {
3037
- nodeInfo = currentView.getNodeInfos();
3038
- }
3012
+ const nodeInfo = this.getCurrentView()?.getNodeInfos();
3039
3013
 
3040
3014
  var result;
3041
3015
  if (nodeInfo) {
@@ -3101,11 +3075,7 @@ sap.ui.define([
3101
3075
  * @private
3102
3076
  */
3103
3077
  ViewStateManager.prototype.getRestTransformation = function(nodeRef) {
3104
- var nodeInfo;
3105
- var currentView = this.getCurrentView();
3106
- if (currentView) {
3107
- nodeInfo = currentView.getNodeInfos();
3108
- }
3078
+ const nodeInfo = this.getCurrentView()?.getNodeInfos();
3109
3079
 
3110
3080
  var result;
3111
3081
  if (nodeInfo) {
@@ -4012,7 +3982,7 @@ sap.ui.define([
4012
3982
  * @private
4013
3983
  */
4014
3984
  ViewStateManager.prototype.getTotalOpacity = function(nodeRef) {
4015
- return nodeRef._vkGetTotalOpacity(this._jointCollection);
3985
+ return nodeRef._vkGetTotalOpacity(this._jointCollection, this);
4016
3986
  };
4017
3987
 
4018
3988
  /**
@@ -4035,7 +4005,7 @@ sap.ui.define([
4035
4005
  parentTotal = nodeRef.parent._vkGetTotalOpacity(this._jointCollection);
4036
4006
  }
4037
4007
 
4038
- var opacity = this.getOpacity(nodeRef);
4008
+ let opacity = this._getOpacity(nodeRef);
4039
4009
 
4040
4010
  if (parentTotal !== 0.0) {
4041
4011
  opacity = totalOpacity / parentTotal;
@@ -4043,16 +4013,16 @@ sap.ui.define([
4043
4013
  totalOpacity = 0.0;
4044
4014
  }
4045
4015
 
4046
- nodeRef._vkSetOpacity(opacity, this._jointCollection);
4016
+ this.setOpacity(nodeRef, opacity);
4047
4017
 
4048
4018
  var eventParameters = {
4049
4019
  changed: nodeRef,
4050
- opacity: opacity
4020
+ opacity
4051
4021
  };
4052
4022
 
4053
4023
  this.fireOpacityChanged(eventParameters);
4054
4024
 
4055
- return { opacity: opacity, totalOpacity: totalOpacity };
4025
+ return { opacity, totalOpacity };
4056
4026
  };
4057
4027
 
4058
4028
  /**
@@ -4076,10 +4046,7 @@ sap.ui.define([
4076
4046
  return null;
4077
4047
  }
4078
4048
 
4079
- var value = 1;
4080
- if (nodeRef.userData && nodeRef.userData.opacity !== undefined && nodeRef.userData.opacity !== null) {
4081
- value = nodeRef.userData.opacity;
4082
- }
4049
+ let value = this.getOpacity(nodeRef) ?? 1;
4083
4050
 
4084
4051
  var restOpacity = this.getRestOpacity(nodeRef);
4085
4052
  // for converted absolute track, 0 rest opacity is assumed to be 1 when being converted to relative track
@@ -4250,5 +4217,343 @@ sap.ui.define([
4250
4217
  }
4251
4218
  };
4252
4219
 
4220
+ ViewStateManager.prototype._getNodeState = function(nodeRef, createIfNotExists) {
4221
+ const nodeStates = this._nodeStates;
4222
+ let state = nodeStates.get(nodeRef);
4223
+ if (state == null && createIfNotExists) {
4224
+ state = {
4225
+ visible: null,
4226
+ originalVisible: null,
4227
+ selected: false,
4228
+ ancestorSelected: false,
4229
+ tintColor: null,
4230
+ ancestorTintColor: null,
4231
+ highlightColor: null,
4232
+ ancestorHighlightColor: null,
4233
+ opacity: null,
4234
+ ancestorOverridesOpacity: false,
4235
+ boundingBoxNode: null,
4236
+ material: null,
4237
+ material2: null,
4238
+ originalMaterial: null
4239
+ };
4240
+ nodeStates.set(nodeRef, state);
4241
+ }
4242
+ return state;
4243
+ };
4244
+
4245
+ ViewStateManager.prototype._deleteUnusedNodeStates = function() {
4246
+ const materialCache = this._materialCache;
4247
+ this._nodeStates.forEach(function(state, nodeRef, nodeStates) {
4248
+ if (state.visible == null
4249
+ && !state.selected
4250
+ && !state.ancestorSelected
4251
+ && state.tintColor == null
4252
+ && state.ancestorTintColor == null
4253
+ && state.highlightColor == null
4254
+ && state.ancestorHighlightColor == null
4255
+ && state.opacity == null
4256
+ && !state.ancestorOverridesOpacity
4257
+ ) {
4258
+ nodeStates.delete(nodeRef);
4259
+ if (state.material != null) {
4260
+ materialCache.releaseMaterial(state.material);
4261
+ }
4262
+ if (state.material2 != null) {
4263
+ materialCache.releaseMaterial(state.material2);
4264
+ }
4265
+ }
4266
+ });
4267
+
4268
+ return this;
4269
+ };
4270
+
4271
+ ViewStateManager.prototype._applyVisibilityNodeState = function(nodeRefs, visibilities) {
4272
+ nodeRefs.forEach(function(nodeRef, index) {
4273
+ const newVisibility = visibilities[index];
4274
+ const ownVisibility = nodeRef.visible;
4275
+ const newVisibilityIsDifferentFromOwn = newVisibility !== ownVisibility;
4276
+ const state = this._getNodeState(nodeRef, newVisibilityIsDifferentFromOwn);
4277
+ if (state) {
4278
+ if (newVisibilityIsDifferentFromOwn) {
4279
+ state.visible = newVisibility;
4280
+ } else {
4281
+ // When we assign `null` the state is marked for deletion as it might be identical to the unmodified
4282
+ // original state.
4283
+ state.visible = null;
4284
+ }
4285
+ }
4286
+ }, this);
4287
+ };
4288
+
4289
+ ViewStateManager.prototype._applySelectionNodeState = function(nodeRefs, selected) {
4290
+ if (selected) {
4291
+ nodeRefs.forEach(function(nodeRef) {
4292
+ this._selectedNodes.add(nodeRef);
4293
+ if (this._showSelectionBoundingBox) {
4294
+ this._addBoundingBox(nodeRef);
4295
+ }
4296
+ const state = this._getNodeState(nodeRef, true);
4297
+ const nodeWasSelected = nodeIsSelected(state);
4298
+ state.selected = true;
4299
+ if (nodeIsSelected(state) !== nodeWasSelected) {
4300
+ this._setNeedsMaterialUpdate(nodeRef);
4301
+ }
4302
+ // If state.ancestorSelected === true then descendants have already been processed.
4303
+ if (!state.ancestorSelected) {
4304
+ nodeRef.children.forEach(this._setAncestorSelectedRecursively.bind(this, true), this);
4305
+ }
4306
+ }, this);
4307
+ } else {
4308
+ nodeRefs.forEach(function(nodeRef) {
4309
+ this._selectedNodes.delete(nodeRef);
4310
+ if (this._showSelectionBoundingBox) {
4311
+ this._removeBoundingBox(nodeRef);
4312
+ }
4313
+ const state = this._nodeStates.get(nodeRef);
4314
+ if (state != null) {
4315
+ const nodeWasSelected = nodeIsSelected(state);
4316
+ state.selected = false;
4317
+ if (nodeIsSelected(state) !== nodeWasSelected) {
4318
+ this._setNeedsMaterialUpdate(nodeRef);
4319
+ }
4320
+ // If state.ancestorSelected === true then the descendants also are highlighted and we should not
4321
+ // unset the state, they inherit it from the node's ancestor.
4322
+ if (!state.ancestorSelected) {
4323
+ nodeRef.children.forEach(this._setAncestorSelectedRecursively.bind(this, false), this);
4324
+ }
4325
+ }
4326
+ }, this);
4327
+ }
4328
+
4329
+ return this;
4330
+ };
4331
+
4332
+ ViewStateManager.prototype._setAncestorSelectedRecursively = function(ancestorSelected, nodeRef) {
4333
+ const state = this._getNodeState(nodeRef, ancestorSelected);
4334
+ if (state != null && state.ancestorSelected !== ancestorSelected) {
4335
+ const nodeWasSelected = nodeIsSelected(state);
4336
+ state.ancestorSelected = ancestorSelected;
4337
+ if (nodeIsSelected(state) !== nodeWasSelected) {
4338
+ this._setNeedsMaterialUpdate(nodeRef);
4339
+ }
4340
+ // If state.selected === true then its descendants have already been processed.
4341
+ if (!state.selected) {
4342
+ nodeRef.children.forEach(this._setAncestorSelectedRecursively.bind(this, ancestorSelected), this);
4343
+ }
4344
+ }
4345
+
4346
+ return this;
4347
+ };
4348
+
4349
+ ViewStateManager.prototype._applyTintColorNodeState = function(nodeRefs, colors) {
4350
+ nodeRefs.forEach(function(nodeRef, index) {
4351
+ const tintColor = colors[index];
4352
+ const state = this._getNodeState(nodeRef, tintColor != null);
4353
+ if (state != null) {
4354
+ state.tintColor = tintColor;
4355
+ this._setNeedsMaterialUpdate(nodeRef);
4356
+ nodeRef.children.forEach(this._setAncestorTintColorRecursively.bind(this,
4357
+ tintColor != null ? tintColor : state.ancestorTintColor), this);
4358
+ }
4359
+ }, this);
4360
+
4361
+ return this;
4362
+ };
4363
+
4364
+ ViewStateManager.prototype._setAncestorTintColorRecursively = function(ancestorTintColor, nodeRef) {
4365
+ const state = this._getNodeState(nodeRef, ancestorTintColor != null);
4366
+ if (state != null && state.ancestorTintColor !== ancestorTintColor) {
4367
+ const previousEffectiveTintColor = effectiveTintColor(state);
4368
+ state.ancestorTintColor = ancestorTintColor;
4369
+ if (effectiveTintColor(state) !== previousEffectiveTintColor) {
4370
+ this._setNeedsMaterialUpdate(nodeRef);
4371
+ }
4372
+ // If this node has its own tint color then this tint color is already propagated to its descendants and we
4373
+ // don't need to do anything. But if we remove tint color from this node then we need to propagate its
4374
+ // ancestor's tint color (or its absence) to this node's descendants.
4375
+ if (state.tintColor == null) {
4376
+ nodeRef.children.forEach(this._setAncestorTintColorRecursively.bind(this, ancestorTintColor), this);
4377
+ }
4378
+ }
4379
+
4380
+ return this;
4381
+ };
4382
+
4383
+ ViewStateManager.prototype._setAncestorHighlightColorRecursively = function(ancestorHighlightColor, nodeRef) {
4384
+ const state = this._getNodeState(nodeRef, ancestorHighlightColor != null);
4385
+ if (state != null && state.ancestorHighlightColor !== ancestorHighlightColor) {
4386
+ const previousEffectiveHighlightColor = effectiveHighlightColor(state);
4387
+ state.ancestorHighlightColor = ancestorHighlightColor;
4388
+ if (effectiveHighlightColor(state) !== previousEffectiveHighlightColor) {
4389
+ this._setNeedsMaterialUpdate(nodeRef);
4390
+ }
4391
+ // If this node has its own highlight color then this highlight color is already propagated to its descendants and we
4392
+ // don't need to do anything. But if we remove highlight color from this node then we need to propagate its
4393
+ // ancestor's highlight color (or its absence) to this node's descendants.
4394
+ if (state.highlightColor == null) {
4395
+ nodeRef.children.forEach(this._setAncestorHighlightColorRecursively.bind(this, ancestorHighlightColor), this);
4396
+ }
4397
+ }
4398
+
4399
+ return this;
4400
+ };
4401
+
4402
+ ViewStateManager.prototype._applyOpacityNodeState = function(nodeRefs, opacities) {
4403
+ nodeRefs.forEach(function(nodeRef, index) {
4404
+ const opacity = opacities[index];
4405
+ const state = this._getNodeState(nodeRef, opacity != null);
4406
+ if (state != null) {
4407
+ state.opacity = opacity;
4408
+ // We do not compare whether the effective (world) opacity has changed, as this is a relatively
4409
+ // expensive computation, and if the opacity is assigned, we expect it to be assigned a new value, so
4410
+ // the effective opacity has surely changed.
4411
+ this._setNeedsMaterialUpdate(nodeRef);
4412
+
4413
+ nodeRef.children.forEach(this._setAncestorOverridesOpacityRecursively.bind(this, opacity != null), this);
4414
+ }
4415
+ }, this);
4416
+
4417
+ return this;
4418
+ };
4419
+
4420
+ ViewStateManager.prototype._setAncestorOverridesOpacityRecursively = function(ancestorOverridesOpacity, nodeRef) {
4421
+ const state = this._getNodeState(nodeRef, ancestorOverridesOpacity);
4422
+ if (state != null) {
4423
+ state.ancestorOverridesOpacity = ancestorOverridesOpacity;
4424
+ this._setNeedsMaterialUpdate(nodeRef);
4425
+
4426
+ nodeRef.children.forEach(this._setAncestorOverridesOpacityRecursively.bind(this, state.opacity != null || ancestorOverridesOpacity), this);
4427
+ }
4428
+
4429
+ return this;
4430
+ };
4431
+
4432
+ ViewStateManager.prototype._computeWorldOpacity = function(nodeRef, state) {
4433
+ state ??= this._nodeStates.get(nodeRef);
4434
+ const localOpacity = state?.opacity ?? nodeRef.userData.opacity ?? 1; // Default opacity value is 1
4435
+ return nodeRef.parent ? localOpacity * this._computeWorldOpacity(nodeRef.parent) : localOpacity;
4436
+ };
4437
+
4438
+ ViewStateManager.prototype._setNeedsMaterialUpdate = function(nodeRef) {
4439
+ if (nodeRef.material != null) {
4440
+ this._needsMaterialUpdate.add(nodeRef);
4441
+ }
4442
+ };
4443
+
4444
+ function customizeMaterial(material, originalMaterial, color, opacity) {
4445
+ material.color.copy(originalMaterial.color);
4446
+ material.emissive?.copy(originalMaterial.emissive);
4447
+ material.specular?.copy(originalMaterial.specular);
4448
+
4449
+ material.userData.customColor = color.w > 0;
4450
+ if (material.userData.customColor) {
4451
+ const blendColor = new THREE.Color(color.x, color.y, color.z);
4452
+ material.color.lerp(blendColor, color.w);
4453
+ material.emissive?.lerp(blendColor, color.w * 0.5);
4454
+ material.specular?.multiplyScalar(1 - 0.5 * color.w); // reduce specular as it may overexpose the highlighting
4455
+ }
4456
+
4457
+ const wasTransparent = material.transparent;
4458
+ material.opacity = opacity;
4459
+ material.transparent = opacity < 1;
4460
+ material.needsUpdate = material.transparent !== wasTransparent; // need to update the material if "transparent" state has changed
4461
+ }
4462
+
4463
+ ViewStateManager.prototype._updateNodeStateMaterials = function() {
4464
+ if (this._needsMaterialUpdate.size === 0) {
4465
+ return this; // nothing to do
4466
+ }
4467
+
4468
+ const materialCache = this._materialCache;
4469
+ const selectionColor = abgrToColor(this._highlightColorABGR);
4470
+ const selectionColorV4 = new THREE.Vector4(selectionColor.red / 255.0, selectionColor.green / 255.0, selectionColor.blue / 255.0, 1);
4471
+
4472
+ this._needsMaterialUpdate.forEach(function(nodeRef) {
4473
+ assert(nodeRef.material != null, "Node should have a material to update its state");
4474
+ const state = this._nodeStates.get(nodeRef);
4475
+ if (state == null) {
4476
+ return; // no state for this node, nothing to do
4477
+ }
4478
+
4479
+ const worldOpacity = this._computeWorldOpacity(nodeRef, state);
4480
+ const color = new THREE.Vector4(0, 0, 0, 0);
4481
+
4482
+ const highlightColor = effectiveHighlightColor(state);
4483
+ if (highlightColor != null) { // view highlighting animation (HighlightPlayer)
4484
+ color.fromArray(highlightColor);
4485
+ }
4486
+
4487
+ const tintColor = effectiveTintColor(state);
4488
+ if (tintColor != null) { // tinting
4489
+ const c = abgrToColor(tintColor);
4490
+ color.lerp(new THREE.Vector4(c.red / 255.0, c.green / 255.0, c.blue / 255.0, 1), c.alpha);
4491
+ }
4492
+
4493
+ if (state.selected || state.ancestorSelected) { // selection
4494
+ state.material2 ??= materialCache.cloneMaterial(nodeRef.material);
4495
+ customizeMaterial(state.material2, nodeRef.material, color, worldOpacity); // material2 is used to render the outline selection
4496
+
4497
+ color.lerp(selectionColorV4, selectionColor.alpha);
4498
+ }
4499
+
4500
+ state.material ??= materialCache.cloneMaterial(nodeRef.material);
4501
+ customizeMaterial(state.material, nodeRef.material, color, worldOpacity);
4502
+
4503
+ state.customMaterial = color.w > 0 || worldOpacity < 1;
4504
+ }, this);
4505
+
4506
+ this._needsMaterialUpdate.clear();
4507
+
4508
+ return this;
4509
+ };
4510
+
4511
+ ViewStateManager.prototype.applyNodeStates = function(ignoreSelection) {
4512
+ this._updateNodeStateMaterials();
4513
+
4514
+ this._nodeStates.forEach(function(state, nodeRef) {
4515
+ if (state.material != null && state.customMaterial) {
4516
+ state.originalMaterial = nodeRef.material;
4517
+ nodeRef.material = ignoreSelection && (state.selected || state.ancestorSelected) ? (state.material2 ?? state.material) : state.material;
4518
+ nodeRef.userData.customMaterial = true; // mark the node as having a custom material
4519
+ }
4520
+ if (state.visible != null) {
4521
+ state.originalVisible = nodeRef.visible;
4522
+ nodeRef.visible = state.visible;
4523
+ }
4524
+ });
4525
+ };
4526
+
4527
+ ViewStateManager.prototype.revertNodeStates = function() {
4528
+ this._nodeStates.forEach(function(state, nodeRef) {
4529
+ if (state.originalMaterial) {
4530
+ nodeRef.material = state.originalMaterial;
4531
+ state.originalMaterial = null;
4532
+ nodeRef.userData.customMaterial = false; // mark the node as not having a custom material
4533
+ }
4534
+ if (state.originalVisible != null) {
4535
+ nodeRef.visible = state.originalVisible;
4536
+ state.originalVisible = null;
4537
+ }
4538
+ });
4539
+ };
4540
+
4541
+ function nodeIsSelected(state) {
4542
+ assert(state != null, "Node state should not be null");
4543
+ return state.selected || state.ancestorSelected;
4544
+ }
4545
+
4546
+ function effectiveVisibility(nodeRef, state) {
4547
+ return state?.visible ?? nodeRef?.visible ?? false;
4548
+ }
4549
+
4550
+ function effectiveTintColor(state) {
4551
+ return state.tintColor || state.ancestorTintColor;
4552
+ }
4553
+
4554
+ function effectiveHighlightColor(state) {
4555
+ return state.highlightColor || state.ancestorHighlightColor;
4556
+ }
4557
+
4253
4558
  return ViewStateManager;
4254
4559
  });