From 45035e67e03ed39dc2f2bd70a27ade63b8ce5f29 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sat, 14 Feb 2026 23:11:38 -0600 Subject: [PATCH] AR Version Pre-test pt2 --- SousChefAI.xcodeproj/project.pbxproj | 2 + SousChefAI/Config/AppConfig.swift | 13 +- SousChefAI/ContentView.swift | 6 +- SousChefAI/Services/ARVisionService.swift | 10 +- .../ViewModels/CookingModeViewModel.swift | 2 +- SousChefAI/ViewModels/ScannerViewModel.swift | 2 +- SousChefAI/Views/ScannerView.swift | 111 ++++++++++++++---- 7 files changed, 105 insertions(+), 41 deletions(-) diff --git a/SousChefAI.xcodeproj/project.pbxproj b/SousChefAI.xcodeproj/project.pbxproj index 04d278d..4271788 100644 --- a/SousChefAI.xcodeproj/project.pbxproj +++ b/SousChefAI.xcodeproj/project.pbxproj @@ -361,6 +361,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -417,6 +418,7 @@ MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_VERSION = 6.0; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/SousChefAI/Config/AppConfig.swift b/SousChefAI/Config/AppConfig.swift index 753274e..880583a 100644 --- a/SousChefAI/Config/AppConfig.swift +++ b/SousChefAI/Config/AppConfig.swift @@ -7,13 +7,8 @@ import Foundation -enum AppConfig { - // MARK: - Overshoot Vision API - /// Overshoot API key for real-time video inference - /// [INSERT_OVERSHOOT_API_KEY_HERE] - static let overshootAPIKey = "INSERT_KEY_HERE" - static let overshootWebSocketURL = "wss://api.overshoot.ai/v1/stream" // Placeholder URL - + +enum AppConfig: Sendable { // MARK: - Google Gemini API /// Google Gemini API key for recipe generation and reasoning /// [INSERT_GEMINI_API_KEY_HERE] @@ -27,6 +22,10 @@ enum AppConfig { /// 2. Add it to the Xcode project root /// 3. Ensure it's added to the target + // MARK: - AR Configuration + /// Enable AR-based scanning features + static let enableARScanning = true + // MARK: - Feature Flags static let enableRealTimeDetection = true static let enableCookingMode = true diff --git a/SousChefAI/ContentView.swift b/SousChefAI/ContentView.swift index 96cb279..65c9aca 100644 --- a/SousChefAI/ContentView.swift +++ b/SousChefAI/ContentView.swift @@ -149,11 +149,11 @@ struct ContentView: View { Section("API Configuration") { VStack(alignment: .leading, spacing: 8) { - Text("Overshoot API") + Text("AR Scanning") .font(.headline) - Text(AppConfig.overshootAPIKey == "INSERT_KEY_HERE" ? "Not configured" : "Configured") + Text(AppConfig.enableARScanning ? "Enabled" : "Disabled") .font(.caption) - .foregroundStyle(AppConfig.overshootAPIKey == "INSERT_KEY_HERE" ? .red : .green) + .foregroundStyle(AppConfig.enableARScanning ? .green : .red) } VStack(alignment: .leading, spacing: 8) { diff --git a/SousChefAI/Services/ARVisionService.swift b/SousChefAI/Services/ARVisionService.swift index a1af8e7..d57265d 100644 --- a/SousChefAI/Services/ARVisionService.swift +++ b/SousChefAI/Services/ARVisionService.swift @@ -15,9 +15,11 @@ import ARKit /// AR-based implementation for vision and spatial scanning final class ARVisionService: VisionService, @unchecked Sendable { + nonisolated init() {} + // MARK: - VisionService Protocol Implementation - func detectIngredients(from stream: AsyncStream) async throws -> [Ingredient] { + nonisolated func detectIngredients(from stream: AsyncStream) async throws -> [Ingredient] { // Mock implementation - in a real app, this would use ML models // to detect ingredients from AR camera frames var detectedIngredients: [Ingredient] = [] @@ -49,11 +51,11 @@ final class ARVisionService: VisionService, @unchecked Sendable { .sorted { $0.confidence > $1.confidence } } - func detectIngredients(from pixelBuffer: CVPixelBuffer) async throws -> [Ingredient] { + nonisolated func detectIngredients(from pixelBuffer: CVPixelBuffer) async throws -> [Ingredient] { return try await processARFrame(pixelBuffer) } - func analyzeCookingProgress(from stream: AsyncStream, for step: String) async throws -> CookingProgress { + nonisolated func analyzeCookingProgress(from stream: AsyncStream, for step: String) async throws -> CookingProgress { // Mock implementation for cooking progress monitoring return CookingProgress( isComplete: false, @@ -64,7 +66,7 @@ final class ARVisionService: VisionService, @unchecked Sendable { // MARK: - Private Helper Methods - private func processARFrame(_ pixelBuffer: CVPixelBuffer) async throws -> [Ingredient] { + nonisolated private func processARFrame(_ pixelBuffer: CVPixelBuffer) async throws -> [Ingredient] { // Mock ingredient detection // In a real implementation, this would use Vision framework or ML models // to detect objects in the AR camera feed diff --git a/SousChefAI/ViewModels/CookingModeViewModel.swift b/SousChefAI/ViewModels/CookingModeViewModel.swift index 62321c8..c926f1d 100644 --- a/SousChefAI/ViewModels/CookingModeViewModel.swift +++ b/SousChefAI/ViewModels/CookingModeViewModel.swift @@ -44,7 +44,7 @@ final class CookingModeViewModel: ObservableObject { } nonisolated init(recipe: Recipe, - visionService: VisionService = OvershootVisionService(), + visionService: VisionService = ARVisionService(), recipeService: RecipeService = GeminiRecipeService(), cameraManager: CameraManager = CameraManager()) { self.recipe = recipe diff --git a/SousChefAI/ViewModels/ScannerViewModel.swift b/SousChefAI/ViewModels/ScannerViewModel.swift index 10b2236..a6a818b 100644 --- a/SousChefAI/ViewModels/ScannerViewModel.swift +++ b/SousChefAI/ViewModels/ScannerViewModel.swift @@ -23,7 +23,7 @@ final class ScannerViewModel: ObservableObject { private let cameraManager: CameraManager private var scanTask: Task? - nonisolated init(visionService: VisionService = OvershootVisionService(), + nonisolated init(visionService: VisionService = ARVisionService(), cameraManager: CameraManager = CameraManager()) { self.visionService = visionService self.cameraManager = cameraManager diff --git a/SousChefAI/Views/ScannerView.swift b/SousChefAI/Views/ScannerView.swift index 94925ea..b3f8030 100644 --- a/SousChefAI/Views/ScannerView.swift +++ b/SousChefAI/Views/ScannerView.swift @@ -2,23 +2,35 @@ // ScannerView.swift // SousChefAI // -// Camera view for scanning and detecting ingredients in real-time +// AR camera view for scanning and detecting ingredients in real-time // import SwiftUI -import AVFoundation +import ARKit +import RealityKit struct ScannerView: View { @StateObject private var viewModel = ScannerViewModel() @State private var showingInventory = false @State private var showingManualEntry = false + @State private var detectedPlanes = 0 + @State private var lastRaycastResult = "" + @State private var showARView = false var body: some View { NavigationStack { ZStack { - // Camera preview - CameraPreviewView(previewLayer: viewModel.getPreviewLayer()) + // AR camera preview or regular camera + if showARView { + ARViewContainer( + detectedPlanes: $detectedPlanes, + lastRaycastResult: $lastRaycastResult + ) .ignoresSafeArea() + } else { + CameraPreviewView(previewLayer: viewModel.getPreviewLayer()) + .ignoresSafeArea() + } // Overlay UI VStack { @@ -26,6 +38,12 @@ struct ScannerView: View { statusBar .padding() + // AR Debug info (only when AR is active) + if showARView { + arDebugInfo + .padding(.horizontal) + } + Spacer() // Detected ingredients list @@ -38,7 +56,7 @@ struct ScannerView: View { .padding() } } - .navigationTitle("Scan Ingredients") + .navigationTitle(showARView ? "AR Scanner" : "Camera Preview") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { @@ -50,8 +68,10 @@ struct ScannerView: View { } } .task { - await viewModel.setupCamera() - viewModel.startCamera() + if !showARView { + await viewModel.setupCamera() + viewModel.startCamera() + } } .onDisappear { viewModel.cleanup() @@ -81,7 +101,7 @@ struct ScannerView: View { private var statusBar: some View { HStack { VStack(alignment: .leading, spacing: 4) { - Text(viewModel.scanProgress) + Text(showARView ? "AR Mode Active" : viewModel.scanProgress) .font(.headline) .foregroundStyle(.white) @@ -107,6 +127,23 @@ struct ScannerView: View { .clipShape(RoundedRectangle(cornerRadius: 12)) } + private var arDebugInfo: some View { + VStack(alignment: .leading, spacing: 4) { + Text("Detected Planes: \(detectedPlanes)") + .font(.caption) + .foregroundStyle(.white) + + if !lastRaycastResult.isEmpty { + Text(lastRaycastResult) + .font(.caption2) + .foregroundStyle(.white) + } + } + .padding(8) + .background(.ultraThinMaterial) + .clipShape(RoundedRectangle(cornerRadius: 8)) + } + private var detectedIngredientsOverlay: some View { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 12) { @@ -131,30 +168,54 @@ struct ScannerView: View { private var controlsBar: some View { VStack(spacing: 16) { - // Main action button - if viewModel.isScanning { + // AR Toggle button + if !viewModel.isScanning { Button { - viewModel.stopScanning() + withAnimation { + showARView.toggle() + if !showARView { + viewModel.startCamera() + } else { + viewModel.stopCamera() + } + } } label: { - Label("Stop Scanning", systemImage: "stop.circle.fill") + Label(showARView ? "Exit AR Mode" : "Start AR Scan", systemImage: showARView ? "camera.fill" : "arkit") .font(.headline) .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() - .background(Color.red) + .background(showARView ? Color.orange : Color.blue) .clipShape(RoundedRectangle(cornerRadius: 16)) } - } else { - Button { - viewModel.startScanning() - } label: { - Label("Scan Fridge", systemImage: "camera.fill") - .font(.headline) - .foregroundStyle(.white) - .frame(maxWidth: .infinity) - .padding() - .background(Color.blue) - .clipShape(RoundedRectangle(cornerRadius: 16)) + } + + // Main scanning action button (only in non-AR mode) + if !showARView { + if viewModel.isScanning { + Button { + viewModel.stopScanning() + } label: { + Label("Stop Scanning", systemImage: "stop.circle.fill") + .font(.headline) + .foregroundStyle(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color.red) + .clipShape(RoundedRectangle(cornerRadius: 16)) + } + } else { + Button { + viewModel.startScanning() + } label: { + Label("Detect Ingredients", systemImage: "camera.viewfinder") + .font(.headline) + .foregroundStyle(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color.green) + .clipShape(RoundedRectangle(cornerRadius: 16)) + } } } @@ -168,7 +229,7 @@ struct ScannerView: View { .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() - .background(Color.green) + .background(Color.purple) .clipShape(RoundedRectangle(cornerRadius: 16)) } }