Module: Compass::Magick::Shapes

Extended by:
Shapes
Included in:
Shapes
Defined in:
lib/magick/shapes.rb

Overview

Drawing methods shared by Compass Magick commands.

All of the drawing is done using B/W pixels with varying alpha. These shapes are used to build masks which are then applied to fill Types to generate the final canvas.

Instance Method Summary collapse

Instance Method Details

#circle(radius, width, feather = 1.0) ⇒ Canvas

Draws a circle mask.

Copyright © 2003 by Nils Haeck M.Sc. (Simdesign) www.simdesign.nl

> The [..] DrawDisk routines is optimized quite well but do not claim > to be the fastest solution :) It is a floating point precision > implementation. Further optimisation would be possible if an > integer approach was chosen (but that would also loose > functionality).

Parameters:

  • radius (Integer)

    The radius of the circle.

  • feather (Float) (defaults to: 1.0)

    The feater value determines the anti-aliasing the circle will get, defaults to 1.0.

Returns:

  • (Canvas)

    A Canvas with a circle B/W mask.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/magick/shapes.rb', line 25

def circle(radius, width, feather = 1.0)
  mask = ChunkyPNG::Canvas.new(radius, radius, ChunkyPNG::Color.rgba(0, 0, 0, 0))
  if radius <= width
    center = (radius - 1) / 2.0
    rpf2   = (center + feather / 2.0) ** 2
    rmf2   = (center - feather / 2.0) ** 2
    lx     = [(center - rpf2).floor, 0].max
    ly     = [(center - rpf2).floor, 0].max
    rx     = [(center + rpf2).ceil, radius - 1].min
    ry     = [(center + rpf2).ceil, radius - 1].min
    sqx    = Array.new(rx - lx + 1)
    for x in lx..rx
      sqx[x - lx] = (x - center) ** 2
    end
    for y in ly..ry
      sqy = (y - center) ** 2
      for x in lx..rx
        sqdist = sqy + sqx[x - lx]
        if sqdist < rmf2
          mask.set_pixel(x, y, ChunkyPNG::Color::WHITE)
        else
          if sqdist < rpf2
            fact = (((center - Math.sqrt(sqdist)) * 2.0 / feather) * 0.5 + 0.5)
            mask.set_pixel(x, y, ChunkyPNG::Color.rgba(255, 255, 255, (255 * [0, [fact, 1].min].max).to_i))
          end
        end
      end
    end
  else
    center  = (radius - 1) / 2.0
    inrad   = (center + feather / 2.0) - width
    ropf2   = (center + feather / 2.0) ** 2
    romf2   = (center - feather / 2.0) ** 2
    ripf2   = (inrad  + feather / 2.0) ** 2
    rimf2   = (inrad  - feather / 2.0) ** 2
    lx      = [(center - ropf2).floor, 0].max
    ly      = [(center - ropf2).floor, 0].max
    rx      = [(center + ropf2).ceil, radius - 1].min
    ry      = [(center + ropf2).ceil, radius - 1].min
    feather = width if feather > width
    sqx     = Array.new(rx - lx + 1)
    for x in lx..rx
      sqx[x - lx] = (x - center) ** 2
    end
    for y in ly..ry
      sqy = (y - center) ** 2
      for x in lx..rx
        sqdist = sqy + sqx[x - lx]
        if sqdist >= rimf2
          if sqdist < ropf2
            if sqdist < romf2
              if sqdist < ripf2
                fact = (((Math.sqrt(sqdist) - inrad) * 2 / feather) * 0.5 + 0.5)
                mask.set_pixel(x, y, ChunkyPNG::Color.rgba(255, 255, 255, (255 * [0, [fact, 1].min].max).to_i))
              else
                mask.set_pixel(x, y, ChunkyPNG::Color::WHITE)
              end
            else
              fact = (((center - Math.sqrt(sqdist)) * 2 / feather) * 0.5 + 0.5)
              mask.set_pixel(x, y, ChunkyPNG::Color.rgba(255, 255, 255, (255 * [0, [fact, 1].min].max).to_i))
            end
          end
        end
      end
    end
  end
  mask
end