added files via upload

This commit is contained in:
Balackburn
2023-06-27 09:54:41 +02:00
commit 2ff6aac218
1420 changed files with 88898 additions and 0 deletions

View File

@@ -0,0 +1,278 @@
//
// SceneKit+Snapshot.m
// FLEX
//
// Created by Tanner Bennett on 1/8/20.
//
#import "SceneKit+Snapshot.h"
#import "FHSSnapshotNodes.h"
/// This value is chosen such that this offset can be applied to avoid
/// z-fighting amongst nodes at the same z-position, but small enough
/// that they appear to visually be on the same plane.
CGFloat const kFHSSmallZOffset = 0.05;
CGFloat const kHeaderVerticalInset = 8.0;
#pragma mark SCNGeometry
@interface SCNGeometry (SnapshotPrivate)
@end
@implementation SCNGeometry (SnapshotPrivate)
- (void)addDoubleSidedMaterialWithDiffuseContents:(id)contents {
SCNMaterial *material = [SCNMaterial new];
material.doubleSided = YES;
material.diffuse.contents = contents;
[self insertMaterial:material atIndex:0];
}
@end
#pragma mark SCNNode
@implementation SCNNode (Snapshot)
- (SCNNode *)nearestAncestorSnapshot {
SCNNode *node = self;
while (!node.name && node) {
node = node.parentNode;
}
return node;
}
+ (instancetype)shapeNodeWithSize:(CGSize)size materialDiffuse:(id)contents offsetZ:(BOOL)offsetZ {
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(
0, 0, size.width, size.height
)];
SCNShape *shape = [SCNShape shapeWithPath:path materialDiffuse:contents];
SCNNode *node = [SCNNode nodeWithGeometry:shape];
if (offsetZ) {
node.position = SCNVector3Make(0, 0, kFHSSmallZOffset);
}
return node;
}
+ (instancetype)highlight:(FHSViewSnapshot *)view color:(UIColor *)color {
return [self shapeNodeWithSize:view.frame.size materialDiffuse:color offsetZ:YES];
}
+ (instancetype)snapshot:(FHSViewSnapshot *)view {
id image = view.snapshotImage;
return [self shapeNodeWithSize:view.frame.size materialDiffuse:image offsetZ:NO];
}
+ (instancetype)lineFrom:(SCNVector3)v1 to:(SCNVector3)v2 color:(UIColor *)lineColor {
SCNVector3 vertices[2] = { v1, v2 };
int32_t _indices[2] = { 0, 1 };
NSData *indices = [NSData dataWithBytes:_indices length:sizeof(_indices)];
SCNGeometrySource *source = [SCNGeometrySource geometrySourceWithVertices:vertices count:2];
SCNGeometryElement *element = [SCNGeometryElement
geometryElementWithData:indices
primitiveType:SCNGeometryPrimitiveTypeLine
primitiveCount:2
bytesPerIndex:sizeof(int32_t)
];
SCNGeometry *geometry = [SCNGeometry geometryWithSources:@[source] elements:@[element]];
[geometry addDoubleSidedMaterialWithDiffuseContents:lineColor];
return [SCNNode nodeWithGeometry:geometry];
}
- (instancetype)borderWithColor:(UIColor *)color {
struct { SCNVector3 min, max; } bb;
[self getBoundingBoxMin:&bb.min max:&bb.max];
SCNVector3 topLeft = SCNVector3Make(bb.min.x, bb.max.y, kFHSSmallZOffset);
SCNVector3 bottomLeft = SCNVector3Make(bb.min.x, bb.min.y, kFHSSmallZOffset);
SCNVector3 topRight = SCNVector3Make(bb.max.x, bb.max.y, kFHSSmallZOffset);
SCNVector3 bottomRight = SCNVector3Make(bb.max.x, bb.min.y, kFHSSmallZOffset);
SCNNode *top = [SCNNode lineFrom:topLeft to:topRight color:color];
SCNNode *left = [SCNNode lineFrom:bottomLeft to:topLeft color:color];
SCNNode *bottom = [SCNNode lineFrom:bottomLeft to:bottomRight color:color];
SCNNode *right = [SCNNode lineFrom:bottomRight to:topRight color:color];
SCNNode *border = [SCNNode new];
[border addChildNode:top];
[border addChildNode:left];
[border addChildNode:bottom];
[border addChildNode:right];
return border;
}
+ (instancetype)header:(FHSViewSnapshot *)view {
SCNText *text = [SCNText labelGeometry:view.title font:[UIFont boldSystemFontOfSize:13.0]];
SCNNode *textNode = [SCNNode nodeWithGeometry:text];
struct { SCNVector3 min, max; } bb;
[textNode getBoundingBoxMin:&bb.min max:&bb.max];
CGFloat textWidth = bb.max.x - bb.min.x;
CGFloat textHeight = bb.max.y - bb.min.y;
CGFloat snapshotWidth = view.frame.size.width;
CGFloat headerWidth = MAX(snapshotWidth, textWidth);
CGRect frame = CGRectMake(0, 0, headerWidth, textHeight + (kHeaderVerticalInset * 2));
SCNNode *headerNode = [SCNNode nodeWithGeometry:[SCNShape
nameHeader:view.headerColor frame:frame corners:8
]];
[headerNode addChildNode:textNode];
textNode.position = SCNVector3Make(
(frame.size.width / 2.f) - (textWidth / 2.f),
(frame.size.height / 2.f) - (textHeight / 2.f),
kFHSSmallZOffset
);
headerNode.position = SCNVector3Make(
(snapshotWidth / 2.f) - (headerWidth / 2.f),
view.frame.size.height,
kFHSSmallZOffset
);
return headerNode;
}
+ (instancetype)snapshot:(FHSViewSnapshot *)view
parent:(FHSViewSnapshot *)parent
parentNode:(SCNNode *)parentNode
root:(SCNNode *)rootNode
depth:(NSInteger *)depthOut
nodesMap:(NSMutableDictionary<NSString *, FHSSnapshotNodes *> *)nodesMap
hideHeaders:(BOOL)hideHeaders {
NSInteger const depth = *depthOut;
// Ignore elements that are not visible.
// These should appear in the list, but not in the 3D view.
if (view.hidden || CGSizeEqualToSize(view.frame.size, CGSizeZero)) {
return nil;
}
// Create a node whose contents are the snapshot of the element
SCNNode *node = [self snapshot:view];
node.name = view.view.identifier;
// Begin building node tree
FHSSnapshotNodes *nodes = [FHSSnapshotNodes snapshot:view depth:depth];
nodes.snapshot = node;
// The node must be added to the root node
// for the coordinate space calculations below to work
[rootNode addChildNode:node];
node.position = ({
// Flip the y-coordinate since SceneKit has a
// flipped version of the UIKit coordinate system
CGRect pframe = parent ? parent.frame : CGRectZero;
CGFloat y = parent ? pframe.size.height - CGRectGetMaxY(view.frame) : 0;
// To simplify calculating the z-axis spacing between the layers, we make
// each snapshot node a direct child of the root rather than embedding
// the nodes in their parent nodes in the same structure as the UI elements
// themselves. With this flattened hierarchy, the z-position can be
// calculated for every node simply by multiplying the spacing by the depth.
//
// `parentSnapshotNode` as referenced here is NOT the actual parent node
// of `node`, it is the node corresponding to the parent of the UI element.
// It is used to convert from frame coordinates, which are relative to
// the bounds of the parent, to coordinates relative to the root node.
SCNVector3 positionRelativeToParent = SCNVector3Make(view.frame.origin.x, y, 0);
SCNVector3 positionRelativeToRoot;
if (parent) {
positionRelativeToRoot = [rootNode convertPosition:positionRelativeToParent fromNode:parentNode];
} else {
positionRelativeToRoot = positionRelativeToParent;
}
positionRelativeToRoot.z = 50 * depth;
positionRelativeToRoot;
});
// Make border node
nodes.border = [node borderWithColor:view.headerColor];
[node addChildNode:nodes.border];
// Make header node
nodes.header = [SCNNode header:view];
[node addChildNode:nodes.header];
if (hideHeaders) {
nodes.header.hidden = YES;
}
nodesMap[view.view.identifier] = nodes;
NSMutableArray<FHSViewSnapshot *> *checkForIntersect = [NSMutableArray new];
NSInteger maxChildDepth = depth;
// Recurse to child nodes; overlapping children have higher depths
for (FHSViewSnapshot *child in view.children) {
NSInteger childDepth = depth + 1;
// Children that intersect a sibling are rendered
// in a separate layer above the previous siblings
for (FHSViewSnapshot *sibling in checkForIntersect) {
if (CGRectIntersectsRect(sibling.frame, child.frame)) {
childDepth = maxChildDepth + 1;
break;
}
}
id didMakeNode = [SCNNode
snapshot:child
parent:view
parentNode:node
root:rootNode
depth:&childDepth
nodesMap:nodesMap
hideHeaders:hideHeaders
];
if (didMakeNode) {
maxChildDepth = MAX(childDepth, maxChildDepth);
[checkForIntersect addObject:child];
}
}
*depthOut = maxChildDepth;
return node;
}
@end
#pragma mark SCNShape
@implementation SCNShape (Snapshot)
+ (instancetype)shapeWithPath:(UIBezierPath *)path materialDiffuse:(id)contents {
SCNShape *shape = [SCNShape shapeWithPath:path extrusionDepth:0];
[shape addDoubleSidedMaterialWithDiffuseContents:contents];
return shape;
}
+ (instancetype)nameHeader:(UIColor *)color frame:(CGRect)frame corners:(CGFloat)radius {
UIBezierPath *path = [UIBezierPath
bezierPathWithRoundedRect:frame
byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
cornerRadii:CGSizeMake(radius, radius)
];
return [SCNShape shapeWithPath:path materialDiffuse:color];
}
@end
#pragma mark SCNText
@implementation SCNText (Snapshot)
+ (instancetype)labelGeometry:(NSString *)text font:(UIFont *)font {
NSParameterAssert(text);
SCNText *label = [self new];
label.string = text;
label.font = font;
label.alignmentMode = kCAAlignmentCenter;
label.truncationMode = kCATruncationEnd;
return label;
}
@end