rubyvis 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +13 -1
  3. data/Manifest.txt +19 -0
  4. data/examples/antibiotics/antibiotics.rb +96 -0
  5. data/examples/antibiotics/antibiotics_data.rb +20 -0
  6. data/examples/area.rb +103 -0
  7. data/examples/bar_column_chart.rb +55 -0
  8. data/examples/barley/barley.rb +29 -19
  9. data/examples/barley/barley_data.rb +122 -0
  10. data/examples/crimea/crimea_grouped_bar.rb +59 -0
  11. data/examples/dot.rb +19 -0
  12. data/examples/first.rb +1 -6
  13. data/examples/line.rb +84 -0
  14. data/examples/pie_and_donut.rb +38 -0
  15. data/examples/scatterplot.rb +55 -0
  16. data/examples/second.rb +3 -4
  17. data/examples/third.rb +1 -1
  18. data/lib/rubyvis.rb +3 -1
  19. data/lib/rubyvis/color/color.rb +31 -3
  20. data/lib/rubyvis/color/colors.rb +3 -3
  21. data/lib/rubyvis/format/number.rb +80 -10
  22. data/lib/rubyvis/internals.rb +11 -5
  23. data/lib/rubyvis/javascript_behaviour.rb +1 -0
  24. data/lib/rubyvis/mark.rb +43 -20
  25. data/lib/rubyvis/mark/anchor.rb +1 -1
  26. data/lib/rubyvis/mark/area.rb +15 -13
  27. data/lib/rubyvis/mark/bar.rb +2 -2
  28. data/lib/rubyvis/mark/dot.rb +85 -0
  29. data/lib/rubyvis/mark/label.rb +1 -1
  30. data/lib/rubyvis/mark/line.rb +7 -6
  31. data/lib/rubyvis/mark/panel.rb +0 -1
  32. data/lib/rubyvis/mark/rule.rb +5 -4
  33. data/lib/rubyvis/mark/wedge.rb +124 -0
  34. data/lib/rubyvis/nest.rb +158 -0
  35. data/lib/rubyvis/scale.rb +4 -0
  36. data/lib/rubyvis/scale/log.rb +55 -0
  37. data/lib/rubyvis/scale/ordinal.rb +34 -11
  38. data/lib/rubyvis/scale/quantitative.rb +17 -3
  39. data/lib/rubyvis/scene/svg_area.rb +197 -0
  40. data/lib/rubyvis/scene/svg_dot.rb +67 -0
  41. data/lib/rubyvis/scene/svg_label.rb +17 -15
  42. data/lib/rubyvis/scene/svg_line.rb +0 -2
  43. data/lib/rubyvis/scene/svg_rule.rb +2 -2
  44. data/lib/rubyvis/scene/svg_scene.rb +8 -1
  45. data/lib/rubyvis/scene/svg_wedge.rb +56 -0
  46. data/lib/rubyvis/sceneelement.rb +2 -1
  47. data/spec/bar_spec.rb +27 -3
  48. data/spec/label_spec.rb +1 -1
  49. data/spec/nest_spec.rb +41 -0
  50. data/spec/panel_spec.rb +1 -1
  51. data/spec/scale_linear_spec.rb +2 -2
  52. data/spec/scale_ordinal_spec.rb +81 -0
  53. data/spec/spec.opts +0 -1
  54. metadata +24 -3
  55. metadata.gz.sig +0 -0
@@ -8,9 +8,9 @@ module Rubyvis
8
8
  end
9
9
 
10
10
  @properties=Mark.properties.dup
11
- attr_accessor_dsl :width, :height, :line_width, :stroke_style, :fill_style
11
+ attr_accessor_dsl :width, :height, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
12
12
  def self.defaults
13
- Bar.new.extend(Mark.defaults).line_width(1.5).fill_style(Rubyvis.Colors.category20().by(Rubyvis.parent))
13
+ Bar.new.extend(Mark.defaults).line_width(1.5).fill_style( lambda {Rubyvis.Colors.category20().scale(self.parent.index)})
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,85 @@
1
+ module Rubyvis
2
+ def self.Dot
3
+ Rubyvis::Dot
4
+ end
5
+ class Dot < Mark
6
+ def type
7
+ "dot"
8
+ end
9
+
10
+ @properties=Mark.properties.dup
11
+
12
+ attr_accessor_dsl :shape, :shape_angle, :shape_radius, :shape_size, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
13
+
14
+ def self.defaults()
15
+ Dot.new().extend(Mark.defaults).shape("circle"). line_width(1.5). stroke_style(lambda {pv.Colors.category10().scale(self.parent.index)})
16
+ end
17
+
18
+ def anchor(name)
19
+
20
+
21
+ mark_anchor(name).left(lambda {
22
+ s=scene.target[self.index]
23
+ case self.name
24
+ when 'bottom' then s.left;
25
+ when 'top' then s.left;
26
+ when 'center' then s.left;
27
+ when 'left' then nil;
28
+ else
29
+ s.left+s.shape_radius
30
+ end
31
+ }).right(lambda {
32
+ s=scene.target[self.index]
33
+ self.name()=='left' ? s.right+s.shape_radius : nil
34
+
35
+ }).top(lambda {
36
+ s=scene.target[self.index]
37
+ case self.name
38
+ when 'left' then s.top;
39
+ when 'right' then s.top;
40
+ when 'center' then s.top;
41
+ when 'top' then nil;
42
+ else
43
+ s.top+s.shape_radius
44
+ end
45
+ }).bottom(lambda {
46
+ s=scene.target[self.index]
47
+ self.name()=='top' ? s.bottom+s.shape_radius : nil
48
+ }).text_align(lambda {
49
+ case self.name
50
+ when 'left' then 'right';
51
+ when 'bottom' then 'center';
52
+ when 'top' then 'center';
53
+ when 'center' then 'center';
54
+ else
55
+ 'left'
56
+ end
57
+ }).text_baseline( lambda {
58
+ case self.name
59
+ when 'right' then 'middle';
60
+ when 'left' then 'middle';
61
+ when 'center' then 'middle';
62
+ when 'bottom' then 'top';
63
+ else
64
+ 'bottom'
65
+ end
66
+
67
+ })
68
+ end
69
+ def build_implied(s)
70
+ r = s.shape_radius
71
+ z = s.shape_size
72
+ if (r.nil?)
73
+ if (z.nil?)
74
+ s.shape_size = 20.25;
75
+ s.shape_radius = 4.5;
76
+ else
77
+ s.shape_radius = Math.sqrt(z)
78
+ end
79
+ elsif (z.nil?)
80
+ s.shape_size = r * r;
81
+ end
82
+ mark_build_implied(s)
83
+ end
84
+ end
85
+ end
@@ -5,7 +5,7 @@ module Rubyvis
5
5
 
6
6
  class Label < Mark
7
7
  @properties=Mark.properties.dup
8
- attr_accessor_dsl :text, :font, :text_angle, :text_style, :text_align, :text_baseline, :text_margin, :text_decoration, :text_shadow
8
+ attr_accessor_dsl :text, :font, :text_angle, [:text_style, lambda {|d| pv.color(d)}], :text_align, :text_baseline, :text_margin, :text_decoration, :text_shadow
9
9
  def type
10
10
  'label'
11
11
  end
@@ -5,18 +5,19 @@ module Rubyvis
5
5
  module LinePrototype
6
6
  include AreaPrototype
7
7
  def line_anchor(name)
8
- area_anchor(name).text_align(lambda {|d|
9
- {'left'=>'right','bottom'=>'center', 'top'=>'center','center'=>'center','right'=>'left'}[d]
8
+ anchor=area_anchor(name).text_align(lambda {|d|
9
+ {'left'=>'right','bottom'=>'center', 'top'=>'center','center'=>'center','right'=>'left'}[self.name]
10
10
  }).text_baseline(lambda{|d|
11
- {'top'=>'bottom','right'=>'middle', 'left'=>'middle','center'=>'middle','bottom'=>'top'}[d]
11
+ {'top'=>'bottom', 'right'=>'middle', 'left'=>'middle','center'=>'middle','bottom'=>'top'}[self.name]
12
12
  })
13
+ return anchor
13
14
  end
14
15
  end
15
16
  class Line < Mark
16
17
  include AreaPrototype
17
18
  include LinePrototype
18
19
  @properties=Mark.properties.dup
19
- attr_accessor_dsl :line_width, :line_join, :stroke_style, :fill_style, :segmented, :interpolate, :eccentricity, :tension
20
+ attr_accessor_dsl :line_width, :line_join, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}], :segmented, :interpolate, :eccentricity, :tension
20
21
  def type
21
22
  "line"
22
23
  end
@@ -29,8 +30,8 @@ module Rubyvis
29
30
  def build_instance(*args)
30
31
  area_build_instance(*args)
31
32
  end
32
- def defaults
33
- Line.new.extend(Rubyvis::Mark).line_join('miter').line_width(1.5).stroke_style(RubyVis::Color.category10().by(pv.parent)).interpolate('linear').eccentricity(0).tension(7)
33
+ def self.defaults
34
+ Line.new.extend(Mark.defaults).line_join('miter').line_width(1.5).stroke_style( lambda {return Rubyvis::Colors.category10().scale(self.parent.index)}).interpolate('linear').eccentricity(0).tension(7)
34
35
  end
35
36
  end
36
37
  end
@@ -74,7 +74,6 @@ module Rubyvis
74
74
  bar = REXML::Formatters::Default.new
75
75
  out = String.new
76
76
  bar.write(v[1].elements[1], out)
77
- out
78
77
  }.join
79
78
  end
80
79
  def build_implied(s)
@@ -5,9 +5,9 @@ module Rubyvis
5
5
  class Rule < Mark
6
6
  include LinePrototype
7
7
  @properties=Mark.properties.dup
8
- attr_accessor_dsl :width, :height, :line_width, :stroke_style
8
+ attr_accessor_dsl :width, :height, :line_width, [:stroke_style, lambda {|d| pv.color(d)}]
9
9
  def self.defaults
10
- Rule.new.extend(Mark.defaults).line_width(1).stroke_style(pv.color('black')).antialias(false)
10
+ Rule.new.extend(Mark.defaults).line_width(1).stroke_style('black').antialias(false)
11
11
  end
12
12
  def type
13
13
  'rule'
@@ -21,12 +21,13 @@ module Rubyvis
21
21
  r=s.right
22
22
  t=s.top
23
23
  b=s.bottom
24
- if((!s.width.nil?) or ((l.nil?) and (r.nil?)) or ((!r.nil?) or (!l.nil?)))
24
+
25
+ if((!s.width.nil?) or ((l.nil?) and (r.nil?)) or ((!r.nil?) and (!l.nil?)))
25
26
  s.height=0
26
27
  else
27
28
  s.width=0
28
29
  end
29
- super(s)
30
+ mark_build_implied(s)
30
31
  end
31
32
  end
32
33
  end
@@ -0,0 +1,124 @@
1
+ module Rubyvis
2
+ def self.Wedge
3
+ Rubyvis::Wedge
4
+ end
5
+ class Wedge < Mark
6
+ def type
7
+ "wedge"
8
+ end
9
+ @properties=Mark.properties.dup
10
+ attr_accessor_dsl :start_angle, :end_angle, :angle, :inner_radius, :outer_radius, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
11
+ def self.defaults
12
+ Wedge.new.extend(Mark.defaults).start_angle(lambda {s=self.sibling; s ? s.end_angle: -Math::PI.quo(2) } ).inner_radius( 0 ).line_width( 1.5 ).stroke_style( nil ).fill_style( lambda {Rubyvis.Colors.category20().scale(self.parent.index)})
13
+ end
14
+ def mid_radius
15
+ (inner_radius+outer_radius) / 2.0
16
+ end
17
+ def mid_angle
18
+ (start_angle+end_angle) / 2.0
19
+ end
20
+
21
+ def anchor(name)
22
+ partial=lambda {|s| s.inner_radius!=0 ? true : s.angle<2*Math.PI}
23
+ mid_radius=lambda {|s| (s.inner_radius+s.outer_radius) / 2.0}
24
+ mid_angle=lambda {|s| (s.start_angle+s.end_angle) / 2.0 }
25
+
26
+ mark_anchor(name).left(lambda {
27
+ s = self.scene.target[self.index];
28
+ if (partial.call(s))
29
+
30
+ case (self.name())
31
+ when "outer"
32
+ return s.left + s.outer_radius * Math.cos(mid_angle.call(s))
33
+ when "inner"
34
+ return s.left + s.inner_radius * Math.cos(mid_angle.call(s))
35
+ when "start"
36
+ return s.left + mid_radius.call(s) * Math.cos(s.start_angle)
37
+ when "center"
38
+ return s.left + mid_radius.call(s) * Math.cos(mid_angle.call(s))
39
+ when "end"
40
+ return s.left + mid_radius.call(s) * Math.cos(s.end_angle)
41
+ end
42
+ end
43
+ return s.left;
44
+ }).top(lambda {
45
+ s = self.scene.target[self.index];
46
+ if (partial.call(s))
47
+ case (self.name())
48
+ when "outer"
49
+ return s.top + s.outer_radius * Math.sin(mid_angle.call(s))
50
+ when "inner"
51
+ return s.top + s.inner_radius * Math.sin(mid_angle.call(s))
52
+ when "start"
53
+ return s.top + mid_radius.call(s) * Math.sin(s.start_angle)
54
+ when "center"
55
+ return s.top + mid_radius.call(s) * Math.sin(mid_angle.call(s))
56
+ when "end"
57
+ return s.top + mid_radius.call(s) * Math.sin(s.end_angle)
58
+ end
59
+ end
60
+ return s.top;
61
+
62
+ }).text_align(lambda {
63
+ s = self.scene.target[self.index];
64
+ if (partial.call(s))
65
+ case (self.name())
66
+ when 'outer'
67
+ return self.upright(mid_angle.call(s)) ? 'right':'left'
68
+ when 'inner'
69
+ return self.upright(mid_angle.call(s)) ? 'left':'right'
70
+
71
+ end
72
+ end
73
+ return 'center'
74
+ }).text_baseline(lambda {
75
+ s = self.scene.target[self.index];
76
+ if (partial.call(s))
77
+ case (self.name())
78
+ when 'start'
79
+ return self.upright(s.start_angle) ? 'top':'bottom'
80
+ when 'end'
81
+ return self.upright(s.end_angle) ? 'bottom':'top'
82
+
83
+ end
84
+ end
85
+ return 'middle'
86
+ }).text_angle(lambda {
87
+ s = self.scene.target[self.index];
88
+ a=0
89
+ if (partial.call(s))
90
+ case (self.name())
91
+ when 'center'
92
+ a=mid_angle.call(s)
93
+ when 'inner'
94
+ a=mid_angle.call(s)
95
+ when 'outer'
96
+ a=mid_angle.call(s)
97
+ when 'start'
98
+ a=s.start_angle
99
+ when 'end'
100
+ a=s.end_angle
101
+ end
102
+ end
103
+ self.upright(a) ? a: (a+Math::PI)
104
+ })
105
+
106
+ end
107
+
108
+
109
+ def self.upright(angle)
110
+ angle=angle % (2*Math::PI)
111
+ angle=(angle<0) ? (2*Math::PI+angle) : angle
112
+ (angle < Math::PI/2.0) or (angle>=3*Math::PI / 2.0)
113
+
114
+ end
115
+ def build_implied(s)
116
+ if (s.angle.nil?)
117
+ s.angle= s.end_angle-s.start_angle
118
+ elsif s.end_angle.nil?
119
+ s.end_angle=s.start_angle+s.angle
120
+ end
121
+ mark_build_implied(s)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,158 @@
1
+ module Rubyvis
2
+ ##
3
+ # Returns a {@link pv.Nest} operator for the specified array. This is a
4
+ # convenience factory method, equivalent to <tt>new pv.Nest(array)</tt>.
5
+ #
6
+ # @see pv.Nest
7
+ # @param {array} array an array of elements to nest.
8
+ # @returns {Nest} a nest operator for the specified array.
9
+ ##
10
+ def self.nest(array)
11
+ Nest.new(array)
12
+ end
13
+ class NestedArray
14
+ attr_accessor :key, :values
15
+ def initialize(opts)
16
+ @key=opts[:key]
17
+ @values=opts[:values]
18
+ end
19
+ def ==(var)
20
+ key==var.key and values=var.values
21
+ end
22
+ end
23
+ ##
24
+ # Constructs a nest operator for the specified array. This constructor should
25
+ # not be invoked directly; use {@link pv.nest} instead.
26
+ #
27
+ # @class Represents a {@link Nest} operator for the specified array. Nesting
28
+ # allows elements in an array to be grouped into a hierarchical tree
29
+ # structure. The levels in the tree are specified by <i>key</i> functions. The
30
+ # leaf nodes of the tree can be sorted by value, while the internal nodes can
31
+ # be sorted by key. Finally, the tree can be returned either has a
32
+ # multidimensional array via {@link #entries}, or as a hierarchical map via
33
+ # {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing
34
+ # the elements in each leaf node using a summary function.
35
+ #
36
+ # <p>For example, consider the following tabular data structure of Barley
37
+ # yields, from various sites in Minnesota during 1931-2:
38
+ #
39
+ # { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
40
+ # { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
41
+ # { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }
42
+ #
43
+ # To facilitate visualization, it may be useful to nest the elements first by
44
+ # year, and then by variety, as follows:
45
+ #
46
+ # <pre>var nest = pv.nest(yields)
47
+ # .key(function(d) d.year)
48
+ # .key(function(d) d.variety)
49
+ # .entries();</pre>
50
+ #
51
+ # This returns a nested array. Each element of the outer array is a key-values
52
+ # pair, listing the values for each distinct key:
53
+ #
54
+ # <pre>{ key: 1931, values: [
55
+ # { key: "Manchuria", values: [
56
+ # { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
57
+ # { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
58
+ # { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
59
+ # ...
60
+ # ] },
61
+ # { key: "Glabron", values: [
62
+ # { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
63
+ # { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
64
+ # ...
65
+ # ] },
66
+ # ] },
67
+ # { key: 1932, values: ... }</pre>
68
+ #
69
+ # Further details, including sorting and rollup, is provided below on the
70
+ # corresponding methods.
71
+ #
72
+ # @param {array} array an array of elements to nest.
73
+ #/
74
+ class Nest
75
+ attr_accessor :array, :keys, :order
76
+ def initialize(array)
77
+ @array=array
78
+ @keys=[]
79
+ @order=nil
80
+ end
81
+ def key(k)
82
+ @keys.push(k)
83
+ return self
84
+ end
85
+ def sort_keys(order=nil)
86
+ #keys[keys.size-1].order = order.nil? ? pv.natural_order : order
87
+ return self
88
+ end
89
+ def sort_values(order=nil)
90
+ @order = order.nil? ? pv.natural_order : order
91
+ return self
92
+ end
93
+ def map
94
+ i=0
95
+ map={}
96
+ values=[]
97
+ @array.each_with_index {|x,j|
98
+ m=map
99
+ (@keys.size-1).times {|i|
100
+ k=@keys[i].call(x)
101
+ m[k]={} if (!m[k])
102
+ m=m[k]
103
+ }
104
+ k=@keys.last.call(x)
105
+ if(!m[k])
106
+ a=[]
107
+ values.push(a)
108
+ m[k]=a
109
+ end
110
+ m[k].push(x)
111
+ }
112
+ if(self.order)
113
+ values.each_with_index {|v,vi|
114
+ values[vi].sort(&self.order)
115
+ }
116
+ end
117
+ map
118
+ end
119
+ def entries()
120
+ entries_sort(entries_entries(map),0)
121
+ end
122
+ def entries_entries(map)
123
+ array=[]
124
+ map.each_pair {|k,v|
125
+ array.push(NestedArray.new({:key=>k, :values=>(v.is_a? Array) ? v: entries_entries(v)}))
126
+ }
127
+ array
128
+ end
129
+ def entries_sort(array,i)
130
+ o=keys[i].order
131
+ if o
132
+ array.sort {|a,b| o(a.key, b.key)}
133
+ end
134
+ i+=1
135
+ if (i<keys.size)
136
+ array.each {|v|
137
+ entries_sort(v, i)
138
+ }
139
+
140
+ end
141
+ return array
142
+
143
+ end
144
+ def rollup_rollup(map,f)
145
+ map.each_pair {|key,value|
146
+ if value.is_a? Array
147
+ map[key]=f.call(value)
148
+ else
149
+ rollup_rollup(value)
150
+ end
151
+ }
152
+ return map;
153
+ end
154
+ def rollup(f)
155
+ rollup_rollup(self.map,f)
156
+ end
157
+ end
158
+ end