meh
This commit is contained in:
@@ -100,6 +100,13 @@ final class CameraSessionManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
session.addOutput(output)
|
session.addOutput(output)
|
||||||
|
|
||||||
|
if let connection = output.connection(with: .video) {
|
||||||
|
if connection.isVideoMirroringSupported {
|
||||||
|
connection.automaticallyAdjustsVideoMirroring = false
|
||||||
|
connection.isVideoMirrored = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.captureSession = session
|
self.captureSession = session
|
||||||
self.videoOutput = output
|
self.videoOutput = output
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,20 @@ import AVFoundation
|
|||||||
struct CameraPreviewView: NSViewRepresentable {
|
struct CameraPreviewView: NSViewRepresentable {
|
||||||
let previewLayer: AVCaptureVideoPreviewLayer
|
let previewLayer: AVCaptureVideoPreviewLayer
|
||||||
let borderColor: NSColor
|
let borderColor: NSColor
|
||||||
|
let showsBorder: Bool
|
||||||
|
let cornerRadius: CGFloat
|
||||||
|
|
||||||
|
init(
|
||||||
|
previewLayer: AVCaptureVideoPreviewLayer,
|
||||||
|
borderColor: NSColor,
|
||||||
|
showsBorder: Bool = true,
|
||||||
|
cornerRadius: CGFloat = 12
|
||||||
|
) {
|
||||||
|
self.previewLayer = previewLayer
|
||||||
|
self.borderColor = borderColor
|
||||||
|
self.showsBorder = showsBorder
|
||||||
|
self.cornerRadius = cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
func makeNSView(context: Context) -> PreviewContainerView {
|
func makeNSView(context: Context) -> PreviewContainerView {
|
||||||
let view = PreviewContainerView()
|
let view = PreviewContainerView()
|
||||||
@@ -23,6 +37,11 @@ struct CameraPreviewView: NSViewRepresentable {
|
|||||||
view.layer?.addSublayer(previewLayer)
|
view.layer?.addSublayer(previewLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let connection = previewLayer.connection, connection.isVideoMirroringSupported {
|
||||||
|
connection.automaticallyAdjustsVideoMirroring = false
|
||||||
|
connection.isVideoMirrored = true
|
||||||
|
}
|
||||||
|
|
||||||
updateBorder(view: view, color: borderColor)
|
updateBorder(view: view, color: borderColor)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
@@ -42,13 +61,22 @@ struct CameraPreviewView: NSViewRepresentable {
|
|||||||
previewLayer.frame = nsView.bounds
|
previewLayer.frame = nsView.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let connection = previewLayer.connection, connection.isVideoMirroringSupported {
|
||||||
|
connection.automaticallyAdjustsVideoMirroring = false
|
||||||
|
connection.isVideoMirrored = true
|
||||||
|
}
|
||||||
|
|
||||||
updateBorder(view: nsView, color: borderColor)
|
updateBorder(view: nsView, color: borderColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateBorder(view: NSView, color: NSColor) {
|
private func updateBorder(view: NSView, color: NSColor) {
|
||||||
view.layer?.borderColor = color.cgColor
|
if showsBorder {
|
||||||
view.layer?.borderWidth = 4
|
view.layer?.borderColor = color.cgColor
|
||||||
view.layer?.cornerRadius = 12
|
view.layer?.borderWidth = 4
|
||||||
|
} else {
|
||||||
|
view.layer?.borderWidth = 0
|
||||||
|
}
|
||||||
|
view.layer?.cornerRadius = cornerRadius
|
||||||
view.layer?.masksToBounds = true
|
view.layer?.masksToBounds = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ struct EnforceModeCalibrationOverlayView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Color.black.opacity(0.85)
|
cameraBackground
|
||||||
.ignoresSafeArea()
|
|
||||||
|
|
||||||
switch calibrationService.currentStep {
|
switch calibrationService.currentStep {
|
||||||
case .eyeBox:
|
case .eyeBox:
|
||||||
@@ -31,19 +30,23 @@ struct EnforceModeCalibrationOverlayView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var eyeBoxStep: some View {
|
private var eyeBoxStep: some View {
|
||||||
VStack(spacing: 24) {
|
ZStack {
|
||||||
Text("Adjust Eye Box")
|
VStack(spacing: 16) {
|
||||||
.font(.title2)
|
Text("Adjust Eye Box")
|
||||||
.foregroundStyle(.white)
|
.font(.title2)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
"Use the sliders to fit the boxes around your eyes. When it looks right, continue."
|
"Use the sliders to fit the boxes around your eyes. When it looks right, continue."
|
||||||
)
|
)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(.white.opacity(0.8))
|
.foregroundStyle(.white.opacity(0.8))
|
||||||
|
}
|
||||||
eyePreview
|
.padding(.horizontal, 40)
|
||||||
|
.padding(.top, 40)
|
||||||
|
.frame(maxWidth: 520)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .top)
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
Text("Width")
|
Text("Width")
|
||||||
@@ -62,24 +65,29 @@ struct EnforceModeCalibrationOverlayView: View {
|
|||||||
in: 0.01...0.10
|
in: 0.01...0.10
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.padding()
|
.padding(16)
|
||||||
.background(.white.opacity(0.1))
|
.background(.black.opacity(0.6))
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||||
|
.frame(maxWidth: 420)
|
||||||
|
.frame(maxHeight: .infinity, alignment: .center)
|
||||||
|
|
||||||
HStack(spacing: 12) {
|
VStack {
|
||||||
Button("Cancel") {
|
Spacer()
|
||||||
calibrationService.dismissOverlay()
|
HStack(spacing: 12) {
|
||||||
enforceModeService.stopTestMode()
|
Button("Cancel") {
|
||||||
}
|
calibrationService.dismissOverlay()
|
||||||
.buttonStyle(.bordered)
|
enforceModeService.stopTestMode()
|
||||||
|
}
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
|
||||||
Button("Continue") {
|
Button("Continue") {
|
||||||
calibrationService.advance()
|
calibrationService.advance()
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
|
||||||
}
|
}
|
||||||
|
.padding(.bottom, 40)
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var targetStep: some View {
|
private var targetStep: some View {
|
||||||
@@ -99,11 +107,10 @@ struct EnforceModeCalibrationOverlayView: View {
|
|||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color.black.opacity(0.7))
|
.background(Color.black.opacity(0.7))
|
||||||
.frame(maxWidth: .infinity, alignment: .top)
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||||
|
|
||||||
targetDot
|
targetDot
|
||||||
|
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
@@ -135,50 +142,56 @@ struct EnforceModeCalibrationOverlayView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var eyePreview: some View {
|
|
||||||
ZStack {
|
|
||||||
if let layer = eyeTrackingService.previewLayer {
|
|
||||||
CameraPreviewView(previewLayer: layer, borderColor: NSColor.systemBlue)
|
|
||||||
.frame(height: 240)
|
|
||||||
}
|
|
||||||
GeometryReader { geometry in
|
|
||||||
EyeTrackingDebugOverlayView(
|
|
||||||
debugState: eyeTrackingService.debugState,
|
|
||||||
viewSize: geometry.size
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(height: 240)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
|
||||||
}
|
|
||||||
|
|
||||||
private var targetDot: some View {
|
private var targetDot: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
let target = calibrationService.currentTarget()
|
let target = calibrationService.currentTarget()
|
||||||
Circle()
|
let center = CGPoint(
|
||||||
.fill(Color.blue)
|
x: geometry.size.width * target.x,
|
||||||
.frame(width: 100, height: 100)
|
y: geometry.size.height * target.y
|
||||||
.position(
|
)
|
||||||
x: geometry.size.width * target.x,
|
|
||||||
y: geometry.size.height * target.y
|
ZStack {
|
||||||
)
|
Circle()
|
||||||
.overlay(
|
.fill(Color.blue)
|
||||||
Circle()
|
.frame(width: 120, height: 120)
|
||||||
.trim(from: 0, to: CGFloat(calibrationService.countdownProgress))
|
|
||||||
.stroke(Color.blue.opacity(0.8), lineWidth: 6)
|
Circle()
|
||||||
.frame(width: 140, height: 140)
|
.trim(from: 0, to: CGFloat(calibrationService.countdownProgress))
|
||||||
.rotationEffect(.degrees(-90))
|
.stroke(Color.blue.opacity(0.8), lineWidth: 8)
|
||||||
.animation(.linear(duration: 0.02), value: calibrationService.countdownProgress)
|
.frame(width: 160, height: 160)
|
||||||
)
|
.rotationEffect(.degrees(-90))
|
||||||
|
.animation(.linear(duration: 0.02), value: calibrationService.countdownProgress)
|
||||||
|
}
|
||||||
|
.position(center)
|
||||||
}
|
}
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var countdownRing: some View {
|
private var cameraBackground: some View {
|
||||||
Circle()
|
ZStack {
|
||||||
.trim(from: 0, to: CGFloat(calibrationService.countdownProgress))
|
if let layer = eyeTrackingService.previewLayer {
|
||||||
.stroke(Color.blue.opacity(0.8), lineWidth: 6)
|
CameraPreviewView(
|
||||||
.frame(width: 120, height: 120)
|
previewLayer: layer,
|
||||||
.rotationEffect(.degrees(-90))
|
borderColor: .clear,
|
||||||
|
showsBorder: false,
|
||||||
|
cornerRadius: 0
|
||||||
|
)
|
||||||
|
.opacity(0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
if calibrationService.currentStep == .eyeBox {
|
||||||
|
GeometryReader { geometry in
|
||||||
|
EyeTrackingDebugOverlayView(
|
||||||
|
debugState: eyeTrackingService.debugState,
|
||||||
|
viewSize: geometry.size
|
||||||
|
)
|
||||||
|
.opacity(0.8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color.black.opacity(0.35)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user