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