Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,76 @@ Dev log: https://youtu.be/dDn2rafvjMQ?si=mm1Sz-XrjvNQiRWE
- Core strategy workflow is coordinated by `lib/providers/strategy_provider.dart`.
- UI is composed from screens in `lib/` and shared components under `lib/widgets/`.

## Position Handling (Detailed)
Icarus stores object positions in a normalized coordinate system so layouts stay consistent across window sizes, zoom levels, screenshots, and saved files.

### 1) Coordinate spaces
- **Screen space (pixels):** actual rendered location on the current canvas.
- **World space (normalized):** persisted positions for all placed objects.

World-space dimensions are defined in `lib/const/coordinate_system.dart`:
- `normalizedHeight = 1000`
- `worldAspectRatio = 16/9`
- `worldNormalizedWidth = normalizedHeight * worldAspectRatio` (about `1777.78`)

Core transforms:
- `screenToCoordinate`: converts drag/drop pixel offsets into normalized world positions.
- `coordinateToScreen`: converts stored normalized positions back to on-screen pixels.

This is why one saved strategy can be reopened at different resolutions without manual re-alignment.

### 2) Drag/drop and update pipeline
For agents, abilities, utilities, text, images, and lineup placement:
1. UI drag/drop handlers get a global pointer position.
2. Position is converted to local widget coordinates (`globalToLocal`).
3. Local position is converted to normalized world space (`screenToCoordinate`).
4. Provider updates the relevant `Placed*` model with `updatePosition`.

Key references:
- Placement and drag-end conversion: `lib/widgets/draggable_widgets/placed_widget_builder.dart`
- Provider updates: `lib/providers/*_provider.dart`
- Base position/history model: `lib/const/placed_classes.dart`

### 3) Zoom-aware dragging
The map can be scaled (`Transform.scale`). To keep drag behavior correct while zoomed:
- `screen_zoom_provider.dart` adjusts drag anchor strategy and offsets.
- Feedback widgets are wrapped in `ZoomTransform`.
Comment on lines +60 to +62
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README states that zoom is implemented via Transform.scale, but the main map zoom/pan is driven by InteractiveViewer + a TransformationController (lib/interactive_map.dart). Transform.scale is used for drag feedback in ZoomTransform, not for the primary viewport scaling. Please update this section to reference InteractiveViewer for map zoom, and (optionally) mention ZoomTransform/ScreenZoomProvider as the mechanism that keeps draggable feedback/anchors consistent with the current InteractiveViewer scale.

Suggested change
The map can be scaled (`Transform.scale`). To keep drag behavior correct while zoomed:
- `screen_zoom_provider.dart` adjusts drag anchor strategy and offsets.
- Feedback widgets are wrapped in `ZoomTransform`.
The map viewport is zoomed and panned via Flutter's `InteractiveViewer` + a `TransformationController` (see `lib/interactive_map.dart`). To keep drag behavior correct while zoomed:
- `screen_zoom_provider.dart` tracks the current zoom and adjusts drag anchor strategy and offsets.
- Feedback widgets are wrapped in `ZoomTransform`, which applies the current zoom from `ScreenZoomProvider` so feedback and anchors stay aligned with the `InteractiveViewer` scale.

Copilot uses AI. Check for mistakes.

Without this, dropped positions would drift when zoom is not `1.0`.

### 4) Bounds checks and safe anchors
Position validity is checked in normalized space via `CoordinateSystem.isOutOfBounds(...)` with a small tolerance.

Different object types use different anchor/safe-area rules:
- **Agents:** center point check using configured agent size.
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bullet says agents use the configured agent size for the center-point bounds check, but the current implementation computes the center using strategySettingsProvider.abilitySize (see AgentProvider.updatePosition). Please either adjust the README to match the implementation, or update the code to use agentSize and keep this documentation as-is.

Suggested change
- **Agents:** center point check using configured agent size.
- **Agents:** center point check using the current ability size from `strategySettingsProvider.abilitySize`.

Copilot uses AI. Check for mistakes.
- **Abilities:** uses ability-specific anchor (`getAnchorPoint(...)`) and map scale.
- **Non-view-cone utilities, text, and images:** use safe-area/anchor approximations before deciding whether to remove an out-of-bounds object.
- **View-cone utility:** still updates in normalized space, but uses a rotation/length interaction model and currently follows different removal behavior.

### 5) Side switching (attack/defense mirror)
When switching sides, placed content is mirrored across both axes in normalized space.

General flip logic (top-left anchored widgets):
- `x' = worldNormalizedWidth - x - widgetWidthNormalized`
- `y' = normalizedHeight - y - widgetHeightNormalized`

Implementation:
- `getFlippedPosition(...)` in `lib/const/placed_classes.dart`
- Called from each model/provider switch function (`agent`, `ability`, `utility`, `text`, `image`, `lineup`)

For rotatable widgets, the Y calculation applies an extra compensation to keep perceived position stable after mirroring. Rotatable abilities/utilities also add `pi` to rotation so direction stays visually correct.

### 6) Undo/redo and persistence
- Every position change records a `PositionAction` in per-widget history stacks.
- Undo/redo replays stored normalized positions, not screen pixels.
- Positions serialize as `{dx, dy}` via `OffsetConverter` and are stored in Hive/JSON.

Because flips and migration update both current positions and action history stacks, undo/redo remains coherent after side switches and older data migrations.

### 7) Migration compatibility
`StrategyProvider.migrateToWorld16x9(...)` shifts older saved data into the current world-space layout (including agents, abilities, utilities, text, images, drawings, and lineup positions).
This preserves relative placement from pre-16:9-world saves.

## Requirements
- Flutter SDK (Dart >= 3.4.3)

Expand Down