Fix normalized OSC coordinates and verify Motion pixel setter compatibility
这个提交包含在:
1 父节点
71a583e306
当前提交
22cd96a772
2 个文件已更改
+120
-33
没有与搜索条件相匹配的文件
@@ -8,6 +8,9 @@
|
|||||||
@property (assign) id<PROAPIAccessing> apiManager;
|
@property (assign) id<PROAPIAccessing> apiManager;
|
||||||
@property BOOL dragging;
|
@property BOOL dragging;
|
||||||
@property BOOL undoOpen;
|
@property BOOL undoOpen;
|
||||||
|
@property BOOL pointSetterUsesPixels;
|
||||||
|
@property BOOL loggedCoordinates;
|
||||||
|
@property BOOL loggedPointSetter;
|
||||||
@end
|
@end
|
||||||
@implementation PGFaceSelectOSC
|
@implementation PGFaceSelectOSC
|
||||||
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)api {
|
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)api {
|
||||||
@@ -19,27 +22,28 @@
|
|||||||
BOOL selected=NO;id<FxParameterRetrievalAPI_v6> api=[_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
BOOL selected=NO;id<FxParameterRetrievalAPI_v6> api=[_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||||
return api && [api getBoolValue:&selected fromParameter:PGSingleFace atTime:time] && selected;
|
return api && [api getBoolValue:&selected fromParameter:PGSingleFace atTime:time] && selected;
|
||||||
}
|
}
|
||||||
- (BOOL)validObjectBounds:(CGRect *)bounds {
|
|
||||||
id<FxOnScreenControlAPI_v4> api=[self oscAPI];
|
|
||||||
if(!api || !bounds) return NO;
|
|
||||||
CGRect b=[api objectBounds];
|
|
||||||
if(!isfinite(b.origin.x)||!isfinite(b.origin.y)||!isfinite(b.size.width)||!isfinite(b.size.height)||b.size.width<=0||b.size.height<=0) return NO;
|
|
||||||
*bounds=b;return YES;
|
|
||||||
}
|
|
||||||
- (void)drawOSCWithWidth:(NSInteger)width height:(NSInteger)height activePart:(NSInteger)part destinationImage:(FxImageTile *)tile atTime:(CMTime)time {
|
- (void)drawOSCWithWidth:(NSInteger)width height:(NSInteger)height activePart:(NSInteger)part destinationImage:(FxImageTile *)tile atTime:(CMTime)time {
|
||||||
if(!tile.ioSurface||width<=0||height<=0) return;
|
if(!tile.ioSurface||width<=0||height<=0) return;
|
||||||
size_t surfaceW=IOSurfaceGetWidth((__bridge IOSurfaceRef)tile.ioSurface),surfaceH=IOSurfaceGetHeight((__bridge IOSurfaceRef)tile.ioSurface);
|
size_t surfaceW=IOSurfaceGetWidth((__bridge IOSurfaceRef)tile.ioSurface),surfaceH=IOSurfaceGetHeight((__bridge IOSurfaceRef)tile.ioSurface);
|
||||||
if(!surfaceW||!surfaceH) return;
|
if(!surfaceW||!surfaceH) return;
|
||||||
CIImage *output=[[CIImage imageWithColor:CIColor.clearColor] imageByCroppingToRect:CGRectMake(0,0,surfaceW,surfaceH)];
|
CIImage *output=[[CIImage imageWithColor:CIColor.clearColor] imageByCroppingToRect:CGRectMake(0,0,surfaceW,surfaceH)];
|
||||||
CGRect bounds=CGRectZero;
|
if([self selectionEnabledAtTime:time] && [self oscAPI]) {
|
||||||
if([self selectionEnabledAtTime:time] && [self validObjectBounds:&bounds]) {
|
|
||||||
double x=.5,y=.5;
|
double x=.5,y=.5;
|
||||||
id<FxParameterRetrievalAPI_v6> params=[_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
id<FxParameterRetrievalAPI_v6> params=[_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||||
BOOL valid=[params getXValue:&x YValue:&y fromParameter:PGSelection atTime:time] && isfinite(x) && isfinite(y);
|
BOOL valid=[params getXValue:&x YValue:&y fromParameter:PGSelection atTime:time] && isfinite(x) && isfinite(y);
|
||||||
double canvasX=NAN,canvasY=NAN;
|
double canvasX=NAN,canvasY=NAN;
|
||||||
if(valid) [[self oscAPI] convertPointFromSpace:kFxDrawingCoordinates_OBJECT fromX:bounds.origin.x+x*bounds.size.width fromY:bounds.origin.y+y*bounds.size.height toSpace:kFxDrawingCoordinates_CANVAS toX:&canvasX toY:&canvasY];
|
// FxPlug OBJECT space is normalized 0..1, exactly like point parameters.
|
||||||
// CANVAS coordinates already address the OSC surface. Callback width and
|
// objectBounds is a pixel/document rectangle and must not be applied here.
|
||||||
// height may describe the effect image and must not rescale this point.
|
if(valid) [[self oscAPI] convertPointFromSpace:kFxDrawingCoordinates_OBJECT fromX:x fromY:y toSpace:kFxDrawingCoordinates_CANVAS toX:&canvasX toY:&canvasY];
|
||||||
|
if(!_loggedCoordinates) {
|
||||||
|
_loggedCoordinates=YES;
|
||||||
|
CGRect objectBounds=[[self oscAPI] objectBounds];
|
||||||
|
NSLog(@"PGFaceOSC coordinates selection=(%.9g,%.9g) objectBounds=(%.9g,%.9g,%.9g,%.9g) canvas=(%.9g,%.9g) surface=%zux%zu origin=%lu",
|
||||||
|
x,y,objectBounds.origin.x,objectBounds.origin.y,objectBounds.size.width,objectBounds.size.height,
|
||||||
|
canvasX,canvasY,surfaceW,surfaceH,(unsigned long)tile.imageOrigin);
|
||||||
|
}
|
||||||
|
// CANVAS coordinates address the OSC surface. Callback width/height can
|
||||||
|
// describe the effect image and must not rescale the converted point.
|
||||||
double backing=[[self oscAPI] backingScaleFactor];
|
double backing=[[self oscAPI] backingScaleFactor];
|
||||||
CGFloat scale=isfinite(backing)?MAX(1,backing):1;
|
CGFloat scale=isfinite(backing)?MAX(1,backing):1;
|
||||||
CGFloat radius=10*scale;
|
CGFloat radius=10*scale;
|
||||||
@@ -59,21 +63,71 @@
|
|||||||
[context render:output toIOSurface:(__bridge IOSurfaceRef)tile.ioSurface bounds:CGRectMake(0,0,w,h) colorSpace:tile.colorSpace];
|
[context render:output toIOSurface:(__bridge IOSurfaceRef)tile.ioSurface bounds:CGRectMake(0,0,w,h) colorSpace:tile.colorSpace];
|
||||||
}
|
}
|
||||||
- (void)hitTestOSCAtMousePositionX:(double)x mousePositionY:(double)y activePart:(NSInteger *)part atTime:(CMTime)time {
|
- (void)hitTestOSCAtMousePositionX:(double)x mousePositionY:(double)y activePart:(NSInteger *)part atTime:(CMTime)time {
|
||||||
if(!part) return;*part=0;CGRect bounds=CGRectZero;
|
if(!part) return;*part=0;
|
||||||
if(isfinite(x)&&isfinite(y)&&[self selectionEnabledAtTime:time]&&[self validObjectBounds:&bounds]&&CGRectContainsPoint(bounds,CGPointMake(x,y))) *part=1;
|
if(isfinite(x)&&isfinite(y)&&x>=0&&x<=1&&y>=0&&y<=1&&[self selectionEnabledAtTime:time]&&[self oscAPI]) *part=1;
|
||||||
|
}
|
||||||
|
- (BOOL)pointWrite:(id<FxParameterSettingAPI_v5>)setter x:(double)x y:(double)y time:(CMTime)time {
|
||||||
|
return setter && isfinite(x) && isfinite(y) && [setter setXValue:x YValue:y toParameter:PGSelection atTime:time];
|
||||||
|
}
|
||||||
|
- (BOOL)readPoint:(id<FxParameterRetrievalAPI_v6>)reader x:(double *)x y:(double *)y time:(CMTime)time {
|
||||||
|
return reader && [reader getXValue:x YValue:y fromParameter:PGSelection atTime:time] && isfinite(*x) && isfinite(*y);
|
||||||
|
}
|
||||||
|
- (BOOL)pointPixelSize:(CGSize *)size {
|
||||||
|
id<FxOnScreenControlAPI_v4> api=[self oscAPI];if(!api || !size)return NO;
|
||||||
|
CGRect bounds=[api objectBounds];
|
||||||
|
if(!isfinite(bounds.size.width)||!isfinite(bounds.size.height)||bounds.size.width<=4||bounds.size.height<=4)return NO;
|
||||||
|
*size=bounds.size;return YES;
|
||||||
}
|
}
|
||||||
- (void)setSelectionX:(double)x y:(double)y time:(CMTime)time update:(BOOL *)update {
|
- (void)setSelectionX:(double)x y:(double)y time:(CMTime)time update:(BOOL *)update {
|
||||||
if(update) *update=NO;
|
if(update) *update=NO;
|
||||||
CGRect bounds=CGRectZero;
|
if(!isfinite(x)||!isfinite(y)) return;
|
||||||
if(!isfinite(x)||!isfinite(y)||![self validObjectBounds:&bounds]) return;
|
double nx=fmin(1,fmax(0,x)),ny=fmin(1,fmax(0,y));
|
||||||
id<FxParameterSettingAPI_v5> api=[_apiManager apiForProtocol:@protocol(FxParameterSettingAPI_v5)];
|
id<FxParameterSettingAPI_v5> setter=[_apiManager apiForProtocol:@protocol(FxParameterSettingAPI_v5)];
|
||||||
BOOL ok=[api setXValue:MIN(1,MAX(0,(x-bounds.origin.x)/bounds.size.width)) YValue:MIN(1,MAX(0,(y-bounds.origin.y)/bounds.size.height)) toParameter:PGSelection atTime:time];
|
id<FxParameterRetrievalAPI_v6> reader=[_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||||
if(update) *update=ok;
|
double previousX=NAN,previousY=NAN;
|
||||||
|
if(!setter || ![self readPoint:reader x:&previousX y:&previousY time:time])return;
|
||||||
|
CGSize pixels=CGSizeZero;
|
||||||
|
if(_pointSetterUsesPixels && ![self pointPixelSize:&pixels])return;
|
||||||
|
double rawX=_pointSetterUsesPixels ? (nx-.5)*pixels.width : nx;
|
||||||
|
double rawY=_pointSetterUsesPixels ? (ny-.5)*pixels.height : ny;
|
||||||
|
BOOL accepted=[self pointWrite:setter x:rawX y:rawY time:time];
|
||||||
|
BOOL changed=accepted,pixelUnits=_pointSetterUsesPixels;
|
||||||
|
double actualX=NAN,actualY=NAN;
|
||||||
|
BOOL readBack=accepted && [self readPoint:reader x:&actualX y:&actualY time:time];
|
||||||
|
const double tolerance=0.00001;
|
||||||
|
BOOL verified=readBack && fabs(actualX-nx)<=tolerance && fabs(actualY-ny)<=tolerance;
|
||||||
|
// Motion's FxPlug 4 bridge can normalize point reads while its setter still
|
||||||
|
// writes centered pixel values directly to OZChannel2D. Detect that exact
|
||||||
|
// response instead of choosing units from the app version or coordinate size.
|
||||||
|
if(readBack && !verified && !_pointSetterUsesPixels && [self pointPixelSize:&pixels] &&
|
||||||
|
fabs(actualX-(.5+nx/pixels.width))<=tolerance &&
|
||||||
|
fabs(actualY-(.5+ny/pixels.height))<=tolerance) {
|
||||||
|
pixelUnits=YES;
|
||||||
|
rawX=(nx-.5)*pixels.width;rawY=(ny-.5)*pixels.height;
|
||||||
|
accepted=[self pointWrite:setter x:rawX y:rawY time:time];changed=changed||accepted;
|
||||||
|
actualX=actualY=NAN;
|
||||||
|
readBack=accepted && [self readPoint:reader x:&actualX y:&actualY time:time];
|
||||||
|
verified=readBack && fabs(actualX-nx)<=tolerance && fabs(actualY-ny)<=tolerance;
|
||||||
|
if(verified)_pointSetterUsesPixels=YES;
|
||||||
|
}
|
||||||
|
if(!verified && changed) {
|
||||||
|
// Restore the previous point when verification fails. A failure must not
|
||||||
|
// look like a successful drag or silently retain the calibration point.
|
||||||
|
double restoreX=pixelUnits ? (previousX-.5)*pixels.width : previousX;
|
||||||
|
double restoreY=pixelUnits ? (previousY-.5)*pixels.height : previousY;
|
||||||
|
[self pointWrite:setter x:restoreX y:restoreY time:time];
|
||||||
|
}
|
||||||
|
if(!_loggedPointSetter) {
|
||||||
|
_loggedPointSetter=YES;
|
||||||
|
NSLog(@"PGFaceOSC point-write desired=(%.9g,%.9g) setter=(%.9g,%.9g) readback=(%.9g,%.9g) mode=%@ verified=%d",
|
||||||
|
nx,ny,rawX,rawY,actualX,actualY,_pointSetterUsesPixels?@"centered-pixels":@"normalized",verified);
|
||||||
|
}
|
||||||
|
if(update)*update=verified;
|
||||||
}
|
}
|
||||||
- (void)mouseDownAtPositionX:(double)x positionY:(double)y activePart:(NSInteger)part modifiers:(FxModifierKeys)keys forceUpdate:(BOOL *)update atTime:(CMTime)time {
|
- (void)mouseDownAtPositionX:(double)x positionY:(double)y activePart:(NSInteger)part modifiers:(FxModifierKeys)keys forceUpdate:(BOOL *)update atTime:(CMTime)time {
|
||||||
if(update) *update=NO;
|
if(update) *update=NO;
|
||||||
if(part!=1 || !isfinite(x) || !isfinite(y) || ![self selectionEnabledAtTime:time]) return;
|
if(part!=1 || !isfinite(x) || !isfinite(y) || ![self selectionEnabledAtTime:time]) return;
|
||||||
CGRect bounds=CGRectZero;if(![self validObjectBounds:&bounds]) return;
|
if(![self oscAPI]) return;
|
||||||
id<FxUndoAPI> undo=[_apiManager apiForProtocol:@protocol(FxUndoAPI)];
|
id<FxUndoAPI> undo=[_apiManager apiForProtocol:@protocol(FxUndoAPI)];
|
||||||
if(_undoOpen) [undo endUndoGroup];
|
if(_undoOpen) [undo endUndoGroup];
|
||||||
_undoOpen=[undo startUndoGroup:@"选择人脸"];
|
_undoOpen=[undo startUndoGroup:@"选择人脸"];
|
||||||
|
|||||||
+46
-13
@@ -12,11 +12,13 @@
|
|||||||
|
|
||||||
@interface PGOSCAPI : NSObject <PROAPIAccessing>
|
@interface PGOSCAPI : NSObject <PROAPIAccessing>
|
||||||
@property BOOL enabled, rejectRead, rejectPoint, rejectWrite, rejectUndo;
|
@property BOOL enabled, rejectRead, rejectPoint, rejectWrite, rejectUndo;
|
||||||
@property BOOL disableOSC, disableParams, disableSetting, disableUndo;
|
@property BOOL disableOSC, disableParams, disableSetting, disableUndo,pixelSetter;
|
||||||
|
@property BOOL corruptReadback;
|
||||||
@property CGRect bounds;
|
@property CGRect bounds;
|
||||||
@property double x,y,backing;
|
@property double x,y,backing;
|
||||||
@property NSUInteger writes,undoStarts,undoEnds;
|
@property NSUInteger writes,undoStarts,undoEnds;
|
||||||
@property double lastX,lastY;
|
@property double lastX,lastY,lastConvertX,lastConvertY;
|
||||||
|
@property NSUInteger objectBoundsReads;
|
||||||
@property CMTime lastTime;
|
@property CMTime lastTime;
|
||||||
@end
|
@end
|
||||||
@implementation PGOSCAPI
|
@implementation PGOSCAPI
|
||||||
@@ -37,12 +39,17 @@
|
|||||||
}
|
}
|
||||||
- (BOOL)setXValue:(double)x YValue:(double)y toParameter:(UInt32)identifier atTime:(CMTime)time {
|
- (BOOL)setXValue:(double)x YValue:(double)y toParameter:(UInt32)identifier atTime:(CMTime)time {
|
||||||
if(identifier!=PGSelection||_rejectWrite)return NO;
|
if(identifier!=PGSelection||_rejectWrite)return NO;
|
||||||
_writes++;_lastX=x;_lastY=y;_lastTime=time;return YES;
|
_writes++;_lastX=x;_lastY=y;
|
||||||
|
_x=_pixelSetter ? .5+x/_bounds.size.width : x;
|
||||||
|
_y=_pixelSetter ? .5+y/_bounds.size.height : y;
|
||||||
|
if(_corruptReadback){_x=.12345;_y=.87654;}
|
||||||
|
_lastTime=time;return YES;
|
||||||
}
|
}
|
||||||
- (CGRect)objectBounds {return _bounds;}
|
- (CGRect)objectBounds {_objectBoundsReads++;return _bounds;}
|
||||||
- (double)backingScaleFactor {return _backing;}
|
- (double)backingScaleFactor {return _backing;}
|
||||||
- (void)convertPointFromSpace:(FxDrawingCoordinates)from fromX:(double)x fromY:(double)y toSpace:(FxDrawingCoordinates)to toX:(double *)tx toY:(double *)ty {
|
- (void)convertPointFromSpace:(FxDrawingCoordinates)from fromX:(double)x fromY:(double)y toSpace:(FxDrawingCoordinates)to toX:(double *)tx toY:(double *)ty {
|
||||||
*tx=x+200;*ty=y+100;
|
// Match the SDK contract: OBJECT inputs are normalized; CANVAS is pixels.
|
||||||
|
_lastConvertX=x;_lastConvertY=y;*tx=x*320;*ty=y*240;
|
||||||
}
|
}
|
||||||
- (BOOL)startUndoGroup:(NSString *)name {_undoStarts++;return !_rejectUndo;}
|
- (BOOL)startUndoGroup:(NSString *)name {_undoStarts++;return !_rejectUndo;}
|
||||||
- (BOOL)endUndoGroup {_undoEnds++;return YES;}
|
- (BOOL)endUndoGroup {_undoEnds++;return YES;}
|
||||||
@@ -83,19 +90,19 @@ int main(int argc,const char **argv) {@autoreleasepool {
|
|||||||
Check([control[@"supportedPlugins"] isEqual:@[filters.firstObject[@"uuid"]]],@"Dedicated OSC links to the released filter UUID");
|
Check([control[@"supportedPlugins"] isEqual:@[filters.firstObject[@"uuid"]]],@"Dedicated OSC links to the released filter UUID");
|
||||||
api.enabled=NO;[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Disabled selection does not claim mouse events");
|
api.enabled=NO;[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Disabled selection does not claim mouse events");
|
||||||
[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==0 && api.undoStarts==0,@"Disabled mouse-down writes nothing and opens no undo group");
|
[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==0 && api.undoStarts==0,@"Disabled mouse-down writes nothing and opens no undo group");
|
||||||
api.enabled=YES;[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:&part atTime:time];Check(part==1,@"Enabled selection accepts a finite point in object bounds");
|
api.enabled=YES;[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:&part atTime:time];Check(part==1,@"Enabled selection accepts a finite normalized object point");
|
||||||
[osc hitTestOSCAtMousePositionX:1000 mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Outside-object hit test is empty");
|
[osc hitTestOSCAtMousePositionX:1000 mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Outside-object hit test is empty");
|
||||||
[osc hitTestOSCAtMousePositionX:NAN mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Nonfinite hit coordinates are rejected");
|
[osc hitTestOSCAtMousePositionX:NAN mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Nonfinite hit coordinates are rejected");
|
||||||
[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:NULL atTime:time];Check(YES,@"Null hit-test output is safe");
|
[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:NULL atTime:time];Check(YES,@"Null hit-test output is safe");
|
||||||
[osc mouseDownAtPositionX:0 positionY:0 activePart:0 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==0,@"Inactive part does not start selection");
|
[osc mouseDownAtPositionX:0 positionY:0 activePart:0 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==0,@"Inactive part does not start selection");
|
||||||
update=YES;[osc mouseUpAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.undoEnds==0,@"Mouse-up without mouse-down leaves undo balanced");
|
update=YES;[osc mouseUpAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.undoEnds==0,@"Mouse-up without mouse-down leaves undo balanced");
|
||||||
[osc mouseDownAtPositionX:-50 positionY:25 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(update && fabs(api.lastX-.25)<1e-8 && fabs(api.lastY-.75)<1e-8,@"Mouse-down normalizes nonzero object bounds");
|
[osc mouseDownAtPositionX:.25 positionY:.75 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(update && fabs(api.lastX-.25)<1e-8 && fabs(api.lastY-.75)<1e-8,@"OBJECT mouse-down writes normalized point coordinates directly");
|
||||||
Check(CMTimeCompare(api.lastTime,time)==0 && api.undoStarts==1,@"Selection writes use callback time and open one undo group");
|
Check(CMTimeCompare(api.lastTime,time)==0 && api.undoStarts==1,@"Selection writes use callback time and open one undo group");
|
||||||
[osc mouseDraggedAtPositionX:500 positionY:-500 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(update && api.lastX==1 && api.lastY==0,@"Dragging clamps normalized selection to the source");
|
[osc mouseDraggedAtPositionX:500 positionY:-500 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(update && api.lastX==1 && api.lastY==0,@"Dragging clamps normalized selection to the source");
|
||||||
[osc mouseUpAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(update && api.undoEnds==1,@"Gesture completes its matching undo group");
|
[osc mouseUpAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(update && api.undoEnds==1,@"Gesture completes its matching undo group");
|
||||||
NSUInteger writes=api.writes;[osc mouseDraggedAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==writes,@"Stray drag after release writes nothing");
|
NSUInteger writes=api.writes;[osc mouseDraggedAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==writes,@"Stray drag after release writes nothing");
|
||||||
api.bounds=CGRectMake(0,0,NAN,100);[osc hitTestOSCAtMousePositionX:0 mousePositionY:0 activePart:&part atTime:time];Check(part==0,@"Invalid host bounds produce an empty hit");
|
api.bounds=CGRectMake(0,0,NAN,100);[osc hitTestOSCAtMousePositionX:.5 mousePositionY:.5 activePart:&part atTime:time];Check(part==1,@"Normalized hit testing does not depend on document pixel bounds");
|
||||||
update=YES;[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==writes,@"Invalid bounds initialize forceUpdate and avoid parameter writes");api.bounds=CGRectMake(-100,-50,200,100);
|
update=YES;[osc mouseDownAtPositionX:NAN positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.writes==writes,@"Nonfinite event initializes forceUpdate and avoids parameter writes");api.bounds=CGRectMake(-100,-50,200,100);
|
||||||
api.rejectWrite=YES;[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.undoStarts==api.undoEnds,@"Rejected parameter write closes the opened undo group");api.rejectWrite=NO;
|
api.rejectWrite=YES;[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(!update && api.undoStarts==api.undoEnds,@"Rejected parameter write closes the opened undo group");api.rejectWrite=NO;
|
||||||
api.rejectUndo=YES;NSUInteger ends=api.undoEnds;[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];[osc mouseUpAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(api.undoEnds==ends,@"Failed undo start is never paired with an invalid end");api.rejectUndo=NO;
|
api.rejectUndo=YES;NSUInteger ends=api.undoEnds;[osc mouseDownAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];[osc mouseUpAtPositionX:0 positionY:0 activePart:1 modifiers:0 forceUpdate:&update atTime:time];Check(api.undoEnds==ends,@"Failed undo start is never paired with an invalid end");api.rejectUndo=NO;
|
||||||
for(int disabled=0;disabled<3;disabled++){
|
for(int disabled=0;disabled<3;disabled++){
|
||||||
@@ -107,10 +114,36 @@ int main(int argc,const char **argv) {@autoreleasepool {
|
|||||||
[osc keyUpAtPositionX:0 positionY:0 keyPressed:0 modifiers:0 forceUpdate:&update didHandle:&handled atTime:time];Check(!update && !handled,@"Unrecognized key-up clears outputs");
|
[osc keyUpAtPositionX:0 positionY:0 keyPressed:0 modifiers:0 forceUpdate:&update didHandle:&handled atTime:time];Check(!update && !handled,@"Unrecognized key-up clears outputs");
|
||||||
PGOSCTile *tile=Tile();api.x=.25;api.y=.75;
|
PGOSCTile *tile=Tile();api.x=.25;api.y=.75;
|
||||||
[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];
|
[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];
|
||||||
Check(AlphaAt(tile,150,125)>.9,@"Canvas conversion directly positions the visible handle despite effect/surface size mismatch");
|
Check(AlphaAt(tile,80,180)>.9,@"Normalized selection converts to correct CANVAS pixels despite effect/surface size mismatch");
|
||||||
|
Check(fabs(api.lastConvertX-.25)<1e-8 && fabs(api.lastConvertY-.75)<1e-8,@"Host receives normalized OBJECT coordinates rather than document pixel coordinates");
|
||||||
Check(AlphaAt(tile,25,28)<.01,@"OSC surface is not incorrectly rescaled by effect image dimensions");
|
Check(AlphaAt(tile,25,28)<.01,@"OSC surface is not incorrectly rescaled by effect image dimensions");
|
||||||
api.enabled=NO;[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];Check(AlphaAt(tile,150,125)<.01,@"Disabled drawing clears the prior handle");
|
api.enabled=NO;[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];Check(AlphaAt(tile,80,180)<.01,@"Disabled drawing clears the prior handle");
|
||||||
api.enabled=YES;api.backing=NAN;[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];Check(AlphaAt(tile,150,125)>.9,@"Nonfinite backing scale uses a finite fallback");
|
api.enabled=YES;api.backing=NAN;[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];Check(AlphaAt(tile,80,180)>.9,@"Nonfinite backing scale uses a finite fallback");
|
||||||
api.x=NAN;[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];Check(AlphaAt(tile,150,125)<.01,@"Nonfinite stored selection does not create invalid drawing filters");
|
api.x=.5;api.y=.5;api.backing=1;
|
||||||
|
[osc drawOSCWithWidth:1921 height:1080 activePart:0 destinationImage:tile atTime:time];
|
||||||
|
Check(AlphaAt(tile,160,120)>.9 && AlphaAt(tile,0,0)<.01,@"Default .5/.5 selection draws in the center instead of a corner");
|
||||||
|
Check(api.objectBoundsReads==1,@"Drawing reads object bounds once for diagnostics and never uses them to scale the normalized point");
|
||||||
|
api.x=NAN;[osc drawOSCWithWidth:1920 height:1080 activePart:0 destinationImage:tile atTime:time];Check(AlphaAt(tile,80,180)<.01,@"Nonfinite stored selection does not create invalid drawing filters");
|
||||||
|
api.x=.5;api.y=.5;
|
||||||
|
[osc mouseDownAtPositionX:.8 positionY:.3 activePart:1 modifiers:0 forceUpdate:&update atTime:time];
|
||||||
|
[osc mouseUpAtPositionX:.8 positionY:.3 activePart:1 modifiers:0 forceUpdate:&update atTime:time];
|
||||||
|
[osc drawOSCWithWidth:1921 height:1080 activePart:0 destinationImage:tile atTime:time];
|
||||||
|
Check(fabs(api.x-.8)<1e-8 && fabs(api.y-.3)<1e-8 && AlphaAt(tile,256,72)>.9,@"Click writes normalized selection and redraws the handle at that point");
|
||||||
|
PGOSCAPI *motion=[PGOSCAPI new];motion.pixelSetter=YES;motion.bounds=CGRectMake(-1920,-1080,3840,2160);
|
||||||
|
PGFaceSelectOSC *motionOSC=[[PGFaceSelectOSC alloc] initWithAPIManager:motion];
|
||||||
|
[motionOSC mouseDownAtPositionX:.8 positionY:.3 activePart:1 modifiers:0 forceUpdate:&update atTime:time];
|
||||||
|
Check(update && fabs(motion.x-.8)<1e-5 && fabs(motion.y-.3)<1e-5,@"Motion pixel-setter bridge is detected by verified parameter readback");
|
||||||
|
Check(motion.writes==2 && fabs(motion.lastX-1152)<1e-5 && fabs(motion.lastY+432)<1e-5,@"Detected bridge receives the centered pixel inverse transform");
|
||||||
|
NSUInteger beforeCached=motion.writes;
|
||||||
|
[motionOSC mouseDraggedAtPositionX:.7 positionY:.4 activePart:1 modifiers:0 forceUpdate:&update atTime:time];
|
||||||
|
Check(update && motion.writes==beforeCached+1 && fabs(motion.x-.7)<1e-5 && fabs(motion.y-.4)<1e-5,@"Pixel-setter mode is cached only on the same OSC instance");
|
||||||
|
[motionOSC mouseUpAtPositionX:.7 positionY:.4 activePart:1 modifiers:0 forceUpdate:&update atTime:time];
|
||||||
|
[motionOSC drawOSCWithWidth:3840 height:2160 activePart:0 destinationImage:tile atTime:time];
|
||||||
|
Check(AlphaAt(tile,224,96)>.9,@"Motion-compatible setter and normalized drawing round-trip to the same visible point");
|
||||||
|
Check(motion.undoStarts==1 && motion.undoEnds==1,@"Calibration writes remain in one balanced undo gesture");
|
||||||
|
PGOSCAPI *bad=[PGOSCAPI new];bad.corruptReadback=YES;
|
||||||
|
PGFaceSelectOSC *badOSC=[[PGFaceSelectOSC alloc] initWithAPIManager:bad];
|
||||||
|
[badOSC mouseDownAtPositionX:.8 positionY:.3 activePart:1 modifiers:0 forceUpdate:&update atTime:time];
|
||||||
|
Check(!update && bad.undoStarts==bad.undoEnds,@"Unrecognized readback response is reported as failure with balanced undo");
|
||||||
printf("OSC adapter: %lu checks, %lu failures\n",(unsigned long)Checks,(unsigned long)Failures);return Failures?1:0;
|
printf("OSC adapter: %lu checks, %lu failures\n",(unsigned long)Checks,(unsigned long)Failures);return Failures?1:0;
|
||||||
}}
|
}}
|
||||||
在新工单中引用
屏蔽一个用户