Spaces:
Running
Running
| """ | |
| modules/ar_objects.py — FRIDAY AR Object Creator | |
| Creates and places 3D objects in AR via voice commands. | |
| Smart interpretation - FRIDAY understands intent! | |
| """ | |
| import os | |
| import json | |
| import time | |
| import random | |
| from typing import Optional, List | |
| from config import DATA_DIR | |
| OBJECTS_FILE = os.path.join(DATA_DIR, "ar_objects.json") | |
| # === Smart Object Mapping - FRIDAY understands what you want! === | |
| SMART_OBJECTS = { | |
| # Vehicles | |
| "car": "sphere", | |
| "bike": "cylinder", | |
| "plane": "cone", | |
| "rocket": "cone", | |
| "ship": "cylinder", | |
| "boat": "cylinder", | |
| "train": "cylinder", | |
| # Buildings | |
| "house": "pyramid", | |
| "tower": "cylinder", | |
| "building": "pyramid", | |
| "castle": "pyramid", | |
| "skyscraper": "cylinder", | |
| # Nature | |
| "tree": "cone", | |
| "flower": "cone", | |
| "mountain": "pyramid", | |
| "rock": "sphere", | |
| "sun": "sphere", | |
| "moon": "sphere", | |
| "planet": "sphere", | |
| "star": "star", | |
| # Fun | |
| "roller coaster": "grid", | |
| "coaster": "grid", | |
| "ferris wheel": "torus", | |
| "wheel": "torus", | |
| "ring": "torus", | |
| # Characters | |
| "iron man": "mark_1", | |
| "captain": "shield", | |
| "shield": "shield", | |
| "arc reactor": "arc_reactor", | |
| " repulsor": "repulsor", | |
| # Tech vibes | |
| "laser": "pyramid", | |
| "beam": "pyramid", | |
| "portal": "sphere", | |
| "wormhole": "sphere", | |
| # Weapons | |
| "gun": "pyramid", | |
| "sword": "pyramid", | |
| "blade": "pyramid", | |
| # Misc | |
| "heart": "heart", | |
| "diamond": "diamond", | |
| "skull": "skull", | |
| "crystal": "diamond", | |
| "orb": "sphere", | |
| "box": "cube", | |
| } | |
| def _load() -> dict: | |
| if not os.path.exists(OBJECTS_FILE): | |
| return {"objects": [], "placed": []} | |
| try: | |
| with open(OBJECTS_FILE, "r") as f: | |
| return json.load(f) | |
| except: | |
| return {"objects": [], "placed": []} | |
| def _save(data: dict) -> None: | |
| try: | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| with open(OBJECTS_FILE, "w") as f: | |
| json.dump(data, f) | |
| except: | |
| pass | |
| # === Pre-defined AR Objects === | |
| AR_OBJECTS = { | |
| # Basic Shapes | |
| "cube": {"type": "box", "color": "#00FFFF", "size": 0.5}, | |
| "sphere": {"type": "sphere", "color": "#FF00FF", "size": 0.5}, | |
| "cylinder": {"type": "cylinder", "color": "#00FF00", "size": 0.5}, | |
| "pyramid": {"type": "cone", "color": "#FFFF00", "size": 0.5}, | |
| "torus": {"type": "torus", "color": "#FF6600", "size": 0.5}, | |
| # Tech Objects | |
| "arc_reactor": {"type": "glow_ring", "color": "#00FFFF", "size": 0.3}, | |
| "mark_1": {"type": "helmet", "color": "#FF0000", "size": 1.0}, | |
| "shield": {"type": "disk", "color": "#0066FF", "size": 0.8}, | |
| " repulsor": {"type": "glow_orb", "color": "#FF3300", "size": 0.2}, | |
| # UI Elements | |
| "hologram": {"type": "plane", "color": "#00FF88", "size": 1.0}, | |
| "grid": {"type": "wire_grid", "color": "#00FF88", "size": 2.0}, | |
| "target": {"type": "crosshair", "color": "#FF0000", "size": 0.3}, | |
| "radar": {"type": "circle_scan", "color": "#00FF00", "size": 1.0}, | |
| # Decorative | |
| "star": {"type": "star", "color": "#FFFF00", "size": 0.5}, | |
| "heart": {"type": "heart", "color": "#FF0066", "size": 0.3}, | |
| "diamond": {"type": "diamond", "color": "#00FFFF", "size": 0.4}, | |
| "skull": {"type": "skull", "color": "#FFFFFF", "size": 0.3}, | |
| } | |
| def list_available_objects() -> List[str]: | |
| """List all available object types.""" | |
| return list(AR_OBJECTS.keys()) | |
| def create_object(name: str, custom_size: float = 0.5, custom_color: str = "") -> str: | |
| """ | |
| Create a new AR object. | |
| Args: | |
| name: Object type (cube, sphere, etc) | |
| custom_size: Optional custom size | |
| custom_color: Optional custom color | |
| Returns: | |
| Confirmation message | |
| """ | |
| name = name.lower().strip() | |
| if name in AR_OBJECTS: | |
| obj = AR_OBJECTS[name].copy() | |
| else: | |
| # Custom object | |
| obj = {"type": "custom", "color": custom_color or "#00FFFF", "size": custom_size} | |
| if custom_size > 0: | |
| obj["size"] = custom_size | |
| if custom_color: | |
| obj["color"] = custom_color | |
| # Add to objects list | |
| data = _load() | |
| obj_id = f"{name}_{int(time.time())}" | |
| obj["id"] = obj_id | |
| obj["created_at"] = time.time() | |
| data["objects"].append(obj) | |
| _save(data) | |
| return f"Created {name}." | |
| def place_object(name: str, x: float = 0, y: float = 0, z: float = -2) -> str: | |
| """ | |
| Place an object in AR space. | |
| Args: | |
| name: Object name/type | |
| x, y, z: Position in AR space | |
| Returns: | |
| Confirmation message | |
| """ | |
| name = name.lower().strip() | |
| # Get or create object | |
| data = _load() | |
| obj = None | |
| # Look for existing object | |
| for o in data.get("objects", []): | |
| if name in o.get("id", ""): | |
| obj = o | |
| break | |
| if not obj: | |
| # Create new | |
| if name in AR_OBJECTS: | |
| obj = AR_OBJECTS[name].copy() | |
| else: | |
| obj = {"type": "box", "color": "#00FFFF", "size": 0.5} | |
| # Set position | |
| obj["position"] = {"x": x, "y": y, "z": z} | |
| obj["id"] = f"{name}_{int(time.time())}" | |
| obj["placed_at"] = time.time() | |
| # Add to placed | |
| if "placed" not in data: | |
| data["placed"] = [] | |
| data["placed"].append(obj) | |
| _save(data) | |
| return f"Placed {name} at position." | |
| def remove_object(name: str) -> str: | |
| """Remove a placed object.""" | |
| data = _load() | |
| name = name.lower() | |
| placed = data.get("placed", []) | |
| data["placed"] = [o for o in placed if name not in o.get("id", "")] | |
| _save(data) | |
| return f"Removed {name}." | |
| def clear_all_objects() -> str: | |
| """Clear all placed objects.""" | |
| data = _load() | |
| data["placed"] = [] | |
| _save(data) | |
| return "All objects cleared." | |
| def get_placed_objects() -> List[dict]: | |
| """Get all placed objects for AR renderer.""" | |
| data = _load() | |
| return data.get("placed", []) | |
| def get_object_state_for_ar() -> dict: | |
| """Get object state for AR scene.""" | |
| data = _load() | |
| return { | |
| "objects": data.get("placed", []), | |
| "available": list(AR_OBJECTS.keys()), | |
| } | |
| # === AR Scene Integration === | |
| def update_ar_scene() -> None: | |
| """Update AR scene with current objects.""" | |
| try: | |
| from modules.ar_scene import apply_patch | |
| state = get_object_state_for_ar() | |
| apply_patch({"ar_objects": state["objects"]}) | |
| except: | |
| pass | |
| # === Smart Voice Command Handler === | |
| def handle_command(command: str, speak) -> bool: | |
| c = command.lower() | |
| # Check for SMART objects first (natural language) | |
| for natural_name, ar_obj in SMART_OBJECTS.items(): | |
| if natural_name in c: | |
| # "make a roller coaster" → creates grid | |
| # "place a car" → places sphere | |
| if "create" in c or "make" in c or "build" in c: | |
| create_result = create_object(ar_obj) | |
| msg = create_result.replace("Created", "Its") | |
| speak(f"Making {natural_name}! {msg}") | |
| return True | |
| if "place" in c or "put" in c: | |
| place_result = place_object(ar_obj) | |
| msg = place_result.replace("Placed", "Done") | |
| speak(f"Placing {natural_name}! {msg}") | |
| return True | |
| # Create object (direct) | |
| if "create" in c or "make" in c or "build" in c: | |
| for obj in list_available_objects(): | |
| if obj in c: | |
| result = create_object(obj) | |
| speak(result) | |
| return True | |
| # Ask what to make | |
| speak("What should I make? I can do roller coaster, car, plane, house, tree, arc reactor, and more!") | |
| return True | |
| # Also check for smart objects in basic commands | |
| if "place" in c or "put" in c: | |
| for obj in list_available_objects(): | |
| if obj in c: | |
| result = place_object(obj) | |
| speak(result) | |
| return True | |
| speak("Place what?") | |
| return True | |
| # Remove object | |
| if "remove" in c or "delete" in c: | |
| for obj in list_available_objects(): | |
| if obj in c: | |
| result = remove_object(obj) | |
| speak(result) | |
| return True | |
| # Clear all | |
| if "clear all" in c or "remove all" in c: | |
| result = clear_all_objects() | |
| speak(result) | |
| return True | |
| # List objects | |
| if "what objects" in c or "list objects" in c or "show objects" in c: | |
| objects = get_placed_objects() | |
| if objects: | |
| names = [o.get("id", "unknown")[:20] for o in objects] | |
| speak(f"Placed: {', '.join(names)}") | |
| else: | |
| speak("No objects placed. Say 'make a roller coaster' or 'place a car'!") | |
| return True | |
| # Ask what to make (smart suggestion) | |
| if "what can you make" in c or "what do you know" in c: | |
| speak("I can make: roller coaster, car, plane, house, tree, arc reactor, heart, star, and much more! Just say 'make a [whatever]'!") | |
| return True | |
| return False | |
| # === Quick Place Functions === | |
| def quick_place(obj_type: str) -> str: | |
| """Quick place a common object.""" | |
| placements = { | |
| "reactor": "arc_reactor", | |
| "mark": "mark_1", | |
| "shield": "shield", | |
| "hologram": "hologram", | |
| "radar": "radar", | |
| "target": "target", | |
| "grid": "grid", | |
| } | |
| target = placements.get(obj_type.lower(), obj_type) | |
| return place_object(target) |