Class: Scruffy::Layers::AllSmiles

Inherits:
Base
  • Object
show all
Defined in:
lib/scruffy/layers/all_smiles.rb

Overview

Scruffy::Layers::AllSmiles

Author

Brasten Sager

Date

August 8th, 2006

The AllSmiles graph consists of smiley faces for data points, with smiles or frowns depending upon their relative location on the graph. The highest point is crowned with a wizard hat. The Wizard Smiley eventually become ‘Scruffy’, our mascot.

I don’t know why.

This graph only looks decent in SVG mode. If you’re rasterizing the graph with ImageMagick, you must use the :complexity => :minimal option on Graph#render. This will make the graph look really nasty, but still better than if you try to rasterize with all the gradients in place.

Instance Attribute Summary collapse

Attributes inherited from Base

#color, #complexity, #height, #max_value, #min_value, #opacity, #options, #points, #preferred_color, #relevant_data, #title, #width

Instance Method Summary collapse

Methods inherited from Base

#bottom_value, #legend_data, #relevant_data?, #render, #sum_values, #top_value

Constructor Details

#initialize(options = {}) ⇒ AllSmiles

Returns a new AllSmiles graph.

Options:

standalone

If set to true, dashed lines under smilies run vertically, like bar graphs. If false (default), dashed lines run from smiley to smiley, like a line-graph.



24
25
26
27
# File 'lib/scruffy/layers/all_smiles.rb', line 24

def initialize(options = {})
  super
  @standalone = options[:standalone] || false
end

Instance Attribute Details

#standaloneObject

Returns the value of attribute standalone.



17
18
19
# File 'lib/scruffy/layers/all_smiles.rb', line 17

def standalone
  @standalone
end

Instance Method Details

#draw(svg, coords, options = {}) ⇒ Object

Renders graph.



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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/scruffy/layers/all_smiles.rb', line 30

def draw(svg, coords, options={})

  hero_smiley = nil
  coords.each { |c| hero_smiley = c.last if (hero_smiley.nil? || c.last < hero_smiley) }

  svg.defs {
    svg.radialGradient(:id => 'SmileyGradient', :cx => '50%', 
                       :cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
                       
        svg.stop(:offset => '0%', 'stop-color' => '#FFF')
        svg.stop(:offset => '20%', 'stop-color' => '#FFC')
        svg.stop(:offset => '45%', 'stop-color' => '#FF3')
        svg.stop(:offset => '60%', 'stop-color' => '#FF0')
        svg.stop(:offset => '90%', 'stop-color' => '#990')
        svg.stop(:offset => '100%', 'stop-color' => '#220')
    }
    svg.radialGradient(:id => 'HeroGradient', :cx => '50%', 
                       :cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
                       
        svg.stop(:offset => '0%', 'stop-color' => '#FEE')
        svg.stop(:offset => '20%', 'stop-color' => '#F0E0C0')
        svg.stop(:offset => '45%', 'stop-color' => '#8A2A1A')
        svg.stop(:offset => '60%', 'stop-color' => '#821')
        svg.stop(:offset => '90%', 'stop-color' => '#210')
    }
    svg.radialGradient(:id => 'StarGradient', :cx => '50%', 
                       :cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
                       
        svg.stop(:offset => '0%', 'stop-color' => '#FFF')
        svg.stop(:offset => '20%', 'stop-color' => '#EFEFEF')
        svg.stop(:offset => '45%', 'stop-color' => '#DDD')
        svg.stop(:offset => '60%', 'stop-color' => '#BBB')
        svg.stop(:offset => '90%', 'stop-color' => '#888')
    }
  }
      
  unless standalone
    svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none', 
                  :stroke => '#660', 'stroke-width' => scaled(10), 'stroke-dasharray' => "#{scaled(10)}, #{scaled(10)}" )
  end

  # Draw smilies.
  coords.each do |coord|
    if standalone
      svg.line( :x1 => coord.first, :y1 => coord.last, :x2 => coord.first, :y2 => height, :fill => 'none', 
                    :stroke => '#660', 'stroke-width' => scaled(10), 'stroke-dasharray' => "#{scaled(10)}, #{scaled(10)}" )
    end
    svg.circle( :cx => coord.first + scaled(2), :cy => coord.last + scaled(2), :r => scaled(15), 
                :fill => 'black', :stroke => 'none', :opacity => 0.4)
    svg.circle( :cx => coord.first, :cy => coord.last, :r => scaled(15), 
                :fill => (complexity == :minimal ? 'yellow' : 'url(#SmileyGradient)'), :stroke => 'black', 'stroke-width' => scaled(1) )
    svg.line( :x1 => (coord.first - scaled(3)), 
              :x2 => (coord.first - scaled(3)),
              :y1 => (coord.last),
              :y2 => (coord.last  - scaled(7)), :stroke => 'black', 'stroke-width' => scaled(1.4) )
    svg.line( :x1 => (coord.first + scaled(3)), 
              :x2 => (coord.first + scaled(3)),
              :y1 => (coord.last),
              :y2 => (coord.last  - scaled(7)), :stroke => 'black', 'stroke-width' => scaled(1.4) )
            
  
    # Some minor mathematics for the smile/frown
    percent = 1.0 - (coord.last.to_f / height.to_f)
    corners = scaled(8 - (5 * percent))
    anchor  = scaled((20 * percent) - 5)
    
    # Draw the mouth
    svg.path( :d => "M#{coord.first - scaled(9)} #{coord.last + corners} Q#{coord.first} #{coord.last + anchor} #{coord.first + scaled(9)} #{coord.last + corners}",
              :stroke => 'black', 'stroke-width' => scaled(1.4), :fill => 'none' )
            
            
    # Wizard hat for hero smiley.
    if coord.last == hero_smiley
      svg.ellipse(:cx => coord.first, :cy => (coord.last - scaled(13)), 
                  :rx => scaled(17), :ry => scaled(6.5), :fill => (complexity == :minimal ? 'purple' : 'url(#HeroGradient)'), :stroke => 'black', 'stroke-width' => scaled(1.4) )
    
      svg.path(:d => "M#{coord.first} #{coord.last - scaled(60)} " +
                     "L#{coord.first + scaled(10)} #{coord.last - scaled(14)} " +
                     "C#{coord.first + scaled(10)},#{coord.last - scaled(9)} #{coord.first - scaled(10)},#{coord.last - scaled(9)} #{coord.first - scaled(10)},#{coord.last - scaled(14)}" +
                     "L#{coord.first} #{coord.last - scaled(60)}",
                :stroke => 'black', 'stroke-width' => scaled(1.4), :fill => (complexity == :minimal ? 'purple' : 'url(#HeroGradient)'))

      svg.path(:d =>  "M#{coord.first - scaled(4)} #{coord.last - scaled(23)}" +
                      "l-#{scaled(2.5)} #{scaled(10)} l#{scaled(7.5)} -#{scaled(5)} l-#{scaled(10)} 0 l#{scaled(7.5)} #{scaled(5)} l-#{scaled(2.5)} -#{scaled(10)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
      svg.path(:d =>  "M#{coord.first + scaled(2)} #{coord.last - scaled(30)}" +
                      "l-#{scaled(2.5)} #{scaled(10)} l#{scaled(7.5)} -#{scaled(5)} l-#{scaled(10)} 0 l#{scaled(7.5)} #{scaled(5)} l-#{scaled(2.5)} -#{scaled(10)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
      svg.path(:d =>  "M#{coord.first - scaled(2)} #{coord.last - scaled(33)}" +
                      "l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => 'white' )
      svg.path(:d =>  "M#{coord.first - scaled(2.2)} #{coord.last - scaled(32.7)}" +
                      "l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
      svg.path(:d =>  "M#{coord.first + scaled(4.5)} #{coord.last - scaled(20)}" +
                      "l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
      svg.path(:d =>  "M#{coord.first} #{coord.last - scaled(40)}" +
                      "l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )

    end

  end
end

#scaled(pt) ⇒ Object

Legacy (4 days old). Removed scaled from layout engine, changed to #relative, with different math involved. Translate here so I don’t have to entirely redo this graph.



133
134
135
# File 'lib/scruffy/layers/all_smiles.rb', line 133

def scaled(pt)
  relative(pt) / 2
end