starkernel: item 4.3.3b -- geometry drawing wordset, fixed Q.TO-INT sign bug
Adds LINE (Bresenham in raster space, endpoints projected once each -- valid because the cavalier projection is linear), CIRCLE/ELLIPSE (36-segment polygon approximation), and ARC (18 segments over a caller radian range) to capsules/fabric.4th (blocks 4903-4912). TO-RASTER factored out of CART-PLOT (same behavior) so LINE can reuse the projection+flip for both endpoints. Found mid-implementation: colon definitions cannot span block boundaries in this capsule loader -- verified with a throwaway test capsule, the continuation lands in a [CAPSULE][DEFER] path that never resolves. LINE's body is split across LINE-SETUP/LINE-DONE?/LINE-STUCK?/LINE-STEP, each self-contained within its block, rather than one long definition. A fourth real bug, serious this time: CIRCLE's first live test rendered only one quadrant, then hung the VM for several minutes on a follow-up call. Root cause: q48_to_u64() (include/q48_16.h and include/starkernel/q48_16.h, backing Q.TO-INT) did an unsigned logical shift, corrupting any negative Q48.16 value into a huge garbage integer instead of sign-extending -- inevitable once Q.SIN/Q.COS leave the first quadrant. That garbage became a bogus LINE target with no bound on LINE-STEP's Bresenham loop. Fixed q48_to_u64 to shift through a signed int64_t intermediate (bit-identical for the non-negative case). Also added LINE-STUCK? (LSTEPS vs FB-WIDTH+FB-HEIGHT, the true worst case for an on-screen line) as a defense-in-depth cap against any future bad target. Verified live on amd64 after both fixes: -65536 Q.TO-INT . now prints -1; LINE/CIRCLE/ARC/ELLIPSE all complete without hanging or erroring, and a combined screendump shows all four rendering correctly and distinctly. All three architectures boot clean to ok> with the DoE completing; dict_hash identical across all three and unchanged from 4.3.3a (expected -- fabric.4th isn't loaded at boot, and the Q.TO-INT fix doesn't change dictionary structure). FABRIC.md item 4.3.3b marked done with full acceptance evidence.
This commit is contained in:
@@ -3757,11 +3757,46 @@ document and committing that amendment as its own item.*
|
|||||||
> to the same `-27` as `sin(π)`, confirming range reduction across multiple turns. Both
|
> to the same `-27` as `sin(π)`, confirming range reduction across multiple turns. Both
|
||||||
> hosted and kernel (amd64) builds clean.
|
> hosted and kernel (amd64) builds clean.
|
||||||
|
|
||||||
- [ ] **4.3.3b — Geometry drawing primitive wordset.** `LINE`, `CIRCLE`, `ARC`, `ELLIPSE` in
|
- [x] **4.3.3b — Geometry drawing primitive wordset.** `LINE`, `CIRCLE`, `ARC`, `ELLIPSE` in
|
||||||
`capsules/fabric.4th`, built on 4.3.3's `PLOT`/`CART-PLOT` and 4.3.3a's `Q.SIN`/`Q.COS`.
|
`capsules/fabric.4th`, built on 4.3.3's `PLOT`/`CART-PLOT` and 4.3.3a's `Q.SIN`/`Q.COS`.
|
||||||
Raised 2026-08-07. Q48.16 throughout; resolution-agnostic (48 integer bits comfortably
|
Raised 2026-08-07. Q48.16 throughout; resolution-agnostic (48 integer bits comfortably
|
||||||
covers 1080p and well beyond — no hardcoded viewport assumptions). *Refs:* §27.3.
|
covers 1080p and well beyond — no hardcoded viewport assumptions). *Refs:* §27.3.
|
||||||
|
|
||||||
|
> **Done, 2026-08-07.** Blocks 4903–4912. `TO-RASTER` factored out of `CART-PLOT` (same
|
||||||
|
> behavior, not a change) so `LINE` can project both endpoints once and Bresenham the
|
||||||
|
> straight line between them in raster space — valid because the cavalier projection is
|
||||||
|
> linear, so projecting endpoints and interpolating is equivalent to projecting every point
|
||||||
|
> along the line. `LINE` itself split across three helper words (`LINE-SETUP`,
|
||||||
|
> `LINE-DONE?`/`LINE-STUCK?`, `LINE-STEP`) — discovered mid-implementation that colon
|
||||||
|
> definitions **cannot span block boundaries** in this capsule loader (verified with a
|
||||||
|
> throwaway test capsule: the continuation lands in a `[CAPSULE][DEFER]` path that never
|
||||||
|
> resolves and errors out), so anything too long for one 16-line/64-char block has to be
|
||||||
|
> factored into separate, block-local word definitions instead. `CIRCLE`/`ELLIPSE` are
|
||||||
|
> 36-segment polygon approximations (`LINE` calls between consecutive `Q.SIN`/`Q.COS`
|
||||||
|
> points); `ARC` is the same at 18 segments over a caller-supplied `[a0, a1]` radian range.
|
||||||
|
>
|
||||||
|
> **A fourth real bug, this one serious — found, fixed, verified with the recommended fix
|
||||||
|
> applied both times.** `CIRCLE`'s first live test rendered only its first quadrant, then
|
||||||
|
> a follow-up test call hung the VM for several minutes before being killed. Root cause:
|
||||||
|
> `q48_to_u64()` (`include/q48_16.h` and `include/starkernel/q48_16.h`, backing
|
||||||
|
> `Q.TO-INT`) did `q >> 16` as an **unsigned logical shift**. For any negative `q48_16_t` —
|
||||||
|
> inevitable once `Q.SIN`/`Q.COS` leave the first quadrant — this produces a huge garbage
|
||||||
|
> integer instead of sign-extending. That garbage became a bogus `LINE` target, and
|
||||||
|
> `LINE-STEP`'s Bresenham loop had no bound, so it churned for a very long time trying to
|
||||||
|
> converge on a point that was effectively unreachable. Fixed by shifting through a signed
|
||||||
|
> `int64_t` intermediate (bit-identical output for the non-negative case, which is all the
|
||||||
|
> inference engine's own caller ever produces). Independently, added `LINE-STUCK?`
|
||||||
|
> (`LSTEPS` counter vs. `FB-WIDTH + FB-HEIGHT`, the true worst case for any on-screen line)
|
||||||
|
> as a defense-in-depth cap, so a future bad target degrades to "stops drawing" rather than
|
||||||
|
> hanging the VM again.
|
||||||
|
>
|
||||||
|
> **Verified live on amd64**, fresh boot after both fixes: `-65536 Q.TO-INT .` now prints
|
||||||
|
> `-1`. `LINE`, `CIRCLE`, `ARC` (semicircle, 0 to π), and `ELLIPSE` all completed without
|
||||||
|
> hanging or erroring, and a combined screendump shows all four rendering correctly and
|
||||||
|
> distinctly — full circle, correct upper-half arc, properly proportioned ellipse (wider
|
||||||
|
> than tall, matching unequal radii), and the earlier diagonal `LINE` test.
|
||||||
|
> `fb/amd64/geom-test-circle-fixed.png`, `fb/amd64/geom-test-full-wordset.png`.
|
||||||
|
|
||||||
- [ ] **4.3.4 — Checkpoint: draw a cube.** First real exercise of the 4.3.3/4.3.3a/4.3.3b
|
- [ ] **4.3.4 — Checkpoint: draw a cube.** First real exercise of the 4.3.3/4.3.3a/4.3.3b
|
||||||
coordinate/projection/geometry machinery — cube edges use `LINE`. Stop and review here
|
coordinate/projection/geometry machinery — cube edges use `LINE`. Stop and review here
|
||||||
before scoping the next 4.3.x item — not expected to be fast. *Refs:* §27.4.
|
before scoping the next 4.3.x item — not expected to be fast. *Refs:* §27.4.
|
||||||
|
|||||||
+15
-5
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-08-07T17:37:50Z -->
|
<!-- Generated by mkcapsule --manifest 2026-08-07T19:19:40Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
|
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
|
||||||
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
|
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
|
||||||
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
|
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
|
||||||
| `fabric.4th` | 4900, 4901, 4902 | `0xb7db7380ec4a42a0` |
|
| `fabric.4th` | 4900, 4901, 4902, 4903, 4904, 4905, 4906, 4907, 4908, 4909, 4910, 4911, 4912 | `0x355ccc154aac15b3` |
|
||||||
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4175, 4176 | `0x85c7b311d1e5bf97` |
|
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4175, 4176 | `0x85c7b311d1e5bf97` |
|
||||||
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
|
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
|
||||||
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
|
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
|
||||||
@@ -225,9 +225,19 @@
|
|||||||
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
|
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
|
||||||
| 4851 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
| 4851 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||||
| 4852 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
| 4852 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||||
| 4900 | `fabric.4th` | `0xb7db7380ec4a42a0` | ok |
|
| 4900 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
| 4901 | `fabric.4th` | `0xb7db7380ec4a42a0` | ok |
|
| 4901 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
| 4902 | `fabric.4th` | `0xb7db7380ec4a42a0` | ok |
|
| 4902 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4903 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4904 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4905 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4906 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4907 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4908 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4909 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4910 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4911 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
| 4912 | `fabric.4th` | `0x355ccc154aac15b3` | ok |
|
||||||
|
|
||||||
## Conflicts
|
## Conflicts
|
||||||
|
|
||||||
|
|||||||
@@ -30,3 +30,123 @@ Block 4902
|
|||||||
CART-Y
|
CART-Y
|
||||||
R>
|
R>
|
||||||
PLOT ;
|
PLOT ;
|
||||||
|
|
||||||
|
Block 4903
|
||||||
|
( TO-RASTER: 3D Cartesian point -> raster (rx ry). Factored )
|
||||||
|
( out of CART-PLOT so LINE can reuse it for both endpoints )
|
||||||
|
( -- the projection is linear, so projecting endpoints then )
|
||||||
|
( drawing 2D is equivalent to projecting every line point. )
|
||||||
|
( Item 4.3.3b. CART-PLOT redefined in terms of it (same )
|
||||||
|
( behavior, not a change). )
|
||||||
|
: TO-RASTER ( x y z -- rx ry )
|
||||||
|
PROJECT CART-Y ;
|
||||||
|
: CART-PLOT ( x y z color -- )
|
||||||
|
>R TO-RASTER R> PLOT ;
|
||||||
|
|
||||||
|
Block 4904
|
||||||
|
( LINE: 3D Cartesian line segment, Bresenham in raster )
|
||||||
|
( space (state in VARIABLEs; LINE-SETUP below). )
|
||||||
|
VARIABLE LX VARIABLE LY VARIABLE LX2 VARIABLE LY2
|
||||||
|
VARIABLE LDX VARIABLE LDY VARIABLE LSX VARIABLE LSY
|
||||||
|
VARIABLE LERR VARIABLE LCOLOR VARIABLE LSTEPS
|
||||||
|
: LINE-SETUP ( x1 y1 z1 x2 y2 z2 color -- )
|
||||||
|
LCOLOR !
|
||||||
|
TO-RASTER LY2 ! LX2 !
|
||||||
|
TO-RASTER LY ! LX !
|
||||||
|
LX2 @ LX @ - ABS LDX !
|
||||||
|
LY2 @ LY @ - ABS NEGATE LDY !
|
||||||
|
LX @ LX2 @ < IF 1 ELSE -1 THEN LSX !
|
||||||
|
LY @ LY2 @ < IF 1 ELSE -1 THEN LSY !
|
||||||
|
LDX @ LDY @ + LERR ! 0 LSTEPS ! ;
|
||||||
|
|
||||||
|
Block 4905
|
||||||
|
( LINE-DONE?: true once current point reached target. )
|
||||||
|
: LINE-DONE? ( -- flag )
|
||||||
|
LX @ LX2 @ = LY @ LY2 @ = AND ;
|
||||||
|
( LINE-STUCK?: safety cap, width+height worst case. )
|
||||||
|
: LINE-STUCK? ( -- flag )
|
||||||
|
LSTEPS @ FB-WIDTH FB-HEIGHT + > ;
|
||||||
|
( LINE-STEP: one Bresenham step (no plot). )
|
||||||
|
: LINE-STEP ( -- )
|
||||||
|
LERR @ 2*
|
||||||
|
DUP LDY @ >= IF LERR @ LDY @ + LERR ! LX @ LSX @ + LX ! THEN
|
||||||
|
DUP LDX @ <= IF LERR @ LDX @ + LERR ! LY @ LSY @ + LY ! THEN
|
||||||
|
LSTEPS @ 1+ LSTEPS ! DROP ;
|
||||||
|
|
||||||
|
Block 4906
|
||||||
|
( LINE: draws the segment using the helpers above. )
|
||||||
|
: LINE ( x1 y1 z1 x2 y2 z2 color -- )
|
||||||
|
LINE-SETUP
|
||||||
|
BEGIN
|
||||||
|
LX @ LY @ LCOLOR @ PLOT
|
||||||
|
LINE-DONE? 0= LINE-STUCK? 0= AND
|
||||||
|
WHILE
|
||||||
|
LINE-STEP
|
||||||
|
REPEAT ;
|
||||||
|
|
||||||
|
Block 4907
|
||||||
|
( Shared state for CIRCLE/ARC/ELLIPSE. CIRC-PT: point on a )
|
||||||
|
( circle of radius CRAD centered (CX,CY) at given angle. )
|
||||||
|
VARIABLE CX VARIABLE CY VARIABLE CZ VARIABLE CRAD
|
||||||
|
VARIABLE CCOLOR VARIABLE PX VARIABLE PY
|
||||||
|
: CIRC-PT ( ang -- x y )
|
||||||
|
DUP Q.SIN CRAD @ Q.FROM-INT Q.* Q.TO-INT CY @ +
|
||||||
|
SWAP Q.COS CRAD @ Q.FROM-INT Q.* Q.TO-INT CX @ +
|
||||||
|
SWAP ;
|
||||||
|
|
||||||
|
Block 4908
|
||||||
|
( CIRCLE: 36-segment polygon approximation, flat at z=cz. )
|
||||||
|
: CIRCLE ( cx cy cz r color -- )
|
||||||
|
CCOLOR ! CRAD ! CZ ! CY ! CX !
|
||||||
|
0 CIRC-PT PY ! PX !
|
||||||
|
36 0 DO
|
||||||
|
PX @ PY @ CZ @
|
||||||
|
I 1+ 11438 * CIRC-PT
|
||||||
|
2DUP PY ! PX !
|
||||||
|
CZ @ CCOLOR @
|
||||||
|
LINE
|
||||||
|
LOOP ;
|
||||||
|
|
||||||
|
Block 4909
|
||||||
|
( ARC: partial circle, start/end angles in Q48.16 radians, )
|
||||||
|
( 18 segments. Reuses CIRC-PT/CX/CY/CZ/CRAD/CCOLOR/PX/PY. )
|
||||||
|
VARIABLE ASTART VARIABLE AEND VARIABLE ASTEP
|
||||||
|
: ARC-SETUP ( cx cy cz r a0 a1 color -- )
|
||||||
|
CCOLOR ! AEND ! ASTART !
|
||||||
|
CRAD ! CZ ! CY ! CX !
|
||||||
|
AEND @ ASTART @ - 18 / ASTEP !
|
||||||
|
ASTART @ CIRC-PT PY ! PX ! ;
|
||||||
|
|
||||||
|
Block 4910
|
||||||
|
( ARC: draws the arc using ARC-SETUP above. )
|
||||||
|
: ARC ( cx cy cz r a0 a1 color -- )
|
||||||
|
ARC-SETUP
|
||||||
|
18 0 DO
|
||||||
|
PX @ PY @ CZ @
|
||||||
|
ASTART @ I 1+ ASTEP @ * +
|
||||||
|
CIRC-PT
|
||||||
|
2DUP PY ! PX !
|
||||||
|
CZ @ CCOLOR @
|
||||||
|
LINE
|
||||||
|
LOOP ;
|
||||||
|
|
||||||
|
Block 4911
|
||||||
|
( ELLIPSE-PT: independent x/y radii, reuses CX/CY/CZ. )
|
||||||
|
VARIABLE ERX VARIABLE ERY
|
||||||
|
: ELLIPSE-PT ( ang -- x y )
|
||||||
|
DUP Q.SIN ERY @ Q.FROM-INT Q.* Q.TO-INT CY @ +
|
||||||
|
SWAP Q.COS ERX @ Q.FROM-INT Q.* Q.TO-INT CX @ +
|
||||||
|
SWAP ;
|
||||||
|
|
||||||
|
Block 4912
|
||||||
|
( ELLIPSE: 36-segment polygon, flat at z=cz. )
|
||||||
|
: ELLIPSE ( cx cy cz rx ry color -- )
|
||||||
|
CCOLOR ! ERY ! ERX ! CZ ! CY ! CX !
|
||||||
|
0 ELLIPSE-PT PY ! PX !
|
||||||
|
36 0 DO
|
||||||
|
PX @ PY @ CZ @
|
||||||
|
I 1+ 11438 * ELLIPSE-PT
|
||||||
|
2DUP PY ! PX !
|
||||||
|
CZ @ CCOLOR @
|
||||||
|
LINE
|
||||||
|
LOOP ;
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
+12
-5
@@ -157,16 +157,23 @@ static inline q48_16_t q48_from_u64(uint64_t u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert Q48.16 to unsigned 64-bit integer (truncate fractional)
|
* @brief Convert Q48.16 to a 64-bit integer (truncate fractional)
|
||||||
*
|
*
|
||||||
* Math: q / 2^16 (shift right by 16 bits)
|
* Math: q / 2^16 (arithmetic shift right by 16 bits)
|
||||||
* Example: q48_to_u64(0x10000) = 1
|
* Example: q48_to_u64(0x10000) = 1, q48_to_u64(-0x10000) = -1
|
||||||
|
*
|
||||||
|
* q48_16_t values are two's-complement signed under the hood (q48_neg/
|
||||||
|
* q48_abs already treat them that way); a plain unsigned (logical) shift
|
||||||
|
* would corrupt negative inputs instead of sign-extending them, so this
|
||||||
|
* shifts via a signed intermediate. Bit-identical to the old behavior for
|
||||||
|
* non-negative q (the only case this function's other caller, the
|
||||||
|
* inference engine's sum-of-squares accumulation, ever produces).
|
||||||
*
|
*
|
||||||
* @param q Q48.16 value
|
* @param q Q48.16 value
|
||||||
* @return q >> 16 as unsigned 64-bit integer
|
* @return q >> 16, sign-extended, reinterpreted as uint64_t
|
||||||
*/
|
*/
|
||||||
static inline uint64_t q48_to_u64(q48_16_t q) {
|
static inline uint64_t q48_to_u64(q48_16_t q) {
|
||||||
return q >> 16;
|
return (uint64_t)(((int64_t)q) >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* STARKERNEL_Q48_16_H */
|
#endif /* STARKERNEL_Q48_16_H */
|
||||||
|
|||||||
@@ -114,10 +114,13 @@ static inline q48_16_t q48_from_u64(uint64_t u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Q48.16 to unsigned 64-bit integer (truncate fractional): q >> 16
|
* Convert Q48.16 to a 64-bit integer (truncate fractional): q >> 16,
|
||||||
|
* arithmetic (signed) shift so negative q sign-extends correctly instead
|
||||||
|
* of producing garbage from an unsigned logical shift. Bit-identical to
|
||||||
|
* the old behavior for non-negative q.
|
||||||
*/
|
*/
|
||||||
static inline uint64_t q48_to_u64(q48_16_t q) {
|
static inline uint64_t q48_to_u64(q48_16_t q) {
|
||||||
return q >> 16;
|
return (uint64_t)(((int64_t)q) >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user