img_to_script 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +25 -0
  3. data/.vscode/launch.json +21 -0
  4. data/CHANGELOG.md +7 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +271 -0
  7. data/Rakefile +16 -0
  8. data/img_to_script.gemspec +41 -0
  9. data/lib/img_to_script/abs_token_type.rb +33 -0
  10. data/lib/img_to_script/abstract_token/abs_func.rb +19 -0
  11. data/lib/img_to_script/abstract_token/abstract_token.rb +23 -0
  12. data/lib/img_to_script/abstract_token/assign_value.rb +20 -0
  13. data/lib/img_to_script/abstract_token/clear_screen.rb +16 -0
  14. data/lib/img_to_script/abstract_token/data_read.rb +19 -0
  15. data/lib/img_to_script/abstract_token/data_store.rb +19 -0
  16. data/lib/img_to_script/abstract_token/draw_chunk_by_hex_value.rb +19 -0
  17. data/lib/img_to_script/abstract_token/draw_line_by_abs_coords.rb +22 -0
  18. data/lib/img_to_script/abstract_token/draw_pixel_by_abs_coords.rb +20 -0
  19. data/lib/img_to_script/abstract_token/go_to.rb +19 -0
  20. data/lib/img_to_script/abstract_token/if_condition.rb +20 -0
  21. data/lib/img_to_script/abstract_token/loop_begin.rb +21 -0
  22. data/lib/img_to_script/abstract_token/loop_end.rb +19 -0
  23. data/lib/img_to_script/abstract_token/math_add.rb +20 -0
  24. data/lib/img_to_script/abstract_token/math_greater_than.rb +20 -0
  25. data/lib/img_to_script/abstract_token/math_mult.rb +20 -0
  26. data/lib/img_to_script/abstract_token/math_sub.rb +20 -0
  27. data/lib/img_to_script/abstract_token/move_point_to_abs_coords.rb +20 -0
  28. data/lib/img_to_script/abstract_token/parentheses.rb +19 -0
  29. data/lib/img_to_script/abstract_token/program_begin.rb +16 -0
  30. data/lib/img_to_script/abstract_token/program_end.rb +16 -0
  31. data/lib/img_to_script/abstract_token/remark.rb +19 -0
  32. data/lib/img_to_script/abstract_token/sign_func.rb +19 -0
  33. data/lib/img_to_script/abstract_token/wait.rb +19 -0
  34. data/lib/img_to_script/abstract_token.rb +8 -0
  35. data/lib/img_to_script/container.rb +26 -0
  36. data/lib/img_to_script/current_line_placeholder.rb +40 -0
  37. data/lib/img_to_script/formatter.rb +34 -0
  38. data/lib/img_to_script/generators/generator.rb +134 -0
  39. data/lib/img_to_script/generators/hex_mask/default.rb +24 -0
  40. data/lib/img_to_script/generators/hex_mask/enhanced.rb +220 -0
  41. data/lib/img_to_script/generators/hex_mask/hex_mask.rb +100 -0
  42. data/lib/img_to_script/generators/hex_mask.rb +13 -0
  43. data/lib/img_to_script/generators/run_length_encoding/horizontal.rb +78 -0
  44. data/lib/img_to_script/generators/run_length_encoding/run_length_encoding.rb +304 -0
  45. data/lib/img_to_script/generators/run_length_encoding/vertical.rb +79 -0
  46. data/lib/img_to_script/generators/run_length_encoding.rb +10 -0
  47. data/lib/img_to_script/generators/segmental/data_read_draw/data_read_draw.rb +70 -0
  48. data/lib/img_to_script/generators/segmental/data_read_draw/horizontal.rb +54 -0
  49. data/lib/img_to_script/generators/segmental/data_read_draw/vertical.rb +54 -0
  50. data/lib/img_to_script/generators/segmental/data_read_draw.rb +18 -0
  51. data/lib/img_to_script/generators/segmental/direct_draw/direct_draw.rb +62 -0
  52. data/lib/img_to_script/generators/segmental/direct_draw/horizontal.rb +16 -0
  53. data/lib/img_to_script/generators/segmental/direct_draw/vertical.rb +16 -0
  54. data/lib/img_to_script/generators/segmental/direct_draw.rb +12 -0
  55. data/lib/img_to_script/generators/segmental/horizontal_mixin.rb +32 -0
  56. data/lib/img_to_script/generators/segmental/segmental.rb +101 -0
  57. data/lib/img_to_script/generators/segmental/vertical_mixin.rb +38 -0
  58. data/lib/img_to_script/generators/segmental.rb +10 -0
  59. data/lib/img_to_script/generators.rb +27 -0
  60. data/lib/img_to_script/import.rb +5 -0
  61. data/lib/img_to_script/language_token.rb +8 -0
  62. data/lib/img_to_script/languages/mk90_basic/formatters/formatter.rb +49 -0
  63. data/lib/img_to_script/languages/mk90_basic/formatters/minificator.rb +316 -0
  64. data/lib/img_to_script/languages/mk90_basic/formatters/sliceable_tokens_mixin.rb +15 -0
  65. data/lib/img_to_script/languages/mk90_basic/formatters.rb +13 -0
  66. data/lib/img_to_script/languages/mk90_basic/mk90_basic_token.rb +59 -0
  67. data/lib/img_to_script/languages/mk90_basic/translators/mixin.rb +205 -0
  68. data/lib/img_to_script/languages/mk90_basic/translators/mk90_basic_10.rb +27 -0
  69. data/lib/img_to_script/languages/mk90_basic/translators/mk90_basic_20.rb +27 -0
  70. data/lib/img_to_script/languages/mk90_basic/translators/translator.rb +205 -0
  71. data/lib/img_to_script/languages/mk90_basic/translators.rb +13 -0
  72. data/lib/img_to_script/languages/mk90_basic.rb +17 -0
  73. data/lib/img_to_script/languages.rb +10 -0
  74. data/lib/img_to_script/task.rb +26 -0
  75. data/lib/img_to_script/translator.rb +31 -0
  76. data/lib/img_to_script/version.rb +5 -0
  77. data/lib/img_to_script.rb +19 -0
  78. data/sig/img_to_script.rbs +4 -0
  79. metadata +204 -0
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImgToScript
4
+ module Generators
5
+ module HexMask
6
+ #
7
+ # Generates image rendering script using the DRAW/M statement with
8
+ # the 'enhancements'.
9
+ #
10
+ # To save space at the MPO-10 cart, it's possible to replacelong
11
+ # sequences of repeating chunks with a shorter
12
+ # (in terms of number of chars required to store the statement) code,
13
+ # e.g.:
14
+ #
15
+ # xxDRAWMFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF
16
+ # // 43 bytes -- 16 x 'AA'.
17
+ # xxDRAWMFF:FORI=1TO16:DRAWMAA:NEXTI:DRAWMFF
18
+ # // 42 bytes -- repeating chunks were replaced with a loop.
19
+ #
20
+ # Same principle could be applied to eliminate long sequences of white
21
+ # pixels, e.g.:
22
+ # xxDRAWMFF00000000000000000000FF
23
+ # // 31 bytes -- 10 x '00'.
24
+ # xxDRAWMFF:DRAWOxxx,yy:DRAWMFF
25
+ # // 29 bytes -- replaced with a manual shift.
26
+ #
27
+ # The algorithm is:
28
+ # 1. apply RLE to the array of hex chunks (DRAW/M arguments);
29
+ # 2. loop through the array of run-lengths: take an element (a 'chunk') and
30
+ # heck if it's run-length is larger than an estimated value (MIN_REP_CHUNKS
31
+ # or MIN_REP_WH_CHUNKS);
32
+ # 3. if true: add pending chunks to the BASIC code; then append a FOR-NEXT loop or
33
+ # DRAW/O. Clear pending chunks array and continue to loop through the array of
34
+ # run-lengths.
35
+ # 4. otherwise: add current chunk to the array of pending chunks;
36
+ # 5. after the loop: add pending chunks.
37
+ #
38
+ class Enhanced < HexMask
39
+ MIN_REP_CHUNKS = 16
40
+ MIN_REP_WH_CHUNKS = 9
41
+
42
+ private
43
+
44
+ def _generate
45
+ _init_instant_vars
46
+ _process_rle_image
47
+ _append_pending_chunks
48
+ end
49
+
50
+ #
51
+ # Set up initional values for the instant variables.
52
+ #
53
+ def _init_instant_vars
54
+ @x = 0 # It is always = 0, not the x/y_offset. The reason is that
55
+ @y = 0 # we've already applied offset in the #_prepare_image method.
56
+
57
+ @hex_img = _encode_img
58
+ @rle_hex_img = _encode_hex_img
59
+
60
+ @pending_chunks = []
61
+ @run_length_index = 0
62
+ end
63
+
64
+ #
65
+ # Loop through the RLE-encoded hex-image.
66
+ #
67
+ def _process_rle_image
68
+ @rle_hex_img.each_with_index do |rle_item, idx|
69
+ @chunk_count = rle_item.run_length
70
+ @chunk = rle_item.chunk
71
+
72
+ _calc_current_pos
73
+ _process_chunk(idx)
74
+ end
75
+ end
76
+
77
+ #
78
+ # Choose action for a current chunk depending on its run-length
79
+ # and its color hex value (white or non-white).
80
+ #
81
+ # @param [Integer] idx
82
+ # Index of the element in the RLE array.
83
+ #
84
+ def _process_chunk(idx)
85
+ if @chunk_count >= MIN_REP_WH_CHUNKS && @chunk == "00"
86
+ _process_white_segments(idx)
87
+ elsif @chunk_count >= MIN_REP_CHUNKS && @chunk != "00"
88
+ _process_non_white_segments
89
+ else
90
+ _upd_pending_chunks
91
+ _upd_rle_index
92
+ end
93
+ end
94
+
95
+ #
96
+ # Run-length encoding of the @hex_img.
97
+ #
98
+ # @return [Array<RunLengthEncodingRb::RLEElement>]
99
+ #
100
+ def _encode_hex_img
101
+ RunLengthEncodingRb.encode(@hex_img)
102
+ end
103
+
104
+ #
105
+ # Calculate current (X, Y) position.
106
+ #
107
+ def _calc_current_pos
108
+ @y += @chunk_count
109
+ return unless @y > @scr_height - 1
110
+
111
+ # Reached the vertical end of the screen - re-calculate position:
112
+ div_res = @y.div(@scr_height)
113
+ @x += CHUNK_WIDTH * div_res
114
+ @y = @y.remainder(@scr_height)
115
+ end
116
+
117
+ def _upd_rle_index
118
+ @run_length_index += @chunk_count
119
+ end
120
+
121
+ def _clr_pending_chunks
122
+ @pending_chunks = []
123
+ end
124
+
125
+ #
126
+ # Replace long sequences of repeating non-white chunks with a shorter FOR-NEXT loop.
127
+ #
128
+ def _process_non_white_segments
129
+ _append_pending_chunks
130
+ _clr_pending_chunks
131
+
132
+ _append_start_loop(@chunk_count)
133
+ _append_hex_values(Array(@chunk))
134
+ _append_end_loop
135
+
136
+ _upd_rle_index
137
+ end
138
+
139
+ def _append_hex_values(hex_values)
140
+ @tokens.append(
141
+ AbstractToken::DrawChunkByHexValue.new(
142
+ hex_values: hex_values
143
+ )
144
+ )
145
+ end
146
+
147
+ def _append_start_loop(end_value)
148
+ @tokens.append(
149
+ AbstractToken::LoopBegin.new(
150
+ var_name: LOOP_VAR,
151
+ start_value: 1,
152
+ end_value: end_value
153
+ )
154
+ )
155
+ end
156
+
157
+ def _append_end_loop
158
+ @tokens.append(
159
+ AbstractToken::LoopEnd.new(
160
+ var_name: LOOP_VAR
161
+ )
162
+ )
163
+ end
164
+
165
+ def _append_move_point(x, y)
166
+ @tokens.append(
167
+ AbstractToken::MovePointToAbsCoords.new(
168
+ x: x,
169
+ y: y
170
+ )
171
+ )
172
+ end
173
+
174
+ #
175
+ # Replace long sequences of white chunks (0x00) with a shorter manual shift of the (X, Y) point.
176
+ #
177
+ # @param [Integer] idx
178
+ # Index of the element in the RLE array.
179
+ #
180
+ def _process_white_segments(idx)
181
+ # If white segments are the last part of the image,
182
+ # we don't need to store them in the BASIC script at all
183
+ return if idx == @rle_hex_img.length - 1
184
+
185
+ _append_pending_chunks
186
+ _clr_pending_chunks
187
+
188
+ _append_move_point(@x, @y)
189
+
190
+ _upd_rle_index
191
+ end
192
+
193
+ #
194
+ # Append accumulated pending chunks to the tokens array.
195
+ #
196
+ def _append_pending_chunks
197
+ return if @pending_chunks == []
198
+
199
+ _append_hex_values(@pending_chunks)
200
+ end
201
+
202
+ #
203
+ # Update array of the pending chunks.
204
+ #
205
+ def _upd_pending_chunks
206
+ @pending_chunks.concat _current_pending_chunks
207
+ end
208
+
209
+ #
210
+ # Retun chunks that are pending at the current itteration.
211
+ #
212
+ # @return [Array<String>]
213
+ #
214
+ def _current_pending_chunks
215
+ @hex_img.slice(@run_length_index, @chunk_count)
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImgToScript
4
+ module Generators
5
+ module HexMask
6
+ #
7
+ # Base class for the hex-mask-based generators.
8
+ #
9
+ class HexMask < Generator
10
+ CHUNK_WIDTH = 8
11
+
12
+ private
13
+
14
+ #
15
+ # Encode a binary image to a hex-mask encoded image.
16
+ #
17
+ # @return [Array<String>] hex_img
18
+ # An encoded image, represented as an array of hex-chunks.
19
+ #
20
+ def _encode_img
21
+ image = _prepare_image
22
+
23
+ hex_img = []
24
+
25
+ (0...image.width).step(CHUNK_WIDTH).each do |x|
26
+ (0...image.height).each do |y|
27
+ chunk = _grab_chunk(image, x, y)
28
+ hex_img.push(_bin_to_hex(chunk))
29
+ end
30
+ end
31
+
32
+ hex_img
33
+ end
34
+
35
+ #
36
+ # Prepare the image:
37
+ # 1. ensure that image width is divisible by 8 (CHUNK_WIDTH);
38
+ # 2. ensure that image height is equal to @scr_height;
39
+ # 3. offset the image according to the provided @x_offset / @y_offset.
40
+ #
41
+ # Steps 1 & 2 are required by the DRAW/M statement's algorithm.
42
+ #
43
+ def _prepare_image
44
+ @image.extent(_new_width,
45
+ @scr_height,
46
+ @x_offset * -1,
47
+ @y_offset * -1)
48
+ end
49
+
50
+ #
51
+ # Calculate new width for the image.
52
+ #
53
+ # The algorithm of the DRAW/M statement requires that image width should
54
+ # be divisible by 8 (CHUNK_WIDTH). If the image doesn't comply with this
55
+ # condition, it is required to extend the image width to a nearlest number
56
+ # n: n.remainder(CHUNK_WIDTH) == 0.
57
+ #
58
+ # @return [Integer]
59
+ # New width.
60
+ #
61
+ def _new_width
62
+ width = ((@image.width + @x_offset) / CHUNK_WIDTH.to_f).ceil * CHUNK_WIDTH
63
+ width.clamp(CHUNK_WIDTH, @scr_width)
64
+ end
65
+
66
+ #
67
+ # Grab a 8 x 1 rectangle from the image and convert its pixels to a binary string representation.
68
+ #
69
+ # @param [Magick::Image, Magick::BinMagick::Image] image
70
+ #
71
+ # @param [Integer] x, y
72
+ # A current X, Y position at the image.
73
+ #
74
+ # @return [String]
75
+ # An 8-character string of 0's and 1's. Example: "11110011"
76
+ #
77
+ def _grab_chunk(image, x, y)
78
+ chunk_width = CHUNK_WIDTH
79
+ chunk_heigth = 1
80
+ pixels = image.get_pixels(x, y, chunk_width, chunk_heigth)
81
+
82
+ pixels.map { |pixel| pixel.to_color == "black" ? 1 : 0 }.join("")
83
+ end
84
+
85
+ #
86
+ # Convert a string of binary values to a two-digit hex representation.
87
+ #
88
+ # Example: "11110011" => "F3"
89
+ #
90
+ # @param [String] str
91
+ #
92
+ # @return [String]
93
+ #
94
+ def _bin_to_hex(str)
95
+ format("%02x", str.to_i(2)).upcase
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImgToScript
4
+ module Generators
5
+ #
6
+ # Each 8x1 pixels block of a binary image gets encoded as a hex value.
7
+ #
8
+ # The method natively supported only by the Elektronika MK90 BASIC with its
9
+ # DRAWM statement.
10
+ #
11
+ module HexMask; end
12
+ end
13
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImgToScript
4
+ module Generators
5
+ module RunLengthEncoding
6
+ #
7
+ # RLE - horizontal scanlines.
8
+ #
9
+ class Horizontal < RunLengthEncoding
10
+ private
11
+
12
+ def _generate
13
+ @pixels = @image.get_pixels(0, 0, @image.width, @image.height)
14
+ _encode_pixels
15
+ _append_decoder
16
+ end
17
+
18
+ def _append_decoder
19
+ @image_size = @image.width + @x_offset
20
+ @segment_size = @image_size - 1
21
+
22
+ @major_axis_symbol = X_LBL
23
+ @minor_axis_symbol = Y_LBL
24
+
25
+ @major_axis_value = @x_offset
26
+
27
+ @part_line_pattern = _part_line_pattern
28
+ @full_line_pattern = _full_line_pattern
29
+
30
+ super
31
+ end
32
+
33
+ #
34
+ # Part-length line, i.e. a line that fills only
35
+ # a part of the scan line.
36
+ #
37
+ # Formatted for the horizontal scan lines.
38
+ #
39
+ def _part_line_pattern
40
+ AbstractToken::DrawLineByAbsCoords.new(
41
+ x0: X_LBL,
42
+ y0: Y_LBL,
43
+ x1: _x1_expression,
44
+ y1: Y_LBL
45
+ )
46
+ end
47
+
48
+ #
49
+ # Expression to evaluate x1 point.
50
+ #
51
+ def _x1_expression
52
+ AbstractToken::MathSub.new(
53
+ left: AbstractToken::MathAdd.new(
54
+ left: X_LBL,
55
+ right: READ_VAR
56
+ ),
57
+ right: 1
58
+ )
59
+ end
60
+
61
+ #
62
+ # Full-length line pattern, i.e. a line that fills
63
+ # the whole scan line.
64
+ #
65
+ # Formatted for the horizontal scan lines.
66
+ #
67
+ def _full_line_pattern
68
+ AbstractToken::DrawLineByAbsCoords.new(
69
+ x0: X_LBL,
70
+ y0: Y_LBL,
71
+ x1: @segment_size,
72
+ y1: Y_LBL
73
+ )
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,304 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImgToScript
4
+ module Generators
5
+ module RunLengthEncoding
6
+ #
7
+ # Base class for the RLE-based script generators.
8
+ #
9
+ class RunLengthEncoding < Generator
10
+ private
11
+
12
+ #
13
+ # Encode pixels into the RLE data.
14
+ #
15
+ def _encode_pixels
16
+ @run_length_data = RunLengthEncodingRb.encode(@pixels)
17
+ run_lengths = []
18
+
19
+ encode = lambda { |count:, pixel:|
20
+ count = -count if pixel.to_color == "white"
21
+ run_lengths.push(count.to_s)
22
+ }
23
+
24
+ @run_length_data.each { |e| encode.call(pixel: e.chunk, count: e.run_length) }
25
+
26
+ _append_data(run_lengths)
27
+ end
28
+
29
+ def _append_data(arr)
30
+ @tokens.append(
31
+ AbstractToken::DataStore.new(
32
+ data: arr,
33
+ require_nl: true
34
+ )
35
+ )
36
+ end
37
+
38
+ #
39
+ # Append RLE decoder procedure afther the DATA sequence.
40
+ #
41
+ def _append_decoder
42
+ _dec_line01
43
+ _dec_line02
44
+ _dec_line03
45
+ _dec_line04
46
+ _dec_line05
47
+ _dec_line06
48
+ end
49
+
50
+ #
51
+ # 1-st line of the decoder procedure.
52
+ #
53
+ # In this line the X & Y variables are defined. The X and Y variables
54
+ # are used to keep track of the current position on the screen.
55
+ #
56
+ # Then the main loop starts. The loop iterates over each pixel value
57
+ # (represented by the variable READ_VAR) in the input data and translates
58
+ # the RLE-encoded values into the "draw a line" commands that draw lines
59
+ # on the screen.
60
+ #
61
+ def _dec_line01
62
+ _init_x
63
+ _init_y
64
+ _start_loop
65
+ _read_value
66
+ end
67
+
68
+ #
69
+ # 2-nd line of the decoder procedure.
70
+ #
71
+ # The IF statement is used to check cases where the line would extend
72
+ # beyond the bounds of the image/screen. In this case the program jumps
73
+ # to the 5-th line (3 lines down from the current line) of the decoder,
74
+ # that handles this edge case.
75
+ #
76
+ def _dec_line02
77
+ expression = AbstractToken::MathGreaterThan.new(
78
+ left: _sum_of_current_point_and_length,
79
+ right: @segment_size
80
+ )
81
+
82
+ @tokens.append(
83
+ AbstractToken::IfCondition.new(
84
+ expression: expression,
85
+ consequent: CurrentLinePlaceholder.new(3),
86
+ require_nl: true
87
+ )
88
+ )
89
+ end
90
+
91
+ #
92
+ # 3-rd line of the decoder procedure.
93
+ #
94
+ # Draws a line if S is a positive number. Positive numbers represent
95
+ # black pixels. Negative numbers represent white pixels.
96
+ #
97
+ def _dec_line03
98
+ @tokens.append(
99
+ AbstractToken::IfCondition.new(
100
+ expression: _read_var_positive,
101
+ consequent: @part_line_pattern,
102
+ require_nl: true
103
+ )
104
+ )
105
+ end
106
+
107
+ #
108
+ # 4-th line of the decoder procedure.
109
+ #
110
+ # Updates major axis position. Ends the main loop. Jumps to the end
111
+ # of the program.
112
+ #
113
+ def _dec_line04
114
+ _update_current_point
115
+ _end_loop
116
+ _jump_to_the_end
117
+ end
118
+
119
+ #
120
+ # 5-th line of the decoder procedure.
121
+ #
122
+ # This part handles cases where the line would extend beyond the
123
+ # bounds of the image/screen. It draws a line from the current
124
+ # position up to the end of the image/screen.
125
+ #
126
+ def _dec_line05
127
+ @tokens.append(
128
+ AbstractToken::IfCondition.new(
129
+ expression: _read_var_positive,
130
+ consequent: @full_line_pattern,
131
+ require_nl: true
132
+ )
133
+ )
134
+ end
135
+
136
+ #
137
+ # 6-th line of the decoder procedure.
138
+ #
139
+ # This part handles cases where the line would extend beyond the
140
+ # bounds of the image/screen. It updates the current position on
141
+ # the screen and the run-length value. When it jumps back to the
142
+ # line length check (2nd line of the decoder).
143
+ #
144
+ def _dec_line06
145
+ _increment_minor_axis
146
+ _update_read_variable
147
+ _reset_major_axis
148
+ _jump_to_length_check
149
+ end
150
+
151
+ def _read_var_positive
152
+ AbstractToken::MathGreaterThan.new(
153
+ left: READ_VAR,
154
+ right: 0
155
+ )
156
+ end
157
+
158
+ def _init_x
159
+ @tokens.append(
160
+ AbstractToken::AssignValue.new(
161
+ left: X_LBL,
162
+ right: @x_offset,
163
+ require_nl: true
164
+ )
165
+ )
166
+ end
167
+
168
+ def _init_y
169
+ @tokens.append(
170
+ AbstractToken::AssignValue.new(
171
+ left: Y_LBL,
172
+ right: @y_offset
173
+ )
174
+ )
175
+ end
176
+
177
+ def _start_loop
178
+ @tokens.append(
179
+ AbstractToken::LoopBegin.new(
180
+ var_name: LOOP_VAR,
181
+ start_value: 1,
182
+ end_value: @run_length_data.length
183
+ )
184
+ )
185
+ end
186
+
187
+ def _read_value
188
+ @tokens.append(
189
+ AbstractToken::DataRead.new(
190
+ var_list: READ_VAR
191
+ )
192
+ )
193
+ end
194
+
195
+ def _abs_length_value
196
+ AbstractToken::AbsFunc.new(
197
+ expression: READ_VAR
198
+ )
199
+ end
200
+
201
+ def _sum_of_current_point_and_length
202
+ AbstractToken::MathAdd.new(
203
+ left: _abs_length_value,
204
+ right: @major_axis_symbol
205
+ )
206
+ end
207
+
208
+ def _update_current_point
209
+ @tokens.append(
210
+ AbstractToken::AssignValue.new(
211
+ left: @major_axis_symbol,
212
+ right: _sum_of_current_point_and_length,
213
+ require_nl: true
214
+ )
215
+ )
216
+ end
217
+
218
+ def _end_loop
219
+ @tokens.append(
220
+ AbstractToken::LoopEnd.new(
221
+ var_name: LOOP_VAR
222
+ )
223
+ )
224
+ end
225
+
226
+ def _jump_to_the_end
227
+ @tokens.append(
228
+ AbstractToken::GoTo.new(
229
+ line: CurrentLinePlaceholder.new(3)
230
+ )
231
+ )
232
+ end
233
+
234
+ def _sign_func
235
+ AbstractToken::SignFunc.new(
236
+ expression: READ_VAR
237
+ )
238
+ end
239
+
240
+ def _increment_minor_axis
241
+ @tokens.append(
242
+ AbstractToken::AssignValue.new(
243
+ left: @minor_axis_symbol,
244
+ right: AbstractToken::MathAdd.new(
245
+ left: @minor_axis_symbol,
246
+ right: 1
247
+ ),
248
+ require_nl: true
249
+ )
250
+ )
251
+ end
252
+
253
+ def _reset_major_axis
254
+ @tokens.append(
255
+ AbstractToken::AssignValue.new(
256
+ left: @major_axis_symbol,
257
+ right: @major_axis_value
258
+ )
259
+ )
260
+ end
261
+
262
+ def _jump_to_length_check
263
+ @tokens.append(
264
+ AbstractToken::GoTo.new(
265
+ line: CurrentLinePlaceholder.new(-4)
266
+ )
267
+ )
268
+ end
269
+
270
+ def _sum_token
271
+ AbstractToken::MathAdd.new(
272
+ left: _abs_length_value,
273
+ right: @major_axis_symbol
274
+ )
275
+ end
276
+
277
+ def _sub_token
278
+ AbstractToken::MathSub.new(
279
+ left: _sum_token,
280
+ right: @image_size
281
+ )
282
+ end
283
+
284
+ def _mult_token
285
+ AbstractToken::MathMult.new(
286
+ left: AbstractToken::Parentheses.new(
287
+ expression: _sub_token
288
+ ),
289
+ right: _sign_func
290
+ )
291
+ end
292
+
293
+ def _update_read_variable
294
+ @tokens.append(
295
+ AbstractToken::AssignValue.new(
296
+ left: READ_VAR,
297
+ right: _mult_token
298
+ )
299
+ )
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end