3D Fractals

Developer(s): Ted Knyszek
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Demo, Graphics

This program generates and renders a fractal terrain landscape using a midpoint displacement algorithm, converted from an Apple II original to run on the TS-2068. The user specifies a detail level from 1 to 6, which controls the recursion depth; the terrain heights are stored in a 65×33 array `D()` and built up iteratively across X, Y, and diagonal passes. Three rotation subroutines apply horizontal (azimuth) and vertical (elevation) transformations using precomputed sine and cosine values before projecting to screen coordinates. The display loop renders the surface as three sets of isometric line strips using `PLOT`/`DRAW`, with a sea-level clipping routine that interpolates the shoreline crossing point when a line segment transitions between above and below zero height. Scaling factors `XS`, `YS`, and `ZS` are each set to 0.04, and the screen projection applies a fixed perspective correction in the `XP`/`YP` calculation at line 1030.


Program Analysis

Program Structure

The program is organized into clearly delineated phases: initialization, terrain generation, and display. Control flows from the input and setup block (lines 15–52), through the iterative fractal subdivision loop (lines 60–120), then jumps to the display routines starting at line 640. A final GO TO 1130 at line 750 targets a non-existent line, terminating execution after display — a recognized termination idiom. Line 1150 contains a STOP that is never reached.

Terrain Generation — Midpoint Displacement

The fractal surface is built in a 65×33 array D() (line 20), sized for up to 6 levels: DS = 2^LE + 1, giving a maximum of 65 columns. For each level N (line 60), the roughness scale L = 10000 / 1.8^N decreases geometrically, producing finer detail at each pass. Three subroutines perform the midpoint displacement:

  • Lines 150–200: interpolates heights along the X direction.
  • Lines 220–270: interpolates heights along the Y direction.
  • Lines 290–350: interpolates heights along the diagonal.

Each subroutine computes a new height as the average of two neighbors plus a random offset: (D1+D2)/2 + (RND-0.5)*L/2. The step size SK = IB*2 and interval IB = MX/2^N halve each level, refining the mesh.

Array Access with Symmetry Folding

The read subroutine (lines 370–400) and write subroutine (lines 420–450) apply a symmetry fold when J > MY: the indices are remapped as BY = MX+1-J, BX = MX-I. This exploits the triangular structure of the terrain grid, effectively mirroring the second half of the array rather than storing it separately. Array indices are offset by +1 throughout to avoid the zero-based boundary (D(BX+1, BY+1)).

3D Projection Pipeline

Before plotting, each point passes through a multi-stage transformation pipeline:

  1. Sea-level clipping (line 470, subroutine): detects sign changes in ZZ and interpolates the shoreline crossing using parameter W3 = ZZ/(ZZ-Z2).
  2. Scaling (line 950): multiplies XX, YY, ZZ by XS=YS=ZS=0.04.
  3. Horizontal rotation (lines 770–800): applies azimuth rotation using precomputed RC, RS (cosine/sine of PI/6).
  4. Vertical rotation (lines 860–890): applies elevation rotation using VC, VS (cosine/sine of -PI/5).
  5. Screen projection (lines 1030–1050): maps to pixel coordinates with XP = XP*0.55+5 and YP = 175-(24-0.7*YP), then draws with PLOT/DRAW.

Sea-Level and Flag Logic

The sea-level subroutine (lines 470–620) tracks whether the current segment is above or below zero height using a flag variable F1. F1=1 (set by line 1070) suppresses drawing; F1=0 (set by line 1090) re-enables it. The sentinel value XO=-999 signals a pen-up state (start of a new strip), analogous to a “move” vs. “draw” distinction. The clipping interpolation correctly handles both transitions: entering water (line 560) and exiting water (line 590).

Display Loops

Three nested loops at lines 660–740 sweep the terrain in three directions to draw isometric wireframe strips:

  • Lines 660–680: strips along diagonal slices with J from 0 to I.
  • Lines 690–710: strips along rows with I from J to MX.
  • Lines 720–740: strips along the diagonal grain using G and H.

Each strip resets XO=-999 at the start to lift the pen. The isometric world coordinates use XX = I/MX*10000 - YY/2 to apply a horizontal shear consistent with a 30° isometric projection.

Notable Anomalies

  • The REM comment at line 210 reads “HEIGHTS ALONG X” but actually governs the Y-direction pass (lines 220–270); line 140 correctly labels the X pass.
  • Subroutine at line 1110 is an empty stub (just RETURN), presumably a placeholder for screen setup such as clearing or mode switching.
  • Lines 760 and 850 each contain a bare REM with no text, used as spacers between subroutines.
  • The bounds check at line 1045 (Y8>174 OR Y8<0 OR YP>174 OR YP<0) guards against off-screen PLOT/DRAW calls, preventing errors for extreme height values.

Content

Appears On

Related Products

Related Articles

Program to generate fractal landscapes.

Related Content

Image Gallery

Source Code

    5 REM Fractals
    6 REM T. A. Knyszek
    7 REM T-S Horizons n17 p19
   10 REM Converted from Apple II to TS-2068 by Ted Knyszek
   15 BORDER 1: PAPER 5: INK 0: PRINT : CLS 
   20 DIM D(65,33)
   30 INPUT "NUMBER OF LEVELS (1-6)";LE
   40 LET DS=2^LE+1
   50 LET MX=DS-1: LET MY=MX/2: LET RH=PI/6: LET VT=-PI/5
   51 LET RC=COS (RH): LET RS=SIN (RH)
   52 LET VC=COS (VT): LET VS=SIN (VT)
   60 FOR N=1 TO LE: LET L=10000/1.8^N
   70 PRINT #1;AT 0,0;"WORKING ON LEVEL ";N
   80 LET IB=MX/2^N: LET SK=IB*2
   90 GO SUB 150: REM ASSIGN HEIGHTS ALONG X IN ARRAY
  100 GO SUB 220: REM ASSIGN HEIGHTS ALONG Y IN ARRAY
  110 GO SUB 290: REM ASSIGN HEIGHTS ALONG DIAG IN ARRAY
  120 NEXT N
  130 GO TO 640
  140 REM HEIGHTS ALONG X
  150 FOR T=0 TO MX-1 STEP SK
  160 FOR K=IB+T TO MX STEP SK
  170 LET I=K-IB: LET J=T: GO SUB 370: LET D1=D: LET I=K+IB: GO SUB 370: LET D2=D
  180 LET D=(D1+D2)/2+(RND-.5)*L/2: LET I=K: LET J=T: GO SUB 420
  190 NEXT K
  200 NEXT T: RETURN 
  210 REM HEIGHTS ALONG X
  220 FOR K=MX TO 1 STEP -SK
  230 FOR T=IB TO K STEP SK
  240 LET I=K: LET J=T+IB: GO SUB 370: LET D1=D: LET J=T-IB: GO SUB 370: LET D2=D
  250 LET D=(D1+D2)/2+(RND-.5)*L/2: LET I=K: LET J=T: GO SUB 420
  260 NEXT T
  270 NEXT K: RETURN 
  280 REM HEIGHTS ALONG DIAG
  290 FOR K=0 TO MX-1 STEP SK
  300 FOR T=IB TO MX-K STEP SK
  310 LET I=K+T-IB: LET J=T-IB: GO SUB 370: LET D1=D
  320 LET I=K+T+IB: LET J=T+IB: GO SUB 370: LET D2=D
  330 LET I=K+T: LET J=T: LET D=(D1+D2)/2+(RND-.5)*L/2: GO SUB 420
  340 NEXT T
  350 NEXT K: RETURN 
  360 REM RETURN DATA FROM ARRAY
  370 IF J>MY THEN GO TO 390
  380 LET BY=J: LET BX=I: GO TO 400
  390 LET BY=MX+1-J: LET BX=MX-I
  400 LET D=D(BX+1,BY+1): RETURN 
  410 REM PUT DATA IN ARRAY
  420 IF J>MY THEN GO TO 440
  430 LET BY=J: LET BX=I: GO TO 450
  440 LET BY=MX+1-J: LET BX=MX-I
  450 LET D(BX+1,BY+1)=D: RETURN 
  460 REM PUT IN SEA LEVEL HERE
  470 IF XO<>-999 THEN GO TO 500
  480 IF ZZ<0 THEN GO SUB 1070: LET Z2=ZZ: LET ZZ=0: GO TO 620
  490 GO SUB 1090: GO TO 610
  500 IF Z2>0 AND ZZ>0 THEN GO TO 610
  510 IF Z2<0 AND ZZ<0 THEN LET Z2=ZZ: LET ZZ=0: GO TO 620
  520 LET W3=ZZ/(ZZ-Z2): LET X3=(X2-XX)*W3+XX: LET Y3=(Y2-YY)*W3+YY: LET Z3=0
  530 LET ZT=ZZ: LET YT=YY: LET XT=XX
  540 IF ZZ>0 THEN GO TO 590
  550 REM GOING INTO WATER
  560 LET ZZ=Z3: LET YY=Y3: LET XX=X3: GO SUB 950
  570 GO SUB 1070: LET ZZ=0: LET YY=YT: LET XX=XT: LET Z2=ZT: GO TO 620
  580 REM COMING OUT OF WATER
  590 LET ZZ=Z3: LET YY=Y3: LET XX=X3: GO SUB 950
  600 GO SUB 1090: LET ZZ=ZT: LET YY=YT: LET XX=XT
  610 LET Z2=ZZ
  620 LET X2=XX: LET Y2=YY: RETURN 
  630 REM DISPLAY HERE
  640 GO SUB 1110: REM SET UP PLOTTING DEVICE ON SCREEN
  650 LET XS=.04: LET YS=.04: LET ZS=.04: REM SCALING FACTORS
  660 FOR I=0 TO MX: LET XO=-999: FOR J=0 TO I
  670 GO SUB 370: LET ZZ=D: LET YY=J/MX*10000: LET XX=I/MX*10000-YY/2
  680 GO SUB 940: NEXT J: NEXT I
  690 FOR J=0 TO MX: LET XO=-999: FOR I=J TO MX
  700 GO SUB 370: LET ZZ=D: LET YY=J/MX*10000: LET XX=I/MX*10000-YY/2
  710 GO SUB 940: NEXT I: NEXT J
  720 FOR G=0 TO MX: LET XO=-999: FOR H=0 TO MX-G
  730 LET I=G+H: LET J=H: GO SUB 370: LET ZZ=D: LET YY=J/MX*10000
  740 LET XX=I/MX*10000-YY/2: GO SUB 940: NEXT H: NEXT G
  750 GO TO 1130: REM 
  760 REM 
  770 LET OX=XX
  780 LET XX=XX*RC-YY*RS
  790 LET YY=OX*RS+YY*RC
  800 RETURN 
  850 REM 
  860 LET OX=XX
  870 LET XX=VC*XX-VS*ZZ
  880 LET ZZ=VS*OX+VC*ZZ
  890 RETURN 
  930 REM MOVE OR PLOT
  940 GO SUB 470
  950 LET XX=XX*XS: LET YY=YY*YS: LET ZZ=ZZ*ZS
  960 GO SUB 770
  970 GO SUB 860
  990 LET XP=INT (YY)+1: LET YP=INT (ZZ)
 1000 GO SUB 1030
 1010 RETURN 
 1020 REM 
 1030 LET XP=XP*0.55+5: LET YP=175-(24-0.7*YP)
 1040 IF XO=-999 OR F1=1 THEN LET X8=XP: LET Y8=YP: LET XO=XP
 1045 IF Y8>174 OR Y8<0 OR YP>174 OR YP<0 THEN RETURN 
 1050 PLOT X8,Y8: DRAW (XP-X8),(YP-Y8): LET X8=XP: LET Y8=YP: RETURN 
 1060 REM 
 1070 LET F1=1: RETURN 
 1080 REM 
 1090 LET F1=0: RETURN 
 1110 RETURN 
 1150 STOP 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top