@aguacerowx/react-native 0.0.17 → 0.0.20

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 (108) hide show
  1. package/README.md +66 -0
  2. package/aguacerowx-react-native.podspec +27 -9
  3. package/android/build.gradle +31 -7
  4. package/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +17 -1
  5. package/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayerView.java +5 -0
  6. package/android/src/main/java/com/aguacerowx/reactnative/GridRenderManager.java +8 -0
  7. package/android/src/main/res/raw/fragment_shader.glsl +120 -66
  8. package/ios/GridRenderLayer.swift +113 -92
  9. package/ios/compiled-shaders/Shaders-device.metallib +0 -0
  10. package/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
  11. package/ios/compiled-shaders/Shaders.metallib +0 -0
  12. package/lib/commonjs/README.md +66 -0
  13. package/lib/commonjs/aguacerowx-react-native.podspec +27 -9
  14. package/lib/commonjs/android/build.gradle +31 -7
  15. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +17 -1
  16. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayerView.java +5 -0
  17. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderManager.java +8 -0
  18. package/lib/commonjs/android/src/main/res/raw/fragment_shader.glsl +120 -66
  19. package/lib/commonjs/ios/GridRenderLayer.swift +113 -92
  20. package/lib/commonjs/ios/compiled-shaders/Shaders-device.metallib +0 -0
  21. package/lib/commonjs/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
  22. package/lib/commonjs/ios/compiled-shaders/Shaders.metallib +0 -0
  23. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js +128 -68
  24. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js.map +1 -1
  25. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js +39 -12
  26. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js.map +1 -1
  27. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js +38 -20
  28. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js.map +1 -1
  29. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js +0 -6
  30. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js.map +1 -1
  31. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/package.json +1 -1
  32. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js +28 -2
  33. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js.map +1 -1
  34. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js +35 -8
  35. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js.map +1 -1
  36. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js +38 -20
  37. package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js.map +1 -1
  38. package/lib/commonjs/package.json +9 -4
  39. package/lib/commonjs/scripts/compile-shaders.sh +27 -0
  40. package/lib/commonjs/src/GridRenderLayer.js +8 -0
  41. package/lib/commonjs/src/GridRenderLayer.js.map +1 -1
  42. package/lib/commonjs/src/WeatherLayerManager.js +5 -0
  43. package/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
  44. package/lib/module/README.md +66 -0
  45. package/lib/module/aguacerowx-react-native.podspec +27 -9
  46. package/lib/module/android/build.gradle +31 -7
  47. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +17 -1
  48. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayerView.java +5 -0
  49. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderManager.java +8 -0
  50. package/lib/module/android/src/main/res/raw/fragment_shader.glsl +120 -66
  51. package/lib/module/ios/GridRenderLayer.swift +113 -92
  52. package/lib/module/ios/compiled-shaders/Shaders-device.metallib +0 -0
  53. package/lib/module/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
  54. package/lib/module/ios/compiled-shaders/Shaders.metallib +0 -0
  55. package/lib/module/lib/commonjs/README.md +66 -0
  56. package/lib/module/lib/commonjs/aguacerowx-react-native.podspec +27 -9
  57. package/lib/module/lib/commonjs/android/build.gradle +31 -7
  58. package/lib/module/lib/commonjs/ios/GridRenderLayer.swift +113 -92
  59. package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders-device.metallib +0 -0
  60. package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
  61. package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders.metallib +0 -0
  62. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js +128 -68
  63. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js.map +1 -1
  64. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js +39 -12
  65. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js.map +1 -1
  66. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js +38 -20
  67. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js.map +1 -1
  68. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js +0 -6
  69. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js.map +1 -1
  70. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/package.json +1 -1
  71. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js +28 -2
  72. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js.map +1 -1
  73. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js +35 -8
  74. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js.map +1 -1
  75. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js +38 -20
  76. package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js.map +1 -1
  77. package/lib/module/lib/commonjs/package.json +9 -4
  78. package/lib/module/lib/commonjs/scripts/compile-shaders.sh +27 -0
  79. package/lib/module/lib/commonjs/src/GridRenderLayer.js +8 -0
  80. package/lib/module/lib/commonjs/src/GridRenderLayer.js.map +1 -1
  81. package/lib/module/lib/commonjs/src/WeatherLayerManager.js +5 -0
  82. package/lib/module/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
  83. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js +128 -68
  84. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js.map +1 -1
  85. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js +39 -12
  86. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js.map +1 -1
  87. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js +38 -20
  88. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js.map +1 -1
  89. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/index.js +0 -6
  90. package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/index.js.map +1 -1
  91. package/lib/module/node_modules/@aguacerowx/javascript-sdk/package.json +1 -1
  92. package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js +28 -2
  93. package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js.map +1 -1
  94. package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js +35 -8
  95. package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js.map +1 -1
  96. package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js +38 -20
  97. package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js.map +1 -1
  98. package/lib/module/package.json +9 -4
  99. package/lib/module/scripts/compile-shaders.sh +27 -0
  100. package/lib/module/src/GridRenderLayer.js +8 -0
  101. package/lib/module/src/GridRenderLayer.js.map +1 -1
  102. package/lib/module/src/WeatherLayerManager.js +5 -0
  103. package/lib/module/src/WeatherLayerManager.js.map +1 -1
  104. package/lib/typescript/src/GridRenderLayer.d.ts.map +1 -1
  105. package/lib/typescript/src/WeatherLayerManager.d.ts.map +1 -1
  106. package/package.json +9 -4
  107. package/src/GridRenderLayer.js +13 -0
  108. package/src/WeatherLayerManager.js +7 -0
@@ -5,94 +5,148 @@ varying vec2 v_texCoord;
5
5
 
6
6
  uniform sampler2D u_data_texture;
7
7
  uniform sampler2D u_colormap_texture;
8
+
9
+ // Data parameters
8
10
  uniform float u_scale;
9
11
  uniform float u_offset;
10
12
  uniform float u_missing_quantized;
13
+ uniform int u_scale_type; // 0 for linear, 1 for sqrt
14
+
15
+ // Style parameters
11
16
  uniform float u_opacity;
12
17
  uniform vec2 u_data_range;
18
+
19
+ // Rendering parameters
13
20
  uniform vec2 u_texture_size;
14
21
  uniform int u_smoothing;
22
+ uniform int u_is_ptype; // ADDED: Flag for ptype variables
15
23
 
16
- // --- START OF FIX: Add the new uniform ---
17
- uniform int u_scale_type; // 0 for linear, 1 for sqrt
18
- // --- END OF FIX ---
19
-
20
- // Custom bilinear filtering that respects NaN boundaries
21
- float sampleDataTextureSmooth(vec2 uv) {
22
- float missing_in_texture_range = u_missing_quantized + 128.0;
23
-
24
- // Get the four corner samples for bilinear interpolation
25
- vec2 pixel_coord = uv * u_texture_size;
26
- vec2 pixel_floor = floor(pixel_coord - 0.5) + 0.5;
27
- vec2 f = pixel_coord - pixel_floor;
28
-
29
- // Sample the 2x2 grid
30
- float s00 = texture2D(u_data_texture, (pixel_floor + vec2(0.0, 0.0)) / u_texture_size).a * 255.0;
31
- float s10 = texture2D(u_data_texture, (pixel_floor + vec2(1.0, 0.0)) / u_texture_size).a * 255.0;
32
- float s01 = texture2D(u_data_texture, (pixel_floor + vec2(0.0, 1.0)) / u_texture_size).a * 255.0;
33
- float s11 = texture2D(u_data_texture, (pixel_floor + vec2(1.0, 1.0)) / u_texture_size).a * 255.0;
34
-
35
- // Check which samples are valid (not NaN)
36
- bool valid00 = abs(s00 - missing_in_texture_range) >= 0.5;
37
- bool valid10 = abs(s10 - missing_in_texture_range) >= 0.5;
38
- bool valid01 = abs(s01 - missing_in_texture_range) >= 0.5;
39
- bool valid11 = abs(s11 - missing_in_texture_range) >= 0.5;
40
-
41
- // If ANY sample is NaN, return NaN (this pixel is on an edge)
42
- if (!valid00 || !valid10 || !valid01 || !valid11) {
43
- return missing_in_texture_range;
24
+ // --- HELPER FUNCTIONS ---
25
+
26
+ // Dequantizes a signed value (-127 to 127) into a physical value
27
+ float dequantize_val(float q_val) {
28
+ if (abs(q_val - u_missing_quantized) < 0.5) {
29
+ return -9999.0;
30
+ }
31
+ float intermediate = q_val * u_scale + u_offset;
32
+ if (u_scale_type == 1) { // 1 = sqrt
33
+ return intermediate * abs(intermediate);
44
34
  }
35
+ return intermediate;
36
+ }
45
37
 
46
- // All samples are valid - do proper bilinear interpolation
47
- float s0 = mix(s00, s10, f.x);
48
- float s1 = mix(s01, s11, f.x);
49
- return mix(s0, s1, f.y);
38
+ // Samples texture, converts 0-255 to signed -128-127 range.
39
+ // Returns a quantized value, or a large positive sentinel for missing data.
40
+ float get_value(vec2 coord) {
41
+ float value_0_to_255 = texture2D(u_data_texture, coord).a * 255.0;
42
+ float val = value_0_to_255 - 128.0;
43
+ float missing_in_texture_range_0_255 = u_missing_quantized + 128.0;
44
+ if (abs(value_0_to_255 - missing_in_texture_range_0_255) < 0.5) {
45
+ return 99999.0; // Sentinel for "missing"
46
+ }
47
+ return val;
50
48
  }
51
49
 
52
- // Simple nearest-neighbor sampling
53
- float sampleDataTextureNearest(vec2 uv) {
54
- // Snap to exact pixel center
55
- vec2 snapped_coord = floor(uv * u_texture_size + 0.5) / u_texture_size;
56
- return texture2D(u_data_texture, snapped_coord).a * 255.0;
50
+ // Maps a physical value to a precipitation type category
51
+ float get_ptype_from_physical(float val) {
52
+ if (val < 0.0) return -1.0; // Missing/Invalid
53
+ if (val < 100.0) return 0.0; // Rain
54
+ if (val < 200.0) return 1.0; // Mix
55
+ if (val < 300.0) return 2.0; // Snow
56
+ return 3.0; // Pellets
57
57
  }
58
58
 
59
+
59
60
  void main() {
60
- // Choose sampling method based on smoothing setting
61
- float value_in_texture;
62
- if (u_smoothing == 1) {
63
- value_in_texture = sampleDataTextureSmooth(v_texCoord);
64
- } else {
65
- value_in_texture = sampleDataTextureNearest(v_texCoord);
61
+ float raw_value;
62
+ float total_weight = 1.0; // Used for alpha feathering at data edges
63
+
64
+ if (u_smoothing == 0) { // Nearest neighbor
65
+ float quantized_value = get_value(v_texCoord);
66
+ if (quantized_value >= 99999.0) {
67
+ discard;
68
+ }
69
+ raw_value = dequantize_val(quantized_value);
70
+
71
+ } else { // Bilinear interpolation
72
+ vec2 texel_size = 1.0 / u_texture_size;
73
+ vec2 tex_coord_in_texels = v_texCoord * u_texture_size;
74
+ vec2 bl_texel_index = floor(tex_coord_in_texels - 0.5);
75
+ vec2 f = fract(tex_coord_in_texels - 0.5); // Interpolation factors
76
+
77
+ // Coordinates of the 4 neighboring texels
78
+ vec2 v00_coord = (bl_texel_index + vec2(0.5, 0.5)) * texel_size;
79
+ vec2 v10_coord = (bl_texel_index + vec2(1.5, 0.5)) * texel_size;
80
+ vec2 v01_coord = (bl_texel_index + vec2(0.5, 1.5)) * texel_size;
81
+ vec2 v11_coord = (bl_texel_index + vec2(1.5, 1.5)) * texel_size;
82
+
83
+ if (u_is_ptype == 1) {
84
+ // --- PTYPE-SPECIFIC INTERPOLATION ---
85
+ float v00 = dequantize_val(get_value(v00_coord));
86
+ float v10 = dequantize_val(get_value(v10_coord));
87
+ float v01 = dequantize_val(get_value(v01_coord));
88
+ float v11 = dequantize_val(get_value(v11_coord));
89
+
90
+ float p00 = get_ptype_from_physical(v00);
91
+ float p10 = get_ptype_from_physical(v10);
92
+ float p01 = get_ptype_from_physical(v01);
93
+ float p11 = get_ptype_from_physical(v11);
94
+
95
+ // Determine the dominant precipitation type for this pixel
96
+ float dominant_ptype = -1.0;
97
+ if (p00 != -1.0) dominant_ptype = p00;
98
+ else if (p10 != -1.0) dominant_ptype = p10;
99
+ else if (p01 != -1.0) dominant_ptype = p01;
100
+ else if (p11 != -1.0) dominant_ptype = p11;
101
+
102
+ if (dominant_ptype == -1.0) discard;
103
+
104
+ // Interpolate ONLY the values that match the dominant ptype
105
+ float total_value = 0.0;
106
+ total_weight = 0.0;
107
+ if (p00 == dominant_ptype) { float w = (1.0-f.x)*(1.0-f.y); total_value+=v00*w; total_weight+=w; }
108
+ if (p10 == dominant_ptype) { float w = f.x*(1.0-f.y); total_value+=v10*w; total_weight+=w; }
109
+ if (p01 == dominant_ptype) { float w = (1.0-f.x)*f.y; total_value+=v01*w; total_weight+=w; }
110
+ if (p11 == dominant_ptype) { float w = f.x*f.y; total_value+=v11*w; total_weight+=w; }
111
+
112
+ if (total_weight <= 0.0) discard;
113
+ raw_value = total_value / total_weight;
114
+
115
+ // Force full alpha to prevent dark borders between ptype regions
116
+ total_weight = 1.0;
117
+
118
+ } else {
119
+ // --- STANDARD INTERPOLATION ---
120
+ float v00_q = get_value(v00_coord);
121
+ float v10_q = get_value(v10_coord);
122
+ float v01_q = get_value(v01_coord);
123
+ float v11_q = get_value(v11_coord);
124
+
125
+ float total_value_q = 0.0;
126
+ total_weight = 0.0;
127
+ if (v00_q < 99999.0) { float w = (1.0-f.x)*(1.0-f.y); total_value_q+=v00_q*w; total_weight+=w; }
128
+ if (v10_q < 99999.0) { float w = f.x*(1.0-f.y); total_value_q+=v10_q*w; total_weight+=w; }
129
+ if (v01_q < 99999.0) { float w = (1.0-f.x)*f.y; total_value_q+=v01_q*w; total_weight+=w; }
130
+ if (v11_q < 99999.0) { float w = f.x*f.y; total_value_q+=v11_q*w; total_weight+=w; }
131
+
132
+ if (total_weight <= 0.0) discard;
133
+ raw_value = dequantize_val(total_value_q / total_weight);
134
+ }
66
135
  }
67
136
 
68
- float missing_in_texture_range = u_missing_quantized + 128.0;
137
+ // --- COMMON FINAL STEPS ---
69
138
 
70
- // Check if this pixel is a predefined missing value
71
- if (abs(value_in_texture - missing_in_texture_range) < 0.5) {
139
+ if (raw_value < u_data_range.x) {
72
140
  discard;
73
141
  }
74
142
 
75
- // Step 1: Standard linear dequantization
76
- float quantized_value = value_in_texture - 128.0;
77
- float intermediate_value = quantized_value * u_scale + u_offset;
78
- float raw_value = intermediate_value; // Default to linear
79
-
80
- // Step 2: Apply non-linear inverse scaling if specified
81
- if (u_scale_type == 1) { // 1 represents 'sqrt'
82
- // Square the intermediate value while preserving its sign
83
- raw_value = intermediate_value * abs(intermediate_value);
84
- }
85
-
86
- // If the data is below the specified minimum range, make it invisible.
87
- if (raw_value < u_data_range.x) {
143
+ float colormap_coord = clamp((raw_value - u_data_range.x) / (u_data_range.y - u_data_range.x), 0.0, 1.0);
144
+ vec4 color = texture2D(u_colormap_texture, vec2(colormap_coord, 0.5));
145
+
146
+ if (color.a < 0.1) {
88
147
  discard;
89
148
  }
90
149
 
91
- // Normalize the value to a 0-1 coordinate for colormap lookup
92
- float colormap_coord = (raw_value - u_data_range.x) / (u_data_range.y - u_data_range.x);
93
- colormap_coord = clamp(colormap_coord, 0.0, 1.0);
94
-
95
- // Sample the colormap and apply opacity
96
- vec4 color = texture2D(u_colormap_texture, vec2(colormap_coord, 0.5));
97
- gl_FragColor = vec4(color.rgb, color.a * u_opacity);
150
+ float final_alpha = color.a * u_opacity * total_weight;
151
+ gl_FragColor = vec4(color.rgb * final_alpha, final_alpha);
98
152
  }
@@ -783,99 +783,120 @@ public class GridRenderLayer: NSObject {
783
783
 
784
784
  // MARK: - Internal methods for CustomLayerHost (called by wrapper)
785
785
 
786
- internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFormat: UInt, depthStencilPixelFormat: UInt) {
787
- print("🟢 [GridRenderLayer] renderingWillStart called")
788
- self.device = metalDevice
789
- self.commandQueue = metalDevice.makeCommandQueue()
790
-
791
- let bundle = Bundle(for: GridRenderLayer.self)
792
- guard let shadersUrl = bundle.url(forResource: "AguaceroMetalShaders", withExtension: "bundle") else {
793
- print("❌ [GridRenderLayer] Could not find shader bundle")
794
- return
795
- }
796
- guard let metalBundle = Bundle(url: shadersUrl) else {
797
- print("❌ [GridRenderLayer] Could not load shader bundle")
798
- return
799
- }
800
- guard let defaultLibrary = try? metalDevice.makeDefaultLibrary(bundle: metalBundle) else {
801
- print("❌ [GridRenderLayer] Could not create Metal library")
802
- return
803
- }
804
-
805
- let vertexFunction = defaultLibrary.makeFunction(name: "vertex_main")
806
- let fragmentFunction = defaultLibrary.makeFunction(name: "fragment_main")
807
-
808
- // Set up vertex descriptor
809
- let vertexDescriptor = MTLVertexDescriptor()
810
- vertexDescriptor.attributes[0].format = .float2 // position (x, y)
811
- vertexDescriptor.attributes[0].offset = 0
812
- vertexDescriptor.attributes[0].bufferIndex = 0
813
-
814
- vertexDescriptor.attributes[1].format = .float2 // texCoord (u, v)
815
- vertexDescriptor.attributes[1].offset = MemoryLayout<Float>.size * 2
816
- vertexDescriptor.attributes[1].bufferIndex = 0
817
-
818
- vertexDescriptor.layouts[0].stride = MemoryLayout<Float>.size * 4 // 2 floats for pos + 2 for texCoord
819
- vertexDescriptor.layouts[0].stepFunction = .perVertex
820
-
821
- let pipelineDescriptor = MTLRenderPipelineDescriptor()
822
- pipelineDescriptor.vertexDescriptor = vertexDescriptor // ADD THIS LINE
823
- pipelineDescriptor.vertexFunction = vertexFunction
824
- pipelineDescriptor.fragmentFunction = fragmentFunction
825
-
826
- pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat(rawValue: colorPixelFormat) ?? .bgra8Unorm
827
- pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat) ?? .depth32Float_stencil8
828
- pipelineDescriptor.stencilAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat) ?? .depth32Float_stencil8
829
-
830
- pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
831
- pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
832
- pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
833
- pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
834
- pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
835
- pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
836
- pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
837
-
838
- do {
839
- self.pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor)
840
- } catch {
841
- print("❌ [GridRenderLayer] Failed to create pipeline state: \(error)")
842
- return
843
- }
844
-
845
- let dataSamplerDesc = MTLSamplerDescriptor()
846
- dataSamplerDesc.minFilter = .nearest
847
- dataSamplerDesc.magFilter = .nearest
848
- self.dataSamplerState = metalDevice.makeSamplerState(descriptor: dataSamplerDesc)
849
-
850
- let colormapSamplerDesc = MTLSamplerDescriptor()
851
- colormapSamplerDesc.minFilter = .nearest
852
- colormapSamplerDesc.magFilter = .nearest
853
- colormapSamplerDesc.sAddressMode = .clampToEdge
854
- self.colormapSamplerState = metalDevice.makeSamplerState(descriptor: colormapSamplerDesc)
855
-
856
- print("🟢 [GridRenderLayer] Metal setup complete")
857
-
858
- // Process any pending updates that came in before Metal was ready
859
- if let pendingGeometry = pendingGeometryUpdate {
860
- print("🟡 [GridRenderLayer] Processing pending geometry update")
861
- updateGeometry(corners: pendingGeometry.corners, gridDef: pendingGeometry.gridDef)
862
- pendingGeometryUpdate = nil
863
- }
864
-
865
- if let pendingColormap = pendingColormapUpdate {
866
- print("🟡 [GridRenderLayer] Processing pending colormap update")
867
- updateColormapTexture(colormapAsBase64: pendingColormap)
868
- pendingColormapUpdate = nil
869
- }
870
-
871
- if let pendingData = pendingDataUpdate {
872
- print("🟡 [GridRenderLayer] Processing pending data update")
873
- updateDataTexture(data: pendingData.data, nx: pendingData.nx, ny: pendingData.ny,
874
- scale: pendingData.scale, offset: pendingData.offset,
875
- missing: pendingData.missing, scaleType: pendingData.scaleType)
876
- pendingDataUpdate = nil
877
- }
786
+ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFormat: UInt, depthStencilPixelFormat: UInt) {
787
+ print("🟢 [GridRenderLayer] renderingWillStart called")
788
+ self.device = metalDevice
789
+ self.commandQueue = metalDevice.makeCommandQueue()
790
+
791
+ let bundle = Bundle(for: GridRenderLayer.self)
792
+
793
+ // Determine if we're running on simulator or device
794
+ #if targetEnvironment(simulator)
795
+ let metallibName = "Shaders-simulator"
796
+ #else
797
+ let metallibName = "Shaders-device"
798
+ #endif
799
+
800
+ // Try to load pre-compiled metallib for the current platform
801
+ let defaultLibrary: MTLLibrary?
802
+ if let metallibUrl = bundle.url(forResource: metallibName, withExtension: "metallib"),
803
+ let library = try? metalDevice.makeLibrary(URL: metallibUrl) {
804
+ print("✅ [GridRenderLayer] Loaded pre-compiled metallib (\(metallibName))")
805
+ defaultLibrary = library
806
+ }
807
+ // Fall back to compiling from .metal source (for development with npm link)
808
+ else if let metalUrl = bundle.url(forResource: "Shaders", withExtension: "metal"),
809
+ let source = try? String(contentsOf: metalUrl),
810
+ let library = try? metalDevice.makeLibrary(source: source, options: nil) {
811
+ print("✅ [GridRenderLayer] Compiled Metal shader from source")
812
+ defaultLibrary = library
813
+ }
814
+ // Neither worked
815
+ else {
816
+ print("❌ [GridRenderLayer] Could not find or compile Metal shaders")
817
+ print(" Bundle path: \(bundle.bundlePath)")
818
+ return
819
+ }
820
+
821
+ guard let library = defaultLibrary else {
822
+ print("❌ [GridRenderLayer] Failed to create Metal library")
823
+ return
824
+ }
825
+
826
+ let vertexFunction = library.makeFunction(name: "vertex_main")
827
+ let fragmentFunction = library.makeFunction(name: "fragment_main")
828
+
829
+ // Set up vertex descriptor
830
+ let vertexDescriptor = MTLVertexDescriptor()
831
+ vertexDescriptor.attributes[0].format = .float2 // position (x, y)
832
+ vertexDescriptor.attributes[0].offset = 0
833
+ vertexDescriptor.attributes[0].bufferIndex = 0
834
+
835
+ vertexDescriptor.attributes[1].format = .float2 // texCoord (u, v)
836
+ vertexDescriptor.attributes[1].offset = MemoryLayout<Float>.size * 2
837
+ vertexDescriptor.attributes[1].bufferIndex = 0
838
+
839
+ vertexDescriptor.layouts[0].stride = MemoryLayout<Float>.size * 4 // 2 floats for pos + 2 for texCoord
840
+ vertexDescriptor.layouts[0].stepFunction = .perVertex
841
+
842
+ let pipelineDescriptor = MTLRenderPipelineDescriptor()
843
+ pipelineDescriptor.vertexDescriptor = vertexDescriptor
844
+ pipelineDescriptor.vertexFunction = vertexFunction
845
+ pipelineDescriptor.fragmentFunction = fragmentFunction
846
+
847
+ pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat(rawValue: colorPixelFormat) ?? .bgra8Unorm
848
+ pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat) ?? .depth32Float_stencil8
849
+ pipelineDescriptor.stencilAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat) ?? .depth32Float_stencil8
850
+
851
+ pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
852
+ pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
853
+ pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
854
+ pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
855
+ pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
856
+ pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
857
+ pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
858
+
859
+ do {
860
+ self.pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor)
861
+ } catch {
862
+ print("❌ [GridRenderLayer] Failed to create pipeline state: \(error)")
863
+ return
878
864
  }
865
+
866
+ let dataSamplerDesc = MTLSamplerDescriptor()
867
+ dataSamplerDesc.minFilter = .nearest
868
+ dataSamplerDesc.magFilter = .nearest
869
+ self.dataSamplerState = metalDevice.makeSamplerState(descriptor: dataSamplerDesc)
870
+
871
+ let colormapSamplerDesc = MTLSamplerDescriptor()
872
+ colormapSamplerDesc.minFilter = .nearest
873
+ colormapSamplerDesc.magFilter = .nearest
874
+ colormapSamplerDesc.sAddressMode = .clampToEdge
875
+ self.colormapSamplerState = metalDevice.makeSamplerState(descriptor: colormapSamplerDesc)
876
+
877
+ print("🟢 [GridRenderLayer] Metal setup complete")
878
+
879
+ // Process any pending updates that came in before Metal was ready
880
+ if let pendingGeometry = pendingGeometryUpdate {
881
+ print("🟡 [GridRenderLayer] Processing pending geometry update")
882
+ updateGeometry(corners: pendingGeometry.corners, gridDef: pendingGeometry.gridDef)
883
+ pendingGeometryUpdate = nil
884
+ }
885
+
886
+ if let pendingColormap = pendingColormapUpdate {
887
+ print("🟡 [GridRenderLayer] Processing pending colormap update")
888
+ updateColormapTexture(colormapAsBase64: pendingColormap)
889
+ pendingColormapUpdate = nil
890
+ }
891
+
892
+ if let pendingData = pendingDataUpdate {
893
+ print("🟡 [GridRenderLayer] Processing pending data update")
894
+ updateDataTexture(data: pendingData.data, nx: pendingData.nx, ny: pendingData.ny,
895
+ scale: pendingData.scale, offset: pendingData.offset,
896
+ missing: pendingData.missing, scaleType: pendingData.scaleType)
897
+ pendingDataUpdate = nil
898
+ }
899
+ }
879
900
  internal func internalRender(_ parameters: CustomLayerRenderParameters, mtlCommandBuffer: MTLCommandBuffer, mtlRenderPassDescriptor: MTLRenderPassDescriptor) {
880
901
  guard isVisible,
881
902
  let pipeline = pipelineState,