Advertisement
Guest User

Complex plot GIFs

a guest
Jul 5th, 2014
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 8.17 KB | None | 0 0
  1. ##########################################
  2. ################ main.rb #################
  3. require '.\complex_plot.rb'
  4. require 'rmagick' # See note below if you don't want to fuck around with this
  5. require 'cmath'
  6.  
  7. include CMath
  8. include Cryoplot
  9. include Magick
  10.  
  11. N = 16          #** Number of frames **#
  12. DELAY = 20      #** Delay between frames, in hundredths of a second **#
  13. NAME = "pulsy.gif"  #** Output filename **#
  14.  
  15. I = Complex(0.0, 1.0)
  16.  
  17. # For each frame...
  18. for i in 0...N
  19.     print "..."
  20.     #---DEFINE BOUNDS---#
  21.     x = ComplexPlot.new(400, 300, -2.0, 2.0, -1.5, 1.5) #** width, height, min real, max real, min img, max img **#
  22.     #----END BOUNDS----#
  23.     #-------FUNCTION HERE--------#
  24.     c1 = cos(2*PI*i/N)  #** This is a coefficient, calculated only once each frame **#
  25.     x.plot { |z|
  26.         (z+c1)/(z-c1)   #** This is your function definition **#
  27.     }
  28.     #--------FUNCTION END--------#
  29.     x.save(".\\samples\\temp\\IMG_00#{i}.png")
  30.     puts "#{i}"
  31. end
  32.  
  33. list = []
  34. for i in 0...N
  35.     list << ".\\samples\\temp\\IMG_00#{i}.png"
  36. end
  37.  
  38. # The next three lines generate the gif from each frame.
  39. # If you can't get RMagick/ImageMagick working, you can
  40. # get rid of these lines and stitch them with another tool
  41. # manually or otherwise.
  42. animation = ImageList.new(*list)
  43. animation.delay = DELAY
  44. animation.write(".\\samples\\#{NAME}")
  45.  
  46.  
  47. ##########################################
  48. ##### Everything below here you do not need
  49. ##### to modify.  Look above
  50.  
  51. ##########################################
  52. ############ complex_plot.rb #############
  53. require 'cmath'
  54. require 'chunky_png'
  55.  
  56. module Cryoplot # :nodoc:
  57.  
  58.     # Cryoplot::ComplexPlot represents an individual complex-valued plot.
  59.     class ComplexPlot < ChunkyPNG::Image
  60.  
  61.         # Specify a domain colored plot
  62.         DOMAIN_COLORED_PLOT     = 0x1
  63.         # Specify a contour map plot
  64.         COMPLEX_CONTOUR_PLOT    = 0x2
  65.  
  66.         # Default number of contour lines in either dimension.
  67.         DLINES = 10
  68.  
  69.         # The number of functions on this plot.  Starts at 0, increments each time plot is called.
  70.         attr_reader :size
  71.  
  72.         # The width and height of the graph portion of the plot, in pixels, but not necessarily of the
  73.         # entire plot, if margins are non-zero.
  74.         attr_reader :width, :height
  75.  
  76.         # The real and imaginary bounds of the graph.
  77.         attr_reader :min_r, :max_r, :min_i, :max_i
  78.  
  79.         # Size of margins, in pixels, around the graph.
  80.         attr_reader :top_margin, :bottom_margin, :left_margin, :right_margin
  81.  
  82.         # Background color for margins and contour maps.
  83.         attr_reader :bg_color
  84.  
  85.         # Initializes a new instance of Cryoplot::ComplexPlot
  86.         # @param width [Integer] The width of the plot area, in pixels (excluding margins)
  87.         # @param height [Integer] The height of the plot area, in pixels (excluding margins)
  88.         # @param min_r [Float] Lowest real part of complex numbers in the domain
  89.         # @param max_r [Float] Highest real part of complex numbers in the domain
  90.         # @param min_i [Float] Lowest imaginary part of complex numbers in the domain
  91.         # @param max_i [Float] Highest imaginary part of complex numbers in the domain
  92.         # @param top_margin [Integer] Height of top margin, in pixels
  93.         # @param bottom_margin [Integer] Height of bottom margin, in pixels
  94.         # @param left_margin [Integer] Width of left margin, in pixels
  95.         # @param right_margin [Integer] Width of right margin, in pixels
  96.         # @param bg_color [ChunkyPNG::Color] Default background color.
  97.         def initialize(width, height, min_r, max_r, min_i, max_i,
  98.                 top_margin=0, bottom_margin=0, left_margin=0, right_margin=0,
  99.                 bg_color = ChunkyPNG::Color::PREDEFINED_COLORS[:whitesmoke]+0xFF)
  100.             super(width+left_margin+right_margin, height+top_margin+bottom_margin, bg_color)
  101.             @width, @height = width, height
  102.             @min_r, @max_r = min_r, max_r
  103.             @min_i, @max_i = min_i, max_i
  104.             @top_margin, @bottom_margin = top_margin, bottom_margin
  105.             @left_margin, @right_margin = left_margin, right_margin
  106.             @bg_color = bg_color
  107.             @size = 0;
  108.         end
  109.  
  110.         # Draws a plot of a complex-valued function.  Multiple functions may be graphed on one ComplexPlot instance.
  111.         # However, only one domain-colored plot can be drawn and it must come first, or it will draw over other plots.
  112.         # Parameters must be referenced by key.
  113.         # @param type [Integer] A constant reffering to the plot type, either DOMAIN_COLORED_PLOT or COMPLEX_CONTOUR_PLOT
  114.         # @param color [ChunkyPNG::Color] If contour plot, the color of the contours.  If domain-colored, color of out-of-domain values.
  115.         # @param vert [Integer] Number of vertical lines in the un-transformed map (if creating a contour plot).  Values must be >= 2
  116.         # @param horz [Integer] Number of horizontal lines in the un-transformed map.  >= 2
  117.         # :yields: z [Complex] Iterates through the domain of the plot.  The return value of each iteration in the block will be plotted at a pixel.
  118.         def plot(opts = {}, &block)
  119.             default_opts = {:type => DOMAIN_COLORED_PLOT, :color => ChunkyPNG::Color::BLACK,
  120.                 :vert => DLINES, :horz => DLINES}
  121.             opts = default_opts.merge(opts)
  122.             if opts[:type] == DOMAIN_COLORED_PLOT and @size > 0
  123.                 warn "Warning in #{self.class}::plot: DOMAIN_COLORED_PLOT will overdraw existing #{@size} plots.  Draw 1st for correct behavior."
  124.             end
  125.             case opts[:type]
  126.                 when DOMAIN_COLORED_PLOT then plotDCPlot(opts, &block)
  127.                 when COMPLEX_CONTOUR_PLOT then plotCCPlot(opts, &block)
  128.                 else raise RangeError, "Invalid plot type #{opts[:type]}."
  129.             end
  130.             @size += 1
  131.         end
  132.  
  133.         #--
  134.         # Private Methods below
  135.  
  136.         # TODO: Finish other private methods
  137.         # Like this: http://en.wikipedia.org/wiki/Domain_coloring
  138.         def plotDCPlot(opts)
  139.             for pixel_x in @left_margin..(@width+@left_margin-1)
  140.                 for pixel_y in @top_margin..(@top_margin+@height-1)
  141.                     z = pixelToComplex(pixel_x, pixel_y)
  142.                     begin
  143.                         fz = yield(z)
  144.                         self[pixel_x, pixel_y] = complexToColor(fz)
  145.                     rescue
  146.                         self[pixel_x, pixel_y] = opts[:color]
  147.                     end
  148.                 end
  149.             end
  150.         end
  151.  
  152.         # TODO: Make this actually do stuff
  153.         # Like this: http://en.wikipedia.org/wiki/Conformal_map
  154.         # Described here under "Using conformal maps": http://www.pacifict.com/ComplexFunctions.html
  155.         def plotCCPlot(opts)
  156.             verticals = linspace(@left_margin, opts[:vert], @left_margin+@width-1)
  157.             horizontals = linspace(@top_margin, opts[:horz], @top_margin+@height-1)
  158.             for pixel_x in verticals
  159.                 old_p = nil
  160.                 for pixel_y in @top_margin..(@top_margin+@height-1)
  161.                     z = pixelToComplex(pixel_x, pixel_y)
  162.                     begin
  163.                         fz = yield(z)
  164.                         new_p = complexToPixel(fz)
  165.                          if !old_p.nil? and in_range?(old_p) and in_range?(new_p)
  166.                             self.line(old_p[0],old_p[1],new_p[0],new_p[1],0x000000ff)
  167.                         end
  168.                         old_p = Array.new(new_p)
  169.                     rescue
  170.                         old_p = nil
  171.                     end
  172.                 end
  173.             end
  174.         end
  175.  
  176.         def pixelToComplex(px, py)
  177.             Complex((px-@left_margin)*(@max_r-@min_r)/@width.to_f+@min_r,
  178.                     (py-@top_margin)*(@min_i-@max_i)/@height.to_f+@max_i)
  179.         end
  180.  
  181.         def complexToPixel(z)
  182.             [@width*(z.real-@min_r)/(@max_r-@min_r) + @left_margin,
  183.             @height*(z.imag-@max_i)/(@min_i-@max_i) + @top_margin]
  184.         end
  185.  
  186.         def complexToColor(z)
  187.             hue = z.angle * 180.0/CMath::PI
  188.             r = CMath.log(1.0 + z.magnitude)
  189.             sat = (1.0 + CMath.sin(2*CMath::PI*r))*0.25 + 0.5
  190.             val = (1.0 + CMath.cos(2*CMath::PI*r))*0.25 + 0.5
  191.             # HSV to RGB
  192.             hue = hue % 360.0
  193.             col = sat * val
  194.             hp = hue/60.0
  195.             xcol = col * (1.0 - (hp%2.0 - 1.0).abs)
  196.             if 0<=hp and hp<1
  197.                 r,g,b = col, xcol, 0.0
  198.             elsif 1<=hp and hp<2
  199.                 r,g,b = xcol, col, 0.0
  200.             elsif 2<=hp and hp<3
  201.                 r,g,b = 0.0, col, xcol
  202.             elsif 3<=hp and hp<4
  203.                 r,g,b = 0.0, xcol, col
  204.             elsif 4<=hp and hp<5
  205.                 r,g,b = xcol, 0.0, col
  206.             elsif 5<=hp and hp<=6
  207.                 r,g,b = col, 0.0, xcol
  208.             else
  209.                 r,g,b = 0.0, 0.0, 0.0
  210.             end
  211.             m = val - col
  212.             r += m
  213.             g += m
  214.             b += m
  215.             return ChunkyPNG::Color.rgb((255*r).to_i, (255*g).to_i, (255*b).to_i)
  216.         end
  217.  
  218.         # Integer linspace from x to y, with size n
  219.         def linspace(x, n, y)
  220.             a = []
  221.             for i in 0..(n-1)
  222.                 a[i] = x*(n-1-i)/(n-1) + y*i/(n-1)
  223.             end
  224.             return a
  225.         end
  226.  
  227.         def in_range?(pix)
  228.             pix[0].between?(@left_margin, @left_margin+@width-1) and pix[1].between?(@top_margin, @top_margin+@height-1)
  229.         end
  230.  
  231.         private :plotDCPlot, :plotCCPlot, :pixelToComplex, :complexToColor, :complexToPixel, :linspace, :in_range?
  232.  
  233.     end
  234.  
  235. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement