Implement bucket-fill tool
This commit is contained in:
parent
3b00ba7ec7
commit
51e1e85f54
263
tungsten.tal
263
tungsten.tal
|
@ -344,6 +344,12 @@
|
|||
%PRINTF(%b) { ;print_byte_binary CALL }
|
||||
%PRINTF(%b\s) { PRINTF(%b) PRINTF(\s) }
|
||||
%PRINTF(%b\n) { PRINTF(%b) PRINTF(\n) }
|
||||
%PRINTF(%?) { ;print_bool CALL }
|
||||
%PRINTF(%?\s) { PRINTF(%?) PRINTF(\s) }
|
||||
%PRINTF(%?\n) { PRINTF(%?) PRINTF(\n) }
|
||||
%PRINTF(%?*) { ;print_bool_short CALL }
|
||||
%PRINTF(%?*\s) { PRINTF(%?) PRINTF(\s) }
|
||||
%PRINTF(%?*\n) { PRINTF(%?) PRINTF(\n) }
|
||||
|
||||
|
||||
|
||||
|
@ -352,6 +358,14 @@
|
|||
|
||||
|0100
|
||||
@program_start ( -- )
|
||||
|
||||
( #0008 /V.CANVAS.WIDTH!
|
||||
#0008 /V.CANVAS.HEIGHT!
|
||||
COL_1 #0001 #0002
|
||||
;fill_canvas_from_point CALL
|
||||
HALT
|
||||
)
|
||||
|
||||
( Initialise variables )
|
||||
#0080 /V.CANVAS.WIDTH!
|
||||
#0060 /V.CANVAS.HEIGHT!
|
||||
|
@ -362,25 +376,230 @@
|
|||
|
||||
|
||||
( ---------------------------------------------------------------------------- )
|
||||
( C A N V A S S T U F F )
|
||||
( C A N V A S P R O C E D U R E S )
|
||||
|
||||
( ALL OF THESE ROUTINES USE CANVAS-SPACE COORDINATES )
|
||||
|
||||
( Get the colour of the canvas pixel at the given coordinates. )
|
||||
@get_canvas_pixel_colour ( x* y* -- colour )
|
||||
;get_mask_and_address CALL LDA AND TO_BOOL RETURN
|
||||
@set_canvas_pixel_colour ( colour x* y* -- )
|
||||
;get_mask_and_address CALL STHk* LDA ROT ,&col_1 JCN ( mask byte | row_addr* )
|
||||
&col_0 EOR ,&end JMP
|
||||
&col_1 ORA
|
||||
&end STHr* STA RETURN
|
||||
|
||||
@check_in_canvas_bounds ( x* y* -- in_bounds? )
|
||||
/V.CANVAS.HEIGHT? LTH* STH /V.CANVAS.WIDTH? LTH* STHr AND RETURN
|
||||
|
||||
( Return 1 if pixel is in-bounds and the given colour, otherwise return 0 )
|
||||
@check_canvas_pixel_colour ( colour x* y* -- is_colour? )
|
||||
OVR* OVR* ;check_in_canvas_bounds CALL ,&cont JCN
|
||||
( bail out ) POP* POP* POP #00 RETURN
|
||||
&cont ;get_canvas_pixel_colour CALL EQU RETURN
|
||||
|
||||
( Note: These routines all use canvas-space coordinates )
|
||||
@get_mask_and_address ( x* y* -- mask row_address* )
|
||||
OVR* OVR* ;get_sprite_address CALL ( x* y* sprite_addr* )
|
||||
SWP* MOD8* ADD* ( x* row_addr* )
|
||||
STH* ;get_mask CALL STHr* RETURN
|
||||
|
||||
@get_sprite_address ( x* y* -- sprite_address* )
|
||||
SWP* DIV8* SWP* DIV8* ( spritex* spritey* )
|
||||
/V.CANVAS.WIDTH? CEIL8DIV8* ( sprite_x* sprite_y* sprite_width* )
|
||||
MUL* ADD* MUL8* ;canvas_buffer ADD* ( sprite_addr* )
|
||||
RETURN
|
||||
|
||||
@get_mask ( x* -- mask )
|
||||
NIP MOD8 #0107 ROT SUB SHL4 SFT RETURN
|
||||
|
||||
|
||||
@fill_canvas_from_point ( colour x* y* )
|
||||
( Store starting values )
|
||||
STH* ROT ;&col_p STA ( x* | y* )
|
||||
DUP* ;&paint_x STA* ( x* | y* )
|
||||
STHr* DUP* ;&paint_y STA* ( x* y* )
|
||||
( Return if coords not in bounds )
|
||||
;check_in_canvas_bounds CALL ( in_bounds? )
|
||||
IF_FALSE RETURN ( -- )
|
||||
( Find target colour )
|
||||
;&paint_x LDA* ;&paint_y LDA* ( x* y* )
|
||||
;get_canvas_pixel_colour CALL ( target_colour )
|
||||
DUP ;&col_t STA ( target_colour )
|
||||
( Return if target and paint colours are the same )
|
||||
;&col_p LDA IF_EQUAL RETURN ( -- )
|
||||
|
||||
( Clear variables )
|
||||
#0000 OVRk ;&mark1_a STA ;&mark2_a STA
|
||||
;&backtrack STA ;&findloop STA ;&paint_d STA
|
||||
( Move the painter to a wall to start )
|
||||
&move_into_place ( -- )
|
||||
;&front? CALL NOT ,&START JCN
|
||||
;&move_forward CALL ,&move_into_place JMP
|
||||
&MAIN_LOOP
|
||||
;&move_forward CALL
|
||||
( if right-pixel is inside, then )
|
||||
;&right? CALL NOT ,&START JCN
|
||||
( if backtrack is true and findloop is false and either front-pixel or left-pixel is inside, then )
|
||||
;&backtrack LDA ;&findloop LDA NOT AND ;&front? CALL ;&left? CALL ORA AND NOT ,&no_findloop JCN
|
||||
#01 ;&findloop STA
|
||||
&no_findloop
|
||||
;&turn_right CALL
|
||||
&PAINT
|
||||
;&move_forward CALL
|
||||
&START
|
||||
;&update_neighbour_wall_count CALL
|
||||
;&wall_count LDA #04 EQU ,&check_1 JCN
|
||||
( Orient the painter to face right along a wall )
|
||||
&orient_right
|
||||
;&front? CALL NOT ,&orient_left JCN
|
||||
;&turn_right CALL ,&orient_right JMP
|
||||
&orient_left
|
||||
;&front? CALL ,&check_1 JCN
|
||||
;&turn_left CALL ,&orient_left JMP
|
||||
&check_1
|
||||
;&wall_count LDA #01 NEQ ,&check_2 JCN
|
||||
( If backtrack is True, findloop = True )
|
||||
&1_1 ;&backtrack LDA NOT ,&1_2 JCN
|
||||
#01 ;&findloop STA ;&MAIN_LOOP GOTO
|
||||
( Else if findloop is True, make mark1 active )
|
||||
&1_2 ;&findloop LDA NOT ,&1_3 JCN
|
||||
#01 ;&mark1_a STA ;&MAIN_LOOP GOTO
|
||||
( Else if front-left and back-left are both inside )
|
||||
&1_3 ;&front_left? CALL ;&back_left? CALL AND NOT ;&MAIN_LOOP JCN*
|
||||
#00 ;&mark1_a STA
|
||||
;&paint_here CALL
|
||||
;&PAINT GOTO
|
||||
&check_2
|
||||
;&wall_count LDA #02 NEQ ;&check_3 JCN*
|
||||
( If back is not inside )
|
||||
&2_1 ;&back? CALL ,&2_2 JCN
|
||||
( If front-left is inside )
|
||||
;&front_left? CALL NOT ;&MAIN_LOOP JCN*
|
||||
#00 ;&mark1_a STA
|
||||
;&paint_here CALL
|
||||
;&PAINT GOTO
|
||||
( If mark1 is not active )
|
||||
&2_2 ;&mark1_a LDA ,&2_3 JCN
|
||||
;&paint_x LDA* ;&mark1_x STA*
|
||||
;&paint_y LDA* ;&mark1_y STA*
|
||||
;&paint_d LDA ;&mark1_d STA
|
||||
#0001 ;&mark1_a STA ;&mark2_a STA
|
||||
#0000 ;&backtrack STA ;&findloop STA
|
||||
;&MAIN_LOOP GOTO
|
||||
( Else if mark2 is not active )
|
||||
&2_3 ;&mark2_a LDA ;&2_4 JCN*
|
||||
( If painter is at mark1 )
|
||||
&2_3_1 ;&mark1_a LDA NOT ,&2_3_2 JCN
|
||||
;&paint_x LDA* ;&mark1_x LDA* NEQ* ,&2_3_2 JCN
|
||||
;&paint_y LDA* ;&mark1_y LDA* NEQ* ,&2_3_2 JCN
|
||||
( If painter and mark1 are facing in the same direction )
|
||||
&2_3_1_1 ;&paint_d LDA ;&mark1_d LDA NEQ ,&2_3_1_else
|
||||
#00 ;&mark1_a STA
|
||||
;&turn_around CALL
|
||||
;&paint_here CALL
|
||||
;&PAINT GOTO
|
||||
&2_3_1_else
|
||||
#0001 ;&backtrack STA ;&findloop STA
|
||||
;&mark1_d LDA ;&paint_d STA
|
||||
;&MAIN_LOOP GOTO
|
||||
( If findloop is True )
|
||||
&2_3_2 ;&findloop LDA NOT ,&2_3_end JCN
|
||||
;&paint_x LDA* ;&mark2_x STA*
|
||||
;&paint_y LDA* ;&mark2_y STA*
|
||||
;&paint_d LDA ;&mark2_d STA
|
||||
#01 ;&mark2_a STA
|
||||
&2_3_end ;&MAIN_LOOP GOTO
|
||||
( Else if painter is at mark1 )
|
||||
&2_4 ;&mark1_a LDA NOT ,&2_5 JCN
|
||||
;&paint_x LDA* ;&mark1_x LDA* NEQ* ,&2_5 JCN
|
||||
;&paint_y LDA* ;&mark1_y LDA* NEQ* ,&2_5 JCN
|
||||
;&mark2_x LDA* ;&paint_x STA*
|
||||
;&mark2_y LDA* ;&paint_y STA*
|
||||
;&mark2_d LDA ;&paint_d STA
|
||||
#0000 ;&mark1_a STA ;&mark2_a STA
|
||||
#00 ;&backtrack STA
|
||||
;&turn_around CALL
|
||||
;&paint_here CALL
|
||||
;&PAINT GOTO
|
||||
( Else if painter is at mark2 )
|
||||
&2_5 ;&mark2_a LDA NOT ,&2_end JCN
|
||||
;&paint_x LDA* ;&mark2_x LDA* NEQ* ,&2_end JCN
|
||||
;&paint_y LDA* ;&mark2_y LDA* NEQ* ,&2_end JCN
|
||||
;&paint_x LDA* ;&mark1_x STA*
|
||||
;&paint_y LDA* ;&mark1_y STA*
|
||||
;&mark2_d LDA ;&mark1_d STA
|
||||
;&mark2_d LDA ;&paint_d STA
|
||||
#0001 ;&mark1_a STA ;&mark2_a STA
|
||||
&2_end ;&MAIN_LOOP GOTO
|
||||
&check_3
|
||||
;&wall_count LDA #03 NEQ ,&check_4 JCN
|
||||
#00 ;&mark1_a STA
|
||||
;&paint_here CALL
|
||||
;&PAINT GOTO
|
||||
&check_4
|
||||
;&wall_count LDA #04 NEQ ;&MAIN_LOOP JCN*
|
||||
;&paint_here CALL_RET
|
||||
|
||||
( Update the variable tracking the number of walls around the painter )
|
||||
&update_neighbour_wall_count ( -- wall_count )
|
||||
#04 ,&front? JSR ,&back? JSR ,&left? JSR ,&right? JSR
|
||||
ADD* ADD SUB ;&wall_count STA RETURN
|
||||
|
||||
( Return 1 if the pixel in the direction is inside the fill region, otherwise 0 )
|
||||
&front? #00 ,&dir_inside? JMP
|
||||
&back? #02 ,&dir_inside? JMP
|
||||
&left? #03 ,&dir_inside? JMP
|
||||
&right? #01 ,&dir_inside? JMP
|
||||
&front_left? #03 ,&diag_inside? JMP
|
||||
&back_left? #02 ,&diag_inside? JMP
|
||||
&dir_inside? ;&col_t LDA SWP ,&get_dir_coords JSR ;check_canvas_pixel_colour CALL_RET
|
||||
&diag_inside? ;&col_t LDA SWP ,&get_diag_coords JSR ;check_canvas_pixel_colour CALL_RET
|
||||
( 0:front 1:right 2:back 3:left )
|
||||
&get_dir_coords ( relative_d -- x* y* )
|
||||
;&paint_d LDA ADD MOD4 ( d )
|
||||
TO_SHORT DUP* INC MOD4 SWP* ( d+1* d* )
|
||||
DOUBLE* ;&_dir_offsets ADD* LDA* SWP* ( d+1* | y_offset* )
|
||||
DOUBLE* ;&_dir_offsets ADD* LDA* SWP* ( x_offset* | y_offset* )
|
||||
,&to_abs_coords JMP
|
||||
( 0:front-right 1:back-right 2:back-left 3:back-left )
|
||||
&get_diag_coords ( relative_d -- x* y* )
|
||||
;&paint_d LDA ADD MOD4 ( d )
|
||||
#0003 ROT SUB INCk* MOD4 ( 3-d* 4-d* )
|
||||
DOUBLE* ;&_diag_offsets ADD* LDA* SWP* ( y_offset* 3-d* )
|
||||
DOUBLE* ;&_diag_offsets ADD* LDA* SWP* ( x_offset* y_offset* )
|
||||
,&to_abs_coords JMP
|
||||
&to_abs_coords ( x_offset* y_offset* -- x* y* )
|
||||
SWP* ;&paint_x LDA* ADD* SWP* ( x* y_offset* )
|
||||
;&paint_y LDA* ADD* RETURN ( x* y* )
|
||||
&_dir_offsets ffff 0000 0001 0000
|
||||
&_diag_offsets ffff ffff 0001 0001
|
||||
|
||||
&paint_here ,&col_p LDR ,&paint_x LDR* ,&paint_y LDR* ;set_canvas_pixel_colour CALL_RET
|
||||
&move_forward #00 ;&get_dir_coords CALL ,&paint_y STR* ,&paint_x STR* RETURN
|
||||
&turn_left #ff ,&_turn JMP
|
||||
&turn_right #01 ,&_turn JMP
|
||||
&turn_around #02 ,&_turn JMP
|
||||
&_turn ,&paint_d LDR ADD MOD4 ,&paint_d STR RETURN
|
||||
|
||||
&paint_x $2 &mark1_x $2 &mark2_x $2 ( x-coords )
|
||||
&paint_y $2 &mark1_y $2 &mark2_y $2 ( y-coords )
|
||||
&paint_d $2 &mark1_d $1 &mark2_d $1 ( directions, 0:up 1:right 2:down 3:left )
|
||||
&wall_count $1 &mark1_a $1 &mark2_a $1 ( neighbouring walls count, and mark active bools )
|
||||
&col_p $1 &col_t $1 ( paint colour and target colour )
|
||||
&backtrack $1 &findloop $1
|
||||
|
||||
|
||||
( ---------------------------------------------------------------------------- )
|
||||
( T O O L B O X )
|
||||
|
||||
@delay ( coarse* -- )
|
||||
#0000 SWP* SUB* #0000 ( coarse* fine* )
|
||||
&loop_fine
|
||||
INC* DUP* ORA ,&loop_fine JCN
|
||||
&loop_coarse
|
||||
SWP* INC* SWP* OVR* ORA ,&loop_fine JCN
|
||||
POP* POP* RETURN
|
||||
|
||||
|
||||
@print_newline LIT <LINEFEED> /CONSOLE.WRITE! RETURN
|
||||
@print_space LIT <SPACE> /CONSOLE.WRITE! RETURN
|
||||
|
||||
|
@ -393,7 +612,7 @@
|
|||
&end POP* RETURN
|
||||
|
||||
|
||||
( Print a single byte to the console )
|
||||
( Print a single byte to the console in decimal )
|
||||
@print_byte_decimal_signed ( byte -- )
|
||||
DUP* IS_POSITIVE ,print_byte_decimal JCN
|
||||
LIT "- /CONSOLE.WRITE! NEGATE
|
||||
|
@ -401,7 +620,7 @@
|
|||
;convert_byte_to_decimal_string CALL
|
||||
PRINTF(%s) RETURN
|
||||
|
||||
( Print a single short to the console )
|
||||
( Print a single short to the console in decimal )
|
||||
@print_short_decimal_signed ( short* -- )
|
||||
DUP* IS_POSITIVE* ,print_short_decimal JCN
|
||||
LIT "- /CONSOLE.WRITE! NEGATE*
|
||||
|
@ -414,9 +633,11 @@
|
|||
&loop LDAk DUP ,&print JCN POP POP* RETURN
|
||||
&print /CONSOLE.WRITE! INC* ,&loop JMP
|
||||
|
||||
( Print a byte as TRUE or FALSE )
|
||||
( Print a value as TRUE or FALSE )
|
||||
@print_bool_short ( bool* -- )
|
||||
TO_BOOL*
|
||||
@print_bool ( bool -- )
|
||||
;&true ROT ,&print JCN POP* ;&false &print PRINTF(%s)
|
||||
;&true ROT ,&print JCN POP* ;&false &print PRINTF(%s) RETURN
|
||||
&true "TRUE <NULL> &false "FALSE <NULL>
|
||||
|
||||
|
||||
|
@ -602,10 +823,15 @@
|
|||
;ui__clear_memory_region CALL
|
||||
( Clear all registered mouse zones )
|
||||
;ui__zones ;ui__zones/end #0000
|
||||
;ui__clear_memory_region CALL_RET
|
||||
;ui__clear_memory_region CALL
|
||||
( Set mouse colour to 0 )
|
||||
COL_T1 ;ui__set_cursor_colour CALL_RET
|
||||
&pop_null_callback POP
|
||||
&null_callback RETURN
|
||||
|
||||
@ui__set_cursor_colour ( sprite_colour -- )
|
||||
;ui__redraw_cursor/cursor_colour STA RETURN
|
||||
|
||||
( Fill a region of memory with a given value. )
|
||||
@ui__clear_memory_region ( start_addr* end_addr* value* -- )
|
||||
STH* SWP* ( end* addr* | val* )
|
||||
|
@ -663,7 +889,7 @@
|
|||
COL_00 AND_FG /SCREEN.SPRITE!
|
||||
( Draw the mouse cursor under the current mouse position )
|
||||
/MOUSE.X? /SCREEN.X! /MOUSE.Y? /SCREEN.Y!
|
||||
COL_T1 AND_FG /SCREEN.SPRITE!
|
||||
[ LIT &cursor_colour $1 ] AND_FG /SCREEN.SPRITE!
|
||||
RETURN
|
||||
|
||||
@ui__restart_mouse1_drag ( -- )
|
||||
|
@ -999,6 +1225,7 @@
|
|||
@canvas_screen
|
||||
;ui__clear CALL
|
||||
;colour_palette_canvas #04 ;ui__set_colour_palette CALL
|
||||
#03 ;ui__set_cursor_colour CALL
|
||||
;canvas_screen__draw CALL
|
||||
;canvas_screen__tool ;ui__register_mouse1_callback CALL
|
||||
;canvas_screen__B ;ui__register_B_callback CALL
|
||||
|
@ -1008,7 +1235,7 @@
|
|||
@canvas_screen__B NORETURN ;tool_palette_screen GOTO
|
||||
@canvas_screen__tool
|
||||
( Dispatch control to the routine of the active tool )
|
||||
/V.ACTIVE_TOOL? MUL4* JMP
|
||||
/V.ACTIVE_TOOL? MUL4 JMP
|
||||
;&pan GOTO ;&zoom GOTO ;&brush_1 GOTO ;&brush_2 GOTO
|
||||
;&brush_3 GOTO ;&line GOTO ;&rect GOTO ;&fill GOTO
|
||||
|
||||
|
@ -1045,16 +1272,17 @@
|
|||
/MOUSE.X? /SCREEN.X! /MOUSE.Y? /SCREEN.Y! COL_1 /SCREEN.PIXEL!
|
||||
RETURN
|
||||
|
||||
|
||||
|
||||
&brush_2 RETURN
|
||||
&brush_3 RETURN
|
||||
&line RETURN
|
||||
&rect RETURN
|
||||
&fill RETURN
|
||||
|
||||
|
||||
|
||||
&fill ( released? -- )
|
||||
IF_FALSE RETURN #01
|
||||
/MOUSE.X? /V.CANVAS.X? SUB*
|
||||
/MOUSE.Y? /V.CANVAS.Y? SUB*
|
||||
;fill_canvas_from_point CALL
|
||||
;draw_canvas CALL
|
||||
RETURN
|
||||
|
||||
( Render the canvas to the screen from the raw program data )
|
||||
@draw_canvas ( -- )
|
||||
|
@ -1580,11 +1808,10 @@ e060 6060 6060 f000 ( l ) 0000 6cfe d6d6 c600 ( m )
|
|||
0008 0008 0008 0008 0000 0000 0000 0000 0020 0020 0020 0020
|
||||
0008 0d00 0000 0000 0000 5500 0000 0000 0020 6000 0000 0000
|
||||
|
||||
|
||||
|
||||
@canvas_buffer
|
||||
(
|
||||
This label needs to be at the end of the
|
||||
program, to ensure that there is enough space
|
||||
to work with the largest possible image.
|
||||
)
|
||||
ffa1 adad a1bf 81ff ff81 f5d1 f781 c1ff ffff 0000 ffff 0000
|
||||
|
|
Loading…
Reference in New Issue