@rafaelaugusto/jolt-native 0.1.0-beta.14 → 0.1.0-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,3 +13,5 @@ SceneKit appearance supports light intensity/color and material transparency/sol
13
13
  See the [native properties guide](https://jolt.rafaelaugusto.dev/guide/native-properties) for capabilities, authoring, ownership and execution limits.
14
14
 
15
15
  Release maintainers: publish this optional package together with compatible core releases (`npm publish` from this directory). Build and pack the core at the repository root. The demo uses local file dependencies and never needs the package published for validation.
16
+
17
+ SceneKit perspective camera field-of-view bindings use `sceneKitFieldOfView(camera)` from the core `/native` entry. See the [camera guide](https://jolt.rafaelaugusto.dev/guide/scene-camera) for projection, lens ownership and lifecycle limits. Available since beta.15. Install matching core/native versions and rebuild the native app to include this adapter.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rafaelaugusto/jolt-native",
3
- "version": "0.1.0-beta.14",
3
+ "version": "0.1.0-beta.15",
4
4
  "description": "Optional compiled property adapters for Jolt (MapKit, MapLibre and SceneKit).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "sideEffects": false,
16
16
  "peerDependencies": {
17
- "@rafaelaugusto/jolt": ">=0.1.0-beta.14",
17
+ "@rafaelaugusto/jolt": ">=0.1.0-beta.15",
18
18
  "@nativescript/core": ">=8.9.0"
19
19
  },
20
20
  "nativescript": {
@@ -4,6 +4,10 @@ NS_ASSUME_NONNULL_BEGIN
4
4
  + (JoltPropertySink *)mapCoordinate:(NSObject *)annotation;
5
5
  + (JoltPropertySink *)mapCamera:(NSObject *)mapView;
6
6
  + (JoltPropertySink *)scenePose:(NSObject *)node;
7
+ + (JoltPropertySink *)sceneFieldOfView:(NSObject *)camera;
8
+ /** Diagnostic counts for camera FOV sinks and writes; native camera targets are weak. */
9
+ + (NSUInteger)sceneCameraSinkCount;
10
+ + (NSUInteger)sceneCameraWriteCount;
7
11
  + (JoltPropertySink *)sceneLightIntensity:(NSObject *)light;
8
12
  + (JoltPropertySink *)sceneLightColor:(NSObject *)light;
9
13
  + (JoltPropertySink *)sceneMaterialTransparency:(NSObject *)material;
@@ -48,6 +48,61 @@ static double longitude(double value) { return fmod(fmod(value+180,360)+360,360)
48
48
  [SCNTransaction commit];
49
49
  }
50
50
  @end
51
+ // A standalone SCNCamera has no presentation getter. Read its current model FOV,
52
+ // and make each Jolt write immediate, just as for the appearance adapters below.
53
+ static NSUInteger cameraSinks, cameraWrites;
54
+ static BOOL validFieldOfView(double value) { return isfinite(value) && value >= 1 && value <= 179; }
55
+ static BOOL automaticPerspective(SCNCamera *camera) {
56
+ if (!camera || camera.usesOrthographicProjection || !validFieldOfView(camera.fieldOfView)) return NO;
57
+ if (camera.projectionDirection != SCNCameraProjectionDirectionVertical && camera.projectionDirection != SCNCameraProjectionDirectionHorizontal) return NO;
58
+ // A custom projectionTransform ignores viewport size. Inspect two projections
59
+ // through the public API without mutating the target or allocating a second camera.
60
+ // Checking only one matrix would accept a custom matrix equal to that viewport's
61
+ // automatic projection, which a fieldOfView write would silently replace.
62
+ SCNMatrix4 square = [camera projectionTransformWithViewportSize:CGSizeMake(100, 100)];
63
+ SCNMatrix4 wide = [camera projectionTransformWithViewportSize:CGSizeMake(200, 100)];
64
+ return isfinite(square.m11) && isfinite(square.m22) && isfinite(wide.m11) && isfinite(wide.m22)
65
+ && square.m11 > 0 && square.m22 > 0 && !SCNMatrix4EqualToMatrix4(square, wide);
66
+ }
67
+ @interface JoltSceneCameraSink : JoltPropertySink
68
+ @property(nonatomic) BOOL released;
69
+ + (instancetype)camera:(NSObject *)target;
70
+ @end
71
+ @implementation JoltSceneCameraSink
72
+ + (instancetype)camera:(NSObject *)target {
73
+ NSParameterAssert(NSThread.isMainThread);
74
+ if (![target isKindOfClass:SCNCamera.class])
75
+ [NSException raise:NSInvalidArgumentException format:@"Jolt field of view requires an SCNCamera, not its SCNNode"];
76
+ if (!automaticPerspective((SCNCamera *)target))
77
+ [NSException raise:NSInvalidArgumentException format:@"Jolt field of view requires an automatic perspective camera with an angle in 1..179 degrees; orthographic and custom projection matrices are unsupported"];
78
+ JoltSceneCameraSink *sink = [[self alloc] initWithTarget:target dimensions:1];
79
+ cameraSinks++;
80
+ return sink;
81
+ }
82
+ - (BOOL)alive { return automaticPerspective((SCNCamera *)self.target); }
83
+ - (NSArray<NSNumber *> *)read {
84
+ NSParameterAssert(NSThread.isMainThread);
85
+ if (!self.alive) [NSException raise:NSInvalidArgumentException format:@"Jolt camera is no longer a supported perspective target"];
86
+ return @[@([(SCNCamera *)self.target fieldOfView])];
87
+ }
88
+ - (void)write:(NSArray<NSNumber *> *)values {
89
+ NSParameterAssert(NSThread.isMainThread);
90
+ if (!self.alive) [NSException raise:NSInvalidArgumentException format:@"Jolt will not overwrite an unsupported camera projection"];
91
+ if (values.count != 1 || !validFieldOfView(values[0].doubleValue))
92
+ [NSException raise:NSInvalidArgumentException format:@"Jolt field of view needs one finite angle in 1..179 degrees"];
93
+ [SCNTransaction begin]; [SCNTransaction setDisableActions:YES];
94
+ @try {
95
+ [(SCNCamera *)self.target setFieldOfView:values[0].doubleValue];
96
+ cameraWrites++;
97
+ } @finally { [SCNTransaction commit]; }
98
+ }
99
+ - (void)dispose {
100
+ if (!self.released) { self.released = YES; cameraSinks--; }
101
+ [super dispose];
102
+ }
103
+ - (void)dealloc { if (!self.released) cameraSinks--; }
104
+ @end
105
+
51
106
  // Appearance is read from the model: SceneKit exposes presentationNode, but no public
52
107
  // presentation object for a standalone SCNLight/SCNMaterial. Our immediate writes keep
53
108
  // the model current. Apps must remove competing animations before transferring control.
@@ -160,6 +215,9 @@ static CGColorRef copySceneColor(id value) CF_RETURNS_RETAINED {
160
215
  @end
161
216
 
162
217
  @implementation JoltReferenceProperties
218
+ + (JoltPropertySink *)sceneFieldOfView:(NSObject *)camera { return [JoltSceneCameraSink camera:camera]; }
219
+ + (NSUInteger)sceneCameraSinkCount { return cameraSinks; }
220
+ + (NSUInteger)sceneCameraWriteCount { return cameraWrites; }
163
221
  + (JoltPropertySink *)sceneLightIntensity:(NSObject *)light { return [JoltSceneAppearanceSink target:light kind:JoltSceneIntensity]; }
164
222
  + (JoltPropertySink *)sceneLightColor:(NSObject *)light { return [JoltSceneAppearanceSink target:light kind:JoltSceneLightColor]; }
165
223
  + (JoltPropertySink *)sceneMaterialTransparency:(NSObject *)material { return [JoltSceneAppearanceSink target:material kind:JoltSceneTransparency]; }