Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

started adding test coverage

  • Loading branch information...
commit c34609bf41e1d9f4d39a42ed4a47aa197ec4abd1 1 parent 2ad7f0c
@mattetti authored
View
23 examples/canvas_example.rb
@@ -7,22 +7,21 @@ class CustomView < NSView
include MRGraphics
def drawRect(rect)
- canvas = Canvas.for_image(:size => [400,400]) do
- background(Color.black)
-
+ canvas = Canvas.for_image(:size => [400,400]) do |c|
+ c.background(Color.black)
white = Color.white
- fill(white)
- stroke(0.2)
- strokewidth(1)
- font("Zapfino")
+ c.fill(white)
+ c.stroke(0.2)
+ c.stroke_width(1)
+ c.font("Zapfino")
80.times do
- fontsize rand(170)
- fill(white.copy.darken(rand(0.8)))
+ c.font_size rand(170)
+ c.fill(white.copy.darken(rand(0.8)))
letters = %W{ g i a n a }
- text(letters[rand(letters.size)],
- rand(width),
- rand(height))
+ c.text(letters[rand(letters.size)],
+ rand(c.width),
+ rand(c.height))
end
end
View
2  examples/image_blend_example.rb
@@ -12,7 +12,7 @@ def drawRect(rect)
canvas = Canvas.for_image(:size => dimensions) do
background(Color.white)
font('Skia')
- fontsize(14)
+ font_size(14)
# set image width,height
w, h = [95,95]
# set initial drawing position
View
105 graphics.rb
@@ -9,14 +9,13 @@
# in Java and Python, respectively. RCG was created to offer similar functionality using
# the Ruby programming language.
#
-# Author:: James Reynolds (mailto:drtoast@drtoast.com)
+# Author:: James Reynolds (mailto:drtoast@drtoast.com), Matt Aimonetti
# Copyright:: Copyright (c) 2008 James Reynolds
# License:: Distributes under the same terms as Ruby
# More information about Quartz 2D is available on the Apple's website:
# http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_overview/dq_overview.html#//apple_ref/doc/uid/TP30001066-CH202-TPXREF101
-
framework 'Cocoa'
framework 'Quartz'
@@ -26,44 +25,44 @@ module MRGraphics
TEST = 'OK'
# convert degrees to radians
- def radians(deg)
+ def self.radians(deg)
deg * (Math::PI / 180.0)
end
# convert radians to degrees
- def degrees(rad)
+ def self.degrees(rad)
rad * (180 / Math::PI)
end
# return the angle of the line joining the two points
- def angle(x0, y0, x1, y1)
+ def self.angle(x0, y0, x1, y1)
degrees(Math.atan2(y1-y0, x1-x0))
end
# return the distance between two points
- def distance(x0, y0, x1, y1)
+ def self.distance(x0, y0, x1, y1)
Math.sqrt((x1-x0)**2 + (y1-y0)**2)
end
# return the coordinates of a new point at the given distance and angle from a starting point
- def coordinates(x0, y0, distance, angle)
+ def self.coordinates(x0, y0, distance, angle)
x1 = x0 + Math.cos(radians(angle)) * distance
y1 = y0 + Math.sin(radians(angle)) * distance
[x1,y1]
end
# return the lesser of a,b
- def min(a, b)
+ def self.min(a, b)
a < b ? a : b
end
# return the greater of a,b
- def max(a, b)
+ def self.max(a, b)
a > b ? a : b
end
# restrict the value to stay within the range
- def inrange(value, min, max)
+ def self.in_range(value, min, max)
if value < min
min
elsif value > max
@@ -74,7 +73,7 @@ def inrange(value, min, max)
end
# return a random number within the range, or a float from 0 to the number
- def random(left=nil, right=nil)
+ def self.random(left=nil, right=nil)
if right
rand * (right - left) + left
elsif left
@@ -84,14 +83,14 @@ def random(left=nil, right=nil)
end
end
- def reflect(x0, y0, x1, y1, d=1.0, a=180)
+ def self.reflect(x0, y0, x1, y1, d=1.0, a=180)
d *= distance(x0, y0, x1, y1)
a += angle(x0, y0, x1, y1)
x, y = coordinates(x0, y0, d, a)
[x,y]
end
- def choose(object)
+ def self.choose(object)
case object
when Range
case object.first
@@ -108,55 +107,51 @@ def choose(object)
end
# given an object's x,y coordinates and dimensions, return the distance
- # needed to move in order to orient the object at the given location (:center, :bottomleft, etc)
+ # needed to move in order to orient the object at the given location (:center, :bottom_left, etc)
def self.reorient(x, y, w, h, location)
case location
- when :bottomleft
- movex = -x
- movey = -y
- when :centerleft
- movex = -x
- movey = -y - h / 2
- when :topleft
- movex = -x
- movey = -x - h
- when :bottomright
- movex = -x - w
- movey = -y
- when :centerright
- movex = -x - w
- movey = -y - h / 2
- when :topright
- movex = -x - w
- movey = -y - h
- when :bottomcenter
- movex = -x - w / 2
- movey = -y
+ when :bottom_left
+ move_x = -x
+ move_y = -y
+ when :center_left
+ move_x = -x
+ move_y = -y - h / 2
+ when :top_left
+ move_x = -x
+ move_y = -x - h
+ when :bottom_right
+ move_x = -x - w
+ move_y = -y
+ when :center_right
+ move_x = -x - w
+ move_y = -y - h / 2
+ when :top_right
+ move_x = -x - w
+ move_y = -y - h
+ when :bottom_center
+ move_x = -x - w / 2
+ move_y = -y
when :center
- movex = -x - w / 2
- movey = -y - h / 2
- when :topcenter
- movex = -x - w / 2
- movey = -y - h
+ move_x = -x - w / 2
+ move_y = -y - h / 2
+ when :top_center
+ move_x = -x - w / 2
+ move_y = -y - h
else
raise "ERROR: image origin locator not recognized: #{location}"
end
- #newx = oldx + movex
- #newy = oldy + movey
- [movex,movey]
+ [move_x,move_y]
end
end
-require 'pathname'
-here = Pathname.new(File.dirname(__FILE__))
-
-require here.join('lib/canvas')
-require here.join('lib/color')
-require here.join('lib/gradient')
-require here.join('lib/image')
-require here.join('lib/path')
-require here.join('lib/pdf')
-require here.join('lib/elements/particle')
-require here.join('lib/elements/rope')
-require here.join('lib/elements/sandpainter')
+require File.join(here, 'lib', 'canvas')
+require File.join(here, 'lib', 'color')
+require File.join(here, 'lib', 'gradient')
+require File.join(here, 'lib', 'image')
+require File.join(here, 'lib', 'path')
+require File.join(here, 'lib', 'pdf')
+require File.join(here, 'lib', 'elements/particle')
+require File.join(here, 'lib', 'elements/rope')
+require File.join(here, 'lib', 'elements/sandpainter')
View
179 lib/canvas.rb
@@ -9,7 +9,7 @@
# in Java and Python, respectively. RCG was created to offer similar functionality using
# the Ruby programming language.
#
-# Author:: James Reynolds (mailto:drtoast@drtoast.com)
+# Author:: James Reynolds (mailto:drtoast@drtoast.com), Matt Aimonetti
# Copyright:: Copyright (c) 2008 James Reynolds
# License:: Distributes under the same terms as Ruby
@@ -41,10 +41,10 @@ class Canvas
:saturation => KCGBlendModeSaturation,
:color => KCGBlendModeColor,
:luminosity => KCGBlendModeLuminosity,
- }.freeze
+ }
BlendModes.default(KCGBlendModeNormal)
- DefaultOptions = {:quality => 0.8, :width => 400, :height => 400}.freeze
+ DefaultOptions = {:quality => 0.8, :width => 400, :height => 400}
attr_accessor :width, :height
@@ -86,7 +86,7 @@ class ParamsError < StandardError; end
# create a new canvas with the given width, height, and output filename (pdf, png, jpg, gif, or tif)
def initialize(options={}, &block)
if options[:size]
- options[:width] = options[:size][0]
+ options[:width] = options[:size][0]
options[:height] = options[:size][1]
end
options = DefaultOptions.merge(options)
@@ -102,10 +102,8 @@ def initialize(options={}, &block)
when :pdf
@filetype = :pdf
# CREATE A PDF DRAWING CONTEXT
- # url = NSURL.fileURLWithPath(image)
url = CFURLCreateFromFileSystemRepresentation(nil, @output, @output.length, false)
pdfrect = CGRect.new(CGPoint.new(0, 0), CGSize.new(width, height)) # Landscape
- #@ctx = CGPDFContextCreateWithURL(url, pdfrect, nil)
consumer = CGDataConsumerCreateWithURL(url);
pdfcontext = CGPDFContextCreate(consumer, pdfrect, nil);
CGPDFContextBeginPage(pdfcontext, nil)
@@ -116,17 +114,15 @@ def initialize(options={}, &block)
extension = File.extname(@output).downcase[1..-1]
@filetype = extension.to_sym unless extension.nil?
end
-
@bits_per_component = 8
- @colorspace = CGColorSpaceCreateDeviceRGB() # => CGColorSpaceRef
- #alpha = KCGImageAlphaNoneSkipFirst # opaque background
- alpha = KCGImageAlphaPremultipliedFirst # transparent background
-
+ @colorspace = CGColorSpaceCreateDeviceRGB() # => CGColorSpaceRef
+ #alpha = KCGImageAlphaNoneSkipFirst # opaque background
+ alpha = KCGImageAlphaPremultipliedFirst # transparent background
# 8 integer bits/component; 32 bits/pixel; 3-component colorspace; kCGImageAlphaPremultipliedFirst; 57141 bytes/row.
bytes = @bits_per_component * 4 * @width.ceil
- @ctx = CGBitmapContextCreate(nil, @width, @height, @bits_per_component, bytes, @colorspace, alpha) # => CGContextRef
+ @ctx = CGBitmapContextCreate(nil, @width, @height, @bits_per_component, bytes, @colorspace, alpha) # => CGContextRef
when :context
- @ctx = options[:context]
+ @ctx = options[:context]
else
raise ParamsError, "The output file type #{ext} was not recognized"
end
@@ -135,23 +131,16 @@ def initialize(options={}, &block)
CGContextSetAllowsAntialiasing(@ctx, true)
# set defaults
- fill # set the default fill
- nostroke # no stroke by default
- strokewidth # set the default stroke width
- font # set the default font
- antialias # set the default antialias state
- autoclosepath # set the autoclosepath default
+ fill # set the default fill
+ no_stroke # no stroke by default
+ stroke_width # set the default stroke width
+ font # set the default font
+ antialias # set the default antialias state
+ autoclose_path! # set the autoclosepath default
quality(options[:quality]) # set the compression default
- push # save the pristine default default graphics state (retrieved by calling "reset")
- push # create a new graphics state for the user to mess up
- if block_given?
- case block.arity
- when 0
- send(:instance_eval, &block)
- else
- block.call(self)
- end
- end
+ push # save the pristine default graphics state (retrieved by calling "reset")
+ push # create a new graphics state for the user to mess up
+ block.call(self) if block_given?
end
# SET CANVAS GLOBAL PARAMETERS
@@ -190,7 +179,7 @@ def fill(r=0, g=0, b=0, a=1)
end
# remove current fill
- def nofill
+ def no_fill
CGContextSetRGBFillColor(@ctx, 0.0, 0.0, 0.0, 0.0) # RGBA
@fill = nil
end
@@ -209,20 +198,20 @@ def stroke(r=0, g=0, b=0, a=1.0)
CGContextSetRGBStrokeColor(@ctx, r, g, b, a) # RGBA
@stroke = true
end
-
- # set stroke width
- def strokewidth(width=1)
- CGContextSetLineWidth(@ctx, width.to_f)
- end
-
+
# don't use a stroke for subsequent drawing operations
- def nostroke
+ def no_stroke
CGContextSetRGBStrokeColor(@ctx, 0, 0, 0, 0) # RGBA
@stroke = false
end
+
+ # set stroke width
+ def stroke_width(width=1)
+ CGContextSetLineWidth(@ctx, width.to_f)
+ end
# set cap style to round, square, or butt
- def linecap(style=:butt)
+ def line_cap(style=:butt)
case style
when :round
cap = KCGLineCapRound
@@ -237,7 +226,7 @@ def linecap(style=:butt)
end
# set line join style to round, miter, or bevel
- def linejoin(style=:miter)
+ def line_join(style=:miter)
case style
when :round
join = KCGLineJoinRound
@@ -252,17 +241,16 @@ def linejoin(style=:miter)
end
# set lengths of dashes and spaces, and distance before starting dashes
- def linedash(lengths=[10,2], phase=0.0)
+ def line_dash(lengths=[10,2], phase=0.0)
count=lengths.size
CGContextSetLineDash(@ctx, phase, lengths, count)
end
# revert to solid lines
- def nodash
+ def no_dash
CGContextSetLineDash(@ctx, 0.0, nil, 0)
end
-
# DRAWING SHAPES ON CANVAS
# draw a rectangle starting at x,y and having dimensions w,h
@@ -322,17 +310,17 @@ def cartesian(res=50, stroke=1.0, fsize=10)
# save previous state
new_state do
# set font and stroke
- fontsize(fsize)
+ font_size(fsize)
fill(Color.black)
stroke(Color.red)
- strokewidth(stroke)
+ stroke_width(stroke)
# draw vertical numbered grid lines
- for x in (-width / res)..(width / res) do
+ (-width / res)..(width / res).each do |x|
line(x * res, -height, x * res, height)
text("#{x * res}", x * res, 0)
end
# draw horizontal numbered grid lines
- for y in (-height / res)..(height / res) do
+ (-height / res)..(height / res).each do |y|
line(-width, y * res, width, y * res)
text("#{y * res}", 0, y * res)
end
@@ -354,15 +342,14 @@ def cartesian(res=50, stroke=1.0, fsize=10)
def line(x1, y1, x2, y2)
CGContextAddLines(@ctx, [NSPoint.new(x1, y1), NSPoint.new(x2, y2)], 2)
CGContextDrawPath(@ctx, KCGPathStroke) # apply stroke
- endpath
-
+ end_path
end
# draw a series of lines connecting the given array of points
def lines(points)
CGContextAddLines(@ctx, points, points.size)
CGContextDrawPath(@ctx, KCGPathStroke) # apply stroke
- endpath
+ end_path
end
# draw the arc of a circle with center point x,y, radius, start angle (0 deg = 12 o'clock) and end angle
@@ -378,14 +365,14 @@ def arc(x, y, radius, start_angle, end_angle)
def curve(cp1x, cp1y, cp2x, cp2y, x1, y1, x2, y2)
beginpath(x1, y1)
CGContextAddCurveToPoint(@ctx, cp1x, cp1y, cp2x, cp2y, x2, y2)
- endpath
+ end_path
end
# draw a quadratic bezier curve from x1,y1 to x2,y2, given the coordinates of one control point
def qcurve(cpx, cpy, x1, y1, x2, y2)
beginpath(x1, y1)
CGContextAddQuadCurveToPoint(@ctx, cpx, cpy, x2, y2)
- endpath
+ end_path
end
# draw the given Path object
@@ -396,59 +383,66 @@ def draw(object, *args)
when Image
draw_image(object, *args)
else
- raise ArgumentError.new("first parameter must be a Path or Image object not a #{object.class}")
+ raise ArgumentError, "first parameter must be a Path or Image object not a #{object.class}"
end
end
# CONSTRUCTING PATHS ON CANVAS
- # if true, automatically close the path after it is ended
- def autoclosepath(tf=false)
- @autoclosepath = tf
+ # automatically close the path after it is ended
+ def autoclose_path!
+ @autoclosepath = true
+ end
+
+ def autoclose_path=(bool)
+ if bool == true
+ autoclose_path!
+ else
+ @autoclose_path = false
+ end
end
def new_path(x, y, &block)
beginpath(x, y)
block.call
- endpath
+ end_path
end
# begin drawing a path at x,y
- def beginpath(x, y)
+ def begin_path(x, y)
CGContextBeginPath(@ctx)
CGContextMoveToPoint(@ctx, x, y)
end
# end the current path and draw it
- def endpath
+ def end_path
CGContextClosePath(@ctx) if @autoclosepath
- #mode = KCGPathStroke
mode = KCGPathFillStroke
CGContextDrawPath(@ctx, mode) # apply fill and stroke
end
# move the "pen" to x,y
- def moveto(x, y)
+ def move_to(x, y)
CGContextMoveToPoint(@ctx, x, y)
end
# draw a line from the current point to x,y
- def lineto(x, y)
+ def line_to(x, y)
CGContextAddLineToPoint(@ctx ,x, y)
end
# draw a bezier curve from the current point, given the coordinates of two handle control points and an end point
- def curveto(cp1x, cp1y, cp2x, cp2y, x, y)
+ def curve_to(cp1x, cp1y, cp2x, cp2y, x, y)
CGContextAddCurveToPoint(@ctx, cp1x, cp1y, cp2x, cp2y, x, y)
end
# draw a quadratic bezier curve from the current point, given the coordinates of one control point and an end point
- def qcurveto(cpx, cpy, x, y)
+ def qcurve_to(cpx, cpy, x, y)
CGContextAddQuadCurveToPoint(@ctx, cpx, cpy, x, y)
end
# draw an arc given the endpoints of two tangent lines and a radius
- def arcto(x1, y1, x2, y2, radius)
+ def arc_to(x1, y1, x2, y2, radius)
CGContextAddArcToPoint(@ctx, x1, y1, x2, y2, radius)
end
@@ -521,9 +515,8 @@ def pop
# restore the initial context
def reset
- until (@stacksize <= 1)
- pop # retrieve graphics states until we get to the default state
- end
+ # retrieve graphics states until we get to the default state
+ pop until (@stacksize <= 1)
push # push the retrieved pristine default state back onto the stack
end
@@ -536,27 +529,26 @@ def shadow(dx=0.0, dy=0.0, a=2.0/3.0, blur=5)
CGContextSetShadowWithColor(@ctx, [dx, dy], blur, color)
end
+ # stop using a shadow
+ def no_shadow
+ CGContextSetShadowWithColor(@ctx, [0,0], 1, nil)
+ end
+
# apply a glow with offset dx,dy, alpha, and blur
def glow(dx=0.0, dy=0.0, a=2.0/3.0, blur=5)
color = CGColorCreate(@colorspace, [1.0, 1.0, 0.0, a])
CGContextSetShadowWithColor(@ctx, [dx, dy], blur, color)
end
- # stop using a shadow
- def noshadow
- CGContextSetShadowWithColor(@ctx, [0,0], 1, nil)
- end
-
# set the canvas blend mode (:normal, :darken, :multiply, :screen, etc)
def blend(mode)
CGContextSetBlendMode(@ctx, BlendModes[mode])
end
-
# CLIPPING MASKS
# clip subsequent drawing operations within the given path
- def beginclip(p, &block)
+ def begin_clip(p, &block)
push
CGContextAddPath(@ctx, p.path)
CGContextClip(@ctx)
@@ -567,18 +559,16 @@ def beginclip(p, &block)
end
# stop clipping drawing operations
- def endclip
+ def end_clip
pop
end
# DRAW TEXT TO CANVAS
-
- # NOTE: may want to switch to ATSUI for text handling
- # http://developer.apple.com/documentation/Carbon/Reference/ATSUI_Reference/Reference/reference.html
-
+
# write the text at x,y using the current fill
- def text(txt="A", x=0, y=0)
- txt = txt.to_s unless txt.kind_of?(String)
+ def text(txt="", x=0, y=0)
+ # not sure that's worth doing that here
+ txt = txt.to_s
if @registration == :center
width = textwidth(txt)
x = x - width / 2
@@ -588,7 +578,7 @@ def text(txt="A", x=0, y=0)
end
# determine the width of the given text without drawing it
- def textwidth(txt, width=nil)
+ def text_width(txt, width=nil)
push
start = CGContextGetTextPosition(@ctx)
CGContextSetTextDrawingMode(@ctx, KCGTextInvisible)
@@ -598,34 +588,34 @@ def textwidth(txt, width=nil)
final.x - start.x
end
- # def textheight(txt)
+ # def text_height(txt)
# # need to use ATSUI
# end
#
- # def textmetrics(txt)
+ # def text_metrics(txt)
# # need to use ATSUI
# end
# set font by name and optional size
def font(name="Helvetica", size=nil)
- fontsize(size) if size
+ font_size(size) if size
@fname = name
- fontsize unless @fsize
+ font_size unless @fsize
CGContextSelectFont(@ctx, @fname, @fsize, KCGEncodingMacRoman)
end
# set font size in points
- def fontsize(points=20)
+ def font_size(points=20)
@fsize = points
font unless @fname
- #CGContextSetFontSize(@ctx,points)
+ # encoding could have also been set as KCGEncodingFontSpecific
CGContextSelectFont(@ctx, @fname, @fsize, KCGEncodingMacRoman)
end
# SAVING/EXPORTING
- def nsimage
+ def ns_image
image = NSImage.alloc.init
image.addRepresentation(NSBitmapImageRep.alloc.initWithCGImage(cgimage))
image
@@ -644,19 +634,18 @@ def ciimage
end
# begin a new PDF page
- def newpage
+ def new_page
if (@filetype == :pdf)
CGContextFlush(@ctx)
CGPDFContextEndPage(@ctx)
CGPDFContextBeginPage(@ctx, nil)
else
- puts "WARNING: newpage only valid when using PDF output"
+ puts "WARNING: new_page only valid when using PDF output"
end
end
# save the image to a file
def save
-
properties = {}
# exif = {}
# KCGImagePropertyExifDictionary
@@ -685,6 +674,7 @@ def save
bitmaprep = NSBitmapImageRep.alloc.initWithCGImage(cgimageref) # => NSBitmapImageRep
blob = bitmaprep.representationUsingType(format, properties:properties) # => NSConcreteData
blob.writeToFile(@output, atomically:true)
+ puts @output
true
end
@@ -728,7 +718,7 @@ def draw_path(p, tx=0, ty=0, iterations=1)
# PICK AND ADJUST FILL/STROKE COLORS:
[:fill,:stroke].each do |kind|
# PICK A COLOR
- if (p.inc[kind]) then
+ if (p.inc[kind])
# increment color from array
colorindex = i % p.inc[kind].size
c = p.inc[kind][colorindex].copy
@@ -830,11 +820,10 @@ def draw_image(img, x=0, y=0, w=nil, h=nil, pagenum=1)
h ||= img.height
img.draw(@ctx, x, y, w, h)
else
- raise ArgumentError.new("canvas.image: not a recognized image type: #{img.class}")
+ raise ArgumentError, "canvas.image: not a recognized image type: #{img.class}"
end
end
end
-
end
View
20 lib/color.rb
@@ -177,7 +177,7 @@ def initialize(r=0.0, g=0.0, b=1.0, a=1.0)
"cyan" => [0.00, 0.68, 0.94],
#"transparent" => [0.00, 0.00, 0.00, 0.00],
"bark" => [0.25, 0.19, 0.13]
- }.freeze
+ }
RYBWheel = [
[ 0, 0], [ 15, 8],
@@ -193,7 +193,7 @@ def initialize(r=0.0, g=0.0, b=1.0, a=1.0)
[300, 267], [315, 282],
[330, 298], [345, 329],
[360, 0 ]
- ].freeze
+ ]
COLORNAMES.each_key do |name|
metaclass = (class << self; self; end)
@@ -212,15 +212,15 @@ def self.named(name)
color = Color.new(r, g, b, 1.0)
elsif name.match(/^(dark|deep|light|bright)?(.*?)(ish)?$/)
#puts "matched #{$1}-#{$2}-#{$3}"
- value = $1
+ value = $1
color_name = $2
- ish = $3
- analogval = value ? 0 : 0.1
- r, g, b = COLORNAMES[color_name] || [0.0, 0.0, 0.0]
- color = Color.new(r, g, b, 1.0)
- color = c.analog(20, analogval) if ish
- color.lighten(0.2) if value and value.match(/light|bright/)
- color.darken(0.2) if value and value.match(/dark|deep/)
+ ish = $3
+ analogval = value ? 0 : 0.1
+ r, g, b = COLORNAMES[color_name] || [0.0, 0.0, 0.0]
+ color = Color.new(r, g, b, 1.0)
+ color = c.analog(20, analogval) if ish
+ color.lighten(0.2) if value && value.match(/light|bright/)
+ color.darken(0.2) if value && value.match(/dark|deep/)
else
color = Color.black
end
View
8 lib/gradient.rb
@@ -23,7 +23,7 @@ class Gradient
# create a new gradient from black to white
def initialize(*colors)
@colorspace = CGColorSpaceCreateWithName(KCGColorSpaceGenericRGB)
- colors = colors[0] if colors[0].class == Array
+ colors = colors[0] if colors[0].class == Array
set(colors)
pre(true)
post(true)
@@ -33,10 +33,10 @@ def initialize(*colors)
# create a gradient that evenly distributes the given colors
def set(colors)
colors ||= [Color.black, Color.white]
- cgcolors = []
+ cgcolors = []
locations = []
increment = 1.0 / (colors.size - 1).to_f
- i = 0
+ i = 0
colors.each do |c|
cgcolor = CGColorCreate(@colorspace, [c.r, c.g, c.b, c.a])
cgcolors.push(cgcolor)
@@ -60,4 +60,4 @@ def post(tf=nil)
end
end
-end
View
3  lib/image.rb
@@ -17,7 +17,6 @@ module MRGraphics
# load a raw image file for use on a canvas
class Image
-
BlendModes = {
:normal => 'CISourceOverCompositing',
@@ -44,7 +43,7 @@ class Image
:in => 'CISourceInCompositing',
:out => 'CISourceOutCompositing',
:over => 'CISourceOverCompositing'
- }.freeze
+ }
BlendModes.default('CISourceOverCompositing')
attr_reader :cgimage
View
92 lib/path.rb
@@ -22,9 +22,9 @@ class Path
# create a new path, starting at optional x,y
def initialize(x=0, y=0, &block)
- @path = CGPathCreateMutable()
+ @path = CGPathCreateMutable()
@transform = CGAffineTransformMakeTranslation(0,0)
- moveto(x, y)
+ move_to(x, y)
# set randomized rendering parameters
@rand = {}
@@ -46,17 +46,10 @@ def initialize(x=0, y=0, &block)
increment(:scaley, 1.0)
@strokewidth = 1.0
- @x = 0.0
- @y = 0.0
+ @x = 0.0
+ @y = 0.0
- if block
- case block.arity
- when 0
- self.send(:instance_eval, &block)
- else
- block.call(self)
- end
- end
+ block.call(self) if block
self
end
@@ -80,9 +73,9 @@ def increment(parameter, value)
# return a mutable clone of this path
def clone
- newpath = self.dup
- newpath.path = CGPathCreateMutableCopy(@path)
- newpath
+ new_path = self.dup
+ new_path.path = CGPathCreateMutableCopy(@path)
+ new_path
end
@@ -94,12 +87,12 @@ def registration(mode=:center)
end
# print drawing operations if verbose is true
- def verbose(tf=true)
- @verbose = tf
+ def verbose(bool=true)
+ @verbose = bool
end
# draw without stroke
- def nostroke
+ def no_stroke
@stroke = nil
end
@@ -111,12 +104,12 @@ def to_s
end
# return the x coordinate of the path's origin
- def originx
+ def origin_x
CGPathGetBoundingBox(@path).origin.x
end
# return the y coordinate of the path's origin
- def originy
+ def origin_y
CGPathGetBoundingBox(@path).origin.y
end
@@ -131,7 +124,7 @@ def height
end
# return the current point
- def currentpoint
+ def current_point
CGPathGetCurrentPoint(@path)
end
@@ -145,8 +138,8 @@ def contains(x,y)
# ADD SHAPES TO PATH
# add another path to this path
- def addpath(p)
- CGPathAddPath(@path, @transform, p.path)
+ def add_path(p)
+ CGPathadd_path(@path, @transform, p.path)
end
# add a rectangle starting at [x,y] with dimensions w x h
@@ -161,7 +154,7 @@ def rect(x=0, y=0, w=20, h=20, reg=@registration)
end
# draw a rounded rectangle using quadratic curved corners (FIXME)
- def roundrect(x=0, y=0, width=20, height=20, roundness=0, reg=@registration)
+ def round_rect(x=0, y=0, width=20, height=20, roundness=0, reg=@registration)
if roundness == 0
p.rect(x, y, width, height, reg)
else
@@ -171,17 +164,17 @@ def roundrect(x=0, y=0, width=20, height=20, roundness=0, reg=@registration)
end
curve = min(width * roundness, height * roundness)
p = Path.new
- p.moveto(x, y+curve)
- p.curveto(x, y, x, y, x+curve, y)
- p.lineto(x+width-curve, y)
- p.curveto(x+width, y, x+width, y, x+width, y+curve)
- p.lineto(x+width, y+height-curve)
- p.curveto(x+width, y+height, x+width, y+height, x+width-curve, y+height)
- p.lineto(x+curve, y+height)
- p.curveto(x, y+height, x, y+height, x, y+height-curve)
- p.endpath
+ p.move_to(x, y+curve)
+ p.curve_to(x, y, x, y, x+curve, y)
+ p.line_to(x+width-curve, y)
+ p.curve_to(x+width, y, x+width, y, x+width, y+curve)
+ p.line_to(x+width, y+height-curve)
+ p.curve_to(x+width, y+height, x+width, y+height, x+width-curve, y+height)
+ p.line_to(x+curve, y+height)
+ p.curve_to(x, y+height, x, y+height, x, y+height-curve)
+ p.end_path
end
- addpath(p)
+ add_path(p)
self
end
@@ -211,8 +204,8 @@ def line(x1, y1, x2, y2)
# draw the arc of a circle with center point x,y, radius, start angle (0 deg = 12 o'clock) and end angle
def arc(x, y, radius, start_angle, end_angle)
- start_angle = radians(90 - start_angle)
- end_angle = radians(90 - end_angle)
+ start_angle = MRGraphics.radians(90 - start_angle)
+ end_angle = MRGraphics.radians(90 - end_angle)
clockwise = 1 # 1 = clockwise, 0 = counterclockwise
CGPathAddArc(@path, @transform, x, y, radius, start_angle, end_angle, clockwise)
self
@@ -228,37 +221,37 @@ def lines(points)
# CONSTRUCT PATHS IN PATH OBJECT
# move the "pen" to x,y
- def moveto(x, y)
+ def move_to(x, y)
CGPathMoveToPoint(@path, @transform,x,y)
self
end
# draw a line from the current point to x,y
- def lineto(x,y)
+ def line_to(x,y)
CGPathAddLineToPoint(@path, @transform, x, y)
self
end
# draw a bezier curve from the current point, given the coordinates of two handle control points and an end point
- def curveto(cp1x, cp1y, cp2x, cp2y, x, y)
+ def curve_to(cp1x, cp1y, cp2x, cp2y, x, y)
CGPathAddCurveToPoint(@path, @transform, cp1x, cp1y, cp2x, cp2y, x, y)
self
end
# draw a quadratic curve given a single control point and an end point
- def qcurveto(cpx, cpy, x, y)
+ def qcurve_to(cpx, cpy, x, y)
CGPathAddQuadCurveToPoint(@path, @transform, cpx, cpy, x, y)
self
end
# draw an arc given the endpoints of two tangent lines and a radius
- def arcto(x1, y1, x2, y2, radius)
+ def arc_to(x1, y1, x2, y2, radius)
CGPathAddArcToPoint(@path, @transform, x1, y1, x2, y2, radius)
self
end
# end the current path
- def endpath
+ def end_path
CGPathCloseSubpath(@path)
end
@@ -268,7 +261,7 @@ def endpath
# specify rotation for subsequent operations
def rotate(deg)
puts "path.rotate #{deg}" if @verbose
- @transform = CGAffineTransformRotate(@transform, radians(deg))
+ @transform = CGAffineTransformRotate(@transform, MRGraphics.radians(deg))
end
# scale by horizontal/vertical scaling factors sx,sy for subsequent drawing operations
@@ -293,10 +286,10 @@ def translate(x,y)
# draw a petal starting at x,y with w x h and center bulge height using quadratic curves
def petal(x=0, y=0, w=10, h=50, bulge=h/2)
- moveto(x,y)
- qcurveto(x - w, y + bulge, x, y + h)
- qcurveto(x + w, y + bulge, x, y)
- endpath
+ move_to(x,y)
+ qcurve_to(x - w, y + bulge, x, y + h)
+ qcurve_to(x + w, y + bulge, x, y)
+ end_path
self
end
@@ -304,7 +297,7 @@ def petal(x=0, y=0, w=10, h=50, bulge=h/2)
def kaleidoscope(path,qty)
deg = 360 / qty
qty.times do
- addpath(path)
+ add_path(path)
rotate(deg)
end
end
@@ -313,13 +306,12 @@ def kaleidoscope(path,qty)
#path, rotation, scale, translation, iterations
def spiral(path=nil, rotation=20, scalex=0.95, scaley=0.95, tx=10, ty=10, iterations=30)
iterations.times do
- addpath(path)
+ add_path(path)
rotate(rotation)
scale(scalex, scaley)
translate(tx, ty)
end
end
-
end
end
View
34 specs/canvas_spec.rb
@@ -0,0 +1,34 @@
+require 'minitest/spec'
+require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'graphics')
+MiniTest::Unit.autorun
+include MRGraphics
+
+describe "MRGraphics Canvas" do
+ before do
+ @destination = File.expand_path("#{File.dirname(__FILE__)}/tmp/test.png")
+ @canvas = Canvas.for_image(:filename => @destination) do |c|
+ c.background(Color.black)
+ c.fill(Color.white)
+ c.text('this is a test')
+ end
+ end
+
+ after do
+ File.delete(@destination) if File.exist?(@destination)
+ end
+
+ it "should have a width" do
+ @canvas.width
+ end
+
+ it "should have a height" do
+ @canvas.height
+ end
+
+ it "should save to a file" do
+ File.delete(@destination) if File.exist?(@destination)
+ @canvas.save
+ File.exist?(@destination).must_equal(true)
+ end
+
+end
View
67 specs/graphics_spec.rb
@@ -0,0 +1,67 @@
+require 'minitest/spec'
+require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'graphics')
+MiniTest::Unit.autorun
+
+describe 'MRGraphics module methods' do
+
+ it "should convert degrees to radians" do
+ MRGraphics.radians(30).must_be_close_to(0.523598776)
+ MRGraphics.radians(90).must_be_close_to(1.57079633)
+ MRGraphics.radians(360).must_be_close_to(6.28318531)
+ end
+
+ it "should convert radians to degrees" do
+ MRGraphics.degrees(0.523598776).must_be_close_to(30)
+ MRGraphics.degrees(1.57079633).must_be_close_to(90)
+ MRGraphics.degrees(6.28318531).must_be_close_to(360)
+ end
+
+ it "should calculate the angle of the line joining two points" do
+ MRGraphics.angle(0,0, 10,0).must_be_close_to(0)
+ MRGraphics.angle(0,0, 10,10).must_be_close_to(45)
+ MRGraphics.angle(0,0, 0,10).must_be_close_to(90)
+ end
+
+ it "should calculate the distance between 2 points" do
+ MRGraphics.distance(0,0, 10,0).must_be_close_to(10)
+ MRGraphics.distance(0,0, 10,10).must_be_close_to(14.14, 0.1)
+ end
+
+ it "should calculate the coordinate of a new point" do
+ point = MRGraphics.coordinates(0, 0, 10, 45)
+ point.first.must_be_close_to(7.07, 0.1)
+ point.last.must_be_close_to(7.07, 0.1)
+ end
+
+ it "should let you randomly choose an integer from a range" do
+ from_range = MRGraphics.choose(0..100)
+ from_range.must_be_instance_of(Fixnum)
+ (0..100).must_include(from_range)
+ randoms = []
+ 50.times{randoms << MRGraphics.choose(0..1000) }
+ randoms.uniq.size.must_be_close_to(randoms.size, 5)
+ end
+
+ it "should let you randomly choose a float from a range" do
+ from_range = MRGraphics.choose(0.0..10.0)
+ from_range.must_be_instance_of(Float)
+ (0.0..10.0).must_include(from_range)
+ randoms = []
+ 50.times{randoms << MRGraphics.choose(0.0..100.0) }
+ randoms.uniq.size.must_be_close_to(randoms.size, 5)
+ end
+
+ it "should let you randomly choose an item from an array" do
+ from_range = MRGraphics.choose([1,2,3,4,5])
+ from_range.must_be_instance_of(Fixnum)
+ (1..5).must_include(from_range)
+ end
+
+ it "should return the point to reorient an item" do
+ new_location = MRGraphics.reorient(0, 0, 100, 100, :center)
+ new_location.must_be_instance_of(Array)
+ new_location.first.must_be_same_as(-50)
+ new_location.last.must_be_same_as(-50)
+ end
+
+end
View
1  specs/tmp/.ignore
@@ -0,0 +1 @@
+*.png
Please sign in to comment.
Something went wrong with that request. Please try again.