z_mirror/Tapeline
git clone https://git.raiguard.me/z_mirror/Tapeline
37bb3aa
color-setting
1local area =require ( "__flib__/area" ) 2local table =require ( "__flib__/table" ) 3 4local bring_to_front =rendering . bring_to_front 5local destroy =rendering . destroy 6local draw_line =rendering . draw_line 7local draw_rectangle =rendering . draw_rectangle 8local draw_text =rendering . draw_text 9local set_color =rendering . set_color 10local set_draw_on_ground =rendering . set_draw_on_ground 11local set_from =rendering . set_from 12local set_left_top =rendering . set_left_top 13local set_right_bottom =rendering . set_right_bottom 14local set_target =rendering . set_target 15local set_text =rendering . set_text 16local set_time_to_live =rendering . set_time_to_live 17local set_to =rendering . set_to 18local set_visible =rendering . set_visible 19local set_width =rendering . set_width 20 21local tape ={} 22 23local opposite_corners ={ 24left_top ="right_bottom" , 25left_bottom ="right_top" , 26right_top ="left_bottom" , 27right_bottom ="left_top" , 28} 29 30--- @param objects TapeObjects 31--- @param func fun(id: uint64, ...) 32local function apply_to_all_objects ( objects , func , ... ) 33for _ , v in pairs ( objects ) do 34if type ( v ) == "table" then 35apply_to_all_objects ( v , func , ... ) 36else 37func ( v , ... ) 38end 39end 40end 41 42--- @param player_index uint 43--- @param tape_data TapeData 44--- @param visual_settings VisualSettings 45local function create_objects ( player_index , tape_data , visual_settings ) 46local TapeArea =tape_data . Area 47--- @class TapeObjects 48local objects ={ 49background =draw_rectangle ( { 50color =visual_settings . tape_background_color , 51filled =true , 52left_top =TapeArea . left_top , 53right_bottom =TapeArea . right_bottom , 54surface =tape_data . surface , 55players ={ player_index } , 56draw_on_ground =visual_settings . draw_tape_on_ground , 57} ), 58border =draw_rectangle ( { 59color =visual_settings . tape_border_color , 60width =visual_settings . tape_line_width , 61filled =false , 62left_top =TapeArea . left_top , 63right_bottom =TapeArea . right_bottom , 64surface =tape_data . surface , 65players ={ player_index } , 66draw_on_ground =visual_settings . draw_tape_on_ground , 67} ), 68labels ={ 69x =draw_text ( { 70text =tostring ( TapeArea : width ()), 71surface =tape_data . surface , 72target ={ x =TapeArea : center (). x , y =TapeArea . left_top . y - 0.85 } , 73color =visual_settings . tape_label_color , 74scale =1.5 , 75alignment ="center" , 76visible =false , 77players ={ player_index } , 78} ), 79y =draw_text ( { 80text =tostring ( TapeArea : height ()), 81surface =tape_data . surface , 82target ={ x =TapeArea . left_top . x - 0.85 , y =TapeArea : center (). y } , 83color =visual_settings . tape_label_color , 84scale =1.5 , 85orientation =0.75 , 86alignment ="center" , 87visible =false , 88players ={ player_index } , 89} ), 90} , 91lines ={ 92{ x ={} , y ={} } , 93{ x ={} , y ={} } , 94{ x ={} , y ={} } , 95{ x ={} , y ={} } , 96} , 97} 98return objects 99end 100 101--- @param player_index uint 102--- @param tape_data TapeData 103--- @param tape_settings TapeSettings 104--- @param visual_settings VisualSettings 105local function update_objects ( player_index , tape_data , tape_settings , visual_settings ) 106local TapeArea =tape_data . Area 107local objects =tape_data . objects 108 109local background =objects . background 110set_left_top ( background , TapeArea . left_top ) 111set_right_bottom ( background , TapeArea . right_bottom ) 112 113local border =objects . border 114set_left_top ( border , TapeArea . left_top ) 115set_right_bottom ( border , TapeArea . right_bottom ) 116 117local center =TapeArea : center () 118local height =TapeArea : height () 119local width =TapeArea : width () 120 121local x_label =objects . labels . x 122set_text ( x_label , tostring ( width )) 123set_target ( x_label , { x =center . x , y =TapeArea . left_top . y - 0.85 } ) 124if width > 1 then 125set_visible ( x_label , true ) 126else 127set_visible ( x_label , false ) 128end 129 130local y_label =objects . labels . y 131set_text ( y_label , tostring ( height )) 132set_target ( y_label , { x =TapeArea . left_top . x - 0.85 , y =center . y } ) 133if height > 1 then 134set_visible ( y_label , true ) 135else 136set_visible ( y_label , false ) 137end 138 139-- GRID LINES 140 141local function delete_lines ( axis , grid_index ) 142apply_to_all_objects ( objects . lines [ grid_index ][ axis ], destroy ) 143objects . lines [ grid_index ][ axis ] ={} 144end 145 146local function delete_grid ( grid_index ) 147delete_lines ( "x" , grid_index ) 148delete_lines ( "y" , grid_index ) 149end 150 151-- TODO: optimize to not touch lines that don't need updating 152local function update_lines ( axis , grid_index , grid_step ) 153local lines =objects . lines [ grid_index ][ axis ] 154 155-- iterate from the origin corner outwards 156local start =TapeArea [ tape_data . origin_corner ][ axis ] 157local finish =TapeArea [ opposite_corners [ tape_data . origin_corner ]][ axis ] 158 159local i =0 160 161if ( axis == "x" and width or height ) > 1 then 162for pos =start , finish , start < finish and grid_step or - grid_step do 163local from , to 164if axis == "x" then 165from ={ x =pos , y =TapeArea . left_top . y } 166to ={ x =pos , y =TapeArea . right_bottom . y } 167else 168from ={ x =TapeArea . left_top . x , y =pos } 169to ={ x =TapeArea . right_bottom . x , y =pos } 170end 171 172i =i + 1 173local line =lines [ i ] 174if line then 175set_from ( line , from ) 176set_to ( line , to ) 177bring_to_front ( line ) 178else 179lines [ i ] =draw_line ( { 180color =visual_settings [ "tape_line_color_" .. grid_index ], 181width =visual_settings . tape_line_width , 182from =from , 183to =to , 184surface =tape_data . surface , 185players ={ player_index } , 186draw_on_ground =visual_settings . draw_tape_on_ground , 187} ) 188end 189end 190end 191 192for j =i + 1 , # lines do 193destroy ( lines [ j ]) 194lines [ j ] =nil 195end 196end 197 198local function update_grid ( grid_index , grid_step ) 199update_lines ( "x" , grid_index , grid_step ) 200update_lines ( "y" , grid_index , grid_step ) 201end 202 203-- base grid 204update_grid ( 1 , 1 ) 205 206local mode =tape_settings . mode 207if mode == "subgrid" then 208local subgrid_size =tape_settings . subgrid_divisor 209update_grid ( 2 , subgrid_size ) 210update_grid ( 3 , subgrid_size ^ 2 ) 211update_grid ( 4 , subgrid_size ^ 3 ) 212elseif mode == "split" then 213local num_splits =tape_settings . split_divisor 214update_lines ( "x" , 2 , width / num_splits ) 215update_lines ( "y" , 2 , height / num_splits ) 216update_lines ( "x" , 3 , width / 2 ) 217update_lines ( "y" , 3 , height / 2 ) 218delete_grid ( 4 ) 219end 220 221bring_to_front ( border ) 222end 223 224--- @param player LuaPlayer 225--- @param player_table PlayerTable 226--- @param origin MapPosition 227--- @param surface LuaSurface 228function tape . start_draw ( player , player_table , origin , surface ) 229-- TODO: Convert to flib bounding-box module 230local TapeArea =area . from_position ( origin ): ceil (): corners () 231--- @class TapeData 232local tape_data ={ 233Area =TapeArea , 234last_position =origin , 235last_update_tick =game . ticks_played , 236origin =origin , 237origin_corner ="left_top" , 238surface =surface , 239} 240tape_data . objects =create_objects ( player . index , tape_data , player_table . visual_settings ) 241player_table . tapes . drawing =tape_data 242player_table . flags . drawing =true 243end 244 245--- @param player LuaPlayer 246--- @param player_table PlayerTable 247--- @param new_position MapPosition? 248--- @param is_ghost boolean? 249function tape . update_draw ( player , player_table , new_position , is_ghost ) 250local tape_data =player_table . tapes . drawing 251local TapeArea =area . load ( tape_data . Area ) 252local origin =tape_data . origin 253 254if new_position then 255if not is_ghost then 256if math . abs ( new_position . x - origin . x ) >= math . abs ( new_position . y - origin . y ) then 257new_position . y =math . floor ( origin . y ) 258else 259new_position . x =math . floor ( origin . x ) 260end 261 262-- if the new position is the same as the last, don't actually do anything 263local last_position =tape_data . last_position 264if new_position . x == last_position . x and new_position . y == last_position . y then 265return 266end 267end 268tape_data . last_position =new_position 269else 270new_position =tape_data . last_position 271end 272 273tape_data . last_update_tick =game . ticks_played 274 275-- update area corners 276local x_less =new_position . x < origin . x 277local y_less =new_position . y < origin . y 278TapeArea . left_top ={ 279x =math . floor ( x_less and new_position . x or origin . x ), 280y =math . floor ( y_less and new_position . y or origin . y ), 281} 282TapeArea . right_bottom ={ 283x =math . ceil ( x_less and origin . x or new_position . x ), 284y =math . ceil ( y_less and origin . y or new_position . y ), 285} 286TapeArea : corners () 287-- update origin corner 288local new_origin_corner =(( x_less and "right" or "left" ) .. "_" .. ( y_less and "bottom" or "top" )) 289tape_data . origin_corner =new_origin_corner 290 291update_objects ( player . index , tape_data , player_table . tape_settings , player_table . visual_settings ) 292end 293 294--- @param player LuaPlayer 295--- @param player_table PlayerTable 296--- @param auto_clear boolean 297function tape . complete_draw ( player , player_table , auto_clear ) 298local tapes =player_table . tapes 299local tape_data =tapes . drawing 300local TapeArea =tape_data . Area 301local objects =tape_data . objects 302 303player_table . flags . drawing =false 304 305-- immediately destroy the tape if it is 1x1 306if TapeArea : height () == 1 and TapeArea : width () == 1 then 307apply_to_all_objects ( objects , destroy ) 308tapes . drawing =nil 309return 310end 311 312tape_data . last_position =nil 313 314if auto_clear then 315apply_to_all_objects ( objects , set_time_to_live , player_table . visual_settings . tape_clear_delay * 60 ) 316-- update to fix draw order 317update_objects ( player . index , tape_data , player_table . tape_settings , player_table . visual_settings ) 318else 319-- copy settings into tape so they can be changed later 320tape_data . settings =table . deep_copy ( player_table . tape_settings ) 321tapes [ # tapes + 1 ] =tape_data 322end 323 324if player_table . visual_settings . log_selection_area then 325player . ( TapeArea : width () .. "x" .. TapeArea : height ()) 326end 327tapes . drawing =nil 328end 329 330--- @param player_table PlayerTable 331function tape . cancel_draw ( player_table ) 332player_table . flags . drawing =false 333 334local tape_data =player_table . tapes . drawing 335apply_to_all_objects ( tape_data . objects , destroy ) 336player_table . tapes . drawing =nil 337end 338 339--- @param player_table PlayerTable 340--- @param tape_index number 341function tape . delete ( player_table , tape_index ) 342local tapes =player_table . tapes 343local tape_data =tapes [ tape_index ] 344apply_to_all_objects ( tape_data . objects , destroy ) 345if player_table . flags . editing then 346tape . exit_edit_mode ( player_table ) 347end 348table . remove ( tapes , tape_index ) 349end 350 351--- @param player_index uint 352--- @param TapeArea BoundingBox 353--- @param surface LuaSurface 354local function create_highlight_box ( player_index , TapeArea , surface ) 355return surface . create_entity ( { 356name ="tl-highlight-box" , 357position =TapeArea : center (), 358bounding_box =area . expand ( TapeArea : strip (), 0.3 ), 359cursor_box_type ="electricity" , 360render_player_index =player_index , 361blink_interval =30 , 362} ) 363end 364 365--- @param player LuaPlayer 366--- @param player_table PlayerTable 367--- @param tape_index number 368function tape . enter_edit_mode ( player , player_table , tape_index ) 369local tape_data =player_table . tapes [ tape_index ] 370local TapeArea =area . load ( tape_data . Area ) 371 372tape_data . highlight_box =create_highlight_box ( player . index , TapeArea , tape_data . surface ) 373 374player_table . flags . editing =true 375player_table . tapes . editing =tape_data 376end 377 378--- @param player_table PlayerTable 379function tape . exit_edit_mode ( player_table ) 380local tape_data =player_table . tapes . editing 381tape_data . highlight_box . destroy () 382tape_data . last_position =nil 383player_table . flags . editing =false 384player_table . tapes . editing =nil 385end 386 387--- @param player_index uint 388--- @param player_table PlayerTable 389--- @param mode string 390--- @param divisor number 391function tape . edit_settings ( player_index , player_table , mode , divisor ) 392local tape_data =player_table . tapes . editing 393tape_data . settings . mode =mode 394tape_data . settings [ mode .. "_divisor" ] =divisor 395 396update_objects ( player_index , tape_data , tape_data . settings , player_table . visual_settings ) 397end 398 399--- @param player LuaPlayer 400--- @param player_table PlayerTable 401--- @param new_position MapPosition 402--- @param surface LuaSurface 403function tape . move ( player , player_table , new_position , surface ) 404local tape_data =player_table . tapes . editing 405if surface ~= tape_data . surface then 406return 407end 408 409local TapeArea =area . load ( tape_data . Area ) 410 411local last_position =tape_data . last_position 412if last_position then 413-- calculate delta 414local delta ={ 415x =new_position . x - last_position . x , 416y =new_position . y - last_position . y , 417} 418-- move area and origin 419TapeArea : move ( delta ): corners () 420tape_data . origin ={ 421x =tape_data . origin . x + delta . x , 422y =tape_data . origin . y + delta . y , 423} 424-- update last updated tick 425tape_data . last_update_tick =game . ticks_played 426-- update render objects 427update_objects ( player . index , tape_data , tape_data . settings , player_table . visual_settings ) 428-- update highlight box 429tape_data . highlight_box . destroy () 430tape_data . highlight_box =create_highlight_box ( player . index , TapeArea , tape_data . surface ) 431tape_data . last_position =new_position 432elseif TapeArea : contains_position ( new_position ) then 433tape_data . last_position =new_position 434end 435end 436 437--- @param player_table PlayerTable 438function tape . complete_move ( player_table ) 439player_table . tapes . editing . last_position =nil 440end 441 442--- @param tape_data TapeData 443--- @param settings VisualSettings 444function tape . update_visual_settings ( tape_data , settings ) 445local objects =tape_data . objects 446 447local line_width =settings . tape_line_width 448local draw_on_ground =settings . draw_tape_on_ground 449 450local background =objects . background 451set_color ( background , settings . tape_background_color ) 452set_draw_on_ground ( background , draw_on_ground ) 453 454local border =objects . border 455set_color ( border , settings . tape_border_color ) 456set_draw_on_ground ( border , draw_on_ground ) 457set_width ( border , line_width ) 458 459set_color ( objects . labels . x , settings . tape_label_color ) 460set_color ( objects . labels . y , settings . tape_label_color ) 461 462-- GRID LINES 463 464local lines =objects . lines 465apply_to_all_objects ( lines , set_draw_on_ground , draw_on_ground ) 466apply_to_all_objects ( lines , set_width , line_width ) 467 468apply_to_all_objects ( lines [ 1 ], set_color , settings . tape_line_color_1 ) 469apply_to_all_objects ( lines [ 2 ], set_color , settings . tape_line_color_2 ) 470apply_to_all_objects ( lines [ 3 ], set_color , settings . tape_line_color_3 ) 471apply_to_all_objects ( lines [ 4 ], set_color , settings . tape_line_color_4 ) 472end 473 474return tape