@aguacerowx/react-native 0.0.20 → 0.0.22
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/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +42 -5
- package/android/src/main/res/raw/fragment_shader.glsl +18 -8
- package/ios/FragmentUniforms.swift +2 -0
- package/ios/GridRenderLayer.swift +24 -4
- package/ios/GridRenderLayerManager.mm +12 -0
- package/ios/GridRenderLayerView.h +1 -7
- package/ios/GridRenderLayerView.m +7 -1
- package/ios/Shaders.metal +131 -71
- package/ios/compiled-shaders/Shaders-device.metallib +0 -0
- package/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
- package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +42 -5
- package/lib/commonjs/android/src/main/res/raw/fragment_shader.glsl +18 -8
- package/lib/commonjs/ios/FragmentUniforms.swift +2 -0
- package/lib/commonjs/ios/GridRenderLayer.swift +24 -4
- package/lib/commonjs/ios/GridRenderLayerManager.mm +12 -0
- package/lib/commonjs/ios/GridRenderLayerView.h +1 -7
- package/lib/commonjs/ios/GridRenderLayerView.m +7 -1
- package/lib/commonjs/ios/Shaders.metal +131 -71
- package/lib/commonjs/ios/compiled-shaders/Shaders-device.metallib +0 -0
- package/lib/commonjs/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
- package/lib/commonjs/package.json +3 -3
- package/lib/commonjs/scripts/compile-shaders.js +62 -0
- package/lib/commonjs/scripts/compile-shaders.js.map +1 -0
- package/lib/commonjs/scripts/compile-shaders.sh +12 -0
- package/lib/commonjs/src/WeatherLayerManager.js +51 -17
- package/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
- package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +42 -5
- package/lib/module/android/src/main/res/raw/fragment_shader.glsl +18 -8
- package/lib/module/ios/FragmentUniforms.swift +2 -0
- package/lib/module/ios/GridRenderLayer.swift +24 -4
- package/lib/module/ios/GridRenderLayerManager.mm +12 -0
- package/lib/module/ios/GridRenderLayerView.h +1 -7
- package/lib/module/ios/GridRenderLayerView.m +7 -1
- package/lib/module/ios/Shaders.metal +131 -71
- package/lib/module/ios/compiled-shaders/Shaders-device.metallib +0 -0
- package/lib/module/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
- package/lib/module/lib/commonjs/package.json +3 -3
- package/lib/module/lib/commonjs/scripts/compile-shaders.js +62 -0
- package/lib/module/lib/commonjs/scripts/compile-shaders.js.map +1 -0
- package/lib/module/lib/commonjs/src/WeatherLayerManager.js +51 -17
- package/lib/module/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
- package/lib/module/package.json +3 -3
- package/lib/module/scripts/compile-shaders.js +60 -0
- package/lib/module/scripts/compile-shaders.js.map +1 -0
- package/lib/module/scripts/compile-shaders.sh +12 -0
- package/lib/module/src/WeatherLayerManager.js +51 -17
- package/lib/module/src/WeatherLayerManager.js.map +1 -1
- package/lib/typescript/scripts/compile-shaders.d.ts +3 -0
- package/lib/typescript/scripts/compile-shaders.d.ts.map +1 -0
- package/lib/typescript/src/WeatherLayerManager.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/WeatherLayerManager.js +192 -151
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js +0 -1063
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/coordinate_configs.js +0 -381
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/coordinate_configs.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js +0 -1256
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js +0 -4041
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/events.js +0 -38
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/events.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/fill-layer-worker.js +0 -30
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/fill-layer-worker.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/getBundleId.js +0 -27
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/getBundleId.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js +0 -41
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/map-styles.js +0 -301
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/map-styles.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/unitConversions.js +0 -126
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/unitConversions.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/package.json +0 -48
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js +0 -1055
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/coordinate_configs.js +0 -381
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/coordinate_configs.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js +0 -1256
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js +0 -4041
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/events.js +0 -38
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/events.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/fill-layer-worker.js +0 -30
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/fill-layer-worker.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/getBundleId.js +0 -27
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/getBundleId.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/index.js +0 -41
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/index.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/map-styles.js +0 -301
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/map-styles.js.map +0 -1
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/unitConversions.js +0 -126
- package/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/unitConversions.js.map +0 -1
- package/lib/module/lib/commonjs/android/build.gradle +0 -108
- package/lib/module/lib/commonjs/android/src/main/AndroidManifest.xml +0 -7
- package/lib/module/lib/commonjs/ios/FragmentUniforms.swift +0 -14
- package/lib/module/lib/commonjs/ios/GridRenderLayer.swift +0 -983
- package/lib/module/lib/commonjs/ios/GridRenderLayerBridge.swift +0 -30
- package/lib/module/lib/commonjs/ios/GridRenderLayerManager.mm +0 -146
- package/lib/module/lib/commonjs/ios/GridRenderLayerView.h +0 -23
- package/lib/module/lib/commonjs/ios/GridRenderLayerView.m +0 -199
- package/lib/module/lib/commonjs/ios/InspectorDataCache.swift +0 -66
- package/lib/module/lib/commonjs/ios/InspectorModule.m +0 -10
- package/lib/module/lib/commonjs/ios/InspectorModule.swift +0 -64
- package/lib/module/lib/commonjs/ios/Shaders.metal +0 -131
- package/lib/module/lib/commonjs/ios/WeatherFrameProcessorModule.m +0 -16
- package/lib/module/lib/commonjs/ios/WeatherFrameProcessorModule.swift +0 -104
- package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders-device.metallib +0 -0
- package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders-simulator.metallib +0 -0
- package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders.metallib +0 -0
- package/lib/module/lib/commonjs/ios/generated/AguaceroWxReactNativeSpec-generated.mm +0 -0
- package/lib/module/lib/commonjs/ios/generated/AguaceroWxReactNativeSpec.h +0 -0
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js +0 -1063
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/coordinate_configs.js +0 -381
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/coordinate_configs.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js +0 -1256
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js +0 -4041
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/events.js +0 -38
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/events.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/fill-layer-worker.js +0 -30
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/fill-layer-worker.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/getBundleId.js +0 -27
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/getBundleId.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js +0 -41
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/index.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/map-styles.js +0 -301
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/map-styles.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/unitConversions.js +0 -126
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/dist/unitConversions.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/package.json +0 -48
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js +0 -1059
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/coordinate_configs.js +0 -381
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/coordinate_configs.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js +0 -1256
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js +0 -4041
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/events.js +0 -38
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/events.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/fill-layer-worker.js +0 -30
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/fill-layer-worker.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/getBundleId.js +0 -27
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/getBundleId.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/index.js +0 -41
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/index.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/map-styles.js +0 -301
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/map-styles.js.map +0 -1
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/unitConversions.js +0 -126
- package/lib/module/lib/commonjs/node_modules/@aguacerowx/javascript-sdk/src/unitConversions.js.map +0 -1
- package/lib/module/lib/commonjs/scripts/compile-shaders.sh +0 -27
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js +0 -1063
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/AguaceroCore.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/coordinate_configs.js +0 -381
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/coordinate_configs.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js +0 -1256
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/default-colormaps.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js +0 -4041
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/dictionaries.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/events.js +0 -38
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/events.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/fill-layer-worker.js +0 -30
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/fill-layer-worker.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/getBundleId.js +0 -27
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/getBundleId.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/index.js +0 -41
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/index.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/map-styles.js +0 -301
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/map-styles.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/unitConversions.js +0 -126
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/dist/unitConversions.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/package.json +0 -48
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js +0 -1047
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/AguaceroCore.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/coordinate_configs.js +0 -375
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/coordinate_configs.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js +0 -1250
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/default-colormaps.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js +0 -4035
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/dictionaries.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/events.js +0 -31
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/events.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/fill-layer-worker.js +0 -29
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/fill-layer-worker.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/getBundleId.js +0 -21
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/getBundleId.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/index.js +0 -12
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/index.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/map-styles.js +0 -295
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/map-styles.js.map +0 -1
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/unitConversions.js +0 -119
- package/lib/module/node_modules/@aguacerowx/javascript-sdk/src/unitConversions.js.map +0 -1
|
@@ -1,983 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import MapboxMaps
|
|
3
|
-
import Metal
|
|
4
|
-
import libzstd
|
|
5
|
-
import simd
|
|
6
|
-
|
|
7
|
-
// MARK: - Swift-only wrapper for CustomLayerHost conformance
|
|
8
|
-
@objc internal final class GridRenderLayerHost: NSObject, CustomLayerHost {
|
|
9
|
-
weak var layer: GridRenderLayer?
|
|
10
|
-
|
|
11
|
-
init(layer: GridRenderLayer) {
|
|
12
|
-
self.layer = layer
|
|
13
|
-
super.init()
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
func renderingWillStart(_ metalDevice: MTLDevice, colorPixelFormat: UInt, depthStencilPixelFormat: UInt) {
|
|
17
|
-
layer?.internalRenderingWillStart(metalDevice, colorPixelFormat: colorPixelFormat, depthStencilPixelFormat: depthStencilPixelFormat)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
func render(_ parameters: CustomLayerRenderParameters, mtlCommandBuffer: MTLCommandBuffer, mtlRenderPassDescriptor: MTLRenderPassDescriptor) {
|
|
21
|
-
layer?.internalRender(parameters, mtlCommandBuffer: mtlCommandBuffer, mtlRenderPassDescriptor: mtlRenderPassDescriptor)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func renderingWillEnd() {
|
|
25
|
-
layer?.internalRenderingWillEnd()
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@objc(GridRenderLayer)
|
|
30
|
-
public class GridRenderLayer: NSObject {
|
|
31
|
-
|
|
32
|
-
// MARK: - Properties
|
|
33
|
-
|
|
34
|
-
public var id: String
|
|
35
|
-
@objc internal var hostWrapper: GridRenderLayerHost!
|
|
36
|
-
|
|
37
|
-
// Metal objects
|
|
38
|
-
private var device: MTLDevice!
|
|
39
|
-
private var commandQueue: MTLCommandQueue!
|
|
40
|
-
private var pipelineState: MTLRenderPipelineState!
|
|
41
|
-
private var vertexBuffer: MTLBuffer?
|
|
42
|
-
private var indexBuffer: MTLBuffer?
|
|
43
|
-
private var dataTexture: MTLTexture?
|
|
44
|
-
private var colormapTexture: MTLTexture?
|
|
45
|
-
private var dataSamplerState: MTLSamplerState!
|
|
46
|
-
private var colormapSamplerState: MTLSamplerState!
|
|
47
|
-
private var isDataSamplerLinear: Bool = false
|
|
48
|
-
|
|
49
|
-
private var pendingColormapUpdate: String?
|
|
50
|
-
private var pendingDataUpdate: (data: String, nx: NSNumber, ny: NSNumber, scale: NSNumber, offset: NSNumber, missing: NSNumber, scaleType: String)?
|
|
51
|
-
private var pendingGeometryUpdate: (corners: [String: Any], gridDef: [String: Any])?
|
|
52
|
-
|
|
53
|
-
// Layer state
|
|
54
|
-
private var indexCount: Int = 0
|
|
55
|
-
private var uniforms = FragmentUniforms(
|
|
56
|
-
opacity: 1.0,
|
|
57
|
-
dataRange: SIMD2<Float>(0.0, 1.0),
|
|
58
|
-
scale: 1.0,
|
|
59
|
-
offset: 0.0,
|
|
60
|
-
missingQuantized: 127.0,
|
|
61
|
-
textureSize: SIMD2<Float>(0.0, 0.0),
|
|
62
|
-
smoothing: 1,
|
|
63
|
-
scaleType: 0
|
|
64
|
-
)
|
|
65
|
-
private var isVisible = false
|
|
66
|
-
private var pendingActiveFrameKey: String?
|
|
67
|
-
private let inspectorCache = InspectorDataCache.shared
|
|
68
|
-
private struct FrameMetadata {
|
|
69
|
-
let texture: MTLTexture
|
|
70
|
-
let scale: Float
|
|
71
|
-
let offset: Float
|
|
72
|
-
let missing: Float
|
|
73
|
-
let scaleType: Int
|
|
74
|
-
let nx: Float
|
|
75
|
-
let ny: Float
|
|
76
|
-
let filePath: String
|
|
77
|
-
let originalScale: Float // ADD THIS
|
|
78
|
-
let originalOffset: Float // ADD THIS
|
|
79
|
-
}
|
|
80
|
-
private var frameCache: [String: FrameMetadata] = [:]
|
|
81
|
-
private let highPriorityTextureQueue = DispatchQueue(label: "com.aguacero.texture-processing-high-priority", qos: .userInitiated)
|
|
82
|
-
|
|
83
|
-
// The concurrent queue for background preloading all other frames.
|
|
84
|
-
private let backgroundTextureQueue = DispatchQueue(label: "com.aguacero.texture-processing-background", qos: .utility, attributes: .concurrent)
|
|
85
|
-
// MARK: - Initializer
|
|
86
|
-
private struct VertexInfo {
|
|
87
|
-
var mercX: Float
|
|
88
|
-
var mercY: Float
|
|
89
|
-
var texU: Float
|
|
90
|
-
var texV: Float
|
|
91
|
-
var index: UInt16
|
|
92
|
-
}
|
|
93
|
-
@objc
|
|
94
|
-
public init(id: String) {
|
|
95
|
-
self.id = id
|
|
96
|
-
super.init()
|
|
97
|
-
self.hostWrapper = GridRenderLayerHost(layer: self)
|
|
98
|
-
print("🟢 [GridRenderLayer] Initialized with ID: \(id)")
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
@objc public func getHostWrapper() -> Any {
|
|
102
|
-
return self.hostWrapper as Any
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// MARK: - Public Methods (called from Manager)
|
|
106
|
-
|
|
107
|
-
@objc public func setOpacity(value: Float) {
|
|
108
|
-
self.uniforms.opacity = value
|
|
109
|
-
print("🟢 [GridRenderLayer] Set opacity: \(value)")
|
|
110
|
-
// ADD THIS: Force the map to repaint to show the opacity change immediately.
|
|
111
|
-
NotificationCenter.default.post(name: NSNotification.Name("TriggerMapRepaint"), object: nil)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
@objc public func setDataRange(value: [NSNumber]) {
|
|
115
|
-
self.uniforms.dataRange = SIMD2<Float>(value[0].floatValue, value[1].floatValue)
|
|
116
|
-
print("🟢 [GridRenderLayer] Set data range: \(value)")
|
|
117
|
-
// ADD THIS: Force the map to repaint to show the data range change immediately.
|
|
118
|
-
NotificationCenter.default.post(name: NSNotification.Name("TriggerMapRepaint"), object: nil)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@objc public func setSmoothing(value: Bool) {
|
|
122
|
-
self.uniforms.smoothing = value ? 1 : 0
|
|
123
|
-
print("🟢 [GridRenderLayer] Set smoothing: \(value)")
|
|
124
|
-
// ADD THIS: Force the map to repaint to show the smoothing change immediately.
|
|
125
|
-
NotificationCenter.default.post(name: NSNotification.Name("TriggerMapRepaint"), object: nil)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
@objc public func clear() {
|
|
129
|
-
self.isVisible = false
|
|
130
|
-
self.inspectorCache.clear()
|
|
131
|
-
print("🟢 [GridRenderLayer] Cleared")
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
@objc public func updateDataParameters(scale: NSNumber, offset: NSNumber, missing: NSNumber, scaleType: NSNumber) { // ADD scaleType parameter
|
|
135
|
-
// Update both the inspector cache AND the rendering uniforms
|
|
136
|
-
self.uniforms.scale = scale.floatValue
|
|
137
|
-
self.uniforms.offset = offset.floatValue
|
|
138
|
-
self.uniforms.missingQuantized = missing.floatValue
|
|
139
|
-
self.uniforms.scaleType = Int32(scaleType.intValue) // ADD THIS
|
|
140
|
-
|
|
141
|
-
inspectorCache.update(
|
|
142
|
-
data: inspectorCache.lastDecompressedData,
|
|
143
|
-
nx: inspectorCache.nx,
|
|
144
|
-
ny: inspectorCache.ny,
|
|
145
|
-
scale: scale.floatValue,
|
|
146
|
-
offset: offset.floatValue,
|
|
147
|
-
missing: missing.floatValue,
|
|
148
|
-
scaleType: scaleType.intValue // ADD THIS
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
print("🟢 [GridRenderLayer] Updated data parameters - scale: \(scale), offset: \(offset), missing: \(missing), scaleType: \(scaleType)")
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
@objc public func updateColormapTexture(colormapAsBase64: String) {
|
|
155
|
-
guard let device = self.device else {
|
|
156
|
-
pendingColormapUpdate = colormapAsBase64
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
guard let data = Data(base64Encoded: colormapAsBase64) else { return }
|
|
160
|
-
let textureDescriptor = MTLTextureDescriptor()
|
|
161
|
-
textureDescriptor.pixelFormat = .rgba8Unorm
|
|
162
|
-
textureDescriptor.width = 256
|
|
163
|
-
textureDescriptor.height = 1
|
|
164
|
-
textureDescriptor.usage = .shaderRead
|
|
165
|
-
guard let texture = device.makeTexture(descriptor: textureDescriptor) else { return }
|
|
166
|
-
texture.replace(
|
|
167
|
-
region: MTLRegionMake2D(0, 0, 256, 1),
|
|
168
|
-
mipmapLevel: 0,
|
|
169
|
-
withBytes: (data as NSData).bytes,
|
|
170
|
-
bytesPerRow: 256 * 4
|
|
171
|
-
)
|
|
172
|
-
self.colormapTexture = texture
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
@objc(clearGpuCache) // Explicitly name the Obj-C selector
|
|
176
|
-
public func clearGpuCache() {
|
|
177
|
-
backgroundTextureQueue.async {
|
|
178
|
-
self.frameCache.removeAll()
|
|
179
|
-
print("🟢 [GridRenderLayer] GPU frame cache cleared.")
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private func processRawData(fileData: Data) -> Data? {
|
|
184
|
-
guard let decompressedDeltas = self.decompressZstd(data: fileData) else {
|
|
185
|
-
print("❌ [GridRenderLayer] Failed to decompress zstd data")
|
|
186
|
-
return nil
|
|
187
|
-
}
|
|
188
|
-
let reconstructedData = self.reconstructData(decompressedDeltas: decompressedDeltas)
|
|
189
|
-
let finalTextureBytes = self.transformData(finalData: reconstructedData)
|
|
190
|
-
return finalTextureBytes
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private func createTextureFromBytes(bytes: Data, nx: Int, ny: Int) -> MTLTexture? {
|
|
194
|
-
guard let device = self.device else { return nil }
|
|
195
|
-
let textureDescriptor = MTLTextureDescriptor()
|
|
196
|
-
textureDescriptor.pixelFormat = .r8Unorm
|
|
197
|
-
textureDescriptor.width = nx
|
|
198
|
-
textureDescriptor.height = ny
|
|
199
|
-
textureDescriptor.usage = .shaderRead
|
|
200
|
-
|
|
201
|
-
guard let texture = device.makeTexture(descriptor: textureDescriptor) else { return nil }
|
|
202
|
-
texture.replace(
|
|
203
|
-
region: MTLRegionMake2D(0, 0, nx, ny),
|
|
204
|
-
mipmapLevel: 0,
|
|
205
|
-
withBytes: (bytes as NSData).bytes,
|
|
206
|
-
bytesPerRow: nx
|
|
207
|
-
)
|
|
208
|
-
return texture
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
private func updateInspectorCache(filePath: String, nx: Int, ny: Int, scale: Float, offset: Float, missing: Float, scaleType: Int) {
|
|
212
|
-
highPriorityTextureQueue.async {
|
|
213
|
-
|
|
214
|
-
guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
|
|
215
|
-
print("❌ [Inspector] FATAL: Failed to read file data from path.")
|
|
216
|
-
self.inspectorCache.clear()
|
|
217
|
-
return
|
|
218
|
-
}
|
|
219
|
-
print("💡 [Inspector] 2. Successfully read \(fileData.count) bytes from file.")
|
|
220
|
-
|
|
221
|
-
guard let decompressedDeltas = self.decompressZstd(data: fileData) else {
|
|
222
|
-
print("❌ [Inspector] FATAL: Failed to decompress Zstd data.")
|
|
223
|
-
self.inspectorCache.clear()
|
|
224
|
-
return
|
|
225
|
-
}
|
|
226
|
-
print("💡 [Inspector] 3. Successfully decompressed data.")
|
|
227
|
-
|
|
228
|
-
let reconstructedData = self.reconstructData(decompressedDeltas: decompressedDeltas)
|
|
229
|
-
print("💡 [Inspector] 4. Reconstructed data with \(reconstructedData.count) bytes.")
|
|
230
|
-
|
|
231
|
-
self.inspectorCache.update(
|
|
232
|
-
data: reconstructedData,
|
|
233
|
-
nx: nx,
|
|
234
|
-
ny: ny,
|
|
235
|
-
scale: scale,
|
|
236
|
-
offset: offset,
|
|
237
|
-
missing: missing,
|
|
238
|
-
scaleType: scaleType
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
print("💡 [Inspector] 5. ✅ Cache updated successfully with scaleType=\(scaleType)")
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
@objc(setActiveFrameWithCacheKey:)
|
|
246
|
-
public func setActiveFrame(cacheKey: String) {
|
|
247
|
-
// First, check if the frame is already in the cache.
|
|
248
|
-
if let frame = frameCache[cacheKey] {
|
|
249
|
-
print("⚡️ [GridRenderLayer] Cache HIT for key: \(cacheKey). Swapping texture.")
|
|
250
|
-
|
|
251
|
-
self.dataTexture = frame.texture
|
|
252
|
-
self.uniforms.scale = frame.scale
|
|
253
|
-
self.uniforms.offset = frame.offset
|
|
254
|
-
self.uniforms.missingQuantized = frame.missing
|
|
255
|
-
self.uniforms.textureSize = SIMD2<Float>(frame.nx, frame.ny)
|
|
256
|
-
self.uniforms.scaleType = Int32(frame.scaleType)
|
|
257
|
-
|
|
258
|
-
self.isVisible = true
|
|
259
|
-
self.pendingActiveFrameKey = nil // Clear any pending key
|
|
260
|
-
NotificationCenter.default.post(name: NSNotification.Name("TriggerMapRepaint"), object: nil)
|
|
261
|
-
|
|
262
|
-
// --- ADD THIS ---
|
|
263
|
-
// Now that the frame is active, update the inspector cache with its data.
|
|
264
|
-
self.updateInspectorCache(
|
|
265
|
-
filePath: frame.filePath,
|
|
266
|
-
nx: Int(frame.nx),
|
|
267
|
-
ny: Int(frame.ny),
|
|
268
|
-
scale: frame.scale,
|
|
269
|
-
offset: frame.offset,
|
|
270
|
-
missing: frame.missing,
|
|
271
|
-
scaleType: frame.scaleType
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
} else {
|
|
275
|
-
// --- FIX: If it's a miss, store the key and wait for it to be primed. ---
|
|
276
|
-
print("⚠️ [GridRenderLayer] setActiveFrame cache MISS for key: \(cacheKey). Will apply when primed.")
|
|
277
|
-
self.pendingActiveFrameKey = cacheKey
|
|
278
|
-
self.isVisible = false // Hide the layer until the frame is ready
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
@objc(primeGpuCacheWithFrameInfo:)
|
|
283
|
-
public func primeGpuCache(frameInfo: [String: [String: Any]]) {
|
|
284
|
-
let group = DispatchGroup()
|
|
285
|
-
|
|
286
|
-
if (frameInfo.count > 1) {
|
|
287
|
-
print("🟡 [GridRenderLayer] Starting to prime \(frameInfo.count) textures concurrently...")
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
for (cacheKey, info) in frameInfo {
|
|
291
|
-
group.enter()
|
|
292
|
-
|
|
293
|
-
backgroundTextureQueue.async {
|
|
294
|
-
defer { group.leave() }
|
|
295
|
-
if self.frameCache[cacheKey] != nil { return }
|
|
296
|
-
|
|
297
|
-
guard let filePath = info["filePath"] as? String,
|
|
298
|
-
let nx = info["nx"] as? NSNumber,
|
|
299
|
-
let ny = info["ny"] as? NSNumber,
|
|
300
|
-
let scale = info["scale"] as? NSNumber,
|
|
301
|
-
let offset = info["offset"] as? NSNumber,
|
|
302
|
-
let missing = info["missing"] as? NSNumber,
|
|
303
|
-
let scaleTypeStr = info["scaleType"] as? String,
|
|
304
|
-
let originalScale = info["originalScale"] as? NSNumber, // ADD THIS
|
|
305
|
-
let originalOffset = info["originalOffset"] as? NSNumber, // ADD THIS
|
|
306
|
-
let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)),
|
|
307
|
-
let finalTextureBytes = self.processRawData(fileData: fileData)
|
|
308
|
-
else {
|
|
309
|
-
print("❌ [GridRenderLayer] Skipping prime for \(cacheKey), missing data.")
|
|
310
|
-
return
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
DispatchQueue.main.sync {
|
|
314
|
-
if let texture = self.createTextureFromBytes(bytes: finalTextureBytes, nx: nx.intValue, ny: ny.intValue) {
|
|
315
|
-
let metadata = FrameMetadata(
|
|
316
|
-
texture: texture,
|
|
317
|
-
scale: scale.floatValue,
|
|
318
|
-
offset: offset.floatValue,
|
|
319
|
-
missing: missing.floatValue,
|
|
320
|
-
scaleType: (scaleTypeStr == "sqrt") ? 1 : 0,
|
|
321
|
-
nx: nx.floatValue,
|
|
322
|
-
ny: ny.floatValue,
|
|
323
|
-
filePath: filePath,
|
|
324
|
-
originalScale: originalScale.floatValue, // ADD THIS
|
|
325
|
-
originalOffset: originalOffset.floatValue // ADD THIS
|
|
326
|
-
)
|
|
327
|
-
self.frameCache[cacheKey] = metadata
|
|
328
|
-
print(" ✅ [GridRenderLayer] Primed and cached frame for key: \(cacheKey)")
|
|
329
|
-
|
|
330
|
-
if let pendingKey = self.pendingActiveFrameKey, pendingKey == cacheKey {
|
|
331
|
-
print("👍 [GridRenderLayer] The pending frame is now ready. Activating it.")
|
|
332
|
-
self.setActiveFrame(cacheKey: pendingKey)
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
group.notify(queue: .main) {
|
|
340
|
-
if (frameInfo.count > 1) {
|
|
341
|
-
print("🎉 [GridRenderLayer] All \(frameInfo.count) frames have been processed and cached to the GPU.")
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
@objc public func updateDataTexture(data: String, nx: NSNumber, ny: NSNumber, scale: NSNumber, offset: NSNumber, missing: NSNumber, scaleType: String) {
|
|
347
|
-
print("🚀 [GridRenderLayer] FAST LANE: updateDataTexture called for initial frame...")
|
|
348
|
-
|
|
349
|
-
let filePath = data // Assuming 'data' is always the file path for this method.
|
|
350
|
-
|
|
351
|
-
// USE THE HIGH-PRIORITY QUEUE
|
|
352
|
-
highPriorityTextureQueue.async {
|
|
353
|
-
guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { return }
|
|
354
|
-
|
|
355
|
-
guard let finalTextureBytes = self.processRawData(fileData: fileData) else {
|
|
356
|
-
print("❌ [GridRenderLayer] FAST LANE: Failed to process initial frame data.")
|
|
357
|
-
return
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
DispatchQueue.main.async {
|
|
361
|
-
guard let texture = self.createTextureFromBytes(bytes: finalTextureBytes, nx: nx.intValue, ny: ny.intValue) else {
|
|
362
|
-
return
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
self.dataTexture = texture
|
|
366
|
-
self.uniforms.scale = scale.floatValue
|
|
367
|
-
self.uniforms.offset = offset.floatValue
|
|
368
|
-
self.uniforms.missingQuantized = missing.floatValue
|
|
369
|
-
self.uniforms.scaleType = Int32((scaleType == "sqrt") ? 1 : 0)
|
|
370
|
-
self.uniforms.textureSize = SIMD2<Float>(nx.floatValue, ny.floatValue)
|
|
371
|
-
|
|
372
|
-
self.isVisible = true
|
|
373
|
-
print("✅ [GridRenderLayer] FAST LANE: Initial frame processed and displayed.")
|
|
374
|
-
NotificationCenter.default.post(name: NSNotification.Name("TriggerMapRepaint"), object: nil)
|
|
375
|
-
|
|
376
|
-
// --- ADD THIS ---
|
|
377
|
-
// Update the inspector cache for the initial frame.
|
|
378
|
-
self.updateInspectorCache(
|
|
379
|
-
filePath: filePath,
|
|
380
|
-
nx: nx.intValue,
|
|
381
|
-
ny: ny.intValue,
|
|
382
|
-
scale: scale.floatValue,
|
|
383
|
-
offset: offset.floatValue,
|
|
384
|
-
missing: missing.floatValue,
|
|
385
|
-
scaleType: (scaleType == "sqrt") ? 1 : 0
|
|
386
|
-
)
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
@objc public func updateGeometry(corners: [String: Any], gridDef: [String: Any]) {
|
|
392
|
-
print("🟢 [GridRenderLayer] updateGeometry called")
|
|
393
|
-
print(" device: \(self.device != nil)")
|
|
394
|
-
|
|
395
|
-
guard self.device != nil else {
|
|
396
|
-
print("⚠️ [GridRenderLayer] Device not ready yet, storing geometry for later")
|
|
397
|
-
pendingGeometryUpdate = (corners: corners, gridDef: gridDef)
|
|
398
|
-
return
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
print(" Device is ready, processing geometry immediately")
|
|
402
|
-
|
|
403
|
-
DispatchQueue.global(qos: .userInitiated).async {
|
|
404
|
-
print(" 🔄 Generating geometry on background thread")
|
|
405
|
-
|
|
406
|
-
var vertices: [Float] = []
|
|
407
|
-
var indices: [UInt16] = []
|
|
408
|
-
|
|
409
|
-
self.generateGeometryData(gridDef: gridDef, vertices: &vertices, indices: &indices)
|
|
410
|
-
|
|
411
|
-
if vertices.isEmpty || indices.isEmpty {
|
|
412
|
-
print("❌ [GridRenderLayer] No geometry generated")
|
|
413
|
-
return
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
print(" ✅ Generated \(vertices.count/4) vertices, \(indices.count/3) triangles")
|
|
417
|
-
|
|
418
|
-
self.indexCount = indices.count
|
|
419
|
-
|
|
420
|
-
DispatchQueue.main.async {
|
|
421
|
-
print(" 🎨 Creating buffers on main thread")
|
|
422
|
-
|
|
423
|
-
self.vertexBuffer = self.device.makeBuffer(bytes: vertices, length: vertices.count * MemoryLayout<Float>.size, options: [])
|
|
424
|
-
self.indexBuffer = self.device.makeBuffer(bytes: indices, length: indices.count * MemoryLayout<UInt16>.size, options: [])
|
|
425
|
-
|
|
426
|
-
print("🟢 [GridRenderLayer] Geometry updated: \(vertices.count/4) vertices, \(indices.count/3) triangles")
|
|
427
|
-
print(" vertexBuffer: \(self.vertexBuffer != nil)")
|
|
428
|
-
print(" indexBuffer: \(self.indexBuffer != nil)")
|
|
429
|
-
print(" indexCount: \(self.indexCount)")
|
|
430
|
-
|
|
431
|
-
// TRIGGER A REPAINT - ADD THIS:
|
|
432
|
-
NotificationCenter.default.post(name: NSNotification.Name("TriggerMapRepaint"), object: nil)
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
private func decompressZstd(data: Data) -> Data? {
|
|
438
|
-
let decompressedSize = data.withUnsafeBytes { ptr in
|
|
439
|
-
ZSTD_getFrameContentSize(ptr.baseAddress, data.count)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
guard Int64(bitPattern: decompressedSize) > 0 else {
|
|
443
|
-
print("❌ [GridRenderLayer] Could not determine decompressed size")
|
|
444
|
-
return nil
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
var decompressedData = Data(count: Int(decompressedSize))
|
|
448
|
-
|
|
449
|
-
let result = decompressedData.withUnsafeMutableBytes { decompressedPtr -> Int in
|
|
450
|
-
data.withUnsafeBytes { compressedPtr -> Int in
|
|
451
|
-
let returnValue = ZSTD_decompress(
|
|
452
|
-
decompressedPtr.baseAddress,
|
|
453
|
-
Int(decompressedSize),
|
|
454
|
-
compressedPtr.baseAddress,
|
|
455
|
-
data.count
|
|
456
|
-
)
|
|
457
|
-
return Int(returnValue)
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
guard result > 0 && result == decompressedData.count else {
|
|
462
|
-
if result > 0 {
|
|
463
|
-
let size_t_result = size_t(result)
|
|
464
|
-
if let errorName = ZSTD_getErrorName(size_t_result) {
|
|
465
|
-
let errorString = String(cString: errorName)
|
|
466
|
-
print("❌ [GridRenderLayer] Zstd decompression failed: \(errorString)")
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return nil
|
|
470
|
-
}
|
|
471
|
-
return decompressedData
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// MARK: - Private Helper Methods
|
|
475
|
-
|
|
476
|
-
private func reconstructData(decompressedDeltas: Data) -> Data {
|
|
477
|
-
guard !decompressedDeltas.isEmpty else { return Data() }
|
|
478
|
-
var reconstructedData = Data(count: decompressedDeltas.count)
|
|
479
|
-
reconstructedData[0] = decompressedDeltas[0]
|
|
480
|
-
for i in 1..<decompressedDeltas.count {
|
|
481
|
-
reconstructedData[i] = UInt8(Int8(bitPattern: reconstructedData[i-1]) &+ Int8(bitPattern: decompressedDeltas[i]))
|
|
482
|
-
}
|
|
483
|
-
return reconstructedData
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
private func transformData(finalData: Data) -> Data {
|
|
487
|
-
var transformedData = Data(count: finalData.count)
|
|
488
|
-
for i in 0..<finalData.count {
|
|
489
|
-
transformedData[i] = UInt8(Int16(Int8(bitPattern: finalData[i])) + 128)
|
|
490
|
-
}
|
|
491
|
-
return transformedData
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
private func isLCCType(gridDef: [String: Any]) -> Bool {
|
|
495
|
-
if let type = gridDef["type"] as? String {
|
|
496
|
-
return type == "lambert_conformal_conic"
|
|
497
|
-
}
|
|
498
|
-
return false
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
private func generateLCCGeometry(gridDef: [String: Any], vertices: inout [Float], indices: inout [UInt16]) {
|
|
502
|
-
guard let gridParams = gridDef["grid_params"] as? [String: Any],
|
|
503
|
-
let projParams = gridDef["proj_params"] as? [String: Any],
|
|
504
|
-
let nx = gridParams["nx"] as? Int,
|
|
505
|
-
let ny = gridParams["ny"] as? Int,
|
|
506
|
-
let dx = gridParams["dx"] as? Double,
|
|
507
|
-
let dy = gridParams["dy"] as? Double,
|
|
508
|
-
let x_origin = gridParams["x_origin"] as? Double,
|
|
509
|
-
let y_origin = gridParams["y_origin"] as? Double else {
|
|
510
|
-
return
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
let subdivisions = 60
|
|
514
|
-
let TILE_SIZE: Double = 512.0
|
|
515
|
-
|
|
516
|
-
let x_min = x_origin
|
|
517
|
-
let y_max = y_origin
|
|
518
|
-
let x_max = x_origin + Double(nx - 1) * dx
|
|
519
|
-
let y_min = y_origin + Double(ny - 1) * dy
|
|
520
|
-
|
|
521
|
-
var vertexGrid: [[VertexInfo?]] = Array(repeating: Array(repeating: nil, count: subdivisions + 1), count: subdivisions + 1)
|
|
522
|
-
var validVertexCount: UInt16 = 0
|
|
523
|
-
|
|
524
|
-
// Generate vertices
|
|
525
|
-
for row in 0...subdivisions {
|
|
526
|
-
for col in 0...subdivisions {
|
|
527
|
-
let t_x = Double(col) / Double(subdivisions)
|
|
528
|
-
let t_y = Double(row) / Double(subdivisions)
|
|
529
|
-
|
|
530
|
-
let proj_x = x_min + t_x * (x_max - x_min)
|
|
531
|
-
let proj_y = y_max + t_y * (y_min - y_max)
|
|
532
|
-
|
|
533
|
-
// Convert LCC projection coordinates to lat/lon
|
|
534
|
-
if let (lon, lat) = lccToLonLat(i: proj_x, j: proj_y, gridDef: gridDef) {
|
|
535
|
-
// Convert lat/lon to Mercator
|
|
536
|
-
let mercX_normalized = (lon + 180.0) / 360.0
|
|
537
|
-
let clampedLat = max(-85.05112878, min(85.05112878, lat))
|
|
538
|
-
let sinLatitude = sin(clampedLat * .pi / 180.0)
|
|
539
|
-
let mercY_normalized = 0.5 - log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * .pi)
|
|
540
|
-
|
|
541
|
-
let mercX = mercX_normalized * TILE_SIZE
|
|
542
|
-
let mercY = mercY_normalized * TILE_SIZE
|
|
543
|
-
|
|
544
|
-
// Check for invalid values
|
|
545
|
-
if !mercX.isFinite || !mercY.isFinite {
|
|
546
|
-
vertexGrid[row][col] = nil
|
|
547
|
-
continue
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
let tex_u = Float(t_x)
|
|
551
|
-
let tex_v = Float(t_y)
|
|
552
|
-
|
|
553
|
-
let vInfo = VertexInfo(
|
|
554
|
-
mercX: Float(mercX),
|
|
555
|
-
mercY: Float(mercY),
|
|
556
|
-
texU: tex_u,
|
|
557
|
-
texV: tex_v,
|
|
558
|
-
index: validVertexCount
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
vertexGrid[row][col] = vInfo
|
|
562
|
-
|
|
563
|
-
vertices.append(Float(mercX))
|
|
564
|
-
vertices.append(Float(mercY))
|
|
565
|
-
vertices.append(tex_u)
|
|
566
|
-
vertices.append(tex_v)
|
|
567
|
-
|
|
568
|
-
validVertexCount += 1
|
|
569
|
-
} else {
|
|
570
|
-
vertexGrid[row][col] = nil
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if vertices.isEmpty {
|
|
576
|
-
print("❌ [LCC Geometry] No valid vertices generated")
|
|
577
|
-
return
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Generate indices
|
|
581
|
-
for row in 0..<subdivisions {
|
|
582
|
-
for col in 0..<subdivisions {
|
|
583
|
-
guard let topLeft = vertexGrid[row][col],
|
|
584
|
-
let topRight = vertexGrid[row][col + 1],
|
|
585
|
-
let bottomLeft = vertexGrid[row + 1][col],
|
|
586
|
-
let bottomRight = vertexGrid[row + 1][col + 1] else {
|
|
587
|
-
continue
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
indices.append(topLeft.index)
|
|
591
|
-
indices.append(bottomLeft.index)
|
|
592
|
-
indices.append(topRight.index)
|
|
593
|
-
|
|
594
|
-
indices.append(topRight.index)
|
|
595
|
-
indices.append(bottomLeft.index)
|
|
596
|
-
indices.append(bottomRight.index)
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
private func lccToLonLat(i: Double, j: Double, gridDef: [String: Any]) -> (lon: Double, lat: Double)? {
|
|
602
|
-
guard let projParams = gridDef["proj_params"] as? [String: Any],
|
|
603
|
-
let lat_0 = projParams["lat_0"] as? Double, // latitude of origin
|
|
604
|
-
let lon_0 = projParams["lon_0"] as? Double, // longitude of origin
|
|
605
|
-
let lat_1 = projParams["lat_1"] as? Double, // first standard parallel
|
|
606
|
-
let lat_2 = projParams["lat_2"] as? Double, // second standard parallel (HRRR uses two!)
|
|
607
|
-
let r_earth = projParams["R"] as? Double else {
|
|
608
|
-
print("❌ [LCC Geometry] Failed to extract LCC parameters.")
|
|
609
|
-
return nil
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
let π = Double.pi
|
|
613
|
-
let toRad = π / 180.0
|
|
614
|
-
let toDeg = 180.0 / π
|
|
615
|
-
|
|
616
|
-
let lat1_rad = lat_1 * toRad
|
|
617
|
-
let lat2_rad = lat_2 * toRad
|
|
618
|
-
let lat0_rad = lat_0 * toRad
|
|
619
|
-
let lon0_rad = lon_0 * toRad
|
|
620
|
-
|
|
621
|
-
// Calculate cone constant (n) using two standard parallels
|
|
622
|
-
let n: Double
|
|
623
|
-
if abs(lat_1 - lat_2) < 1e-10 {
|
|
624
|
-
n = sin(lat1_rad) // Single parallel case
|
|
625
|
-
} else {
|
|
626
|
-
n = log(cos(lat1_rad) / cos(lat2_rad)) / log(tan(π/4.0 + lat2_rad/2.0) / tan(π/4.0 + lat1_rad/2.0))
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
let F = cos(lat1_rad) * pow(tan(π/4.0 + lat1_rad/2.0), n) / n
|
|
630
|
-
let rho_0 = r_earth * F * pow(tan(π/4.0 + lat0_rad/2.0), -n)
|
|
631
|
-
|
|
632
|
-
// i, j are the x, y projection coordinates
|
|
633
|
-
let x = i
|
|
634
|
-
let y = j
|
|
635
|
-
|
|
636
|
-
let rho = sqrt(x * x + (rho_0 - y) * (rho_0 - y))
|
|
637
|
-
if rho < 1e-10 {
|
|
638
|
-
return (lon: lon_0, lat: lat_0)
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
let theta = atan2(x, rho_0 - y)
|
|
642
|
-
|
|
643
|
-
let lon = lon0_rad + theta / n
|
|
644
|
-
let lat = 2.0 * atan(pow(r_earth * F / rho, 1.0 / n)) - π/2.0
|
|
645
|
-
|
|
646
|
-
let lonDeg = lon * toDeg
|
|
647
|
-
let latDeg = lat * toDeg
|
|
648
|
-
|
|
649
|
-
if !lonDeg.isFinite || !latDeg.isFinite {
|
|
650
|
-
return nil
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
return (lon: lonDeg, lat: latDeg)
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
private func generateGeometryData(gridDef: [String: Any], vertices: inout [Float], indices: inout [UInt16]) {
|
|
657
|
-
guard let gridParams = gridDef["grid_params"] as? [String: Any] else {
|
|
658
|
-
return
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// GFS Global Grid Path
|
|
662
|
-
if isGFSType(gridParams: gridParams) {
|
|
663
|
-
let subdivisions = 120
|
|
664
|
-
let verticesPerRow = (subdivisions * 3) + 1
|
|
665
|
-
let TILE_SIZE: Double = 512.0
|
|
666
|
-
|
|
667
|
-
for row in 0...subdivisions {
|
|
668
|
-
for col in 0...(subdivisions * 3) {
|
|
669
|
-
let v_interp = Float(row) / Float(subdivisions)
|
|
670
|
-
let u_interp = Float(col) / Float(subdivisions)
|
|
671
|
-
let lon = -540.0 + Double(u_interp) * 1080.0
|
|
672
|
-
let lat = -90.0 + Double(v_interp) * 180.0
|
|
673
|
-
|
|
674
|
-
let merc = lonLatToMercator(lon: lon, lat: lat, tileSize: TILE_SIZE)
|
|
675
|
-
vertices.append(contentsOf: [merc.x, merc.y])
|
|
676
|
-
|
|
677
|
-
let tex_u = Float((lon + 180.0) / 360.0)
|
|
678
|
-
let tex_v = 1.0 - v_interp
|
|
679
|
-
vertices.append(contentsOf: [tex_u, tex_v])
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
for row in 0..<subdivisions {
|
|
684
|
-
for col in 0..<(subdivisions * 3) {
|
|
685
|
-
let tl = UInt16(row * verticesPerRow + col)
|
|
686
|
-
let tr = tl + 1
|
|
687
|
-
let bl = UInt16((row + 1) * verticesPerRow + col)
|
|
688
|
-
let br = bl + 1
|
|
689
|
-
indices.append(contentsOf: [tl, bl, tr, tr, bl, br])
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
return
|
|
693
|
-
}
|
|
694
|
-
if isLCCType(gridDef: gridDef) {
|
|
695
|
-
generateLCCGeometry(gridDef: gridDef, vertices: &vertices, indices: &indices)
|
|
696
|
-
return
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Generic Grid Path (MRMS, etc.)
|
|
700
|
-
let nx = gridParams["nx"] as? Int ?? 0
|
|
701
|
-
let ny = gridParams["ny"] as? Int ?? 0
|
|
702
|
-
let lon_first = gridParams["lon_first"] as? Double ?? 0.0
|
|
703
|
-
let lat_first = gridParams["lat_first"] as? Double ?? 90.0
|
|
704
|
-
let dx = gridParams["dx_degrees"] as? Double ?? 0.0
|
|
705
|
-
let dy = gridParams["dy_degrees"] as? Double ?? 0.0
|
|
706
|
-
let lon_last = gridParams["lon_last"] as? Double ?? (lon_first + Double(nx - 1) * dx)
|
|
707
|
-
let lat_last = gridParams["lat_last"] as? Double ?? (lat_first + Double(ny - 1) * dy)
|
|
708
|
-
let lat_span = lat_last - lat_first
|
|
709
|
-
let isSouthToNorth = lat_span > 0
|
|
710
|
-
|
|
711
|
-
let data_lon_first_180 = lon_first > 180 ? lon_first - 360 : lon_first
|
|
712
|
-
let data_lon_last_180 = lon_last > 180 ? lon_last - 360 : lon_last
|
|
713
|
-
let data_lon_range = data_lon_last_180 - data_lon_first_180
|
|
714
|
-
|
|
715
|
-
let subdivisions_x = 120
|
|
716
|
-
let subdivisions_y = 60
|
|
717
|
-
let verticesPerRow = subdivisions_x + 1
|
|
718
|
-
let TILE_SIZE: Double = 512.0
|
|
719
|
-
|
|
720
|
-
// 🔑 FIX: Only create ONE world copy for regional data, THREE for global data
|
|
721
|
-
let worldCopies = (data_lon_range > 300) ? [-1, 0, 1] : [0] // Only wrap if nearly global
|
|
722
|
-
|
|
723
|
-
for world_copy in worldCopies {
|
|
724
|
-
let vertexStartIndex = UInt16(vertices.count / 4)
|
|
725
|
-
let lon_offset = Double(world_copy) * 360.0
|
|
726
|
-
|
|
727
|
-
// Generate vertices for this world copy
|
|
728
|
-
for row in 0...subdivisions_y {
|
|
729
|
-
for col in 0...subdivisions_x {
|
|
730
|
-
let v_interp = Float(row) / Float(subdivisions_y)
|
|
731
|
-
let u_interp = Float(col) / Float(subdivisions_x)
|
|
732
|
-
|
|
733
|
-
let vertex_lon = data_lon_first_180 + (Double(u_interp) * data_lon_range)
|
|
734
|
-
let vertex_lat = lat_first + (Double(v_interp) * lat_span)
|
|
735
|
-
|
|
736
|
-
let mercX_normalized = ((vertex_lon + lon_offset) + 180.0) / 360.0
|
|
737
|
-
let clampedLat = max(-85.05112878, min(85.05112878, vertex_lat))
|
|
738
|
-
let sinLatitude = sin(clampedLat * .pi / 180.0)
|
|
739
|
-
let mercY_normalized = 0.5 - log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * .pi)
|
|
740
|
-
let mercX = mercX_normalized * TILE_SIZE
|
|
741
|
-
let mercY = mercY_normalized * TILE_SIZE
|
|
742
|
-
|
|
743
|
-
vertices.append(contentsOf: [Float(mercX), Float(mercY)])
|
|
744
|
-
|
|
745
|
-
let tex_u = u_interp
|
|
746
|
-
let tex_v = isSouthToNorth ? (1.0 - v_interp) : v_interp
|
|
747
|
-
|
|
748
|
-
vertices.append(contentsOf: [tex_u, tex_v])
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// Generate indices (same as before)
|
|
753
|
-
for row in 0..<UInt16(subdivisions_y) {
|
|
754
|
-
for col in 0..<UInt16(subdivisions_x) {
|
|
755
|
-
let tl = vertexStartIndex + row * UInt16(verticesPerRow) + col
|
|
756
|
-
let tr = tl + 1
|
|
757
|
-
let bl = vertexStartIndex + (row + 1) * UInt16(verticesPerRow) + col
|
|
758
|
-
let br = bl + 1
|
|
759
|
-
|
|
760
|
-
if isSouthToNorth {
|
|
761
|
-
indices.append(contentsOf: [tl, bl, tr, tr, bl, br])
|
|
762
|
-
} else {
|
|
763
|
-
indices.append(contentsOf: [tl, tr, bl, bl, tr, br])
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
private func isGFSType(gridParams: [String: Any]) -> Bool {
|
|
771
|
-
return (gridParams["lon_first"] as? Double) == 0.0 &&
|
|
772
|
-
abs((gridParams["lat_first"] as? Double) ?? -1) == 90.0
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
private func lonLatToMercator(lon: Double, lat: Double, tileSize: Double) -> (x: Float, y: Float) {
|
|
776
|
-
let mercX_normalized = (lon + 180.0) / 360.0
|
|
777
|
-
let clampedLat = max(-85.05112878, min(85.05112878, lat))
|
|
778
|
-
let sinLatitude = sin(clampedLat * .pi / 180.0)
|
|
779
|
-
let mercY_normalized = 0.5 - log((1.0 + sinLatitude) / (1.0 - sinLatitude)) / (4.0 * .pi)
|
|
780
|
-
|
|
781
|
-
return (x: Float(mercX_normalized * tileSize), y: Float(mercY_normalized * tileSize))
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// MARK: - Internal methods for CustomLayerHost (called by wrapper)
|
|
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
|
-
|
|
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
|
|
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
|
-
}
|
|
900
|
-
internal func internalRender(_ parameters: CustomLayerRenderParameters, mtlCommandBuffer: MTLCommandBuffer, mtlRenderPassDescriptor: MTLRenderPassDescriptor) {
|
|
901
|
-
guard isVisible,
|
|
902
|
-
let pipeline = pipelineState,
|
|
903
|
-
let vertices = vertexBuffer,
|
|
904
|
-
let indices = indexBuffer,
|
|
905
|
-
let dataTex = dataTexture,
|
|
906
|
-
let colormapTex = colormapTexture,
|
|
907
|
-
indexCount > 0,
|
|
908
|
-
let encoder = mtlCommandBuffer.makeRenderCommandEncoder(descriptor: mtlRenderPassDescriptor) else {
|
|
909
|
-
|
|
910
|
-
// OPTIMIZATION: Change log message to be more informative and less alarming.
|
|
911
|
-
// This is an expected state during initial load.
|
|
912
|
-
if (pipelineState != nil) { // Only log if Metal is set up
|
|
913
|
-
print("🟡 [GridRenderLayer] internalRender: Waiting for all resources to be ready before drawing.")
|
|
914
|
-
}
|
|
915
|
-
return
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
print("✅ [GridRenderLayer] Past guards, about to render!")
|
|
919
|
-
print(" 🎨 About to draw \(indexCount) indices (\(indexCount/3) triangles)")
|
|
920
|
-
|
|
921
|
-
let needsLinear = (uniforms.smoothing != 0)
|
|
922
|
-
if isDataSamplerLinear != needsLinear {
|
|
923
|
-
let samplerDesc = MTLSamplerDescriptor()
|
|
924
|
-
samplerDesc.minFilter = needsLinear ? .linear : .nearest
|
|
925
|
-
samplerDesc.magFilter = needsLinear ? .linear : .nearest
|
|
926
|
-
dataSamplerState = device.makeSamplerState(descriptor: samplerDesc)
|
|
927
|
-
isDataSamplerLinear = needsLinear
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// Apply zoom scale transformation
|
|
931
|
-
let zoom = parameters.zoom
|
|
932
|
-
let scale = Float(pow(2.0, zoom))
|
|
933
|
-
|
|
934
|
-
let matrixArray = parameters.projectionMatrix
|
|
935
|
-
var floatArray = matrixArray.map { $0.floatValue }
|
|
936
|
-
|
|
937
|
-
floatArray[0] *= scale // X scale
|
|
938
|
-
floatArray[5] *= scale // Y scale
|
|
939
|
-
|
|
940
|
-
let mvp = matrix_float4x4(
|
|
941
|
-
SIMD4<Float>(floatArray[0], floatArray[1], floatArray[2], floatArray[3]),
|
|
942
|
-
SIMD4<Float>(floatArray[4], floatArray[5], floatArray[6], floatArray[7]),
|
|
943
|
-
SIMD4<Float>(floatArray[8], floatArray[9], floatArray[10], floatArray[11]),
|
|
944
|
-
SIMD4<Float>(floatArray[12], floatArray[13], floatArray[14], floatArray[15])
|
|
945
|
-
)
|
|
946
|
-
|
|
947
|
-
print(" 📐 MVP matrix after scaling: \(mvp)")
|
|
948
|
-
print(" 📍 Zoom: \(zoom), Scale factor: \(scale)")
|
|
949
|
-
|
|
950
|
-
// Add depth state
|
|
951
|
-
let depthStencilDescriptor = MTLDepthStencilDescriptor()
|
|
952
|
-
depthStencilDescriptor.depthCompareFunction = .always
|
|
953
|
-
depthStencilDescriptor.isDepthWriteEnabled = false
|
|
954
|
-
let depthStencilState = device.makeDepthStencilState(descriptor: depthStencilDescriptor)
|
|
955
|
-
|
|
956
|
-
encoder.setRenderPipelineState(pipeline)
|
|
957
|
-
encoder.setDepthStencilState(depthStencilState!)
|
|
958
|
-
encoder.setVertexBuffer(vertices, offset: 0, index: 0)
|
|
959
|
-
encoder.setVertexBytes([mvp], length: MemoryLayout<matrix_float4x4>.size, index: 1)
|
|
960
|
-
|
|
961
|
-
var mutableUniforms = uniforms
|
|
962
|
-
encoder.setFragmentBytes(&mutableUniforms, length: MemoryLayout<FragmentUniforms>.stride, index: 0)
|
|
963
|
-
encoder.setFragmentTexture(dataTex, index: 0)
|
|
964
|
-
encoder.setFragmentTexture(colormapTex, index: 1)
|
|
965
|
-
encoder.setFragmentSamplerState(dataSamplerState, index: 0)
|
|
966
|
-
encoder.setFragmentSamplerState(colormapSamplerState, index: 1)
|
|
967
|
-
|
|
968
|
-
print(" 🎨 Drawing indexed primitives now...")
|
|
969
|
-
encoder.drawIndexedPrimitives(type: .triangle, indexCount: indexCount, indexType: .uint16, indexBuffer: indices, indexBufferOffset: 0)
|
|
970
|
-
print(" ✅ Draw call completed")
|
|
971
|
-
|
|
972
|
-
encoder.endEncoding()
|
|
973
|
-
print(" ✅ Encoder ended")
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
internal func internalRenderingWillEnd() {
|
|
977
|
-
print("🟢 [GridRenderLayer] renderingWillEnd called")
|
|
978
|
-
vertexBuffer = nil
|
|
979
|
-
indexBuffer = nil
|
|
980
|
-
dataTexture = nil
|
|
981
|
-
colormapTexture = nil
|
|
982
|
-
}
|
|
983
|
-
}
|