#!/usr/bin/env ruby # # A Ruby dot plotter created by Nolan Eakins . # Copyright (c) 2008 Nolan Eakins # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # require 'rubygems' require 'parse_tree' require 'rmagick' # # This computes a dot plot between @alpha and @beta. It can be accessed using # either #[] or #data. To create a dot plot you need two arrays which are # then compared element by element. Each row in the dot plot represents one # element in @alpha, and each column is an element in @beta. The dot at that # position will be true if both elements match. # class DotPlot attr_reader :data def initialize(alpha, beta) @alpha = alpha @beta = beta compute! end def [](x, y) @data[y][x] end def width @data[0].length end def height @data.length end def compute! @data = @beta.collect { |a| @alpha.collect { |b| a == b } } end end # # This DotPlot sends the two initializing strings through ParseTree so that # Ruby source files can be compared at the token level. # class RubyDotPlot < DotPlot def initialize(alpha, beta) super(ParseTree.translate(alpha).flatten, ParseTree.translate(beta).flatten) end end class LineDotPlot < DotPlot def initialize(alpha, beta) super(alpha.split("\n"), beta.split("\n")) end end # # This uses RMagick to produce a graphic using the data from a DotPlot. # class Plotter def initialize(plot) @plot = plot end # # Draws and saves the plot to {path}. # def plot(path) canvas = Magick::ImageList.new canvas.new_image(@plot.width, @plot.height) draw = Magick::Draw.new @plot.width.times { |x| @plot.height.times { |y| draw.point(x, y) if @plot[x, y] } } draw.draw(canvas) canvas.write(path) end end if __FILE__ == $0 require 'optparse' def usage "Usage: #{$0} [--help] [options] alpha beta output" end options = { :plotter => LineDotPlot } OptionParser.new do |opts| opts.banner = usage opts.on("-r", "--ruby", "Use the RubyDotPlot") do |v| options[:plotter] = RubyDotPlot end opts.on("-l", "--lines", "Use the LineDotPlot") do |v| options[:plotter] = LineDotPlot end opts.on("-h", "--help", "Show this help") do |v| puts opts exit -1 end end.parse! if ARGV.length != 3 puts usage exit -1 end puts "Plotting #{ARGV[0]} against #{ARGV[1]} to #{ARGV[2]}" plot = options[:plotter].new(File.read(ARGV[0]), File.read(ARGV[1])) Plotter.new(plot).plot(ARGV[2]) end