feat: use AprilTag instead of QR code

This commit is contained in:
Schuwi
2025-09-14 18:52:24 +02:00
parent 788ad54724
commit 589c9964aa
600 changed files with 12814 additions and 122 deletions

79
APRILTAG_MIGRATION.md Normal file
View File

@@ -0,0 +1,79 @@
# AprilTag Migration Summary
## Completed Changes
### 1. Database Migration ✅
- Migrated from `qr_code` string field to `apriltag_id` integer field
- Added constraint to ensure valid AprilTag IDs (0-586)
- Created unique index for apriltag_id
- Preserved old qr_code data as qr_code_old for rollback safety
### 2. Schema Updates ✅
- Updated `StorageLocation` schema to use `apriltag_id` instead of `qr_code`
- Added validation for AprilTag ID range (0-586)
- Implemented auto-assignment of next available ID
- Added unique constraint validation
### 3. Business Logic Refactoring ✅
- Replaced `ComponentsElixir.QRCode` module with `ComponentsElixir.AprilTag` module
- Updated inventory functions to use AprilTag IDs instead of QR code strings
- Implemented AprilTag ID availability checking
- Added bulk SVG generation functionality
### 4. UI/UX Improvements ✅
- Replaced dropdown with 587 options with better UX:
- Radio buttons for "Auto-assign" vs "Manual selection"
- Number input for specific ID selection when manual mode selected
- Shows available ID count and examples
- Different interface for add vs edit forms
- Updated templates to show AprilTag information instead of QR codes
- Added download functionality for AprilTag SVGs
### 5. AprilTag Generation ✅
- Created `ComponentsElixir.AprilTag` module for managing tag36h11 family
- Generated all 587 placeholder SVG files with human-readable IDs
- Added Mix task `mix apriltag.generate_all` for batch generation
- SVG files served statically at `/apriltags/tag36h11_id_XXX.svg`
### 6. Event Handling ✅
- Updated LiveView event handlers for AprilTag scanning/assignment
- Added mode switching for manual vs automatic assignment
- Implemented proper form state management for different modes
## Benefits Achieved
1. **Better UX**: No more 587-option dropdown menu
2. **Future-Ready**: AprilTags designed for multi-tag detection scenarios
3. **Robust**: 587 unique IDs provide ample space without conflicts
4. **Maintainable**: Simpler integer ID system vs complex string encoding
5. **Industry Standard**: AprilTags widely used in robotics/AR applications
## Current State
- ✅ Database schema updated
- ✅ All 587 placeholder SVG files generated
- ✅ UI forms updated with better UX
- ✅ Business logic migrated to AprilTag system
-**Next**: Real AprilTag pattern generation (future enhancement)
-**Next**: Camera detection integration (future enhancement)
## Usage
### Generate AprilTag SVGs
```bash
mix apriltag.generate_all # Generate missing files
mix apriltag.generate_all --force # Regenerate all files
```
### Available AprilTag IDs
- Range: 0-586 (tag36h11 family)
- Auto-assignment picks next available ID
- Manual assignment allows specific ID selection
- Unique constraint prevents conflicts
### File Locations
- SVG files: `priv/static/apriltags/tag36h11_id_XXX.svg`
- URL pattern: `/apriltags/tag36h11_id_XXX.svg`
- Placeholder pattern includes human-readable ID label
The system is now ready for use with AprilTags instead of QR codes! The placeholder SVGs will work perfectly for testing and development until we implement actual AprilTag pattern generation.

View File

@@ -37,8 +37,8 @@ A modern, idiomatic Elixir/Phoenix port of the original PHP-based component inve
- **Search & Filter**: Fast search across component names, descriptions, and keywords - **Search & Filter**: Fast search across component names, descriptions, and keywords
- **Category Organization**: Hierarchical category system for better organization - **Category Organization**: Hierarchical category system for better organization
- **Category Management**: Add, edit, delete categories through the web interface with hierarchical support - **Category Management**: Add, edit, delete categories through the web interface with hierarchical support
- **Storage Location System**: Hierarchical storage locations (shelf → drawer → box) with automatic QR code generation - **Storage Location System**: Hierarchical storage locations (shelf → drawer → box) with automatic AprilTag generation
- **QR Code Integration**: Automatic QR code generation and display for all storage locations with download capability - **AprilTag Integration**: Automatic AprilTag generation and display for all storage locations with download capability
- **Datasheet Links**: Direct links to component datasheets - **Datasheet Links**: Direct links to component datasheets
- **Real-time Updates**: All changes are immediately reflected in the interface - **Real-time Updates**: All changes are immediately reflected in the interface
@@ -100,7 +100,7 @@ The application uses a simple password-based authentication system:
- **`ComponentsElixirWeb.LoginLive`**: Authentication interface - **`ComponentsElixirWeb.LoginLive`**: Authentication interface
- **`ComponentsElixirWeb.ComponentsLive`**: Main component management interface - **`ComponentsElixirWeb.ComponentsLive`**: Main component management interface
- **`ComponentsElixirWeb.CategoriesLive`**: Category management interface - **`ComponentsElixirWeb.CategoriesLive`**: Category management interface
- **`ComponentsElixirWeb.StorageLocationsLive`**: Hierarchical storage location management with QR codes - **`ComponentsElixirWeb.StorageLocationsLive`**: Hierarchical storage location management with AprilTags
### Key Features ### Key Features
- **Real-time updates**: Changes are immediately reflected without page refresh - **Real-time updates**: Changes are immediately reflected without page refresh
@@ -118,16 +118,16 @@ The application uses a simple password-based authentication system:
| Manual editing | `Inventory.update_component/2` | **NEW**: Full edit functionality with validation | | Manual editing | `Inventory.update_component/2` | **NEW**: Full edit functionality with validation |
| `changeAmount.php` | `Inventory.update_component_count/2` | Atomic operations, constraints | | `changeAmount.php` | `Inventory.update_component_count/2` | Atomic operations, constraints |
| Manual category management | `CategoriesLive` + `Inventory.create_category/1` | **NEW**: Full category CRUD with web interface | | Manual category management | `CategoriesLive` + `Inventory.create_category/1` | **NEW**: Full category CRUD with web interface |
| Manual location tracking | `StorageLocationsLive` + `Inventory` context | **NEW**: Hierarchical storage locations with automatic QR codes | | Manual location tracking | `StorageLocationsLive` + `Inventory` context | **NEW**: Hierarchical storage locations with automatic AprilTags |
| `imageUpload.php` | Phoenix LiveView file uploads with `.live_file_input` | **IMPLEMENTED**: Full image upload with preview, validation, and automatic cleanup | | `imageUpload.php` | Phoenix LiveView file uploads with `.live_file_input` | **IMPLEMENTED**: Full image upload with preview, validation, and automatic cleanup |
| Session management | Phoenix sessions + LiveView | Built-in CSRF protection | | Session management | Phoenix sessions + LiveView | Built-in CSRF protection |
## 🚀 Future Enhancements ## 🚀 Future Enhancements
### Component Management ### Component Management
- **Barcode Support** - Generate and scan traditional barcodes in addition to QR codes - **Barcode Support** - Generate and scan traditional barcodes in addition to AprilTags
- **Camera Integration** - JavaScript-based QR scanning with camera access for mobile/desktop - **Camera Integration** - JavaScript-based AprilTag scanning with camera access for mobile/desktop
- **Multi-QR Code Detection** - Spatial analysis and disambiguation for multiple codes in same image - **Multi-AprilTag Detection** - Spatial analysis and disambiguation for multiple tags in same image
- **Bulk Operations** - Import/export components from CSV, batch updates - **Bulk Operations** - Import/export components from CSV, batch updates
- **Search and Filtering** - Advanced search by specifications, tags, location - **Search and Filtering** - Advanced search by specifications, tags, location
- **Component Templates** - Reusable templates for common component types - **Component Templates** - Reusable templates for common component types
@@ -135,7 +135,7 @@ The application uses a simple password-based authentication system:
### Storage Organization ### Storage Organization
- **Physical Layout Mapping** - Visual representation of shelves, drawers, and boxes - **Physical Layout Mapping** - Visual representation of shelves, drawers, and boxes
- **Bulk QR Code Printing** - Generate printable sheets of QR codes for labeling - **Bulk AprilTag Printing** - Generate printable sheets of AprilTags for labeling
## ✅ Recently Implemented Features ## ✅ Recently Implemented Features
@@ -146,11 +146,12 @@ The application uses a simple password-based authentication system:
- **Web Interface** ✅ Complete - Storage locations management page with navigation - **Web Interface** ✅ Complete - Storage locations management page with navigation
- **Component-Storage Integration** ✅ Complete - Components can now be assigned to storage locations via dropdown interface - **Component-Storage Integration** ✅ Complete - Components can now be assigned to storage locations via dropdown interface
### QR Code System - Still Needed 🚧 **PARTIALLY IMPLEMENTED** ### AprilTag System 🚧 **PARTIALLY IMPLEMENTED**
- **Visual QR Code Generation** ✅ Complete - QR code images are generated and displayed for all storage locations - **Visual AprilTag Generation** ❌ Partially Implemented - Placeholder SVGs generated
- **QR Code Scanning** ❌ Missing - No camera integration or scanning functionality - **Flexible Assignment Options** ✅ Complete - Auto-assign, manual selection, or no AprilTag assignment for storage locations
- **QR Code Processing** ❌ Missing - Backend logic for processing scanned codes - **AprilTag Download** ✅ Complete - Individual AprilTag SVG files can be downloaded for printing
- **Multi-QR Disambiguation** ❌ Missing - No handling of multiple QR codes in same image - **AprilTag Scanning** ❌ Missing - No camera integration or scanning functionality (future enhancement)
- **AprilTag Processing** ❌ Missing - Backend logic for processing scanned tags (future enhancement)
### Image Upload System ✅ **COMPLETED** ### Image Upload System ✅ **COMPLETED**
- **Phoenix LiveView file uploads** with `.live_file_input` component - **Phoenix LiveView file uploads** with `.live_file_input` component

View File

@@ -66,6 +66,22 @@ window.addEventListener("phx:download_file", (event) => {
window.URL.revokeObjectURL(url) window.URL.revokeObjectURL(url)
}) })
// Add AprilTag download functionality
window.addEventListener("phx:download_apriltag", (event) => {
const { filename, url } = event.detail
// Create download link using the static file URL
const link = document.createElement('a')
link.href = url
link.download = filename
link.setAttribute('target', '_blank') // Open in new tab as fallback
document.body.appendChild(link)
link.click()
// Clean up
document.body.removeChild(link)
})
// expose liveSocket on window for web console debug logs and latency simulation: // expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug() // >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session

View File

@@ -0,0 +1,325 @@
# AprilTag Storage Location System Design
## Overview
Implement a hierarchical storage location system with AprilTag tag36h11 generation and assignment capabilities to enable quick component location entry and filtering.
## Database Schema
### 1. Storage Locations Table
```sql
CREATE TABLE storage_locations (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
apriltag_id INTEGER UNIQUE,
parent_id INTEGER REFERENCES storage_locations(id),
level INTEGER NOT NULL DEFAULT 0,
path TEXT NOT NULL, -- Materialized path: "shelf1/drawer2/box3"
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
CONSTRAINT apriltag_id_range CHECK (apriltag_id >= 0 AND apriltag_id <= 586)
);
CREATE INDEX idx_storage_locations_parent_id ON storage_locations(parent_id);
CREATE UNIQUE INDEX idx_storage_locations_apriltag_id ON storage_locations(apriltag_id);
CREATE INDEX idx_storage_locations_path ON storage_locations USING gin(path gin_trgm_ops);
CREATE UNIQUE INDEX idx_storage_locations_name_parent ON storage_locations(name, parent_id);
```
### 2. Modified Components Table
```sql
-- Migration to add storage_location_id to components
ALTER TABLE components
ADD COLUMN storage_location_id INTEGER REFERENCES storage_locations(id),
ADD COLUMN legacy_position VARCHAR(255); -- Keep old position data for migration
-- Move existing position data to legacy_position
UPDATE components SET legacy_position = position;
```
## AprilTag Design Strategy
### AprilTag tag36h11 Family
- **Total tags available**: 587 (IDs 0-586)
- **Format**: Pre-generated SVG files with human-readable ID labels
- **Storage**: `/priv/static/apriltags/tag36h11_id_XXX.svg`
- **Assignment**: One-to-one mapping between AprilTag ID and storage location
### AprilTag Components:
- **AprilTag ID**: Integer (0-586) stored in database
- **SVG File**: Pre-generated with 2-module margin and ID label
- **Human Readable**: "ID XXX" format for easy identification
- **Uniqueness**: Database constraint ensures no duplicate assignments
### Advantages over QR Codes:
1. **Robust Detection**: AprilTags designed for reliable multi-tag detection
2. **Standard Format**: Well-established robotics/AR standard
3. **Better Performance**: More resistant to lighting/angle variations
4. **Multi-tag Support**: Designed specifically for multiple tags in same view
## Multi-AprilTag Detection Strategy
### 1. Spatial Independence
```
Unlike QR codes, AprilTags are designed for multi-tag scenarios:
- Each tag has unique geometric properties
- No interference between nearby tags
- Reliable detection at various scales and angles
```
### 2. Detection Priority
```
Selection Priority:
1. User selection (tap/click on detected tag)
2. Closest tag to center of frame
3. Largest tag in frame (closest to camera)
4. Most recent successfully scanned tag
```
### 3. Visual Feedback
```
Camera Overlay:
- Draw bounding boxes around each detected AprilTag
- Show AprilTag ID and location name
- Highlight "best candidate" with different color
- Allow tap-to-select for disambiguation
```
## Implementation Components
### 1. Elixir Modules
#### Storage Location Schema
```elixir
defmodule ComponentsElixir.Inventory.StorageLocation do
use Ecto.Schema
import Ecto.Changeset
schema "storage_locations" do
field :name, :string
field :description, :string
field :apriltag_id, :integer
field :level, :integer, virtual: true
field :path, :string, virtual: true
field :is_active, :boolean, default: true
belongs_to :parent, __MODULE__
has_many :children, __MODULE__, foreign_key: :parent_id
has_many :components, Component
timestamps()
end
end
```
#### AprilTag Management
```elixir
defmodule ComponentsElixir.AprilTag do
def generate_apriltag_svg(apriltag_id, opts \\ []) do
# Generate SVG with actual AprilTag pattern
# Include human-readable ID below tag
end
def get_apriltag_url(storage_location) do
# Return URL to pre-generated SVG file
end
def available_apriltag_ids() do
# Return list of unused AprilTag IDs (0-586)
end
def generate_all_apriltag_svgs() do
# Pre-generate all 587 SVG files
end
end
```
### 2. Phoenix LiveView Components
#### AprilTag Assignment Interface
```elixir
defmodule ComponentsElixirWeb.StorageLocationsLive do
use ComponentsElixirWeb, :live_view
def mount(_params, _session, socket) do
socket =
socket
|> assign(:scanning, false)
|> assign(:available_apriltag_ids, AprilTag.available_apriltag_ids())
|> assign(:selected_location, nil)
{:ok, socket}
end
def handle_event("assign_apriltag", %{"apriltag_id" => id}, socket) do
# Assign specific AprilTag ID to storage location
end
def handle_event("apriltag_detected", %{"ids" => ids}, socket) do
# Handle multiple AprilTag detection
parsed_tags = Enum.map(ids, &resolve_storage_location/1)
socket =
socket
|> assign(:detected_tags, parsed_tags)
|> maybe_auto_select_location(parsed_tags)
{:noreply, socket}
end
end
```
### 3. JavaScript AprilTag Detection (Future)
#### Camera Integration
```javascript
// assets/js/apriltag_scanner.js
import { AprilTagDetector } from "apriltag-js";
export const AprilTagScanner = {
mounted() {
this.detector = new AprilTagDetector();
this.video = this.el.querySelector('video');
this.canvas = this.el.querySelector('canvas');
this.startCamera();
this.scanLoop();
},
scanLoop() {
if (this.video.readyState === this.video.HAVE_ENOUGH_DATA) {
const detections = this.detector.detect(this.canvas);
if (detections.length > 0) {
const apriltag_ids = detections.map(d => d.id);
this.pushEvent("apriltag_detected", { ids: apriltag_ids });
}
}
requestAnimationFrame(() => this.scanLoop());
}
};
```
## User Experience Flow
### 1. Adding Components with AprilTag Scan
```
1. User clicks "Add Component"
2. Position field shows camera icon
3. Click camera → AprilTag scanner opens
4. Scan storage location AprilTag
5. If multiple tags detected:
- Show overlay with detected locations
- User taps to select specific location
6. Location path auto-filled: "Shelf A → Drawer 2 → Box 5"
7. Component saved with storage_location_id
```
### 2. Storage Location Management
```
1. New "Storage Locations" section in admin
2. Add/edit locations with AprilTag ID selection
3. Auto-assign next available ID or manual selection
4. Download AprilTag SVG with location info
5. Print labels for physical attachment
```
### 3. AprilTag Assignment
```
1. Available AprilTag IDs shown in dropdown (0-586)
2. Current assignments displayed
3. Bulk assignment for initial setup
4. Re-assignment with validation
5. Conflict prevention (one tag per location)
```
## Handling Multiple AprilTags in Same Image
### Strategy 1: Designed for Multi-Tag
- AprilTags inherently support multiple detection
- No spatial interference between tags
- Each tag independently detectable
### Strategy 2: User Selection
- Display all detected tags with IDs
- Show corresponding location names
- Allow tap/click to select intended tag
- Remember user preferences
### Strategy 3: Smart Defaults
- Prioritize by distance to frame center
- Consider tag size (closer = larger)
- Use most recently accessed locations
- Provide manual override always
## Migration Strategy
### Phase 1: Database Migration
1. Run migration to add apriltag_id field
2. Remove qr_code field and constraints
3. Update schema and validation rules
### Phase 2: AprilTag Generation
1. Generate all 587 SVG files using Mix task
2. Serve static files via Phoenix
3. Update UI to show AprilTag instead of QR code
### Phase 3: Assignment Interface
1. Add AprilTag selection to location forms
2. Implement auto-assignment logic
3. Create bulk assignment tools
4. Add conflict detection
### Phase 4: Detection (Future)
1. Integrate AprilTag detection library
2. Implement multi-tag camera interface
3. Add disambiguation UI
4. Test detection robustness
## Technical Dependencies
### Elixir Dependencies
```elixir
# mix.exs - no additional dependencies needed for generation
# Future detection would require:
# {:apriltag, "~> 0.1"} # hypothetical
```
### JavaScript Dependencies (Future Detection)
```javascript
// package.json
{
"apriltag-js": "^1.0.0", // hypothetical
"camera-utils": "^2.0.0" // camera access utilities
}
```
## Database Indexes for Performance
```sql
-- Fast location lookups
CREATE INDEX idx_components_storage_location_id ON components(storage_location_id);
-- Hierarchical queries
CREATE INDEX idx_storage_locations_path_gin ON storage_locations USING gin(path gin_trgm_ops);
-- AprilTag uniqueness and fast lookup
CREATE UNIQUE INDEX idx_storage_locations_apriltag_id ON storage_locations(apriltag_id);
-- Valid AprilTag ID range constraint
ALTER TABLE storage_locations ADD CONSTRAINT apriltag_id_range
CHECK (apriltag_id >= 0 AND apriltag_id <= 586);
```
## Benefits Over QR Code System
1. **Robust Multi-Tag Detection**: AprilTags designed specifically for multiple tags in same view
2. **Better Performance**: More reliable detection under various lighting and angle conditions
3. **Industry Standard**: Widely used in robotics and AR applications
4. **No Encoding Needed**: Simple integer ID mapping instead of complex string encoding
5. **Collision Avoidance**: 587 unique IDs provide ample space without conflicts
6. **Future-Proof**: Ready for advanced detection and tracking features
This design provides a robust foundation for AprilTag-based storage management while leveraging the inherent advantages of AprilTags for multi-tag detection scenarios.

View File

@@ -361,13 +361,7 @@ export const QRScanner = {
``` ```
### JavaScript Dependencies ### JavaScript Dependencies
```json ???
// package.json
{
"jsqr": "^1.4.0",
"qr-scanner": "^1.4.2"
}
```
## Database Indexes for Performance ## Database Indexes for Performance
```sql ```sql

View File

@@ -0,0 +1,226 @@
defmodule ComponentsElixir.AprilTag do
@moduledoc """
AprilTag generation and management for storage locations.
Provides functionality to generate AprilTag images for storage locations
and manage the tag36h11 family (IDs 0-586).
"""
import Ecto.Query
@tag36h11_count 587
@apriltag_size 200
@doc """
Returns the total number of available AprilTags in the tag36h11 family.
"""
def tag36h11_count, do: @tag36h11_count
@doc """
Validates if an AprilTag ID is valid for tag36h11 family.
## Examples
iex> ComponentsElixir.AprilTag.valid_apriltag_id?(42)
true
iex> ComponentsElixir.AprilTag.valid_apriltag_id?(587)
false
"""
def valid_apriltag_id?(id) when is_integer(id) do
id >= 0 and id < @tag36h11_count
end
def valid_apriltag_id?(_), do: false
@doc """
Gets the SVG file path for a given AprilTag ID.
## Examples
iex> ComponentsElixir.AprilTag.get_apriltag_svg_path(42)
"/apriltags/tag36h11_id_042.svg"
"""
def get_apriltag_svg_path(apriltag_id) when is_integer(apriltag_id) do
if valid_apriltag_id?(apriltag_id) do
"/apriltags/tag36h11_id_#{String.pad_leading(to_string(apriltag_id), 3, "0")}.svg"
else
nil
end
end
@doc """
Gets the SVG file URL for a storage location's AprilTag.
## Examples
iex> location = %StorageLocation{apriltag_id: 42}
iex> ComponentsElixir.AprilTag.get_apriltag_url(location)
"/apriltags/tag36h11_id_042.svg"
"""
def get_apriltag_url(storage_location) do
case storage_location.apriltag_id do
nil -> nil
apriltag_id -> get_apriltag_svg_path(apriltag_id)
end
end
@doc """
Returns a list of all available AprilTag IDs.
"""
def all_apriltag_ids do
0..(@tag36h11_count - 1) |> Enum.to_list()
end
@doc """
Returns a list of used AprilTag IDs in the system.
"""
def used_apriltag_ids do
ComponentsElixir.Repo.all(
from sl in ComponentsElixir.Inventory.StorageLocation,
where: not is_nil(sl.apriltag_id),
select: sl.apriltag_id
)
end
@doc """
Returns a list of available (unused) AprilTag IDs.
"""
def available_apriltag_ids do
used = used_apriltag_ids()
all_apriltag_ids() -- used
end
@doc """
Gets the next available AprilTag ID, or nil if all are used.
"""
def next_available_apriltag_id do
available_apriltag_ids() |> List.first()
end
@doc """
Generates label data for a storage location with AprilTag.
This could be used to generate PDF labels or send to a label printer.
"""
def generate_label_data(storage_location) do
%{
apriltag_id: storage_location.apriltag_id,
apriltag_url: get_apriltag_url(storage_location),
name: storage_location.name,
path: storage_location.path,
level: storage_location.level,
description: storage_location.description
}
end
@doc """
Generates an SVG string for an AprilTag with the given ID.
This creates a basic SVG representation of the AprilTag pattern
with the ID displayed below it for human readability.
Note: This is a simplified implementation. For production use,
you'd want to use the actual AprilTag generation algorithm or
pre-generated assets.
"""
def generate_apriltag_svg(apriltag_id, opts \\ []) do
size = Keyword.get(opts, :size, @apriltag_size)
margin = Keyword.get(opts, :margin, div(size, 10))
# For now, create a placeholder square pattern
# In a real implementation, you'd generate the actual AprilTag pattern
square_size = size - (2 * margin)
"""
<svg width="#{size}" height="#{size + 30}" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="#{size}" height="#{size + 30}" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="#{margin}" y="#{margin}" width="#{square_size}" height="#{square_size}"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="#{margin + 10}" y="#{margin + 10}" width="#{square_size - 20}" height="#{square_size - 20}"
fill="black"/>
<rect x="#{margin + 20}" y="#{margin + 20}" width="#{square_size - 40}" height="#{square_size - 40}"
fill="white"/>
<!-- ID text below -->
<text x="#{size / 2}" y="#{size + 20}" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: #{String.pad_leading(to_string(apriltag_id), 3, "0")}
</text>
</svg>
"""
end
@doc """
Generates and saves all 587 AprilTag SVG files to the static directory.
This should be run once during setup to pre-generate all AprilTag images.
"""
def generate_all_apriltag_svgs(opts \\ []) do
static_dir = Path.join([
Application.app_dir(:components_elixir, "priv/static"),
"apriltags"
])
# Ensure directory exists
File.mkdir_p!(static_dir)
force_regenerate = Keyword.get(opts, :force_regenerate, false)
results =
all_apriltag_ids()
|> Task.async_stream(
fn apriltag_id ->
filename = "tag36h11_id_#{String.pad_leading(to_string(apriltag_id), 3, "0")}.svg"
file_path = Path.join(static_dir, filename)
if force_regenerate || !File.exists?(file_path) do
svg_content = generate_apriltag_svg(apriltag_id, opts)
case File.write(file_path, svg_content) do
:ok -> {:ok, apriltag_id, file_path}
{:error, reason} -> {:error, apriltag_id, reason}
end
else
{:ok, apriltag_id, file_path}
end
end,
timeout: :infinity,
max_concurrency: System.schedulers_online() * 2
)
|> Enum.map(fn {:ok, result} -> result end)
success_count = results |> Enum.count(&match?({:ok, _, _}, &1))
error_count = results |> Enum.count(&match?({:error, _, _}, &1))
%{
total: @tag36h11_count,
success: success_count,
errors: error_count,
results: results
}
end
@doc """
Cleans up AprilTag SVG file for a specific ID.
Should be called when storage locations are deleted to prevent orphaned files.
"""
def cleanup_apriltag_svg(apriltag_id) do
filename = "tag36h11_id_#{String.pad_leading(to_string(apriltag_id), 3, "0")}.svg"
file_path = Path.join([
Application.app_dir(:components_elixir, "priv/static/apriltags"),
filename
])
if File.exists?(file_path) do
File.rm(file_path)
else
:ok
end
end
end

View File

@@ -24,11 +24,9 @@ defmodule ComponentsElixir.Inventory do
processed_locations = compute_hierarchy_fields_batch(locations) processed_locations = compute_hierarchy_fields_batch(locations)
|> Enum.sort_by(&{&1.level, &1.name}) |> Enum.sort_by(&{&1.level, &1.name})
# Ensure QR codes exist for all locations (in background) # Ensure AprilTag SVGs exist for all locations
spawn(fn -> spawn(fn ->
Enum.each(processed_locations, fn location -> ComponentsElixir.AprilTag.generate_all_apriltag_svgs()
ComponentsElixir.QRCode.get_qr_image_url(location)
end)
end) end)
processed_locations processed_locations
@@ -109,11 +107,11 @@ defmodule ComponentsElixir.Inventory do
end end
@doc """ @doc """
Gets a storage location by QR code. Gets a storage location by AprilTag ID.
""" """
def get_storage_location_by_qr_code(qr_code) do def get_storage_location_by_apriltag_id(apriltag_id) do
StorageLocation StorageLocation
|> where([sl], sl.qr_code == ^qr_code) |> where([sl], sl.apriltag_id == ^apriltag_id)
|> preload(:parent) |> preload(:parent)
|> Repo.one() |> Repo.one()
|> case do |> case do
@@ -138,8 +136,6 @@ defmodule ComponentsElixir.Inventory do
case result do case result do
{:ok, location} -> {:ok, location} ->
# Automatically generate QR code image
spawn(fn -> ComponentsElixir.QRCode.get_qr_image_url(location) end)
{:ok, location} {:ok, location}
error -> error ->
error error
@@ -159,8 +155,6 @@ defmodule ComponentsElixir.Inventory do
case result do case result do
{:ok, updated_location} -> {:ok, updated_location} ->
# Automatically regenerate QR code image if name or hierarchy changed
spawn(fn -> ComponentsElixir.QRCode.get_qr_image_url(updated_location, force_regenerate: true) end)
{:ok, updated_location} {:ok, updated_location}
error -> error ->
error error
@@ -171,9 +165,6 @@ defmodule ComponentsElixir.Inventory do
Deletes a storage location. Deletes a storage location.
""" """
def delete_storage_location(%StorageLocation{} = storage_location) do def delete_storage_location(%StorageLocation{} = storage_location) do
# Clean up QR code image before deleting
ComponentsElixir.QRCode.cleanup_qr_image(storage_location.id)
Repo.delete(storage_location) Repo.delete(storage_location)
end end
@@ -185,17 +176,17 @@ defmodule ComponentsElixir.Inventory do
end end
@doc """ @doc """
Parses a QR code string and returns storage location information. Parses an AprilTag ID and returns storage location information.
""" """
def parse_qr_code(qr_string) do def parse_apriltag_id(apriltag_id) when is_integer(apriltag_id) do
case get_storage_location_by_qr_code(qr_string) do case get_storage_location_by_apriltag_id(apriltag_id) do
nil -> nil ->
{:error, :not_found} {:error, :not_found}
location -> location ->
{:ok, %{ {:ok, %{
type: :storage_location, type: :storage_location,
location: location, location: location,
qr_code: qr_string apriltag_id: apriltag_id
}} }}
end end
end end

View File

@@ -7,13 +7,14 @@ defmodule ComponentsElixir.Inventory.StorageLocation do
""" """
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query
alias ComponentsElixir.Inventory.{StorageLocation, Component} alias ComponentsElixir.Inventory.{StorageLocation, Component}
schema "storage_locations" do schema "storage_locations" do
field :name, :string field :name, :string
field :description, :string field :description, :string
field :qr_code, :string field :apriltag_id, :integer
field :is_active, :boolean, default: true field :is_active, :boolean, default: true
# Computed/virtual fields - not stored in database # Computed/virtual fields - not stored in database
@@ -31,13 +32,14 @@ defmodule ComponentsElixir.Inventory.StorageLocation do
@doc false @doc false
def changeset(storage_location, attrs) do def changeset(storage_location, attrs) do
storage_location storage_location
|> cast(attrs, [:name, :description, :parent_id, :is_active]) |> cast(attrs, [:name, :description, :parent_id, :is_active, :apriltag_id])
|> validate_required([:name]) |> validate_required([:name])
|> validate_length(:name, min: 1, max: 100) |> validate_length(:name, min: 1, max: 100)
|> validate_length(:description, max: 500) |> validate_length(:description, max: 500)
|> validate_apriltag_id()
|> foreign_key_constraint(:parent_id) |> foreign_key_constraint(:parent_id)
|> validate_no_circular_reference() |> validate_no_circular_reference()
|> put_qr_code() |> put_apriltag_id()
end end
# Prevent circular references (location being its own ancestor) # Prevent circular references (location being its own ancestor)
@@ -79,19 +81,24 @@ defmodule ComponentsElixir.Inventory.StorageLocation do
end end
@doc """ @doc """
Returns the QR code format for this storage location. Returns the AprilTag format for this storage location.
Format: SL:{level}:{qr_code}:{parent_qr_or_ROOT} Returns the AprilTag ID that corresponds to this location.
""" """
def qr_format(storage_location, parent \\ nil) do def apriltag_format(storage_location) do
parent_code = if parent, do: parent.qr_code, else: "ROOT" storage_location.apriltag_id
"SL:#{storage_location.level}:#{storage_location.qr_code}:#{parent_code}"
end end
# Private functions for changeset processing # Private functions for changeset processing
defp put_qr_code(changeset) do defp validate_apriltag_id(changeset) do
case get_field(changeset, :qr_code) do changeset
nil -> put_change(changeset, :qr_code, generate_qr_code()) |> validate_number(:apriltag_id, greater_than_or_equal_to: 0, less_than_or_equal_to: 586)
|> unique_constraint(:apriltag_id, message: "AprilTag ID is already in use")
end
defp put_apriltag_id(changeset) do
case get_field(changeset, :apriltag_id) do
nil -> put_change(changeset, :apriltag_id, get_next_available_apriltag_id())
_ -> changeset _ -> changeset
end end
end end
@@ -118,16 +125,22 @@ defmodule ComponentsElixir.Inventory.StorageLocation do
"#{compute_path(parent)}/#{name}" "#{compute_path(parent)}/#{name}"
end end
defp generate_qr_code do defp get_next_available_apriltag_id do
# Generate a unique 6-character alphanumeric code # Get all used AprilTag IDs
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" used_ids = ComponentsElixir.Repo.all(
from sl in ComponentsElixir.Inventory.StorageLocation,
where: not is_nil(sl.apriltag_id),
select: sl.apriltag_id
)
1..6 # Find the first available ID (0-586)
|> Enum.map(fn _ -> 0..586
chars |> Enum.find(&(&1 not in used_ids))
|> String.graphemes() |> case do
|> Enum.random() nil ->
end) # All IDs are used - this should be handled at the application level
|> Enum.join() raise "All AprilTag IDs are in use"
id -> id
end
end end
end end

View File

@@ -17,7 +17,7 @@ defmodule ComponentsElixirWeb do
those modules here. those modules here.
""" """
def static_paths, do: ~w(assets fonts images user_generated favicon.ico robots.txt) def static_paths, do: ~w(assets fonts images user_generated apriltags favicon.ico robots.txt)
def router do def router do
quote do quote do

View File

@@ -1,12 +1,12 @@
defmodule ComponentsElixirWeb.StorageLocationsLive do defmodule ComponentsElixirWeb.StorageLocationsLive do
@moduledoc """ @moduledoc """
LiveView for managing storage locations and QR codes. LiveView for managing storage locations and AprilTags.
""" """
use ComponentsElixirWeb, :live_view use ComponentsElixirWeb, :live_view
alias ComponentsElixir.{Inventory, Auth} alias ComponentsElixir.{Inventory, Auth}
alias ComponentsElixir.Inventory.StorageLocation alias ComponentsElixir.Inventory.StorageLocation
alias ComponentsElixir.QRCode alias ComponentsElixir.AprilTag
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
@@ -24,8 +24,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
|> assign(:show_edit_form, false) |> assign(:show_edit_form, false)
|> assign(:editing_location, nil) |> assign(:editing_location, nil)
|> assign(:form, nil) |> assign(:form, nil)
|> assign(:qr_scanner_open, false) |> assign(:apriltag_scanner_open, false)
|> assign(:scanned_codes, []) |> assign(:scanned_tags, [])
|> assign(:page_title, "Storage Location Management")} |> assign(:page_title, "Storage Location Management")}
end end
end end
@@ -38,7 +38,9 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
{:noreply, {:noreply,
socket socket
|> assign(:show_add_form, true) |> assign(:show_add_form, true)
|> assign(:form, form)} |> assign(:form, form)
|> assign(:available_apriltag_ids, AprilTag.available_apriltag_ids())
|> assign(:apriltag_mode, "auto")}
end end
def handle_event("hide_add_form", _params, socket) do def handle_event("hide_add_form", _params, socket) do
@@ -64,7 +66,9 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
socket socket
|> assign(:show_edit_form, true) |> assign(:show_edit_form, true)
|> assign(:editing_location, location) |> assign(:editing_location, location)
|> assign(:form, form)} |> assign(:form, form)
|> assign(:available_apriltag_ids, AprilTag.available_apriltag_ids())
|> assign(:edit_apriltag_mode, "keep")}
end end
def handle_event("hide_edit_form", _params, socket) do def handle_event("hide_edit_form", _params, socket) do
@@ -76,7 +80,32 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
end end
def handle_event("save_location", %{"storage_location" => location_params}, socket) do def handle_event("save_location", %{"storage_location" => location_params}, socket) do
case Inventory.create_storage_location(location_params) do # Process AprilTag assignment based on mode
processed_params = case socket.assigns.apriltag_mode do
"none" ->
# Remove any apriltag_id from params to ensure it's nil
Map.delete(location_params, "apriltag_id")
"auto" ->
# Auto-assign next available AprilTag ID
case AprilTag.next_available_apriltag_id() do
nil ->
# No available IDs, proceed without AprilTag
Map.delete(location_params, "apriltag_id")
apriltag_id ->
Map.put(location_params, "apriltag_id", apriltag_id)
end
"manual" ->
# Use the manually entered apriltag_id (validation will be handled by changeset)
location_params
_ ->
# Fallback: remove apriltag_id
Map.delete(location_params, "apriltag_id")
end
case Inventory.create_storage_location(processed_params) do
{:ok, _location} -> {:ok, _location} ->
{:noreply, {:noreply,
socket socket
@@ -121,57 +150,86 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
end end
end end
def handle_event("open_qr_scanner", _params, socket) do def handle_event("open_apriltag_scanner", _params, socket) do
{:noreply, assign(socket, :qr_scanner_open, true)} {:noreply, assign(socket, :apriltag_scanner_open, true)}
end end
def handle_event("close_qr_scanner", _params, socket) do def handle_event("close_apriltag_scanner", _params, socket) do
{:noreply, assign(socket, :qr_scanner_open, false)} {:noreply, assign(socket, :apriltag_scanner_open, false)}
end end
def handle_event("qr_scanned", %{"code" => code}, socket) do def handle_event("apriltag_scanned", %{"apriltag_id" => apriltag_id_str}, socket) do
case QRCode.parse_qr_data(code) do case Integer.parse(apriltag_id_str) do
{:ok, parsed} -> {apriltag_id, ""} when apriltag_id >= 0 and apriltag_id <= 586 ->
case Inventory.get_storage_location_by_qr_code(parsed.code) do case Inventory.get_storage_location_by_apriltag_id(apriltag_id) do
nil -> nil ->
{:noreply, put_flash(socket, :error, "Storage location not found for QR code: #{code}")} {:noreply, put_flash(socket, :error, "Storage location not found for AprilTag ID: #{apriltag_id}")}
location -> location ->
scanned_codes = [%{code: code, location: location} | socket.assigns.scanned_codes] scanned_tags = [%{apriltag_id: apriltag_id, location: location} | socket.assigns.scanned_tags]
{:noreply, {:noreply,
socket socket
|> assign(:scanned_codes, scanned_codes) |> assign(:scanned_tags, scanned_tags)
|> put_flash(:info, "Scanned: #{location.path}")} |> put_flash(:info, "Scanned: #{location.path}")}
end end
{:error, reason} -> _ ->
{:noreply, put_flash(socket, :error, "Invalid QR code: #{reason}")} {:noreply, put_flash(socket, :error, "Invalid AprilTag ID: #{apriltag_id_str}")}
end end
end end
def handle_event("clear_scanned", _params, socket) do def handle_event("clear_scanned", _params, socket) do
{:noreply, assign(socket, :scanned_codes, [])} {:noreply, assign(socket, :scanned_tags, [])}
end end
def handle_event("download_qr", %{"id" => id}, socket) do def handle_event("set_apriltag_mode", %{"mode" => mode}, socket) do
{:noreply, assign(socket, :apriltag_mode, mode)}
end
def handle_event("set_edit_apriltag_mode", %{"mode" => mode}, socket) do
# Clear the apriltag_id field when switching modes
form = case mode do
"remove" ->
socket.assigns.form
|> Phoenix.Component.to_form()
|> Map.put(:params, Map.put(socket.assigns.form.params || %{}, "apriltag_id", nil))
"keep" ->
current_id = socket.assigns.editing_location.apriltag_id
socket.assigns.form
|> Phoenix.Component.to_form()
|> Map.put(:params, Map.put(socket.assigns.form.params || %{}, "apriltag_id", current_id))
_ ->
socket.assigns.form
end
{:noreply,
socket
|> assign(:edit_apriltag_mode, mode)
|> assign(:form, form)}
end
def handle_event("download_apriltag", %{"id" => id}, socket) do
case Inventory.get_storage_location!(id) do case Inventory.get_storage_location!(id) do
%{apriltag_id: nil} ->
{:noreply, put_flash(socket, :error, "No AprilTag assigned to this location")}
location -> location ->
case QRCode.generate_qr_image(location) do case AprilTag.get_apriltag_url(location) do
{:ok, png_data} -> nil ->
filename = "#{location.name |> String.replace(" ", "_")}_QR.png" {:noreply, put_flash(socket, :error, "Failed to get AprilTag URL")}
apriltag_url ->
filename = "#{location.name |> String.replace(" ", "_")}_AprilTag_#{location.apriltag_id}.svg"
# Send file download to browser # Send file download to browser
{:noreply, {:noreply,
socket socket
|> push_event("download_file", %{ |> push_event("download_apriltag", %{
filename: filename, filename: filename,
data: Base.encode64(png_data), url: apriltag_url,
mime_type: "image/png" apriltag_id: location.apriltag_id
})} })}
{:error, _reason} ->
{:noreply, put_flash(socket, :error, "Failed to generate QR code")}
end end
end end
end end
@@ -230,8 +288,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
Inventory.count_components_in_storage_location(location_id) Inventory.count_components_in_storage_location(location_id)
end end
defp get_qr_image_url(location) do defp get_apriltag_url(location) do
QRCode.get_qr_image_url(location) AprilTag.get_apriltag_url(location)
end end
# Component for rendering individual storage location items with QR code support # Component for rendering individual storage location items with QR code support
@@ -284,21 +342,21 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<p class="text-xs text-gray-400"> <p class="text-xs text-gray-400">
{count_components_in_location(@location.id)} components {count_components_in_location(@location.id)} components
</p> </p>
<%= if @location.qr_code do %> <%= if @location.apriltag_id do %>
<span class="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded"> <span class="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded">
QR: {@location.qr_code} AprilTag: {@location.apriltag_id}
</span> </span>
<% end %> <% end %>
</div> </div>
</div> </div>
<%= if @location.qr_code do %> <%= if @location.apriltag_id do %>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<%= if get_qr_image_url(@location) do %> <%= if get_apriltag_url(@location) do %>
<div class="qr-code-container flex-shrink-0"> <div class="apriltag-container flex-shrink-0">
<img <img
src={get_qr_image_url(@location)} src={get_apriltag_url(@location)}
alt={"QR Code for #{@location.name}"} alt={"AprilTag for #{@location.name}"}
class="w-16 h-16 border border-gray-200 rounded bg-white" class="w-16 h-auto border border-gray-200 rounded bg-white"
onerror="this.style.display='none'" onerror="this.style.display='none'"
/> />
</div> </div>
@@ -308,10 +366,10 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div> </div>
<% end %> <% end %>
<button <button
phx-click="download_qr" phx-click="download_apriltag"
phx-value-id={@location.id} phx-value-id={@location.id}
class="inline-flex items-center px-3 py-1.5 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 flex-shrink-0" class="inline-flex items-center px-3 py-1.5 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 flex-shrink-0"
title="Download QR Code" title="Download AprilTag"
> >
<.icon name="hero-arrow-down-tray" class="w-4 h-4 mr-1.5" /> <.icon name="hero-arrow-down-tray" class="w-4 h-4 mr-1.5" />
Download Download
@@ -427,6 +485,76 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<.input field={@form[:description]} type="textarea" /> <.input field={@form[:description]} type="textarea" />
</div> </div>
<div>
<label class="block text-sm font-medium text-gray-700">AprilTag ID (Optional)</label>
<div class="space-y-2">
<div class="flex items-center space-x-2">
<input
type="radio"
name="apriltag_assignment"
value="none"
id="apriltag_none"
checked={@apriltag_mode == "none"}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
phx-click="set_apriltag_mode"
phx-value-mode="none"
/>
<label for="apriltag_none" class="text-sm text-gray-700">
No AprilTag assignment
</label>
</div>
<div class="flex items-center space-x-2">
<input
type="radio"
name="apriltag_assignment"
value="auto"
id="apriltag_auto"
checked={@apriltag_mode == "auto"}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
phx-click="set_apriltag_mode"
phx-value-mode="auto"
/>
<label for="apriltag_auto" class="text-sm text-gray-700">
Auto-assign next available ID
</label>
</div>
<div class="flex items-center space-x-2">
<input
type="radio"
name="apriltag_assignment"
value="manual"
id="apriltag_manual"
checked={@apriltag_mode == "manual"}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
phx-click="set_apriltag_mode"
phx-value-mode="manual"
/>
<label for="apriltag_manual" class="text-sm text-gray-700">
Choose specific ID
</label>
</div>
<%= if @apriltag_mode == "manual" do %>
<div class="ml-6 space-y-2">
<.input
field={@form[:apriltag_id]}
type="number"
min="0"
max="586"
placeholder="Enter ID (0-586)"
class="w-32"
/>
<div class="text-xs text-gray-500">
Available IDs: <%= length(@available_apriltag_ids) %> of 587
<%= if length(@available_apriltag_ids) < 20 do %>
<br/>Next available: <%= @available_apriltag_ids |> Enum.take(10) |> Enum.join(", ") %>
<%= if length(@available_apriltag_ids) > 10, do: "..." %>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
<div class="flex justify-end space-x-3 pt-4"> <div class="flex justify-end space-x-3 pt-4">
<button <button
type="button" type="button"
@@ -483,6 +611,75 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<.input field={@form[:description]} type="textarea" /> <.input field={@form[:description]} type="textarea" />
</div> </div>
<div>
<label class="block text-sm font-medium text-gray-700">AprilTag ID</label>
<div class="space-y-2">
<div class="flex items-center space-x-2">
<input
type="radio"
name="edit_apriltag_assignment"
value="keep"
id="edit_apriltag_keep"
checked={@edit_apriltag_mode == "keep"}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
phx-click="set_edit_apriltag_mode"
phx-value-mode="keep"
/>
<label for="edit_apriltag_keep" class="text-sm text-gray-700">
Keep current assignment
</label>
</div>
<div class="flex items-center space-x-2">
<input
type="radio"
name="edit_apriltag_assignment"
value="change"
id="edit_apriltag_change"
checked={@edit_apriltag_mode == "change"}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
phx-click="set_edit_apriltag_mode"
phx-value-mode="change"
/>
<label for="edit_apriltag_change" class="text-sm text-gray-700">
Change to different ID
</label>
</div>
<div class="flex items-center space-x-2">
<input
type="radio"
name="edit_apriltag_assignment"
value="remove"
id="edit_apriltag_remove"
checked={@edit_apriltag_mode == "remove"}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
phx-click="set_edit_apriltag_mode"
phx-value-mode="remove"
/>
<label for="edit_apriltag_remove" class="text-sm text-gray-700">
Remove AprilTag assignment
</label>
</div>
<%= if @edit_apriltag_mode == "change" do %>
<div class="ml-6 space-y-2">
<.input
field={@form[:apriltag_id]}
type="number"
min="0"
max="586"
placeholder="Enter new ID (0-586)"
class="w-32"
/>
<div class="text-xs text-gray-500">
Available IDs: <%= length(@available_apriltag_ids) %> of 587
</div>
</div>
<% end %>
<p class="text-xs text-gray-500 mt-1">
Current: <%= if @editing_location.apriltag_id, do: "ID #{@editing_location.apriltag_id}", else: "None" %>
</p>
</div>
</div>
<div class="flex justify-end space-x-3 pt-4"> <div class="flex justify-end space-x-3 pt-4">
<button <button
type="button" type="button"
@@ -504,43 +701,43 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div> </div>
<% end %> <% end %>
<!-- QR Scanner Modal --> <!-- AprilTag Scanner Modal -->
<%= if @qr_scanner_open do %> <%= if @apriltag_scanner_open do %>
<div class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"> <div class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-10 mx-auto p-5 border w-11/12 md:w-1/2 shadow-lg rounded-md bg-white"> <div class="relative top-10 mx-auto p-5 border w-11/12 md:w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3"> <div class="mt-3">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">QR Code Scanner</h3> <h3 class="text-lg font-medium text-gray-900">AprilTag Scanner</h3>
<button <button
phx-click="close_qr_scanner" phx-click="close_apriltag_scanner"
class="text-gray-400 hover:text-gray-600" class="text-gray-400 hover:text-gray-600"
> >
<.icon name="hero-x-mark" class="w-6 h-6" /> <.icon name="hero-x-mark" class="w-6 h-6" />
</button> </button>
</div> </div>
<!-- QR Scanner Interface --> <!-- AprilTag Scanner Interface -->
<div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center"> <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
<.icon name="hero-qr-code" class="mx-auto h-12 w-12 text-gray-400" /> <.icon name="hero-qr-code" class="mx-auto h-12 w-12 text-gray-400" />
<p class="mt-2 text-sm text-gray-600">Camera QR scanner would go here</p> <p class="mt-2 text-sm text-gray-600">Camera AprilTag scanner would go here</p>
<p class="text-xs text-gray-500 mt-1">In a real implementation, this would use JavaScript QR scanning</p> <p class="text-xs text-gray-500 mt-1">In a real implementation, this would use JavaScript AprilTag detection</p>
<!-- Test buttons for demo --> <!-- Test buttons for demo -->
<div class="mt-4 space-y-2"> <div class="mt-4 space-y-2">
<p class="text-sm font-medium text-gray-700">Test with sample codes:</p> <p class="text-sm font-medium text-gray-700">Test with sample AprilTag IDs:</p>
<button <button
phx-click="qr_scanned" phx-click="apriltag_scanned"
phx-value-code="SL:0:1MTKDM:ROOT" phx-value-apriltag_id="0"
class="block w-full px-3 py-2 text-sm bg-gray-100 hover:bg-gray-200 rounded" class="block w-full px-3 py-2 text-sm bg-gray-100 hover:bg-gray-200 rounded"
> >
Scan "Shelf A" Scan AprilTag ID 0
</button> </button>
<button <button
phx-click="qr_scanned" phx-click="apriltag_scanned"
phx-value-code="SL:1:VDI701:1MTKDM" phx-value-apriltag_id="1"
class="block w-full px-3 py-2 text-sm bg-gray-100 hover:bg-gray-200 rounded" class="block w-full px-3 py-2 text-sm bg-gray-100 hover:bg-gray-200 rounded"
> >
Scan "Drawer 1" Scan AprilTag ID 1
</button> </button>
</div> </div>
</div> </div>
@@ -549,8 +746,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div> </div>
<% end %> <% end %>
<!-- Scanned Codes Display --> <!-- Scanned Tags Display -->
<%= if length(@scanned_codes) > 0 do %> <%= if length(@scanned_tags) > 0 do %>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div class="bg-green-50 border border-green-200 rounded-lg p-4"> <div class="bg-green-50 border border-green-200 rounded-lg p-4">
<div class="flex justify-between items-center mb-2"> <div class="flex justify-between items-center mb-2">
@@ -563,10 +760,10 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</button> </button>
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<div :for={scan <- @scanned_codes} class="flex items-center justify-between bg-white p-2 rounded border"> <div :for={scan <- @scanned_tags} class="flex items-center justify-between bg-white p-2 rounded border">
<div> <div>
<span class="font-medium text-gray-900">{location_display_name(scan.location)}</span> <span class="font-medium text-gray-900">{location_display_name(scan.location)}</span>
<span class="text-sm text-gray-600 ml-2">({scan.code})</span> <span class="text-sm text-gray-600 ml-2">(AprilTag ID {scan.apriltag_id})</span>
</div> </div>
<span class="text-xs text-green-600 bg-green-100 px-2 py-1 rounded"> <span class="text-xs text-green-600 bg-green-100 px-2 py-1 rounded">
Level {scan.location.level} Level {scan.location.level}
@@ -582,7 +779,7 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<div class="bg-white shadow overflow-hidden sm:rounded-md"> <div class="bg-white shadow overflow-hidden sm:rounded-md">
<div class="px-6 py-4 border-b border-gray-200"> <div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">Storage Location Hierarchy</h2> <h2 class="text-lg font-medium text-gray-900">Storage Location Hierarchy</h2>
<p class="text-sm text-gray-500 mt-1">Manage your physical storage locations and QR codes</p> <p class="text-sm text-gray-500 mt-1">Manage your physical storage locations and AprilTags</p>
</div> </div>
<%= if Enum.empty?(@storage_locations) do %> <%= if Enum.empty?(@storage_locations) do %>

View File

@@ -0,0 +1,57 @@
defmodule Mix.Tasks.Apriltag.GenerateAll do
@moduledoc """
Generates all 587 AprilTag tag36h11 SVG files for the storage system.
## Examples
mix apriltag.generate_all
mix apriltag.generate_all --force
Options:
* `--force` - Regenerate all files even if they already exist
"""
use Mix.Task
@shortdoc "Generate all AprilTag SVG files"
def run(args) do
{opts, [], []} = OptionParser.parse(args, strict: [force: :boolean])
force_regenerate = Keyword.get(opts, :force, false)
Mix.Task.run("app.start")
IO.puts("Generating AprilTag SVG files...")
IO.puts("Force regenerate: #{force_regenerate}")
start_time = System.monotonic_time(:millisecond)
result = ComponentsElixir.AprilTag.generate_all_apriltag_svgs(
force_regenerate: force_regenerate
)
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
IO.puts("Generation completed in #{duration}ms")
IO.puts("Total: #{result.total}")
IO.puts("Success: #{result.success}")
IO.puts("Errors: #{result.errors}")
if result.errors > 0 do
IO.puts("\nErrors encountered:")
result.results
|> Enum.filter(&match?({:error, _, _}, &1))
|> Enum.each(fn {:error, id, reason} ->
IO.puts(" AprilTag ID #{id}: #{inspect(reason)}")
end)
end
if result.success == result.total do
IO.puts("\n✅ All AprilTag SVG files generated successfully!")
IO.puts("Files are available in: priv/static/apriltags/")
else
IO.puts("\n⚠️ Some files failed to generate. Check the errors above.")
System.halt(1)
end
end
end

View File

@@ -0,0 +1,35 @@
defmodule ComponentsElixir.Repo.Migrations.MigrateQrToApriltag do
use Ecto.Migration
def up do
# Rename qr_code column to qr_code_old for backup
rename table(:storage_locations), :qr_code, to: :qr_code_old
# Add new apriltag_id column as integer
alter table(:storage_locations) do
add :apriltag_id, :integer
end
# Create unique index for apriltag_id
create unique_index(:storage_locations, [:apriltag_id])
# Add constraint to ensure apriltag_id is in valid range (0-586 for tag36h11)
create constraint(:storage_locations, :apriltag_id_range, check: "apriltag_id >= 0 AND apriltag_id <= 586")
# Note: We keep qr_code_old for now in case we need to rollback
# It can be removed in a future migration after confirming everything works
end
def down do
# Remove apriltag_id column and constraints
drop constraint(:storage_locations, :apriltag_id_range)
drop unique_index(:storage_locations, [:apriltag_id])
alter table(:storage_locations) do
remove :apriltag_id
end
# Rename back to qr_code
rename table(:storage_locations), :qr_code_old, to: :qr_code
end
end

View File

@@ -0,0 +1,18 @@
defmodule ComponentsElixir.Repo.Migrations.DropQrCodeOldColumn do
use Ecto.Migration
def up do
# Drop the qr_code_old column since we've fully migrated to AprilTags
alter table(:storage_locations) do
remove :qr_code_old
end
end
def down do
# If we need to rollback, re-add the qr_code_old column
# Note: This will not restore the original data
alter table(:storage_locations) do
add :qr_code_old, :string, null: false, default: ""
end
end
end

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 000
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 001
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 002
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 003
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 004
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 005
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 006
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 007
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 008
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 009
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 010
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 011
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 012
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 013
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 014
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 015
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 016
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 017
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 018
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 019
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 020
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 021
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 022
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 023
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 024
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 025
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 026
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 027
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 028
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 029
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 030
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 031
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 032
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 033
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 034
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 035
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 036
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 037
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 038
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 039
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 040
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 041
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 042
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 043
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 044
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 045
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 046
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 047
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 048
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 049
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 050
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 051
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 052
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 053
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 054
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 055
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 056
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 057
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 058
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 059
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 060
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 061
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 062
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 063
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 064
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 065
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 066
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 067
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 068
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 069
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 070
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 071
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 072
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 073
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 074
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 075
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 076
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 077
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 078
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 079
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 080
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 081
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 082
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 083
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 084
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 085
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -0,0 +1,20 @@
<svg width="200" height="230" xmlns="http://www.w3.org/2000/svg">
<!-- White background -->
<rect width="200" height="230" fill="white"/>
<!-- AprilTag placeholder (simplified) -->
<rect x="20" y="20" width="160" height="160"
fill="white" stroke="black" stroke-width="2"/>
<!-- Simplified tag pattern - in reality this would be the actual AprilTag -->
<rect x="30" y="30" width="140" height="140"
fill="black"/>
<rect x="40" y="40" width="120" height="120"
fill="white"/>
<!-- ID text below -->
<text x="100.0" y="220" text-anchor="middle"
font-family="Arial" font-size="14" font-weight="bold">
ID: 086
</text>
</svg>

After

Width:  |  Height:  |  Size: 687 B

Some files were not shown because too many files have changed in this diff Show More