===============================================================================
 SpaceDragon Robot Programmer  -  version 1.0
 Editable Qt project - read this first
===============================================================================

 Visual, block-based movement programming for servo robot arms, with automatic
 Arduino code generation.

 Included robot: Dragon-Arm Demo - 3 axes plus a gripper, MG996R servos on a
 PCA9685 PWM expander, driven by an Arduino over I2C.

 This archive contains the complete, editable source of the application and
 nothing else. There is no personal data, no build output and no machine-
 specific path anywhere in it.


-------------------------------------------------------------------------------
 1. WHAT YOU NEED
-------------------------------------------------------------------------------

 To EDIT the app:
   - Qt Design Studio 4.8 or newer (Community Edition is fine, it is free)
     https://www.qt.io/download-qt-installer
   - Nothing else. The app is pure QML. There is no C++ to compile and no
     compiler needed.

 To BUILD a distributable .exe (optional, see section 7):
   - A Qt 6.5 or newer desktop kit (6.8 / 6.11 both tested)
   - CMake 3.21+, Ninja, and a C++17 compiler (the MinGW that ships with the
     Qt installer works)

 To RUN the generated code on hardware:
   - Arduino IDE
   - Library "Adafruit PWM Servo Driver" (Tools > Manage Libraries)


-------------------------------------------------------------------------------
 2. OPENING AND RUNNING
-------------------------------------------------------------------------------

 In Qt Design Studio:
   File > Open Project  ->  DragonArmStudio.qmlproject
   Press the green Run button (or Alt+Shift+R).

 From a command line (fastest edit-run loop, no IDE):
   <Qt>\6.x.y\mingw_64\bin\qml.exe -I imports content\App.qml

 The "-I imports" matters: it puts the DragonArm module on the QML import
 path. Without it the app will not start. The .qmlproject file already
 declares the same path, which is why Design Studio does not need the flag.

 IMPORTANT ABOUT DESIGN STUDIO:
 The files are plain .qml, not .ui.qml. Design Studio's visual (drag-and-drop)
 editor only fully supports .ui.qml files, but those forbid JavaScript function
 bodies - and most of this app needs them. So: edit in the Code view. The 2D
 preview still works. This is a deliberate choice, not an oversight.


-------------------------------------------------------------------------------
 3. FOLDER LAYOUT
-------------------------------------------------------------------------------

 DragonArmStudio.qmlproject   The project file. Open this one.
 qtquickcontrols2.conf        Control style pinning.

 imports/DragonArm/           ALL state and ALL logic lives here.
   qmldir                     Declares the four singletons.
   Theme.qml                  Colours, spacing, fonts.          (singleton)
   RobotLibrary.qml           Robot catalogue + selected model. (singleton)
   MovementPlan.qml           The timeline. THE document.       (singleton)
   AppState.qml               Navigation, selection, zoom, drag.(singleton)
   RobotModels.js             Hardware catalogue - pure data.
   PlanMath.js                Blocks -> per-joint motion segments.
   Validator.js               Checks a plan against a robot model.
   ArduinoGenerator.js        Emits the Arduino sketch.

 content/                     ALL the user interface.
   App.qml                    The window. Entry point.
   MainShell.qml              Sidebar + workspace, drag proxy, robot dialog.
   common/                    Reusable widgets (buttons, sliders, panels).
   nav/                       Sidebar and the robot model / hardware dialog.
   home/                      Home page and the model carousel.
   blocks/                    Block palette, workspace, inspector, timeline.
   code/                      Generated-code viewer and syntax highlighter.
   images/                    Drop artwork here - see images/README.md.

 packaging/                   Only needed to build a release .exe.
                              Ignore it while editing the app.

 CLAUDE.md                    Deeper architecture notes. Also read
                              automatically by the Claude Code AI assistant if
                              you use it on this project.


-------------------------------------------------------------------------------
 4. HOW IT IS PUT TOGETHER
-------------------------------------------------------------------------------

 One-way dependency, never the other way round:

     user interface  ->  singletons  ->  plain JavaScript logic

 The JavaScript layer (RobotModels / PlanMath / Validator / ArduinoGenerator)
 never imports a Qt type. That is what makes it testable and what lets the
 same code both validate a plan and generate the sketch from it.

 Responsibilities are kept apart on purpose:

   Robot hardware description ....... RobotModels.js + RobotLibrary.qml
   Timeline and execution timing .... MovementPlan.qml
   Trajectory maths ................. PlanMath.js
   Constraint validation ............ Validator.js
   Arduino output ................... ArduinoGenerator.js
   View state ....................... AppState.qml
   Everything visual ................ content/

 If you find yourself putting hardware knowledge into a .qml file in content/,
 or UI knowledge into a .js file, something has gone in the wrong place.


-------------------------------------------------------------------------------
 5. THE RULES THAT MATTER
-------------------------------------------------------------------------------

 Break these and the app will produce code that moves a real machine in ways
 nobody checked. They are worth reading before changing anything.

 1) ANGLES ARE ABSOLUTE.
    "Axis-1 | 60 deg | 6 s" means "travel to servo position 60", never
    "rotate 60 further". A block's starting angle is derived - either the
    joint's start angle or the target of the previous block on that joint.
    Dragging a block along the timeline must never change its target angle.

 2) THE TIMELINE IS THE SOURCE OF TRUTH.
    The block list, the validation results and the generated sketch are all
    derived from MovementPlan.blocks. Nothing keeps a second copy.

 3) CONSTRAINTS ARE CHECKED ALONG THE WHOLE TRAJECTORY, NOT AT THE ENDS.
    Two blocks that are each legal on their own can still sweep the arm
    through an illegal pose while both servos are travelling. Validator.js
    samples every joint's commanded position every 20 ms across the entire
    plan. Checking only the target angles is wrong and will let real
    collisions through.

 4) NEVER SILENTLY CHANGE THE USER'S TARGET ANGLE.
    Out-of-range values are reported and the blocks are highlighted. They are
    not quietly clamped into legality.

 5) NO delay() IN THE GENERATED CODE, ANYWHERE.
    The sketch is entirely millis()-driven so servos move simultaneously and
    independently. Even the start-up pause is a non-blocking check.

 6) THE MOTION CURVE MUST MATCH IN BOTH PLACES.
    PlanMath.applyProfile() and the generated shapeProgress() describe the
    same easing. If they drift apart, the validator is checking a path the arm
    does not actually follow. Change one, change the other.

 7) NEVER NAME A GENERATED CONSTANT PCA9685_something.
    The Adafruit library header defines PCA9685_I2C_ADDRESS, PCA9685_MODE1 and
    about thirty more as preprocessor MACROS. A constant with a matching name
    gets textually replaced and the sketch fails to compile. Generated names
    use PWM_DRIVER_*, SERVO_*, MOVE_*. The struct is ServoMove, not Move.

 8) COLOURS COME FROM Theme.
    No hex colour literals in content/, with two deliberate exceptions: the
    syntax-highlighting palette in code/CodeEditorView.qml and the per-axis
    block accents in RobotModels.js. Both are data, not styling.

 9) EVERY EDIT GOES THROUGH MovementPlan.
    It re-validates, bumps "revision" and emits planChanged() - which is what
    marks the generated sketch out of date. UI code never has to remember to
    do any of that.


-------------------------------------------------------------------------------
 6. COMMON EDITS
-------------------------------------------------------------------------------

 ADD A NEW ROBOT ARM
   Add a factory function in imports/DragonArm/RobotModels.js and list it in
   allModels(). It then appears in the sidebar picker, the home carousel and
   the block palette on its own. Nothing in content/ needs touching.

 ADD AN AXIS TO AN EXISTING ARM
   Add one entry to that model's "joints" array. The palette, the timeline
   lane, the hardware page and the generated enum all follow automatically.

 CHANGE SERVO WIRING OR CALIBRATION
   No code needed - use the Robot button at the bottom of the sidebar. PCA9685
   channel, I2C address, PWM frequency, per-servo pulse widths, mechanical
   limits, start angle and mirrored mounting are all editable there. To change
   the shipped defaults, edit RobotModels.js.

 ADD OR CHANGE A MECHANICAL CONSTRAINT
   Add an entry to the model's "constraints" array, then handle its "type" in
   Validator.validate(). Two worked examples are already there:
     angleDifference - a band on |a - b|   (the 40-140 deg Axis-2/Axis-3 rule)
     angleOrder      - a <= b              (Axis-2 must stay below Axis-3)
   Both are thin wrappers around scanViolationWindows(), which does the sweep
   and groups violations into time windows. A new rule usually needs only a
   one-line test returning how far outside the legal region a pose is.

 CHANGE THE LOOK
   Everything is in imports/DragonArm/Theme.qml - colours, spacing, fonts.
   Change it in one place and the whole app follows.

 ADD ARTWORK
   Drop files into content/images/ and read the README.md there. Every
   placeholder in the running app prints the exact path it is waiting for.

 CHANGE THE WELCOME TEXT
   content/home/HomeModule.qml, the TextArea near the bottom.


-------------------------------------------------------------------------------
 7. BUILDING A DISTRIBUTABLE .EXE
-------------------------------------------------------------------------------

 Only needed if you want to ship the app to someone who does not have Qt.
 Editing and running it does NOT require any of this.

 The packaging/ folder builds the QML into a normal Windows executable. The
 whole app is embedded inside the binary as Qt resources, so a release is one
 .exe plus the Qt runtime DLLs, with no loose QML files to break.

   cmake -S packaging -B build -G Ninja ^
         -DCMAKE_BUILD_TYPE=Release ^
         -DCMAKE_PREFIX_PATH="<Qt>/6.x.y/mingw_64"
   cmake --build build

 Then make it self-contained:

   windeployqt --release --no-translations --compiler-runtime ^
               --qmldir . build\SpaceDragon.exe

 The qmldir flag matters: because the QML is compiled into the binary,
 windeployqt cannot find the imports by scanning the .exe alone.

 You can then delete the qmltooling folder - it is the QML debug server and is
 not needed in a release.

 No Qt path is hard-coded in packaging/CMakeLists.txt, so this builds on any
 machine with a suitable kit. After adding new .qml files, re-run cmake so the
 resource list picks them up.

 BUILDING THE WINDOWS INSTALLER

 packaging/installer.iss builds the setup .exe. You need Inno Setup 6, which
 is free: https://jrsoftware.org/isdl.php

   ISCC.exe /DDeployDir="<the windeployqt folder from above>" ^
            /DOutDir="<where to put the setup exe>" ^
            /DAppVersion=1.0 ^
            packaging\installer.iss

 All paths are passed in, so the .iss file itself stays machine-independent.

 The installer defaults to a per-user install, so downloading and running it
 needs no administrator rights. Users can still choose an all-users install
 on the first page. Keep the AppId GUID in installer.iss unchanged forever -
 it is how Windows recognises a new version as an upgrade instead of
 installing a second copy alongside the old one.

 NOTE ON SMARTSCREEN: the setup .exe is not code-signed, so Windows will show
 an "unknown publisher" warning the first time people download it. That is
 normal for unsigned software. The only real fix is buying a code-signing
 certificate.


-------------------------------------------------------------------------------
 8. THE GENERATED ARDUINO CODE
-------------------------------------------------------------------------------

 Open the Code page in the app, press Convert, then Copy code and paste it
 into the Arduino IDE. Install the "Adafruit PWM Servo Driver" library first.

 The sketch:
   - initialises the PCA9685 over I2C at the configured address and frequency
   - converts absolute angles to pulse widths using each servo's own
     calibration, respecting mirrored mounting and mechanical limits
   - stores the timeline as a table of
       (servo, startMs, durationMs, fromAngle, toAngle) rows
   - interpolates every active row on a millis() tick and writes at most one
     angle per servo per tick, so servos move at the same time and none waits
     for another to finish
   - eases each movement (smoothstep) so travels start and stop gently
   - re-sends the holding position periodically, so a single dropped I2C write
     cannot leave a servo parked at a stale position

 Startup behaviour is configurable in the app:
   Homing OFF (default) - no channel is driven until a servo's first movement,
     so the arm keeps whatever pose it powered up in.
   Homing ON - every servo is driven to its start angle first.
   Start delay (default 1000 ms) - non-blocking pause before the timeline runs.

 The per-servo "start angle" is what the planner validates against even when
 homing is off. An open-loop servo cannot report where it actually is, so the
 plan needs a stated reference pose.

 IF THE SERVOS BUZZ OR THE TRAVEL RANGE IS WRONG:
 Check PWM_DRIVER_OSCILLATOR_HZ. The PCA9685 datasheet says 25 MHz but real
 boards commonly measure 26-27 MHz, and every pulse width scales with it. The
 default here is 27000000. Measure your board's actual output frequency and
 correct the value in the robot model. This is the single most common cause of
 jitter and of a servo not reaching its commanded angle.

 Note that roughly 0.44 degrees per step is a hard limit at 50 Hz (4096 PWM
 ticks over a 20000 microsecond period), so very slow travels will always look
 slightly stepped. That is the hardware, not the software.


-------------------------------------------------------------------------------
 9. IF SOMETHING GOES WRONG
-------------------------------------------------------------------------------

 App will not start from the command line
   You forgot -I imports.

 "DragonArm is not installed" or singletons are undefined
   The imports/DragonArm/qmldir file is missing or was not copied. It is what
   declares the four singletons.

 Sketch will not compile: "expected unqualified-id before numeric constant"
   A generated constant collides with a macro in the Adafruit header. See
   rule 7 in section 5.

 Changes to a .qml file do not show up in a release build
   Release builds embed the QML in the binary. Rebuild, and re-run cmake if
   you added new files.

 Validation rejects a plan that looks fine
   Check the middle of the movement, not the ends. Two axes travelling at once
   can cross through an illegal pose - that is exactly what the validator is
   for. The message names the time window and the angles involved.


-------------------------------------------------------------------------------
 10. CREDITS AND LICENCE
-------------------------------------------------------------------------------

 SpaceDragon Robot Programmer - beta.
 Built with Qt 6 (QML / Qt Quick) and Claude Code.

 Qt is used under the LGPLv3. If you redistribute a built application you must
 comply with the LGPL - in practice: ship the Qt libraries as separate DLLs
 (which the build above already does), state that Qt is used and under which
 licence, and allow users to replace the Qt DLLs. See https://www.qt.io/licensing

 The Adafruit PWM Servo Driver library is BSD licensed and is NOT included
 here - the generated sketch only depends on it. Install it from the Arduino
 Library Manager.

 Set your own licence for this project's own source below:

   <your licence here>

===============================================================================
