2d6e38166fc8624baa3522c35b056b9f920b0821
[selectricity] / vendor / plugins / attachment_fu / lib / geometry.rb
1 # This Geometry class was yanked from RMagick.  However, it lets ImageMagick handle the actual change_geometry.
2 # Use #new_dimensions_for to get new dimensons
3 # Used so I can use spiffy RMagick geometry strings with ImageScience
4 class Geometry
5   # ! and @ are removed until support for them is added
6   FLAGS = ['', '%', '<', '>']#, '!', '@']
7   RFLAGS = { '%' => :percent,
8              '!' => :aspect,
9              '<' => :>,
10              '>' => :<,
11              '@' => :area }
12
13   attr_accessor :width, :height, :x, :y, :flag
14
15   def initialize(width=nil, height=nil, x=nil, y=nil, flag=nil)
16     # Support floating-point width and height arguments so Geometry
17     # objects can be used to specify Image#density= arguments.
18     raise ArgumentError, "width must be >= 0: #{width}"   if width < 0
19     raise ArgumentError, "height must be >= 0: #{height}" if height < 0
20     @width  = width.to_f
21     @height = height.to_f
22     @x      = x.to_i
23     @y      = y.to_i
24     @flag   = flag
25   end
26
27   # Construct an object from a geometry string
28   RE = /\A(\d*)(?:x(\d+))?([-+]\d+)?([-+]\d+)?([%!<>@]?)\Z/
29
30   def self.from_s(str)
31     raise(ArgumentError, "no geometry string specified") unless str
32   
33     if m = RE.match(str)
34       new(m[1].to_i, m[2].to_i, m[3].to_i, m[4].to_i, RFLAGS[m[5]])
35     else
36       raise ArgumentError, "invalid geometry format"
37     end
38   end
39
40   # Convert object to a geometry string
41   def to_s
42     str = ''
43     str << "%g" % @width if @width > 0
44     str << 'x' if (@width > 0 || @height > 0)
45     str << "%g" % @height if @height > 0
46     str << "%+d%+d" % [@x, @y] if (@x != 0 || @y != 0)
47     str << FLAGS[@flag.to_i]
48   end
49   
50   # attempts to get new dimensions for the current geometry string given these old dimensions.
51   # This doesn't implement the aspect flag (!) or the area flag (@).  PDI
52   def new_dimensions_for(orig_width, orig_height)
53     new_width  = orig_width
54     new_height = orig_height
55
56     case @flag
57       when :percent
58         scale_x = @width.zero?  ? 100 : @width
59         scale_y = @height.zero? ? @width : @height
60         new_width    = scale_x.to_f * (orig_width.to_f  / 100.0)
61         new_height   = scale_y.to_f * (orig_height.to_f / 100.0)
62       when :<, :>, nil
63         scale_factor =
64           if new_width.zero? || new_height.zero?
65             1.0
66           else
67             if @width.nonzero? && @height.nonzero?
68               [@width.to_f / new_width.to_f, @height.to_f / new_height.to_f].min
69             else
70               @width.nonzero? ? (@width.to_f / new_width.to_f) : (@height.to_f / new_height.to_f)
71             end
72           end
73         new_width  = scale_factor * new_width.to_f
74         new_height = scale_factor * new_height.to_f
75         new_width  = orig_width  if @flag && orig_width.send(@flag,  new_width)
76         new_height = orig_height if @flag && orig_height.send(@flag, new_height)
77     end
78
79     [new_width, new_height].collect! { |v| v.round }
80   end
81 end
82
83 class Array
84   # allows you to get new dimensions for the current array of dimensions with a given geometry string
85   #
86   #   [50, 64] / '40>' # => [40, 51]
87   def /(geometry)
88     raise ArgumentError, "Only works with a [width, height] pair" if size != 2
89     raise ArgumentError, "Must pass a valid geometry string or object" unless geometry.is_a?(String) || geometry.is_a?(Geometry)
90     geometry = Geometry.from_s(geometry) if geometry.is_a?(String)
91     geometry.new_dimensions_for first, last
92   end
93 end

Benjamin Mako Hill || Want to submit a patch?