I don't think anyone really cares, but if you want to use this piece of work, check out the license. Ignore any previous licences.
1 # Generate images in different formats. For example 2 # 3 # to ascii 4 # 5 # ``` 6 # $ ruby pattern.rb ascii 7 # ``` 8 # 9 # to ppm 10 # 11 # ``` 12 # $ ruby pattern.rb ppm 13 # ``` 14 # 15 # to png 16 # 17 # ``` 18 # $ ruby pattern.rb ppm | convert - -scale 400% pattern.png 19 # ``` 20 21 # Map color identifiers to ASCII codes for command-line image generation. 22 Colors = { 23 "black" => "\e[0;30m", 24 "red" => "\e[0;31m", 25 "green" => "\e[0;32m", 26 "yellow" => "\e[0;33m", 27 "blue" => "\e[0;34m", 28 "magenta" => "\e[0;35m", 29 "cyan" => "\e[0;36m", 30 "white" => "\e[0;37m", 31 32 "bright_black" => "\e[0;90m", 33 "bright_red" => "\e[0;91m", 34 "bright_green" => "\e[0;92m", 35 "bright_yellow" => "\e[0;93m", 36 "bright_blue" => "\e[0;94m", 37 "bright_magenta"=> "\e[0;95m", 38 "bright_cyan" => "\e[0;96m", 39 "bright_white" => "\e[0;97m" 40 } 41 42 # Map the ASCII codes back to color identifiers. 43 IColors = Colors.invert 44 45 # Map color identifiers to PPM-formatted RGB color codes. 46 RGBColors = { 47 "black" => "0 0 0", 48 "red" => "205 49 49", 49 "green" => "13 188 121", 50 "yellow" => "229 229 16", 51 "blue" => "36 114 200", 52 "magenta" => "188 63 188", 53 "cyan" => "17 168 205", 54 "white" => "229 229 229", 55 56 "bright_black" => "102 102 102", 57 "bright_red" => "241 76 76", 58 "bright_green" => "35 209 139", 59 "bright_yellow" => "245 245 67", 60 "bright_blue" => "59 142 234", 61 "bright_magenta" => "214 112 214", 62 "bright_cyan" => "41 184 219", 63 "bright_white" => "255 255 255" 64 } 65 66 class Pattern 67 68 ## 69 # Creates a general pattern object. Patterns are always square and thus 70 # they have rows and columns. The actual pattern is save in a matrix of 71 # size rows*columns, where each element is an index corresponding to the 72 # list of colors or yarns. A special character can be set for drawing on 73 # command line. 74 # 75 76 attr_accessor :rows, :cols, :pattern, :yarns, :char 77 def initialize(rows, cols, pattern, yarns, char='█') 78 @char = char 79 @rows = rows 80 @cols = cols 81 @pattern = pattern 82 @yarns = [Colors["black"]] + yarns 83 end 84 85 # Converts the object into printable string 86 # 87 # Each item in the pattern is written as the chosen special character 88 # (i.e. '█') and wrapped to ascii color codes. 89 def to_s 90 s = "" 91 pattern.each{ |line| 92 s += line.map{ |val| 93 "#{@yarns[val]}#{char}#{char}#{@yarns[0]}" 94 }.join("") 95 96 s += "\n" 97 } 98 return s 99 end 100 101 # Converts the object into ppm image format. 102 # 103 # Each item in the pattern is written as the RGB value corresponding to the 104 # color identifier corresponding to the ascii color code saved in yarns. 105 def to_ppm 106 pattern.each{ |line| 107 line.each{ |val| 108 puts RGBColors[IColors[@yarns[val]]] 109 } 110 } 111 return nil 112 end 113 114 # Glue two patterns on top of each other. 115 def compose(p) 116 p1 = pattern 117 p2 = p.pattern 118 if (@rows == p.rows and @cols == p.cols) 119 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 120 if (p2[i][j] != 0) 121 p1[i][j] = (@yarns.length - 1) + p2[i][j] 122 end 123 }} 124 end 125 Pattern.new(@rows, @cols, p1, @yarns[1..-1] + p.yarns[1..-1]) 126 end 127 128 # Glue two patterns side by side. 129 def concat(p) 130 p1 = pattern 131 p2 = p.pattern 132 tmp = Array.new(@rows){Array.new(@cols + p.cols, 0)} 133 if (@rows == p.rows and @cols == p.cols) 134 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 135 if (p1[i][j] != 0) 136 tmp[i][j] = p1[i][j] 137 end 138 if (p2[i][j] != 0) 139 tmp[i][j+@cols] = (@yarns.length - 1) + p2[i][j] 140 end 141 }} 142 end 143 Pattern.new(@rows, @cols + p.cols, tmp, @yarns[1..-1] + p.yarns[1..-1]) 144 end 145 146 # Swap the patterns rows and coloms. 147 def transpose 148 p = pattern 149 tmp = Array.new(@cols){Array.new(@rows, 0)} 150 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 151 tmp[j][i] = p[i][j] 152 }} 153 Pattern.new(@cols, @rows, tmp, @yarns[1..-1]) 154 end 155 end 156 157 158 class Empty < Pattern 159 160 ## 161 # An blank pattern of single color. Usefull for example as the background 162 # color for composed patterns. 163 # 164 165 def initialize(side, yarns) 166 super(side, side, [], yarns) 167 end 168 169 def pattern 170 return Array.new(@rows){Array.new(@cols, 1)} 171 end 172 173 end 174 175 176 class Cross < Pattern 177 178 ## 179 # A cross pattern spanning for corner to corner. Fixed to one pixel width. 180 # 181 182 def initialize(side, yarns) 183 super(side, side, [], yarns) 184 end 185 186 def pattern 187 tmp = Array.new(@rows){Array.new(@cols, 0)} 188 (0..(@rows-1)).map{ |i| 189 tmp[i][i] = 1 190 tmp[i][(@rows-1)-i] = 1 191 } 192 return tmp 193 end 194 195 end 196 197 198 class Circle < Pattern 199 200 ## 201 # A circle pattern with given diameter centered to the pattern. 202 # 203 204 attr_accessor :diameter 205 def initialize(side, diameter, yarns) 206 @diameter = diameter 207 super(side, side, [], yarns) 208 end 209 210 def pattern 211 tmp = Array.new(@rows){Array.new(@cols, 0)} 212 y = @rows/2 213 x = @cols/2 214 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 215 if (Math.sqrt((i - x)**2 + (j - y)**2) < (@diameter/2)) 216 tmp[i][j] = 1 217 end 218 }} 219 return tmp 220 end 221 222 end 223 224 225 class Diamond < Pattern 226 227 ## 228 # A centered diamond pattern that spans a given diameter from vertex to 229 # vertex. 230 # 231 232 attr_accessor :diameter 233 def initialize(side, diameter, yarns) 234 @diameter = diameter 235 super(side, side, [], yarns) 236 end 237 238 def pattern 239 tmp = Array.new(@rows){Array.new(@cols, 0)} 240 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 241 if (((@cols/2-j).abs - @cols/2).abs > (@rows/2-i).abs and (@rows/2-i).abs < @diameter) 242 tmp[i][j] = 1 243 end 244 }} 245 return tmp 246 end 247 248 end 249 250 251 class Hourglass < Pattern 252 253 ## 254 # An hourglass pattern that spans from the corners to the center. 255 # 256 257 def initialize(side, yarns) 258 super(side, side, [], yarns) 259 end 260 261 def pattern 262 tmp = Array.new(@rows){Array.new(@cols, 0)} 263 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 264 if ((@cols/2-j).abs < (@rows/2-i).abs) 265 tmp[i][j] = 1 266 end 267 }} 268 return tmp 269 end 270 271 end 272 273 274 class Noise < Pattern 275 276 ## 277 # A pattern of random noise. 278 # 279 280 def initialize(side, yarns) 281 super(side, side, [], yarns) 282 end 283 284 def pattern 285 tmp = Array.new(@rows){Array.new(@cols, 0)} 286 (0..(@rows-1)).map{ |i| (0..(@cols-1)).map{ |j| 287 if ((1..2).to_a.sample % 2 == 0) 288 tmp[i][j] = 1 289 end 290 }} 291 return tmp 292 end 293 294 end 295 296 297 # Side length of an individual pattern. 298 side = 13 299 300 # How many patterns to generate. 301 N = 1 302 303 # The ppm header information. 304 if !(ARGV.include? "ascii") and (ARGV.include? "ppm") 305 puts "P3" 306 puts "#{2*side} #{N*side}" 307 puts "255" 308 end 309 310 311 # A dictionary of patterns. 312 patterns = { 313 a1: Diamond.new(side, 1, [Colors["black"]]), 314 a2: Hourglass.new(side, [Colors["black"]]), 315 a3: Cross.new(side, [Colors["black"]]), 316 a4: Circle.new(side, 1, [Colors["black"]]), 317 } 318 319 320 # A helper function to compose multiple patterns together. 321 def compose(patterns, names) 322 result = patterns[names.pop] 323 names.reverse_each do |name| 324 result = patterns[name].compose(result) 325 end 326 result 327 end 328 329 330 # A helper function which returns the original composition and its transpose of 331 # a set of patterns. 332 def make_pattern(patterns, names) 333 c = compose(patterns, names.dup) 334 c2 = c.concat(c.transpose) 335 return c2 336 end 337 338 339 # Print N rows of patterns. 340 (4..((N - 1) + 4)).each do |t| 341 342 # Sample a random set of primitive patterns. 343 names = [] 344 (0..t).each do |i| 345 names[i] = patterns.keys.sample 346 end 347 348 # For each primitive pattern generate random set of parametrization. 349 names.each do |name| 350 351 # For the diamonds vary the diameter. 352 if (patterns[name].instance_of? Diamond) 353 patterns[name].diameter = (0..Integer(side/2)).to_a.sample 354 end 355 356 # For the circles vary the diameter. 357 if (patterns[name].instance_of? Circle) 358 patterns[name].diameter = (Integer(side/2)..side).to_a.sample 359 end 360 361 # For each shape vary the color. 362 patterns[name].yarns[1] = Colors[Colors.keys.sample] 363 end 364 365 366 if !(ARGV.include? "ascii") and (ARGV.include? "ppm") 367 # Compose all the primitive patterns together and print the result in ppm format. 368 puts make_pattern(patterns, names).to_ppm 369 end 370 371 if (ARGV.include? "ascii") 372 # Compose all the primitive patterns together and print the result in ascii. 373 puts make_pattern(patterns, names).to_s 374 end 375 376 end