@ndmspc/ndmvr-core 0.0.1

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/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # ndmvr-aframe
2
+
3
+ Package for creating 3D visualization of histograms.
4
+
5
+ Built on top of [A-Frame](https://aframe.io/)
6
+
7
+ ## Features
8
+
9
+ - A-Frame components for rendering histogram.
10
+ - Components have customizable functionality.
11
+ - RxJs predefined subjects to communicate internally.
12
+ - Ability to make connection to websocket.
13
+
14
+ ## Getting started
15
+
16
+ **_NOTE:_** In this tutorial Vite with vanilla js is used, if you want to use different Vite template please see [Vite guide](https://vite.dev/guide/).
17
+
18
+ To build your own application from scratch you can do.
19
+
20
+ ```
21
+ npm create vite@latest my-app -- --template vanilla
22
+ npm install
23
+ npm install @ndmspc/ndmvr-aframe
24
+ npm run dev
25
+ ```
26
+
27
+ ## Configuration Documentation
28
+
29
+ This project supports a flexible JSON configuration to control positioning, scaling, padding, and visual styling of histograms and canvases.
30
+ <br>Below is the reference for each configuration section.
31
+
32
+ #### Configuration can be passed through config subject.
33
+ ```js
34
+ configSubjectGet().next(config);
35
+ ```
36
+
37
+ The configuration is contained within a main **config** object.
38
+
39
+ ```json
40
+ {
41
+ "config": { ... }
42
+ }
43
+ ```
44
+ ### **_camera_**
45
+
46
+ This section is intended to configure the camera's position. Note: This feature is not yet functional.
47
+
48
+ ```json
49
+ "camera": {
50
+ "position": { "x": 0, "y": 0, "z": 0 }
51
+ }
52
+ ```
53
+
54
+ ### **_canvas_**
55
+
56
+ >Controls the main canvas mesh.<br>
57
+ >**Position**: sets canvas location.<br>
58
+ >**Rotation**: in degrees.<br>
59
+ >**Scale**: width/height/depth scaling (Three.js units).<br>
60
+
61
+ ```json
62
+ "canvas": {
63
+ "position": { "x": 0, "y": 5, "z": -15 },
64
+ "rotation": { "x": 10, "y": 0, "z": 0 },
65
+ "scale": { "x": 10, "y": 10, "z": 0 }
66
+ }
67
+ ```
68
+
69
+ ### **_histogramMatrix_**
70
+
71
+ >Controls all nested histograms.<br>
72
+ >**Position**: moves the histogram matrix as a whole.<br>
73
+ >**Scale**: dimensions in Three.js units.<br>
74
+
75
+ ```json
76
+ "histogramPads": {
77
+ "position": { "x": 0, "y": 0, "z": 10 },
78
+ "scale": { "x": 10, "y": 5, "z": 10 }
79
+ }
80
+ ```
81
+
82
+ ### **_jsrootHistogramMatrix_**
83
+
84
+ >Controls histograms rendered via JSROOT renderer.<br>
85
+ >**Position**: moves the JSROOT-rendered histogram.<br>
86
+ >**Scale**: in 0–1 percentage, relative scaling factor applied to the whole histogram.<br>
87
+
88
+ ```json
89
+ "histogramPads": {
90
+ "position": { "x": 0, "y": 0, "z": 10 },
91
+ "scale": { "x": 10, "y": 5, "z": 10 }
92
+ }
93
+ ```
94
+
95
+ ### **_padding_**
96
+
97
+ >Controls spacing between nested histograms.<br>
98
+ >**default**: applies to all TH1/TH2/TH3 histograms.<br>
99
+ >**layer**: overrides default for specific histogram layers.<br>
100
+
101
+ ```json
102
+ "padding": {
103
+ "default": { "x": 0.1, "y": 0.1, "z": 0.1 },
104
+ "layer": [
105
+ { "x": 0.1, "y": 0.1, "z": 0.1 }
106
+ ]
107
+ }
108
+ ```
109
+
110
+ ### **_TH1ZScale_**
111
+
112
+ >Controls depth of TH1 bins.<br>
113
+ >Values are 0–1 (percentage):<br>
114
+ >>0 = no depth,<br>
115
+ >>1 = full depth.<br>
116
+ >
117
+ >default: applies to all TH1 histograms.<br>
118
+ >layer: overrides default for specific layers.<br>
119
+
120
+ ```json
121
+ "TH1ZScale": {
122
+ "default": 0.8,
123
+ "layer": [0.2, 1, 1, 1]
124
+ }
125
+ ```
126
+
127
+ ### **_Color_**
128
+
129
+ >Defines bin colors as gradients.<br>
130
+ >min/max: hex strings (e.g. "0x0000ff").<br>
131
+ >Gradient is calculated between min and max according to bin content (0 → max bin content).<br>
132
+ >**default**: applied to all histograms.<br>
133
+ >**layer**: overrides default per layer.<br>
134
+ >**set**: overrides both default and layer for specific sets of histograms.<br>
135
+
136
+ ```json
137
+ "color": {
138
+ "default": { "min": "0x0000ff", "max": "0xff0000" },
139
+ "layer": [],
140
+ "set": [
141
+ { "min": "0x222222", "max": "0xffaa00" },
142
+ { "min": "0x00ffff", "max": "0xff7f00" },
143
+ { "min": "0x00ff00", "max": "0x800080" },
144
+ { "min": "0x0000ff", "max": "0xff0000" }
145
+ ]
146
+ }
147
+ ```
148
+
149
+
150
+
151
+ ## How to pass functions to component
152
+
153
+ - You can pass single or array of functions to function subject.
154
+ - event identifies type of event on which the component will then listen to.
155
+ - target identifies objects to whose will those functions will bind to.
156
+ - entity is type of A-Frame entity.
157
+ - id is id of A-Frame component id in scene.
158
+ - function is plain js function which will be started when the event is triggered.
159
+
160
+ **_NOTE:_** Initialization of components can take some time at the start, so it is better to wait 100ms with setTimeout.
161
+
162
+ ```javascript
163
+ const functions = [
164
+ {
165
+ event: "click",
166
+ target: {
167
+ entity: "histogram",
168
+ id: [1, 2],
169
+ },
170
+ function: function (data) {
171
+ const scale = data.target.object3D.scale;
172
+ if (scale.x > 3) {
173
+ scale.x = 1;
174
+ scale.y = 1;
175
+ scale.z = 1;
176
+ }
177
+ data.target.object3D.scale.set(
178
+ scale.x * 1.1,
179
+ scale.y * 1.1,
180
+ scale.z * 1.1,
181
+ );
182
+ },
183
+ },
184
+ {
185
+ event: "mouseenter",
186
+ target: {
187
+ entity: "histogram",
188
+ id: "*",
189
+ },
190
+ function: function (data) {
191
+ console.log(data);
192
+ },
193
+ },
194
+ ];
195
+
196
+ setTimeout(() => functionSubjectGet().addFunctions(functions), 100);
197
+ ```
198
+
199
+ ### How to create connection to websocket
200
+
201
+ BrokerManager provides functionality duplex websocket connection.
202
+
203
+ 1. Using createWsFromParams.
204
+
205
+ In parameters of URL then needs to be URLs of websockets, also option for automatic connections.
206
+
207
+ ```javascript
208
+ // URL e.g. http://host?ws=ws://localhost:8088&autoConnect=true
209
+ brokerManagerGet().createWsFromParams(
210
+ new URL(window.location.href).searchParams,
211
+ );
212
+ ```
213
+
214
+ 2. Using createWs.
215
+
216
+ Arguments of this function is string of URL and boolean value for `autoConnect`.
217
+
218
+ ##### After creating connection
219
+
220
+ - After creation of brokers which handles websocket connections you can then.
221
+
222
+ - Subscribe for incoming messages and set what should be the callback when the message arrives.
223
+
224
+ ```javascript
225
+ brokerManagerGet()
226
+ .getSubject()
227
+ .subscribe((v) => {
228
+ console.log(v);
229
+ });
230
+ ```
231
+
232
+ - Send messages back to server
233
+
234
+ ```javascript
235
+ brokerManagerGet().getBrokerByUrl(URL, autoConnect)
236
+ .send(JSON.stringify({"your data goes here"}));
237
+ ```
238
+
239
+ ### Histogram render example
240
+
241
+ You can see example of rendered histogram at: our [Gitlab pages](https://ndmvr-aframe-af55bd.gitlab.io/hrend.html)
242
+
243
+ This example uses **histogram** component. If you want to use it in your app you can pass root json object to it via:
244
+
245
+ - **histogram_source_url** which is part of the schema of this component.
246
+
247
+ ```html
248
+ <a-entity
249
+ id="histogram18bins"
250
+ position="-140 0 0"
251
+ histogram="histogram_source_url:../../public/histograms/TH3variableBinning55x57x34.json;"
252
+ >
253
+ </a-entity>
254
+ ```
255
+
256
+ - Assigning id to it and passing the root json object by rxjs **histogramSubject**.
257
+
258
+ ```html
259
+ <a-entity id="histogram1" position="0 0 0" histogram> </a-entity>
260
+ ```
261
+
262
+ ```javascript
263
+ import { parse } from "jsroot";
264
+ histogramSubjectGet().next({ id: "histogram1", obj: parse(v) });
265
+ ```
266
+
267
+ ### Dynamic rendering of histogram
268
+
269
+ **histogram** component also supports dynamic rendering of histogram.
270
+
271
+ On how to set it up yourself you can get inspired with our stress test.
272
+
273
+ - **Prerequisites**
274
+
275
+ - Pull **NdmSpc** Docker image from repository: [registry.gitlab.com/ndmspc/ndmspc:v0.20250304.0]()
276
+ - Run the image via Docker or your preferred container tool.
277
+
278
+ ```shell
279
+ docker run --rm --init --name ndmspc -p 8080:8080 registry.gitlab.com/ndmspc/ndmspc:v0.20250304.0 ndmspc-cli serve stress -t 1000
280
+ ```
281
+
282
+ - You can set parameter of **t** which sets frequency with which histograms will be sent.
283
+
284
+ - With prerequisites completed you can head over to our webpage locally <http://localhost:5173/stress.html> or from our demo page from [Gitlab Pages](https://ndmvr-aframe-af55bd.gitlab.io/stress.html).
285
+ - Address that internal websocket will try to connect to is defined in **URL params**. Available parameters are:
286
+ - **ws** Defines address websocket will connect to
287
+ - **autoConnect** Defines whether websocket should connect automatically. If not you either have to call:
288
+ - **connectWsByUrl** from brokerManager.
289
+ - **getBrokerByUrl.connect()** from brokerManager.
290
+ - **timeout** Defines timeout (amount of time client will be trying to connect to server from start, or after disconnect).
291
+ - Final address can then look like this for local or demo page:
292
+
293
+ - <https://http://localhost:5173/stress.html?ws=ws://localhost:8080/ws/root.websocket&autoConnect=true&timeout=60000>
294
+ - <https://ndmvr-aframe-af55bd.gitlab.io/stress.html?ws=ws://localhost:8080/ws/root.websocket&autoConnect=true&timeout=60000>
295
+