rotor-framework 0.3.2

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 (39) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +120 -0
  3. package/package.json +59 -0
  4. package/src/source/RotorFramework.bs +654 -0
  5. package/src/source/RotorFrameworkTask.bs +278 -0
  6. package/src/source/base/BaseModel.bs +52 -0
  7. package/src/source/base/BasePlugin.bs +48 -0
  8. package/src/source/base/BaseReducer.bs +184 -0
  9. package/src/source/base/BaseStack.bs +92 -0
  10. package/src/source/base/BaseViewModel.bs +124 -0
  11. package/src/source/base/BaseWidget.bs +104 -0
  12. package/src/source/base/DispatcherCreator.bs +193 -0
  13. package/src/source/base/DispatcherExternal.bs +260 -0
  14. package/src/source/base/ListenerForDispatchers.bs +246 -0
  15. package/src/source/engine/Constants.bs +74 -0
  16. package/src/source/engine/animator/Animator.bs +334 -0
  17. package/src/source/engine/builder/Builder.bs +213 -0
  18. package/src/source/engine/builder/NodePool.bs +236 -0
  19. package/src/source/engine/builder/PluginAdapter.bs +139 -0
  20. package/src/source/engine/builder/PostProcessor.bs +331 -0
  21. package/src/source/engine/builder/Processor.bs +156 -0
  22. package/src/source/engine/builder/Tree.bs +278 -0
  23. package/src/source/engine/builder/TreeBase.bs +313 -0
  24. package/src/source/engine/builder/WidgetCreate.bs +322 -0
  25. package/src/source/engine/builder/WidgetRemove.bs +72 -0
  26. package/src/source/engine/builder/WidgetUpdate.bs +113 -0
  27. package/src/source/engine/providers/Dispatcher.bs +72 -0
  28. package/src/source/engine/providers/DispatcherProvider.bs +95 -0
  29. package/src/source/engine/services/I18n.bs +169 -0
  30. package/src/source/libs/animate/Animate.bs +753 -0
  31. package/src/source/libs/animate/LICENSE.txt +21 -0
  32. package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
  33. package/src/source/plugins/FieldsPlugin.bs +180 -0
  34. package/src/source/plugins/FocusPlugin.bs +1522 -0
  35. package/src/source/plugins/FontStylePlugin.bs +159 -0
  36. package/src/source/plugins/ObserverPlugin.bs +548 -0
  37. package/src/source/utils/ArrayUtils.bs +495 -0
  38. package/src/source/utils/GeneralUtils.bs +181 -0
  39. package/src/source/utils/NodeUtils.bs +180 -0
@@ -0,0 +1,495 @@
1
+ namespace Rotor.Utils
2
+
3
+ '==========================================================================
4
+ ' Array and AssociativeArray Utility Functions
5
+ '
6
+ ' Comprehensive collection of helper functions for array and associative array operations.
7
+ '
8
+ ' Categories:
9
+ ' - Conversion: Array to hash, object wrapping
10
+ ' - Deep Operations: Deep copy, deep extend, cloning
11
+ ' - Path Resolution: Get/clone by key path
12
+ ' - Filtering & Search: Filter, find in arrays/AAs
13
+ ' - Comparison: Difference checking
14
+ ' - Array Helpers: Ensure array, extend arrays, remove duplicates
15
+ ' - HID Operations: Ancestor/descendant checks
16
+ ' - Index Helpers: Wrapped index calculation
17
+ '
18
+ '==========================================================================
19
+
20
+ '==========================================================================
21
+ ' CONVERSION FUNCTIONS
22
+ '==========================================================================
23
+
24
+ ' ---------------------------------------------------------------------
25
+ ' wrapObject - Wraps a value in an associative array with specified key
26
+ '
27
+ ' @param {string} key - Key name
28
+ ' @param {dynamic} value - Value to wrap
29
+ ' @returns {object} Associative array { key: value }
30
+ '
31
+ function wrapObject(key as string, value as dynamic) as object
32
+ obj = {}
33
+ obj[key] = value
34
+ return obj
35
+ end function
36
+
37
+ '==========================================================================
38
+ ' DEEP OPERATIONS
39
+ '==========================================================================
40
+
41
+ ' ---------------------------------------------------------------------
42
+ ' deepExtendAA - Recursively merges source AA into target AA
43
+ '
44
+ ' Merge behavior:
45
+ ' - Both AA: Recursive merge
46
+ ' - Source overwrites other types
47
+ ' - Skips widgets (isWidget = true)
48
+ '
49
+ ' @param {dynamic} target - Target associative array to extend
50
+ ' @param {dynamic} source - Source associative array providing new values
51
+ ' @returns {dynamic} Modified target associative array
52
+ '
53
+ function deepExtendAA(target as dynamic, source as dynamic) as dynamic
54
+ if type(target) <> "roAssociativeArray" or type(source) <> "roAssociativeArray"
55
+ return source
56
+ end if
57
+
58
+ for each key in source
59
+ sourceVal = source[key]
60
+
61
+ if target.doesExist(key)
62
+ targetVal = target[key]
63
+ sourceType = type(sourceVal)
64
+ targetType = type(targetVal)
65
+
66
+ ' Recursively merge nested AAs (but not widgets)
67
+ if sourceType = "roAssociativeArray" and targetType = "roAssociativeArray" and not (sourceType = "roAssociativeArray" and sourceVal?.isWidget = true)
68
+ target[key] = deepExtendAA(targetVal, sourceVal)
69
+ else
70
+ ' Overwrite with source value
71
+ target[key] = sourceVal
72
+ end if
73
+ else
74
+ ' Key doesn't exist in target, assign directly
75
+ target[key] = sourceVal
76
+ end if
77
+ end for
78
+
79
+ return target
80
+ end function
81
+
82
+ ' ---------------------------------------------------------------------
83
+ ' deepCopy - Creates a deep copy of an object
84
+ '
85
+ ' Recursively copies all nested arrays and associative arrays.
86
+ ' Primitive values are copied by value.
87
+ '
88
+ ' @param {dynamic} source - Source object to copy
89
+ ' @returns {dynamic} Deep copy of source
90
+ '
91
+ function deepCopy(source as dynamic) as dynamic
92
+ if source = invalid
93
+ return invalid
94
+ end if
95
+
96
+ sourceType = type(source)
97
+
98
+ if sourceType = "roArray"
99
+ target = []
100
+ for each item in source
101
+ target.push(deepCopy(item))
102
+ end for
103
+ else if sourceType = "roAssociativeArray"
104
+ target = {}
105
+ for each key in source
106
+ target[key] = deepCopy(source[key])
107
+ end for
108
+ else
109
+ ' Primitive type - return as-is
110
+ return source
111
+ end if
112
+
113
+ return target
114
+ end function
115
+
116
+ ' ---------------------------------------------------------------------
117
+ ' cloneExtendAA - Creates a deep copy of source and extends it with newData
118
+ '
119
+ ' Combines deepCopy and deepExtendAA operations.
120
+ '
121
+ ' @param {object} source - Source AA to clone
122
+ ' @param {object} newData - Data to merge into clone
123
+ ' @returns {object} Extended clone
124
+ '
125
+ function cloneExtendAA(source as object, newData as object)
126
+ clone = deepCopy(source)
127
+ return deepExtendAA(clone, newData)
128
+ end function
129
+
130
+ '==========================================================================
131
+ ' PATH RESOLUTION
132
+ '==========================================================================
133
+
134
+ ' ---------------------------------------------------------------------
135
+ ' getValueByKeyPath - Gets value from nested object using dot-separated path
136
+ '
137
+ ' Traverses nested AAs using path like "parent.child.grandchild".
138
+ '
139
+ ' @param {object} source - Source object to traverse
140
+ ' @param {string} keyPath - Dot-separated path (e.g., "user.address.city")
141
+ ' @param {boolean} lastKeyAsProp - If true, wraps result in { lastKey: value }
142
+ ' @param {string} separator - Path separator (default: ".")
143
+ ' @returns {dynamic} Value at path, or invalid if not found
144
+ '
145
+ function getValueByKeyPath(source as object, keyPath as string, lastKeyAsProp = false as boolean, separator = "." as string) as object
146
+ keys = keyPath.split(separator)
147
+ keysCount = keys.Count()
148
+ current = source
149
+ index = 0
150
+
151
+ ' Traverse path
152
+ while index < keysCount and current <> invalid and current.doesExist(keys[index])
153
+ current = current[keys[index]]
154
+ index++
155
+ end while
156
+
157
+ ' Not found
158
+ if index < keysCount then return invalid
159
+
160
+ ' Return format
161
+ if lastKeyAsProp and current <> invalid
162
+ obj = wrapObject(keys[keysCount - 1], current)
163
+ return obj
164
+ else
165
+ return current
166
+ end if
167
+ end function
168
+
169
+ ' ---------------------------------------------------------------------
170
+ ' getCloneByKeyPath - Gets a deep copy of nested path structure
171
+ '
172
+ ' Creates a nested AA structure matching the path with deep copied leaf value.
173
+ '
174
+ ' @param {object} source - Source object
175
+ ' @param {string} keyPath - Dot-separated path
176
+ ' @param {string} separator - Path separator (default: ".")
177
+ ' @returns {object} Nested structure with cloned value, or invalid if not found
178
+ '
179
+ ' Example:
180
+ ' getCloneByKeyPath({a: {b: {c: 1}}}, "a.b.c") => {a: {b: {c: 1}}}
181
+ '
182
+ function getCloneByKeyPath(source as object, keyPath as string, separator = "." as string) as object
183
+ keys = keyPath.split(separator)
184
+ keysCount = keys.Count()
185
+ current = source
186
+ index = 0
187
+ fullPath = {}
188
+ path = fullPath
189
+
190
+ while index < keysCount and current <> invalid and current.doesExist(keys[index])
191
+ key = keys[index]
192
+ current = current[key]
193
+
194
+ if index = keysCount - 1
195
+ ' Last key - deep copy value
196
+ path[key] = Rotor.Utils.deepCopy(current)
197
+ else
198
+ ' Intermediate key - create nested object
199
+ path[key] = {}
200
+ path = path[key]
201
+ end if
202
+
203
+ index++
204
+ end while
205
+
206
+ ' Not found
207
+ if index < keysCount then return invalid
208
+
209
+ return fullPath
210
+ end function
211
+
212
+ '==========================================================================
213
+ ' FILTERING & SEARCH
214
+ '==========================================================================
215
+
216
+ ' ---------------------------------------------------------------------
217
+ ' filterArrayUseHandler - Filters array using handler function
218
+ '
219
+ ' @param {object} array - Array to filter
220
+ ' @param {function} handler - Filter function (item, context) => boolean
221
+ ' @param {dynamic} context - Context passed to handler
222
+ ' @returns {object} New array with filtered elements
223
+ '
224
+ function filterArrayUseHandler(array as object, handler as function, context) as object
225
+ index = 0
226
+ newArray = []
227
+ while index < array.Count()
228
+ if handler(array[index], context)
229
+ newArray.push(array[index])
230
+ end if
231
+ index++
232
+ end while
233
+ return newArray
234
+ end function
235
+
236
+ ' ---------------------------------------------------------------------
237
+ ' findInArray - Finds target value in array
238
+ '
239
+ ' @param {object} array - Array to search
240
+ ' @param {dynamic} target - Value to find
241
+ ' @returns {integer} Index of target, or -1 if not found
242
+ '
243
+ function findInArray(array as object, target) as integer
244
+ index = 0
245
+ foundIndex = -1
246
+ while foundIndex = -1 and index < array.Count()
247
+ if array[index] = target
248
+ foundIndex = index
249
+ else
250
+ index++
251
+ end if
252
+ end while
253
+ return foundIndex
254
+ end function
255
+
256
+ ' ---------------------------------------------------------------------
257
+ ' findInArrayOfAA - Finds AA in array by key value
258
+ '
259
+ ' Searches array of AAs for first element where element[key] = target.
260
+ '
261
+ ' @param {object} array - Array of associative arrays
262
+ ' @param {string} key - Key to check in each AA
263
+ ' @param {dynamic} target - Target value to find
264
+ ' @returns {integer} Index of matching AA, or -1 if not found
265
+ '
266
+ function findInArrayOfAA(array as object, key as string, target) as integer
267
+ index = 0
268
+ foundIndex = -1
269
+ while foundIndex = -1 and index < array.Count()
270
+ if array[index][key] = target
271
+ foundIndex = index
272
+ else
273
+ index++
274
+ end if
275
+ end while
276
+ return foundIndex
277
+ end function
278
+
279
+ ' ---------------------------------------------------------------------
280
+ ' checkArrayItemsByHandler - Finds array element matching handler condition
281
+ '
282
+ ' Compares array elements using handler function to find best match.
283
+ '
284
+ ' @param {object} array - Array to search
285
+ ' @param {string} targetKey - Key to compare in each element
286
+ ' @param {function} handlerFn - Comparison function (value1, value2) => boolean
287
+ ' @returns {dynamic} Element matching condition
288
+ '
289
+ function checkArrayItemsByHandler(array as object, targetKey as string, handlerFn as function) as dynamic
290
+ targetIndex = 0
291
+ length = array.Count()
292
+
293
+ if length = 1
294
+ return array[0]
295
+ end if
296
+
297
+ for index = 1 to length - 1
298
+ if handlerFn(array[index][targetKey], array[targetIndex][targetKey]) = true
299
+ targetIndex = index
300
+ end if
301
+ end for
302
+
303
+ return array[targetIndex]
304
+ end function
305
+
306
+ ' ---------------------------------------------------------------------
307
+ ' findInAArrayByKey - Finds key in AA where nested value matches target
308
+ '
309
+ ' Searches AA where each value is an AA, looking for value[key] = target.
310
+ ' Case-insensitive for string comparisons.
311
+ '
312
+ ' @param {object} aa - Associative array to search
313
+ ' @param {string} key - Nested key to check
314
+ ' @param {dynamic} value - Target value
315
+ ' @returns {string} Key of matching entry, or empty string if not found
316
+ '
317
+ function findInAArrayByKey(aa as object, key as string, value as dynamic) as string
318
+ keys = aa.Keys()
319
+ keysCount = keys.Count()
320
+ index = 0
321
+ foundIndex = -1
322
+ isTypeString = Rotor.Utils.isString(value)
323
+ if isTypeString then value = LCase(value)
324
+
325
+ while foundIndex = -1 and index < keysCount
326
+ targetValue = aa[keys[index]][key]
327
+ if (isTypeString = true ? LCase(targetValue) : targetValue) = value
328
+ foundIndex = index
329
+ else
330
+ index++
331
+ end if
332
+ end while
333
+
334
+ return foundIndex > -1 ? keys[foundIndex] : ""
335
+ end function
336
+
337
+ ' ---------------------------------------------------------------------
338
+ ' findInArrayByKey - Finds element in array by nested key path
339
+ '
340
+ ' Searches array of AAs using key path (e.g., "user.name").
341
+ ' Case-insensitive for string comparisons.
342
+ '
343
+ ' @param {object} array - Array to search (elements must be AAs)
344
+ ' @param {string} key - Key path to search (e.g., "parent.child")
345
+ ' @param {dynamic} value - Target value
346
+ ' @returns {integer} Index of matching element, or -1 if not found
347
+ '
348
+ function findInArrayByKey(array as object, key as string, value as dynamic) as integer
349
+ arrayCount = array.Count()
350
+ index = 0
351
+ foundIndex = -1
352
+ if Rotor.Utils.isString(value) then value = LCase(value)
353
+
354
+ while foundIndex = -1 and index < arrayCount
355
+ targetValue = Rotor.Utils.getValueByKeyPath(array[index], key)
356
+ if Rotor.Utils.isString(targetValue) then targetValue = LCase(targetValue)
357
+
358
+ if targetValue = value
359
+ foundIndex = index
360
+ else
361
+ index++
362
+ end if
363
+ end while
364
+
365
+ return foundIndex
366
+ end function
367
+
368
+ '==========================================================================
369
+ ' COMPARISON
370
+ '==========================================================================
371
+
372
+ ' ---------------------------------------------------------------------
373
+ ' isDifferent - Checks if two items are different
374
+ '
375
+ ' Comparison logic:
376
+ ' - roSGNode: Uses isSameNode()
377
+ ' - AA/Array: JSON comparison
378
+ ' - Function: String comparison
379
+ ' - Other: Direct comparison
380
+ '
381
+ ' @param {dynamic} item1 - First item
382
+ ' @param {dynamic} item2 - Second item
383
+ ' @returns {boolean} True if items are different
384
+ '
385
+ function isDifferent(item1, item2) as boolean
386
+ if type(item1) = "roSGNode"
387
+ return not item1.isSameNode(item2)
388
+ else if Rotor.Utils.isAssociativeArray(item1) or Rotor.Utils.isArray(item1)
389
+ return FormatJSON(item1) <> FormatJSON(item2)
390
+ else if Rotor.Utils.isFunction(item1)
391
+ return item1.ToStr() <> item2.ToStr()
392
+ else
393
+ return item1 <> item2
394
+ end if
395
+ end function
396
+
397
+ '==========================================================================
398
+ ' ARRAY HELPERS
399
+ '==========================================================================
400
+
401
+ ' ---------------------------------------------------------------------
402
+ ' ensureArray - Ensures value is wrapped in array
403
+ '
404
+ ' @param {dynamic} array - Input value or array
405
+ ' @returns {object} Array containing the value, or original if already array
406
+ '
407
+ function ensureArray(array as dynamic) as object
408
+ if isArray(array)
409
+ resolvedArray = array
410
+ else
411
+ resolvedArray = [array]
412
+ end if
413
+ return resolvedArray
414
+ end function
415
+
416
+ ' ---------------------------------------------------------------------
417
+ ' extendArrayOfStrings - Merges two string arrays without duplicates
418
+ '
419
+ ' Adds strings from sourceArray to targetArray only if not already present.
420
+ '
421
+ ' @param {object} targetArray - Target array to extend
422
+ ' @param {object} sourceArray - Source array to merge from
423
+ ' @returns {object} Extended target array
424
+ '
425
+ function extendArrayOfStrings(targetArray = [] as object, sourceArray = [] as object) as object
426
+ for each itemStr in sourceArray
427
+ foundIndex = Rotor.Utils.findInArray(targetArray, itemStr)
428
+ if foundIndex = -1
429
+ targetArray.push(itemStr)
430
+ end if
431
+ end for
432
+ return targetArray
433
+ end function
434
+
435
+ ' ---------------------------------------------------------------------
436
+ ' removeRedundantValuesInArray - Removes duplicate values from array in-place
437
+ '
438
+ ' Sorts array and removes consecutive duplicates.
439
+ ' Modifies the original array.
440
+ '
441
+ ' @param {object} array - Array to deduplicate
442
+ '
443
+ sub removeRedundantValuesInArray(array as object)
444
+ itemCount = array.Count()
445
+ if itemCount = 0 then return
446
+
447
+ array.Sort()
448
+
449
+ index = 0
450
+ while index + 1 < itemCount
451
+ if array[index] = array[index + 1]
452
+ array.delete(index + 1)
453
+ itemCount--
454
+ else
455
+ index++
456
+ end if
457
+ end while
458
+ end sub
459
+
460
+ '==========================================================================
461
+ ' HID (Hierarchical ID) OPERATIONS
462
+ '==========================================================================
463
+
464
+ ' ---------------------------------------------------------------------
465
+ ' isAncestorHID - Checks if ancestorHID is an ancestor of HID
466
+ '
467
+ ' Uses string prefix matching on HIDs.
468
+ '
469
+ ' @param {string} ancestorHID - Potential ancestor HID
470
+ ' @param {string} HID - HID to check
471
+ ' @returns {boolean} True if ancestorHID is ancestor of HID
472
+ '
473
+ ' Example:
474
+ ' isAncestorHID("scene.header", "scene.header.logo") => true
475
+ '
476
+ function isAncestorHID(ancestorHID as string, HID as string) as boolean
477
+ ancestorHIDLen = Len(ancestorHID)
478
+ return Left(HID, ancestorHIDLen) = ancestorHID and ancestorHIDLen < Len(HID)
479
+ end function
480
+
481
+ ' ---------------------------------------------------------------------
482
+ ' isDescendantHID - Checks if descendantHID is a descendant of HID
483
+ '
484
+ ' Inverse of isAncestorHID.
485
+ '
486
+ ' @param {string} descendantHID - Potential descendant HID
487
+ ' @param {string} HID - HID to check against
488
+ ' @returns {boolean} True if descendantHID is descendant of HID
489
+ '
490
+ function isDescendantHID(descendantHID as string, HID as string) as boolean
491
+ HIDlen = Len(HID)
492
+ return Left(descendantHID, HIDlen) = HID and HIDlen < Len(descendantHID)
493
+ end function
494
+
495
+ end namespace
@@ -0,0 +1,181 @@
1
+ namespace Rotor.Utils
2
+
3
+ '==========================================================================
4
+ ' TYPE CHECKING UTILITIES
5
+ '==========================================================================
6
+
7
+ ' ---------------------------------------------------------------------
8
+ ' isValid - Checks if a value is valid (not invalid or uninitialized)
9
+ '
10
+ ' @param {dynamic} value - Value to check
11
+ ' @returns {boolean} True if value is valid
12
+ '
13
+ function isValid(value as dynamic) as boolean
14
+ return Type(value) <> "<uninitialized>" and value <> invalid
15
+ end function
16
+
17
+ ' ---------------------------------------------------------------------
18
+ ' isAssociativeArray - Checks if value is an associative array
19
+ '
20
+ ' @param {dynamic} value - Value to check
21
+ ' @returns {boolean} True if value is an associative array
22
+ '
23
+ function isAssociativeArray(value as dynamic) as boolean
24
+ return IsValid(value) and GetInterface(value, "ifAssociativeArray") <> invalid
25
+ end function
26
+
27
+ ' ---------------------------------------------------------------------
28
+ ' isArray - Checks if value is an array
29
+ '
30
+ ' @param {dynamic} arr - Value to check
31
+ ' @returns {boolean} True if value is an array
32
+ '
33
+ function isArray(arr as dynamic) as boolean
34
+ return IsValid(arr) and GetInterface(arr, "ifArray") <> invalid
35
+ end function
36
+
37
+ ' ---------------------------------------------------------------------
38
+ ' isString - Checks if value is a string
39
+ '
40
+ ' @param {dynamic} value - Value to check
41
+ ' @returns {boolean} True if value is a string
42
+ '
43
+ function isString(value as dynamic) as boolean
44
+ return isValid(value) and GetInterface(value, "ifString") <> invalid
45
+ end function
46
+
47
+ ' ---------------------------------------------------------------------
48
+ ' isFunction - Checks if value is a function
49
+ '
50
+ ' @param {dynamic} value - Value to check
51
+ ' @returns {boolean} True if value is a function
52
+ '
53
+ function isFunction(value as dynamic) as boolean
54
+ return isValid(value) and GetInterface(value, "ifFunction") <> invalid
55
+ end function
56
+
57
+ ' ---------------------------------------------------------------------
58
+ ' isInteger - Checks if value is an integer
59
+ '
60
+ ' @param {dynamic} value - Value to check
61
+ ' @returns {boolean} True if value is an integer
62
+ '
63
+ function isInteger(value As Dynamic) As Boolean
64
+ Return isValid(value) And GetInterface(value, "ifInt") <> invalid And (Type(value) = "roInt" Or Type(value) = "roInteger" Or Type(value) = "Integer")
65
+ end function
66
+
67
+ ' ---------------------------------------------------------------------
68
+ ' isBoolean - Checks if value is a boolean
69
+ '
70
+ ' @param {dynamic} value - Value to check
71
+ ' @returns {boolean} True if value is a boolean
72
+ '
73
+ function isBoolean(value):
74
+ return getInterface(value, "ifBoolean") <> invalid
75
+ end function
76
+
77
+ '==========================================================================
78
+ ' MATH UTILITIES
79
+ '==========================================================================
80
+
81
+ ' ---------------------------------------------------------------------
82
+ ' min - Returns the minimum of two values
83
+ '
84
+ ' @param {dynamic} a - First value
85
+ ' @param {dynamic} b - Second value
86
+ ' @returns {dynamic} Minimum value
87
+ '
88
+ function min(a, b)
89
+ if a > b
90
+ return b
91
+ else
92
+ return a
93
+ end if
94
+ end function
95
+
96
+ ' ---------------------------------------------------------------------
97
+ ' max - Returns the maximum of two values
98
+ '
99
+ ' @param {dynamic} a - First value
100
+ ' @param {dynamic} b - Second value
101
+ ' @returns {dynamic} Maximum value
102
+ '
103
+ function max(a, b)
104
+ if a < b
105
+ return b
106
+ else
107
+ return a
108
+ end if
109
+ end function
110
+
111
+ ' ---------------------------------------------------------------------
112
+ ' rotateSegment - Rotates a line segment around a center point
113
+ '
114
+ ' @param {float} x1 - X coordinate of first point
115
+ ' @param {float} y1 - Y coordinate of first point
116
+ ' @param {float} x2 - X coordinate of second point
117
+ ' @param {float} y2 - Y coordinate of second point
118
+ ' @param {dynamic} rotation - Rotation angle in radians
119
+ ' @param {dynamic} center - Center point array [x, y]
120
+ ' @returns {object} Object with rotated coordinates {x1, y1, x2, y2}
121
+ '
122
+ function rotateSegment(x1 as float, y1 as float, x2 as float, y2 as float, rotation, center)
123
+ cosVal = Cos(rotation)
124
+ sinVal = Sin(rotation)
125
+
126
+ ' Store original values before modification
127
+ origX1 = x1
128
+ origY1 = y1
129
+ origX2 = x2
130
+ origY2 = y2
131
+
132
+ ' Rotate first point
133
+ newX1 = (cosVal * (origX1 - center[0])) + (sinVal * (origY1 - center[1])) + center[0]
134
+ newY1 = (cosVal * (origY1 - center[1])) - (sinVal * (origX1 - center[0])) + center[1]
135
+
136
+ ' Rotate second point
137
+ newX2 = (cosVal * (origX2 - center[0])) + (sinVal * (origY2 - center[1])) + center[0]
138
+ newY2 = (cosVal * (origY2 - center[1])) - (sinVal * (origX2 - center[0])) + center[1]
139
+
140
+ return {
141
+ x1: newX1,
142
+ y1: newY1,
143
+ x2: newX2,
144
+ y2: newY2
145
+ }
146
+
147
+ end function
148
+
149
+ '==========================================================================
150
+ ' UUID GENERATION
151
+ '==========================================================================
152
+
153
+ ' ---------------------------------------------------------------------
154
+ ' getUUID - Generates a random UUID using roDeviceInfo
155
+ '
156
+ ' @returns {string} UUID string
157
+ '
158
+ function getUUID() as string
159
+ if m.deviceInfoNode = invalid
160
+ m.deviceInfoNode = CreateObject("roDeviceInfo")
161
+ end if
162
+ return m.deviceInfoNode.GetRandomUUID()
163
+ end function
164
+
165
+ ' ---------------------------------------------------------------------
166
+ ' getUUIDHex - Generates a hexadecimal UUID of specified length
167
+ '
168
+ ' @param {integer} length - Length of hex UUID (default: 8)
169
+ ' @returns {string} Hexadecimal UUID string
170
+ '
171
+ function getUUIDHex(length = 8 as Integer) as string
172
+ stack = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]
173
+ uuid = ""
174
+ for i = 0 to length - 1
175
+ uuid += stack[rnd(16) - 1]
176
+ end for
177
+ return uuid
178
+ end function
179
+
180
+ end namespace
181
+