--- title: "Plot demo" id: 55868 type: "computer_media" slug: "plot-demo" url: "http://localhost/computer_media/plot-demo/" markdown_url: "http://localhost/computer_media/plot-demo.md" published_at: "2024-07-06T17:27:01+00:00" modified_at: "2026-03-30T21:44:44+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240706-nxlq.png" excerpt: "A hidden-line 3D surface plotter with optional crosshatching, ported from a BBS download, uses a horizon-array technique to suppress depth-obscured lines." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Ted Knyszek" slug: "ted-knyszek" taxonomy: "indiv" url: "http://localhost/indiv/ted-knyszek/" genre: - name: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" media_contents: - id: 55879 title: "ISTUG Public Domain Library 7" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-7/" media_type: "Program" programmers: - name: "Ted Knyszek" slug: "ted-knyszek" taxonomy: "indiv" url: "http://localhost/indiv/ted-knyszek/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Plot%20demo%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240706-nxlq.png" media_type_tags: "Demo" --- This program renders a 3D perspective surface plot of the function Z = SIN(Y/F)·(X−Y)²/150 using a hidden-line algorithm on a 256×176 pixel display. It projects the X–Y domain onto the screen using isometric-style angle parameters PHI and PSI, maintaining a horizon array H(280) that tracks the topmost plotted screen Y coordinate for each pixel column to suppress hidden lines. The user can optionally enable crosshatching, which adds a second rendering pass with lines running in the perpendicular grid direction. Originally written for the Apple II and converted to the TS-2068, the program was downloaded from the BYTE Magazine BBS. The main plot loop uses DRAW commands between adjacent horizon points rather than individual PLOTs, producing connected line segments for smooth curve rendering. *** ## Program Analysis ### Program Structure The program is organized into four logical sections accessed via `GO TO` and `GO SUB`: 1. **Lines 10–200:** Subroutine — plots X-direction scan lines with hidden-line removal. 2. **Lines 210–390:** Subroutine — plots Y-direction scan lines (crosshatch pass). 3. **Lines 400–510:** Initialization — sets up all parameters, arrays, and constants. 4. **Lines 520–690:** Main program — handles user input, optional axes, border, and the rendering loop. Execution begins at line 10, which immediately jumps to `VAL "410"` (the initialization block), then the main loop calls the two subroutines as needed. ### Hidden-Line Algorithm The core technique is a floating horizon (scan-line) hidden-surface algorithm. A one-dimensional array `H(280)` stores the minimum screen Y value ever plotted for each screen X column. Before plotting any point, the program checks whether the candidate screen Y (`YB`) is less than the stored horizon value `H(XB)`; if so, it updates the horizon and eventually draws the visible segment. This ensures that parts of the surface farther from the viewer (lower on screen in this projection) are suppressed once a nearer surface feature has been drawn at that column. The horizon array is re-initialized to 189 at the start of each rendering pass (line 630), which is safely above the 176-line display height, allowing all first-encountered points through. ### 3D Projection The oblique projection uses two angles, `PHI` (0.5 radians) and `PSI` (0.4 radians), pre-computed into four constants at line 500: | Variable | Meaning | | --- | --- | | `CF` | COS PHI — X-axis screen X scaling | | `SF` | SIN PHI — X-axis screen Y scaling | | `CP` | COS PSI — Y-axis screen X scaling | | `SP` | SIN PSI — Y-axis screen Y scaling | Screen coordinates are computed as `XB = XO + X*CF − Y*CP` and `YB = YO − X*SF − Y*SP − Z`, giving an isometric-style oblique view with the origin at `(100, 170)`. ### The Plotted Function The surface function is defined using a DEF FN at line 10: `FN Y(X) = SIN(Y/F) * (X−Y)² / 150`. Note that this function uses both the formal parameter `X` and the global variable `Y` — the outer loop variable — making it context-dependent rather than a pure function. `F` is set to 10 at line 480, controlling the frequency of the sinusoidal component. The result is clipped to a maximum of `H` (the computed horizon cutoff at line 510) to prevent drawing above a defined ceiling. ### Rendering Passes and Crosshatching The variable `CH` controls how many passes the loop at lines 620–670 executes. Without crosshatching `CH=1` and only the X-direction subroutine (line 20) is called. With crosshatching `CH=2`, both subroutines run. Each pass independently resets the horizon array, so the second pass (Y-direction lines) does not inherit hidden-line state from the first. The step size `T` (initialized to 5 at line 460) controls the spacing between drawn grid lines in each pass. Only every `T`-th line is drawn; intermediate lines only update the horizon array to maintain correct hidden-line suppression. ### Drawing Method Rather than plotting individual pixels, the final drawing loops (lines 180–200 and 370–390) use `PLOT` followed by `DRAW` between adjacent horizon entries. For each column `K` in the visible range, a `DRAW` connects `(K, 175−H(K))` to `(K+1, 175−H(K+1))`, producing a continuous polyline. The coordinate flip `175−H(K)` converts from the internal top-down convention to the display’s bottom-up Y axis. ### Key BASIC Idioms and Techniques - `GO TO VAL "410"` at line 10 — a memory-saving idiom that avoids storing the line number as an integer literal in the BASIC token stream. - `DEF FN` using a global loop variable (`Y`) alongside its formal parameter — an intentional coupling that allows the function to vary along both grid axes. - `PRINT #0;"WORKING"` (line 640) — prints to the lower status line area during computation to indicate progress without disturbing the graphics area. - Pre-computing trigonometric values into `CF`, `SF`, `CP`, `SP` at initialization avoids repeated SIN/COS calls in the inner loops. - The border rectangle is drawn at line 610 using a chain of `DRAW` commands from a single `PLOT`, efficiently outlining the 256×176 screen. ### Notable Anomalies - Line 390 and 200 use lowercase `k` and `x` in `NEXT k: NEXT x`. In Sinclair BASIC, variable names are case-insensitive in practice on this platform; these match the uppercase loop variables and function correctly. - The domain bounds mix origins: `XL=0`, `XR=YO=170`, `YL=XL=0`, `YR=XO=100`, meaning the plotted surface spans a 170×100 unit grid — an asymmetric domain that widens the X extent relative to Y. - The horizon array is dimensioned as `H(280)` (indices 1–280) but screen X spans 0–255. The extra headroom prevents out-of-bounds errors from rounding in the projection for large coordinate values near the domain edges. ## Source Code ``` 10 CLS : DEF FN Y(X)=SIN (Y/F)*(X-Y)*(X-Y)/150: GO TO VAL "410" 20 REM *PLOTTING 30 LET Y=YL 40 FOR X=XL TO XR 50 LET XB=INT (XO+X*CF-Y*CP+.5) 60 LET Z=FN Y(X): IF Z>H THEN LET Z=H 70 LET YB=INT (YO-X*SF-Y*SP-Z+.5) 80 IF YBH THEN LET Z=H 150 LET YB=INT (V-Y*SP-Z+.5) 160 IF YBH THEN LET Z=H 260 LET YB=INT (YO-X*SF-Y*SP-Z+.5) 270 IF YBH THEN LET Z=H 340 LET YB=INT (V-X*SF-Z+.5) 350 IF YB