' PATH EDITOR ' ' AN EDITOR FOR ANIMATION PATHS. A PATH IS ' A SEQUENCE OF CUBIC BEZIER SPLINES. ' ' WRITTEN ENTIRELY ON AN IPHONE! ' ' CODE & GRAPHICS COPYRIGHT (C) 2019 NAT PRYCE ' LICENSE: CC BY-NC-SA 4.0 ' TODO: ' - ONION SKINNING PREV / NEXT PATH ' - MOVE MULTIPLE / ALL ANCHORS ' - GRID SNAPPING ' - ZOOMING ' - LIMIT HANDLES TO BE WITHIN VALID COORDS ' WHEN DRAGGING GLOBAL TRUE, FALSE FALSE = 0 TRUE = NOT FALSE GLOBAL NONE NONE = -1 '-------------------------------------------- ' PATH API ' ' MAINTAINS THE ROM FORMAT IN WORKING RAM GLOBAL PATH_SOURCE PATH_SOURCE = NONE ' COUNT DUPLICATED IN A VARIABLE FOR ' CONVENIENCE GLOBAL PATH_COUNT GLOBAL _PATH_DATA_SIZE _PATH_DATA_SIZE = 0 GLOBAL PATH_COORD_OFFSET PATH_COORD_OFFSET = 32 SUB PATH_SET_SOURCE(ADDR) PATH_SOURCE = ADDR PATH_COUNT = PEEK(PATH_SOURCE) IF PATH_COUNT = 0 THEN _PATH_DATA_SIZE = 1 ELSE LAST = PATH_COUNT-1 LAST_OFFSET = NONE CALL _PATH_OFFSET(LAST, LAST_OFFSET) LAST_SZ = PEEK(LAST_OFFSET) _PATH_DATA_SIZE = LAST_OFFSET+LAST_SZ END IF END SUB SUB _PATH_SET_COUNT(N) POKE PATH_SOURCE, N PATH_COUNT = N END SUB SUB PATH_CLEAR_ALL CALL _PATH_SET_COUNT(0) _PATH_DATA_SIZE = 1 END SUB SUB _PATH_OFFSET(P, RESULT) IADDR = PATH_SOURCE + 1 + 2*P RESULT = 256*PEEK(IADDR)+PEEK(IADDR+1) END SUB SUB _PATH_SET_OFFSET(P, OFFSET) IADDR = PATH_SOURCE + 1 + 2*P POKE IADDR, OFFSET \ 256 POKE IADDR+1, OFFSET MOD 256 END SUB SUB _PATH_REINDEX ' SPACE FOR THE INDEX OFFSET = 1 + 2*PATH_COUNT P = 0 WHILE P < PATH_COUNT CALL _PATH_SET_OFFSET(P, OFFSET) PLEN = PEEK(PATH_SOURCE+OFFSET) OFFSET = OFFSET + 1 + 4*(PLEN+1) P = P + 1 WEND _PATH_DATA_SIZE = OFFSET END SUB SUB PATH_LEN(P, RESULT) OFFSET = NONE CALL _PATH_OFFSET(P, OFFSET) RESULT = PEEK(PATH_SOURCE+OFFSET) END SUB SUB _PATH_SET_LEN(P, PLEN) OFFSET = NONE CALL _PATH_OFFSET(P, OFFSET) POKE PATH_SOURCE+OFFSET, PLEN END SUB SUB _PATH_MOD_LEN(P, DELTA) OFFSET = NONE CALL _PATH_OFFSET(P, OFFSET) ADDR = PATH_SOURCE+OFFSET POKE ADDR, PEEK(ADDR)+DELTA END SUB SUB _PATH_GET_COORD(OFFSET, RESULT) ADDR = PATH_SOURCE + OFFSET RESULT = PEEK(ADDR) - PATH_COORD_OFFSET END SUB SUB _PATH_SET_COORD(OFFSET, COORD) ADDR = PATH_SOURCE + OFFSET POKE ADDR, COORD + PATH_COORD_OFFSET END SUB SUB _PATH_ANCHOR_OFFSET(P, A, AOFFSET) POFFSET = NONE CALL _PATH_OFFSET(P, POFFSET) AOFFSET = POFFSET + 1 + A*4 END SUB SUB PATH_ADD(P, X0,Y0, X1,Y1, X2,Y2, X3,Y3) ' ADD A NEW PATH WITH A SINGLE SEGMENT ' AFTER PATH P ' ' IF PATH_COUNT = 0, P MUST BE NONE (-1) ' ' X0,Y0 AND X3,Y3 ARE THE ANCHOR POINTS ' X1,Y1 AND X2,Y2 ARE THE CONTROL POINTS IF PATH_COUNT = 0 THEN Q = 0 QOFFSET = 3 ELSE Q = P+1 POFFSET = NONE CALL _PATH_OFFSET(P, POFFSET) PLEN = NONE CALL PATH_LEN(P, PLEN) PBYTES = 1 + 4*(PLEN+1) QOFFSET = POFFSET + PBYTES ' MAKE ROOM FOR NEW PATH AT Q AND NEW ' INDEX ENTRY START = PATH_SOURCE + QOFFSET NBYTES = _PATH_DATA_SIZE - QOFFSET COPY START, NBYTES TO START+11 ' MOVE PATHS BEFORE Q TO MAKE ROOM FOR ' NEW INDEX ENTRY P0OFFSET = 1 + 2*PATH_COUNT START = PATH_SOURCE + P0OFFSET NBYTES = QOFFSET - P0OFFSET COPY START, NBYTES TO START+2 QOFFSET = QOFFSET + 2 END IF CALL _PATH_SET_OFFSET(Q, QOFFSET) ' INITIALISE THE NEW PATH CALL _PATH_SET_LEN(Q, 1) CALL PATH_SET_ANCHOR(Q, 0, X0, Y0) CALL PATH_SET_CONTROL(Q, 0, 1, X1, Y1) CALL PATH_SET_ANCHOR(Q, 1, X3, Y3) CALL PATH_SET_CONTROL(Q, 1, -1, X2, Y2) ' RECREATE THE INDEX NOW THE MEMORY ' SHUFFLING IS DONE CALL _PATH_SET_COUNT(PATH_COUNT+1) CALL _PATH_REINDEX END SUB SUB PATH_DELETE(P) PLEN = NONE POFFSET = NONE CALL PATH_LEN(P, PLEN) CALL _PATH_OFFSET(P, POFFSET) PBYTES = 1 + 4*(PLEN+1) ' MOVE PATHS BEFORE BACK 2 BYTES TO REMOVE ' INDEX ENTRY P0OFFSET = NONE CALL _PATH_OFFSET(0, P0OFFSET) SRC = PATH_SOURCE+P0OFFSET NBYTES = POFFSET - P0OFFSET COPY SRC, NBYTES TO SRC-2 ' MOVE PATHS AFTER BACK BY THE SIZE OF THE ' PATH + 2 BYTES SRC = PATH_SOURCE+POFFSET+PLEN NBYTES = _PATH_DATA_SIZE - SRC COPY SRC, NBYTES TO SRC-(PLEN+2) ' RECREATE THE INDEX CALL _PATH_SET_COUNT(PATH_COUNT-1) CALL _PATH_REINDEX END SUB ' INLINES THE RECURSIVE DEFINITION OF ' DE CASTELJAU'S ALGORITHM FOR THE ' CASE OF CUBIC SPLINES. ' ' PRE: ' B() HAS SIZE OF AT LEAST 3 ' C() HAD SIZE OF AT LEAST 2 ' POST: ' D CONTAINS THE INTERPOLATED VALUE ' B() & C() CONTAIN INTERMEDIATE RESULTS ' (USEFUL FOR SPLITTING CURVES) ' SUB DECASTELJAU(V(), T, B(), C(), D) U = 1 - T B(0) = U*V(0) + T*V(1) B(1) = U*V(1) + T*V(2) B(2) = U*V(2) + T*V(3) C(0) = U*B(0) + T*B(1) C(1) = U*B(1) + T*B(2) D = U*C(0) + T*C(1) END SUB SUB PATH_POS(PATH, T, POSX, POSY) S = T \ 1 CALL PATH_SEGMENT_POS(PATH,S,T-S,POSX,POSY) END SUB SUB PATH_SEGMENT_POS(PATH, S, T, POSX, POSY) DIM X(4) DIM Y(4) CALL PATH_SEGMENT_POINTS(PATH, S, X(), Y()) CALL BEZIER_POS(X(), T, POSX) CALL BEZIER_POS(Y(), T, POSY) END SUB SUB BEZIER_POS(V(), T, VOUT) DIM B(2) DIM C(1) CALL DECASTELJAU(V(), T, B(), C(), VOUT) END SUB SUB PATH_SEGMENT_POINTS(P, S, X(), Y()) ' X() AND Y() MUST HAVE AT LEAST 4 ELEMENTS CALL PATH_ANCHOR(P, S, X(0), Y(0)) CALL PATH_CONTROL(P, S, 1, X(1), Y(1)) CALL PATH_CONTROL(P, S+1, -1, X(2), Y(2)) CALL PATH_ANCHOR(P, S+1, X(3), Y(3)) END SUB SUB PATH_ANCHOR(P, A, X, Y) AOFFSET = NONE CALL _PATH_ANCHOR_OFFSET(P, A, AOFFSET) CALL _PATH_GET_COORD(AOFFSET, X) CALL _PATH_GET_COORD(AOFFSET+1, Y) END SUB SUB PATH_SET_ANCHOR(P, A, X, Y) MINX = -PATH_COORD_OFFSET MAXX = 160+PATH_COORD_OFFSET-1 MINY = -PATH_COORD_OFFSET MAXY = 128+PATH_COORD_OFFSET+1 AX = X AY = Y CALL _PATH_CLAMP(AX, MINX, MAXX) CALL _PATH_CLAMP(AY, MINY, MAXY) AOFFSET = NONE CALL _PATH_ANCHOR_OFFSET(P, A, AOFFSET) CALL _PATH_SET_COORD(AOFFSET, AX) CALL _PATH_SET_COORD(AOFFSET+1, AY) END SUB SUB PATH_MOVE_ANCHOR(P, A, DX, DY) X = NONE Y = NONE CALL PATH_ANCHOR(P, A, X, Y) CALL PATH_SET_ANCHOR(P, A, X+DX, Y+DY) END SUB SUB PATH_CONTROL(P, A, SIDE, X, Y) ' P: PATH ' A: ANCHOR INDEX ' SIDE: -1 OR 1 ' INDICATES WHICH SIDE OF THE ANCHOR ' THE CONTROL POINT IS ' X: RETURNS THE X COORD ' Y: RETURNS THE Y COORD AOFFSET = NONE AX = NONE AY = NONE CX = NONE CY = NONE CALL _PATH_ANCHOR_OFFSET(P, A, AOFFSET) CALL _PATH_GET_COORD(AOFFSET, AX) CALL _PATH_GET_COORD(AOFFSET+1, AY) ' TANGENT VECTOR TX = PEEK(PATH_SOURCE+AOFFSET+2)-128 TY = PEEK(PATH_SOURCE+AOFFSET+3)-128 X = AX + SIDE*TX Y = AY + SIDE*TY END SUB SUB PATH_SET_CONTROL(P, A, SIDE, X, Y) AOFFSET = NONE AX = NONE AY = NONE CALL _PATH_ANCHOR_OFFSET(P, A, AOFFSET) CALL _PATH_GET_COORD(AOFFSET, AX) CALL _PATH_GET_COORD(AOFFSET+1, AY) ' TANGENT VECTOR TX = SIDE*(X - AX) TY = SIDE*(Y - AY) CALL _PATH_CLAMP(TX, -128, 127) CALL _PATH_CLAMP(TY, -128, 127) POKE PATH_SOURCE+AOFFSET+2, TX+128 POKE PATH_SOURCE+AOFFSET+3, TY+128 END SUB SUB PATH_MOVE_CONTROL(P, A, SIDE, DX, DY) X = NONE Y = NONE CALL PATH_CONTROL(P, A, SIDE, X, Y) CALL PATH_SET_CONTROL(P, A, SIDE, X+DX,Y+DY) END SUB SUB _PATH_DUP_SEGMENT(P, S) SOFFSET = NONE CALL _PATH_ANCHOR_OFFSET(P, S, SOFFSET) NBYTES = _PATH_DATA_SIZE - SOFFSET SRC = PATH_SOURCE + SOFFSET DST = SRC + 4 COPY SRC, NBYTES TO DST CALL _PATH_MOD_LEN(P, 1) CALL _PATH_REINDEX END SUB SUB PATH_SPLIT_SEGMENT(P, S, T) DIM X(4) DIM Y(4) DIM X1(3) DIM Y1(3) DIM X2(3) DIM Y2(3) CALL PATH_SEGMENT_POINTS(P, S, X(), Y()) CALL _PATH_SPLIT_COORDS(X(), T, X1(), X2()) CALL _PATH_SPLIT_COORDS(Y(), T, Y1(), Y2()) CALL _PATH_DUP_SEGMENT(P, S) CALL PATH_SET_CONTROL(P,S, 1, X1(1),Y1(1)) CALL PATH_SET_ANCHOR(P,S+1, X2(0),Y2(0)) CALL PATH_SET_CONTROL(P,S+1,1, X2(1),Y2(1)) CALL PATH_SET_CONTROL(P,S+2,-1,X2(2),Y2(2)) END SUB SUB _PATH_SPLIT_COORDS(A(), T, A1(), A2()) DIM B(2) DIM C(1) D = NONE CALL DECASTELJAU(A(), T, B(), C(), D) A1(0) = A(0) A1(1) = B(0) A1(2) = C(0) A1(3) = D A2(0) = D A2(1) = C(1) A2(2) = B(2) A2(3) = A(3) END SUB SUB PATH_DELETE_SEGMENT(P, S) AOFFSET = NONE CALL _PATH_ANCHOR_OFFSET(P, S, AOFFSET) AEND = AOFFSET+4 SRC = PATH_SOURCE+AEND NBYTES = _PATH_DATA_SIZE - AEND COPY SRC, NBYTES TO SRC-4 CALL _PATH_MOD_LEN(P, -1) CALL _PATH_REINDEX END SUB SUB PATH_SAVE(F, C$) ' SAVE PATHS TO FILE F WITH COMMENT C$ SAVE F, C$, PATH_SOURCE, _PATH_DATA_SIZE END SUB SUB PATH_LOAD(F) LOAD F, PATH_SOURCE PATH_COUNT = PEEK(PATH_SOURCE) END SUB SUB _PATH_CLAMP(V, MINV, MAXV) V = MAX(MINV, MIN(V, MAXV)) END SUB '-------------------------------------------- ' GENERIC GUI COMPONENTS IN BG LAYER 0 GLOBAL _GUI_MEM_LEN _GUI_MEM_LEN = 20*16*2 GLOBAL GUI_MEM_END GUI_MEM_END = $A000 + _GUI_MEM_LEN GLOBAL GUI_SHOWING GUI_SHOWING = TRUE GLOBAL POPUP_ACTION POPUP_ACTION = 0 SUB GUI_INIT BG SOURCE $A000, 20, 16 TOUCHSCREEN END SUB SUB TOGGLE_GUI GUI_SHOWING = NOT GUI_SHOWING IF GUI_SHOWING THEN DISPLAY (,1,,,) ELSE DISPLAY (,0,,,) END IF END SUB SUB PWRAP(T$, LINE_LEN) ' PRINTS TEXT WITH WORD WRAPPING WS = 1 WE = 0 LOFFSET = 0 WHILE WS <= LEN(T$) WE = INSTR(T$, " ", WS) IF WE < 1 THEN WE = LEN(T$)+1 WLEN = WE - WS WORD$ = MID$(T$, WS, WLEN) CHARS = WLEN - (LOFFSET > 0) IF LOFFSET+CHARS > LINE_LEN THEN PRINT "" LOFFSET = 0 ELSE IF LOFFSET > 0 THEN PRINT " "; LOFFSET = LOFFSET + 1 END IF PRINT WORD$; LOFFSET = LOFFSET + WLEN WS = WE+1 WEND PRINT "" END SUB SUB BTN(X0, Y0, ACTION) BG 0 ATTR(0,,,1,) FOR CY = 0 TO 1 FOR CX = 0 TO 1 X = X0 + CX Y = Y0 + CY CELL X, Y, ACTION + (CY*16) + (CX) MCELL X, Y, ACTION NEXT CX NEXT CY END SUB SUB SMALL_BTN(X, Y, ACTION) BG 0 ATTR(0,,,1,) CELL X, Y, ACTION MCELL X, Y, ACTION END SUB SUB CLEAR_POPUP BG FILL 0,0 TO 19,13 CHAR 0 FILL $A000, _GUI_MEM_LEN, 0 POPUP_ACTION = 0 CALL RESTORE_UI END SUB SUB POPUP(SOURCEX, ACTION) ' FADE OUT BUTTONS ATTR (1) BG FILL 0, 14 TO 19, 15 ' CAPTURE ALL INPUT TRACE "CAPTURE INPUT" FILL $A000, _GUI_MEM_LEN, A_NOOP ' DRAW POPUP ATTR (0) CELL 0, 0, 16 CELL 19, 0, 18 CELL 0, 13, 48 CELL 19, 13, 50 BG FILL 1,0 TO 18,0 CHAR 17 BG FILL 0,1 TO 0,12 CHAR 32 BG FILL 19,1 TO 19,12 CHAR 34 BG FILL 1,13 TO 18,13 CHAR 49 BG FILL 1,1 TO 18,12 CHAR 33 CELL SOURCEX, 13, 7 CALL BTN(1, 11, A_CANCEL) CALL BTN(17, 11, A_OK) POPUP_ACTION = ACTION END SUB SUB POPUP_TITLE(T$) TEXT 1,1, T$ END SUB '-------------------------------------------- ' DIRECT MANIPULATION OF PATHS GLOBAL SELECTED_PATH SELECTED_PATH = NONE ' THE ANCHOR LAST SELECTED BY A TOUCH GESTURE ' WHICH IS THE SUBJECT OF ACTIONS TRIGGERED ' BY BUTTON PRESSES GLOBAL SELECTED_ANCHOR SELECTED_ANCHOR = NONE ' THE ANCHOR BEING AFFECTED BY THE CURRENT ' TOUCH GESTURE GLOBAL DRAGGED_ANCHOR DRAGGED_ANCHOR = NONE ' 0 = ANCHOR DRAGGED, NOT CONTROL POINT ' -1 = PREVIOUS CONTROL DRAGGED ' +1 = FOLLOWING CONTROL DRAGGED GLOBAL DRAGGED_CONTROL DRAGGED_CONTROL = 0 GLOBAL SCROLL_X, SCROLL_Y SCROLL_X = 0 SCROLL_Y = 0 SUB GRAB_HANDLE(TX, TY) DRAGGED_ANCHOR = NONE DRAGGED_CONTROL = 0 P = SELECTED_PATH PLEN = NONE CALL PATH_LEN(P, PLEN) FOR A = 0 TO PLEN X = NONE Y = NONE FOR C = -1 TO 1 STEP 2 IF A = 0 AND C = -1 THEN GOTO NEXTC IF A = PLEN AND C = 1 THEN GOTO NEXTC CALL PATH_CONTROL(P, A, C, X, Y) D = SQR((TX-X)^2 + (TY-Y)^2) IF D <= 8 THEN SELECTED_ANCHOR = A DRAGGED_ANCHOR = A DRAGGED_CONTROL = C EXIT SUB END IF NEXTC: NEXT C CALL PATH_ANCHOR(P, A, X, Y) D = SQR((TX-X)^2 + (TY-Y)^2) IF D <= 8 THEN SELECTED_ANCHOR = A DRAGGED_ANCHOR = A EXIT SUB END IF NEXT A END SUB SUB DRAG_HANDLE(DX, DY) P = SELECTED_PATH A = DRAGGED_ANCHOR IF DRAGGED_CONTROL THEN C = DRAGGED_CONTROL CALL PATH_MOVE_CONTROL(P,A,C,DX,DY) ELSE CALL PATH_MOVE_ANCHOR(P,A,DX,DY) END IF END SUB SUB PATH_NAVBAR N = PATH_COUNT-1 P = SELECTED_PATH M$ = "PATH "+STR$(P)+" OF 0-"+STR$(N) BG FILL 0,0 TO 19,0 CHAR 33 TEXT 2,0, M$ CALL SMALL_BTN(0, 0, A_PREV_PATH) CALL SMALL_BTN(19, 0, A_NEXT_PATH) END SUB SUB SHOW_PATHS SPRITE OFF NEXT_SPRITE = 0 IF PATH_COUNT > 0 THEN IF POPUP_ACTION = 0 THEN CALL PATH_NAVBAR END IF P = SELECTED_PATH CALL SHOW_PATH(P, NEXT_SPRITE) END IF END SUB SUB SHOW_PATH(P, NEXT_SPRITE) CALL SHOW_PATH_HANDLES(P, NEXT_SPRITE) CALL SHOW_PATH_CURVE(P, NEXT_SPRITE, 4) END SUB SUB SHOW_PATH_CURVE(P, NEXT_SPRITE, ANT_COUNT) ANT_TIMER = (TIMER MOD 10)/10 PLEN = NONE CALL PATH_LEN(P, PLEN) FOR S = 0 TO PLEN-1 IF P = SELECTED_PATH THEN IF S = SELECTED_ANCHOR THEN SELECTED = TRUE ELSE IF SELECTED_ANCHOR=PLEN THEN SELECTED = (S = PLEN-1) ELSE SELECTED = FALSE END IF ELSE SELECTED = FALSE END IF FOR I = 0 TO ANT_COUNT-1 T = (I + ANT_TIMER)/ANT_COUNT CALL ANT(NEXT_SPRITE, P, S, T, SELECTED) NEXT I NEXT S END SUB SUB SHOW_PATH_HANDLES(P, NEXT_SPRITE) PLEN = NONE CALL PATH_LEN(P, PLEN) X = NONE Y = NONE CALL AHANDLE(NEXT_SPRITE, 0) FOR A = 1 TO PLEN CALL AHANDLE(NEXT_SPRITE, A) CALL CHANDLE(NEXT_SPRITE, A-1, 1) CALL CHANDLE(NEXT_SPRITE, A, -1) NEXT A END SUB SUB AHANDLE(SPR, A) X = NONE Y = NONE CALL PATH_ANCHOR(SELECTED_PATH, A, X, Y) IF A = SELECTED_ANCHOR THEN SPRITE.A SPR, (3) ELSE SPRITE.A SPR, (2) END IF CALL VIEW_SPRITE(SPR, X-3, Y-3, 5) END SUB SUB CHANDLE(SPR, A, SIDE) X = NONE Y = NONE P = SELECTED_PATH CALL PATH_CONTROL(P, A, SIDE, X, Y) SPRITE.A SPR, (2) CALL VIEW_SPRITE(SPR, X-3, Y-3, 6) END SUB SUB ANT(SPR, P, S, T, SELECTED_SEGMENT) X = NONE Y = NONE CALL PATH_SEGMENT_POS(P, S, T, X, Y) O = PATH_COORD_OFFSET IF X < -O OR X >= 160+O THEN EXIT SUB IF Y < -O OR Y >= 128+O THEN EXIT SUB IF P = SELECTED_PATH THEN IF SELECTED_SEGMENT THEN SPRITE.A SPR, (3) ELSE SPRITE.A SPR, (2) END IF ELSE SPRITE.A SPR, (1) END IF CALL VIEW_SPRITE(SPR, X, Y, 2) END SUB SUB VIEW_SPRITE(NEXT_SPRITE, X, Y, C) VIEWX = X - SCROLL_X VIEWY = Y - SCROLL_Y SPRITE NEXT_SPRITE, VIEWX, VIEWY, C NEXT_SPRITE = NEXT_SPRITE + 1 END SUB '-------------------------------------------- ' THE MAIN APP GLOBAL WAS_TOUCH, WAS_BUTTON WAS_TOUCH = FALSE WAS_BUTTON = FALSE GLOBAL TOUCH_START_X, TOUCH_START_Y GLOBAL TOUCH_LAST_X, TOUCH_LAST_Y GLOBAL CURRENT_FILE GLOBAL CURRENT_FILENAME$ ' UI ACTIONS ' ' A BUTTON ACTION HAS THE CHARACTER CODE OF ' THE BUTTON ICON. ' A CONFIRMATION OF A BUTTON ACTION IS ONE ' MORE THAN THE BUTTON ACTION. ' ACTIONS THAT ARE NOT TRIGGERED BY BUTTONS ' HAVE IDS FROM 192 ONWARDS, WHICH ARE USED ' FOR FONT CHARACTERS, NOT ICONS. GLOBAL A_NOOP A_NOOP = 192 GLOBAL A_CANCEL A_CANCEL = 100 GLOBAL A_OK A_OK = 102 GLOBAL A_HOME A_HOME = 64 GLOBAL A_ADD_PATH A_ADD_PATH = 70 GLOBAL A_DELETE_PATH A_DELETE_PATH = 72 GLOBAL A_ADD_POINT A_ADD_POINT = 66 GLOBAL A_DELETE_POINT A_DELETE_POINT = 68 GLOBAL A_LOAD A_LOAD = 96 GLOBAL A_CONFIRM_LOAD A_CONFIRM_LOAD = A_LOAD+1 GLOBAL A_SAVE A_SAVE = 98 GLOBAL A_CONFIRM_SAVE A_CONFIRM_SAVE = A_SAVE+1 GLOBAL A_ENTER_FILENAME A_ENTER_FILENAME = 193 GLOBAL A_PREV_PATH A_PREV_PATH = 8 GLOBAL A_NEXT_PATH A_NEXT_PATH = 9 GLOBAL A_PREV_FILE A_PREV_FILE = 74 GLOBAL A_NEXT_FILE A_NEXT_FILE = 76 SUB RESTORE_UI CALL BTN(0, 14, A_HOME) CALL BTN(3, 14, A_ADD_PATH) CALL BTN(5, 14, A_DELETE_PATH) CALL BTN(7, 14, A_ADD_POINT) CALL BTN(9, 14, A_DELETE_POINT) CALL BTN(16, 14, A_LOAD) CALL BTN(18, 14, A_SAVE) END SUB SUB PERFORM(ACTION) TRACE "ACTION", ACTION IF ACTION = A_HOME THEN CALL A_HOME ELSE IF ACTION = A_NEXT_PATH THEN CALL A_NEXT_PATH(1) ELSE IF ACTION = A_PREV_PATH THEN CALL A_NEXT_PATH(-1) ELSE IF ACTION = A_LOAD THEN CALL A_LOAD ELSE IF ACTION = A_CONFIRM_LOAD THEN CALL A_CONFIRM_LOAD ELSE IF ACTION = A_SAVE THEN CALL A_SAVE ELSE IF ACTION = A_CONFIRM_SAVE THEN CALL A_CONFIRM_SAVE ELSE IF ACTION = A_CANCEL THEN CALL CLEAR_POPUP ELSE IF ACTION = A_OK THEN CALL PERFORM(POPUP_ACTION) CALL CLEAR_POPUP ELSE IF ACTION = A_ADD_PATH THEN CALL A_ADD_PATH ELSE IF ACTION = A_DELETE_PATH THEN CALL A_DELETE_PATH ELSE IF ACTION = A_ADD_POINT THEN CALL A_ADD_POINT ELSE IF ACTION = A_DELETE_POINT THEN CALL A_DELETE_POINT ELSE IF ACTION = A_PREV_FILE THEN CALL A_PREV_FILE ELSE IF ACTION = A_NEXT_FILE THEN CALL A_NEXT_FILE ELSE IF ACTION = A_ENTER_FILENAME THEN CALL A_ENTER_FILENAME END IF END SUB SUB A_HOME SCROLL_X = 0 SCROLL_Y = 0 END SUB SUB A_NEXT_PATH(D) N = PATH_COUNT IF N > 0 THEN SELECTED_PATH = (SELECTED_PATH+D+N) MOD N END IF END SUB SUB A_ADD_PATH P = SELECTED_PATH CALL PATH_ADD(P, 32,32,80,32,80,96,128,96) SELECTED_PATH = P+1 END SUB SUB A_DELETE_PATH IF PATH_COUNT > 1 THEN CALL PATH_DELETE(SELECTED_PATH) N = PATH_COUNT SELECTED_PATH = (SELECTED_PATH+N-1) MOD N END IF END SUB SUB FILE_POPUP(X, ACTION) CALL POPUP(X, ACTION) CALL BTN(1, 4, A_PREV_FILE) CALL BTN(17, 4, A_NEXT_FILE) CALL SHOW_CURRENT_FILE END SUB SUB SHOW_CURRENT_FILE TEXT 4,4,"FILE "+STR$(CURRENT_FILE)+" " WINDOW 4, 6, 13, 4, 0 CLW CURRENT_FILENAME$ = FILE$(CURRENT_FILE) CALL PWRAP(CURRENT_FILENAME$, 12) END SUB SUB ALLOW_ENTER_FILENAME FOR Y = 6 TO 10 FOR X = 4 TO 17 MCELL X, Y, A_ENTER_FILENAME NEXT X NEXT Y END SUB SUB A_LOAD FILES CALL FILE_POPUP(16, A_CONFIRM_LOAD) CALL POPUP_TITLE("LOAD PATHS") END SUB SUB A_PREV_FILE CURRENT_FILE = (CURRENT_FILE+15) MOD 16 CALL SHOW_CURRENT_FILE END SUB SUB A_NEXT_FILE CURRENT_FILE = (CURRENT_FILE+1) MOD 16 CALL SHOW_CURRENT_FILE END SUB SUB A_ENTER_FILENAME CLW LOCATE 0, 0 INPUT CURRENT_FILENAME$ END SUB SUB A_CONFIRM_LOAD CALL PATH_LOAD(CURRENT_FILE) IF PATH_COUNT = 0 THEN SELECTED_PATH = NONE ELSE SELECTED_PATH = 0 END IF SELECTED_ANCHOR = NONE END SUB SUB A_SAVE FILES CALL FILE_POPUP(18, A_CONFIRM_SAVE) CALL POPUP_TITLE("SAVE PATHS") CALL ALLOW_ENTER_FILENAME END SUB SUB A_CONFIRM_SAVE F = CURRENT_FILE N$ = CURRENT_FILENAME$ CALL PATH_SAVE(F, N$) END SUB SUB A_ADD_POINT IF SELECTED_PATH = NONE THEN EXIT SUB P = SELECTED_PATH IF SELECTED_ANCHOR <> NONE THEN PLEN = NONE CALL PATH_LEN(P, PLEN) ISLAST = (SELECTED_ANCHOR = PLEN) S = SELECTED_ANCHOR + ISLAST ELSE S = 0 END IF CALL PATH_SPLIT_SEGMENT(P, S, 0.5) END SUB SUB A_DELETE_POINT P = SELECTED_PATH PLEN = NONE CALL PATH_LEN(SELECTED_PATH, PLEN) A = SELECTED_ANCHOR IF PLEN > 1 AND A <> NONE THEN CALL PATH_DELETE_SEGMENT(P, A) SELECTED_ANCHOR = NONE END IF END SUB CALL GUI_INIT CALL PATH_SET_SOURCE(GUI_MEM_END) CALL PATH_CLEAR_ALL BG 1 ATTR(1) BG FILL 0, 0 TO 19, 15 CHAR 1 CALL RESTORE_UI CALL A_ADD_PATH CURRENT_FILE = 4 DO WAIT VBL IF TOUCH THEN PX = TOUCH.X + SCROLL_X PY = TOUCH.Y + SCROLL_Y CX = TOUCH.X\8 CY = TOUCH.Y\8 PRESS = NOT WAS_TOUCH IF PRESS THEN TOUCH_START_X = TOUCH.X TOUCH_START_Y = TOUCH.Y ACTION = MCELL.C(CX, CY) IF ACTION = 0 OR NOT GUI_SHOWING THEN CALL GRAB_HANDLE(PX, PY) ELSE CALL PERFORM(ACTION) WAS_BUTTON = TRUE END IF ELSE IF NOT WAS_BUTTON THEN ' DRAGGING DX = TOUCH.X - TOUCH_LAST_X DY = TOUCH.Y - TOUCH_LAST_Y IF DRAGGED_ANCHOR <> NONE THEN CALL DRAG_HANDLE(DX, DY) ELSE ' SCROLL SCREEN SCROLL_X = SCROLL_X - DX SCROLL_Y = SCROLL_Y - DY END IF END IF TOUCH_LAST_X = TOUCH.X TOUCH_LAST_Y = TOUCH.Y ELSE IF WAS_TOUCH THEN TOTAL_DX = TOUCH.X - TOUCH_START_X TOTAL_DY = TOUCH.Y - TOUCH_START_Y IF TOTAL_DX = 0 AND TOTAL_DY = 0 THEN ' TAP B = WAS_BUTTON A = DRAGGED_ANCHOR <> NONE IF NOT (A OR B) THEN CALL TOGGLE_GUI END IF END IF WAS_BUTTON = FALSE END IF WAS_TOUCH = TOUCH SCROLL 1, SCROLL_X, SCROLL_Y CALL SHOW_PATHS LOOP #1:MAIN PALETTES 053F2A15002A1500002F2B16003C3824 003F2A15003F2A15003F2A15003F2A15 #2:MAIN CHARACTERS 00000000000000000000000000000000 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 80000000000000000000000000000000 00000000000000008000000000000000 80000000000000008000000000000000 00386C446C3800000000000000000000 0010106C101000000000000000000000 000000E1120A0602FFFFFF1E0C040000 FE8999B99989817F017F7F7F7F7F7FFF FE91999D9991817F017F7F7F7F7F7FFF FF00000000000000FF00000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000030408080000000000030707 00000000FF0000000000000000FFFFFF 00000000C02010100000000000C0E0E0 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 08080808080808080707070707070707 0000000000000000FFFFFFFFFFFFFFFF 1010101010101010E0E0E0E0E0E0E0E0 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 08080403000000000707030000000000 000000FF00000000FFFFFF0000000000 101020C000000000E0E0C00000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 FF808183878F9FBF007F7F7E7D7A7468 FE01B9F9F9F9F9FD01FFFF6FAF5F2F17 FF80838283808383007F7F7F7F7F7F7F FE01899D8901818101FFFFFFFFFFFFFF FF80838283808183007F7C7D7C7F7F7F FE0195899501018101FF7F7F7FFFFFFF FF80818187878181007F7F7F7F7F7F7F FE018181E1E1818101FFFFFFFFFFFFFF FF80848E8783878E007F7F7F7F7F7F7F FE0141E1C181C1E101FFFFFFFFFFFFFF FF808083838B9BBB007F7F7F7E7E7E7E FE0101F9F919F91901FFFFFF0FEF0FEF FF80809F9F989F98007F7F7F70777077 FE0101C1C1D1D9DD01FFFFFF7F7F7F7F 00000000000000000000000000000000 00000000000000000000000000000000 BF9F9F9F9F9F807F70737272727F7FFF FDF9F9F9F9F901FF0FCF4F4F4FFFFFFF 878381BBAEBB807F7F7F7F7C7D7C7FFF C18101B9E9B901FFFFFFFF7F7F7FFFFF 878383B8AFB8807F7F7F7F7F7F7F7FFF C1818139E93901FFFFFFFFFFFFFFFFFF 808080B8AFB8807F7F7F7F7F7F7F7FFF 0101011DF51D01FFFFFFFFFFFFFFFFFF 848080B8AFB8807F7F7F7F7F7F7F7FFF 41010139E93901FFFFFFFFFFFFFFFFFF 9B8B83838080807F7E7E7E7F7F7F7FFF F979F1E1010101FF7FCF5FFFFFFFFFFF 9F9B9F9F8080807F7376727F7F7F7FFF D9D18101010101FFFF7FFFFFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 FF80808183818D8F007F7F7F7F7F7F79 FE0181C1E1C1D9F901FFFFFFFFFFFFCF FF80808181818D8F007F7F7F7F7F7F7B FE0101C1C1C1D9F901FFFFFFFFFFFFEF FF80889CBE9F8F87007F7F776371787C FE0111397DF9F1E101FFFFEFC78F1F3F FF8080808080889D007F7F7F7F7F7F77 FE010111397DF9F101FFFFFFEFC78F1F 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 8F8F8F8F8F8F807F79787879797F7FFF F9F9F9F9F9F101FFCF0F0FCFCFFFFFFF 8F8F8F8F8F8F807F79787879797F7FFF F9F9F9F9F9F101FFCF8F0FCFCFFFFFFF 878F9FBE9C88807F7C787163777F7FFF E1F1F97D391101FF3F1F8FC7EFFFFFFF BF9F8F878280807F6270787D7F7F7FFF E1C18101010101FF3F7FFFFFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0000000000000000FFFFFFFFFFFFFFFF 0018181818001800FFE7E7E7E7FFE7FF 006C6C2400000000FF9393DBFFFFFFFF 00247E24247E2400FFDB81DBDB81DBFF 00083E380E3E0800FFF7C1C7F1C1F7FF 0062640810264600FF9D9BF7EFD9B9FF 001C34386E643A00FFE3CBC7919BC5FF 0018183000000000FFE7E7CFFFFFFFFF 000C183030180C00FFF3E7CFCFE7F3FF 0030180C0C183000FFCFE7F3F3E7CFFF 000024187E182400FFFFDBE781E7DBFF 000018187E181800FFFFE7E781E7E7FF 0000000018183000FFFFFFFFE7E7CFFF 000000007E000000FFFFFFFF81FFFFFF 0000000000181800FFFFFFFFFFE7E7FF 00060C1830604000FFF9F3E7CF9FBFFF 003C666E76663C00FFC399918999C3FF 0018381818187E00FFE7C7E7E7E781FF 003C660C18307E00FFC399F3E7CF81FF 003C660C06663C00FFC399F3F999C3FF 0066667E06060600FF999981F9F9F9FF 007E607C06067C00FF819F83F9F983FF 001C307C66663C00FFE3CF839999C3FF 007E060C18303000FF81F9F3E7CFCFFF 003C663C66663C00FFC399C39999C3FF 003C663E06663C00FFC399C1F999C3FF 0000001800180000FFFFFFE7FFE7FFFF 0000001800183000FFFFFFE7FFE7CFFF 00000C1830180C00FFFFF3E7CFE7F3FF 0000007E007E0000FFFFFF81FF81FFFF 000030180C183000FFFFCFE7F3E7CFFF 003C660C18001800FFC399F3E7FFE7FF 003C666E6E603C00FFC39991919FC3FF 00183C667E666600FFE7C399819999FF 007C667C66667C00FF839983999983FF 003C666060663C00FFC3999F9F99C3FF 00786C66666C7800FF879399999387FF 007E607860607E00FF819F879F9F81FF 007E607860606000FF819F879F9F9FFF 003C606E66663C00FFC39F919999C3FF 0066667E66666600FF999981999999FF 003C181818183C00FFC3E7E7E7E7C3FF 001E060606663C00FFE1F9F9F999C3FF 00666C78786C6600FF999387879399FF 0060606060607E00FF9F9F9F9F9F81FF 0042667E7E666600FFBD9981819999FF 0066767E6E666600FF998981919999FF 003C666666663C00FFC399999999C3FF 007C667C60606000FF8399839F9F9FFF 003C66666A6C3E00FFC399999593C1FF 007C667C786C6600FF839983879399FF 003E603C06067C00FFC19FC3F9F983FF 007E181818181800FF81E7E7E7E7E7FF 0066666666663C00FF9999999999C3FF 00666666663C1800FF99999999C3E7FF 0066667E7E664200FF9999818199BDFF 00663C183C666600FF99C3E7C39999FF 0066663C18181800FF9999C3E7E7E7FF 007E0C1830607E00FF81F3E7CF9F81FF 003C303030303C00FFC3CFCFCFCFC3FF 006030180C060200FF9FCFE7F3F9FDFF 003C0C0C0C0C3C00FFC3F3F3F3F3C3FF 00183C6600000000FFE7C399FFFFFFFF 0000000000007E00FFFFFFFFFFFF81FF