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.
- checksums.yaml +7 -0
- data/.rubocop.yml +25 -0
- data/.vscode/launch.json +21 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +271 -0
- data/Rakefile +16 -0
- data/img_to_script.gemspec +41 -0
- data/lib/img_to_script/abs_token_type.rb +33 -0
- data/lib/img_to_script/abstract_token/abs_func.rb +19 -0
- data/lib/img_to_script/abstract_token/abstract_token.rb +23 -0
- data/lib/img_to_script/abstract_token/assign_value.rb +20 -0
- data/lib/img_to_script/abstract_token/clear_screen.rb +16 -0
- data/lib/img_to_script/abstract_token/data_read.rb +19 -0
- data/lib/img_to_script/abstract_token/data_store.rb +19 -0
- data/lib/img_to_script/abstract_token/draw_chunk_by_hex_value.rb +19 -0
- data/lib/img_to_script/abstract_token/draw_line_by_abs_coords.rb +22 -0
- data/lib/img_to_script/abstract_token/draw_pixel_by_abs_coords.rb +20 -0
- data/lib/img_to_script/abstract_token/go_to.rb +19 -0
- data/lib/img_to_script/abstract_token/if_condition.rb +20 -0
- data/lib/img_to_script/abstract_token/loop_begin.rb +21 -0
- data/lib/img_to_script/abstract_token/loop_end.rb +19 -0
- data/lib/img_to_script/abstract_token/math_add.rb +20 -0
- data/lib/img_to_script/abstract_token/math_greater_than.rb +20 -0
- data/lib/img_to_script/abstract_token/math_mult.rb +20 -0
- data/lib/img_to_script/abstract_token/math_sub.rb +20 -0
- data/lib/img_to_script/abstract_token/move_point_to_abs_coords.rb +20 -0
- data/lib/img_to_script/abstract_token/parentheses.rb +19 -0
- data/lib/img_to_script/abstract_token/program_begin.rb +16 -0
- data/lib/img_to_script/abstract_token/program_end.rb +16 -0
- data/lib/img_to_script/abstract_token/remark.rb +19 -0
- data/lib/img_to_script/abstract_token/sign_func.rb +19 -0
- data/lib/img_to_script/abstract_token/wait.rb +19 -0
- data/lib/img_to_script/abstract_token.rb +8 -0
- data/lib/img_to_script/container.rb +26 -0
- data/lib/img_to_script/current_line_placeholder.rb +40 -0
- data/lib/img_to_script/formatter.rb +34 -0
- data/lib/img_to_script/generators/generator.rb +134 -0
- data/lib/img_to_script/generators/hex_mask/default.rb +24 -0
- data/lib/img_to_script/generators/hex_mask/enhanced.rb +220 -0
- data/lib/img_to_script/generators/hex_mask/hex_mask.rb +100 -0
- data/lib/img_to_script/generators/hex_mask.rb +13 -0
- data/lib/img_to_script/generators/run_length_encoding/horizontal.rb +78 -0
- data/lib/img_to_script/generators/run_length_encoding/run_length_encoding.rb +304 -0
- data/lib/img_to_script/generators/run_length_encoding/vertical.rb +79 -0
- data/lib/img_to_script/generators/run_length_encoding.rb +10 -0
- data/lib/img_to_script/generators/segmental/data_read_draw/data_read_draw.rb +70 -0
- data/lib/img_to_script/generators/segmental/data_read_draw/horizontal.rb +54 -0
- data/lib/img_to_script/generators/segmental/data_read_draw/vertical.rb +54 -0
- data/lib/img_to_script/generators/segmental/data_read_draw.rb +18 -0
- data/lib/img_to_script/generators/segmental/direct_draw/direct_draw.rb +62 -0
- data/lib/img_to_script/generators/segmental/direct_draw/horizontal.rb +16 -0
- data/lib/img_to_script/generators/segmental/direct_draw/vertical.rb +16 -0
- data/lib/img_to_script/generators/segmental/direct_draw.rb +12 -0
- data/lib/img_to_script/generators/segmental/horizontal_mixin.rb +32 -0
- data/lib/img_to_script/generators/segmental/segmental.rb +101 -0
- data/lib/img_to_script/generators/segmental/vertical_mixin.rb +38 -0
- data/lib/img_to_script/generators/segmental.rb +10 -0
- data/lib/img_to_script/generators.rb +27 -0
- data/lib/img_to_script/import.rb +5 -0
- data/lib/img_to_script/language_token.rb +8 -0
- data/lib/img_to_script/languages/mk90_basic/formatters/formatter.rb +49 -0
- data/lib/img_to_script/languages/mk90_basic/formatters/minificator.rb +316 -0
- data/lib/img_to_script/languages/mk90_basic/formatters/sliceable_tokens_mixin.rb +15 -0
- data/lib/img_to_script/languages/mk90_basic/formatters.rb +13 -0
- data/lib/img_to_script/languages/mk90_basic/mk90_basic_token.rb +59 -0
- data/lib/img_to_script/languages/mk90_basic/translators/mixin.rb +205 -0
- data/lib/img_to_script/languages/mk90_basic/translators/mk90_basic_10.rb +27 -0
- data/lib/img_to_script/languages/mk90_basic/translators/mk90_basic_20.rb +27 -0
- data/lib/img_to_script/languages/mk90_basic/translators/translator.rb +205 -0
- data/lib/img_to_script/languages/mk90_basic/translators.rb +13 -0
- data/lib/img_to_script/languages/mk90_basic.rb +17 -0
- data/lib/img_to_script/languages.rb +10 -0
- data/lib/img_to_script/task.rb +26 -0
- data/lib/img_to_script/translator.rb +31 -0
- data/lib/img_to_script/version.rb +5 -0
- data/lib/img_to_script.rb +19 -0
- data/sig/img_to_script.rbs +4 -0
- 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
|