rubyvis 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,14 +17,14 @@ module Rubyvis
17
17
 
18
18
  d = "M" + s.left.to_s + "," + s.top.to_s
19
19
 
20
- if (scenes.size > 2 and (s.interpolate == "basis" or s.interpolate == "cardinal" or s.interpolate == "monotone"))
20
+ if (scenes.size > 2 and (['basis', 'cardinal', 'monotone'].include? s.interpolate))
21
21
  case (s.interpolate)
22
- when "basis"
23
- d = d+ curve_basis(scenes)
24
- when "cardinal"
25
- d = d+curve_cardinal(scenes, s.tension)
26
- when "monotone"
27
- d = d+curve_monotone(scenes)
22
+ when "basis"
23
+ d = d+ curve_basis(scenes)
24
+ when "cardinal"
25
+ d = d+curve_cardinal(scenes, s.tension)
26
+ when "monotone"
27
+ d = d+curve_monotone(scenes)
28
28
  end
29
29
 
30
30
  else
@@ -68,7 +68,7 @@ module Rubyvis
68
68
  s1 = scenes[i]
69
69
  s2 = scenes[i + 1];
70
70
 
71
- #/* visible */
71
+ # visible
72
72
  next if (!s1.visible and !s2.visible)
73
73
 
74
74
  stroke = s1.stroke_style
@@ -76,14 +76,14 @@ module Rubyvis
76
76
 
77
77
  next if stroke.opacity==0.0
78
78
 
79
- #/* interpolate */
79
+ # interpolate
80
80
  d=nil
81
81
  if ((s1.interpolate == "linear") and (s1.lineJoin == "miter"))
82
82
  fill = stroke;
83
83
  stroke = Rubyvis.Color.transparent;
84
84
  d = path_join(scenes[i - 1], s1, s2, scenes[i + 2]);
85
85
  elsif(paths)
86
- d = paths[i];
86
+ d = paths[i]
87
87
  else
88
88
  d = "M" + s1.left + "," + s1.top + path_segment(s1, s2);
89
89
  end
@@ -109,22 +109,24 @@ module Rubyvis
109
109
 
110
110
  def self.path_segment(s1, s2)
111
111
  l = 1; # sweep-flag
112
- case (s1.interpolate)
113
- when "polar-reverse"
114
- l = 0;
115
- when "polar"
116
- dx = s2.left - s1.left,
112
+ l = 0 if s1.interpolate=='polar-reverse'
113
+
114
+ if s1.interpolate=='polar' or s1.interpolate=='polar-reverse'
115
+ dx = s2.left - s1.left
117
116
  dy = s2.top - s1.top
118
117
  e = 1 - s1.eccentricity
119
118
  r = Math.sqrt(dx * dx + dy * dy) / (2 * e)
120
- if !((e<=0) and (e>1))
119
+ if !((e<=0) or (e>1))
121
120
  return "A#{r},#{r} 0 0,#{l} #{s2.left},#{s2.top}"
122
121
  end
123
- when "step-before"
122
+ end
123
+
124
+ if s1.interpolate=="step-before"
124
125
  return "V#{s2.top}H#{s2.left}"
125
- when "step-after"
126
+ elsif s1.interpolate=="step-after"
126
127
  return "H#{s2.left}V#{s2.top}"
127
128
  end
129
+
128
130
  return "L#{s2.left},#{s2.top}"
129
131
  end
130
132
 
@@ -7,17 +7,18 @@ require 'rubyvis/scene/svg_dot'
7
7
  require 'rubyvis/scene/svg_area'
8
8
  require 'rubyvis/scene/svg_wedge'
9
9
  require 'rubyvis/scene/svg_image'
10
+ require 'rubyvis/scene/svg_curve'
10
11
 
11
12
  class REXML::Element
12
13
  attr_accessor :_scene
13
14
  end
14
15
 
15
16
  module Rubyvis
16
- def self.Scene
17
+ def self.Scene # :nodoc:
17
18
  Rubyvis::SvgScene
18
19
  end
19
- module SvgScene
20
- include REXML
20
+ module SvgScene # :nodoc:
21
+ #include REXML
21
22
  def self.svg
22
23
  "http://www.w3.org/2000/svg"
23
24
  end;
@@ -74,7 +75,7 @@ module Rubyvis
74
75
  end
75
76
  end
76
77
  def self.create(type)
77
- el=Element.new "#{type}"
78
+ el=REXML::Element.new "#{type}"
78
79
  if type=='svg'
79
80
  el.add_namespace(self.svg)
80
81
  #el.add_namespace("xmlns:xmlns", self.xmlns)
@@ -1,5 +1,8 @@
1
1
  module Rubyvis
2
- class SceneElement
2
+ # Scene element
3
+ # Store information about each scene.
4
+ # On javascript, is an Array with custom properties.
5
+ class SceneElement # :nodoc:
3
6
  def initialize
4
7
  @scenes=Array.new
5
8
  end
data/spec/area_spec.rb ADDED
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__)+"/spec_helper.rb"
2
+ describe Rubyvis::Area do
3
+ it "should have correct properties" do
4
+ props=[:antialias, :bottom, :cursor, :data, :events, :fill_style, :height, :id, :interpolate, :left, :line_width, :reverse, :right, :segmented, :stroke_style, :tension, :title, :top, :visible, :width].inject({}) {|ac, v| ac[v]=true; ac}
5
+ Rubyvis::Area.properties.should==props
6
+ end
7
+ context "rendered" do
8
+ before do
9
+ @h=200
10
+ @w=200
11
+ @vis = Rubyvis.Panel.new.width(@w).height(@h)
12
+ @area=@vis.add(pv.Area).
13
+ data([1,2,1,4,1,5]).
14
+ height(lambda {|d| d*20}).
15
+ left(lambda {index*20}).
16
+ bottom(0)
17
+ end
18
+ it "should return correct default (linear) path" do
19
+ @vis.render
20
+ doc=Nokogiri::XML(@vis.to_svg)
21
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180L20 160L40 180L60 120L80 180L100 100L100 200L80 200L60 200L40 200L20 200L0 200Z"
22
+ end
23
+ it "should return correct path for interpolate=step-before" do
24
+ @area.interpolate('step-before')
25
+ @vis.render
26
+ doc=Nokogiri::XML(@vis.to_svg)
27
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180V160L20 160V180L40 180V120L60 120V180L80 180V100L100 100L100 200H80L80 200H60L60 200H40L40 200H20L20 200H0L0 200Z"
28
+ end
29
+ it "should return correct path for interpolate=step-after" do
30
+ @area.interpolate('step-after')
31
+ @vis.render
32
+ doc=Nokogiri::XML(@vis.to_svg)
33
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180H20L20 160H40L40 180H60L60 120H80L80 180H100L100 100L100 200V200L80 200V200L60 200V200L40 200V200L20 200V200L0 200Z"
34
+ end
35
+ it "should return correct path for interpolate=basis" do
36
+ @area.interpolate('basis')
37
+ @vis.render
38
+ doc=Nokogiri::XML(@vis.to_svg)
39
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180C0 180 0 180 3.333333333333333 176.66666666666666C6.666666666666666 173.33333333333331 13.333333333333332 166.66666666666666 20 166.66666666666666C26.666666666666664 166.66666666666666 33.33333333333333 173.33333333333331 40 166.66666666666666C46.666666666666664 160 53.33333333333333 140 60 140C66.66666666666666 140 73.33333333333333 160 80 156.66666666666666C86.66666666666666 153.33333333333331 93.33333333333331 126.66666666666666 96.66666666666666 113.33333333333331C99.99999999999999 99.99999999999999 99.99999999999999 99.99999999999999 99.99999999999997 99.99999999999997L100 200C99.99999999999999 199.99999999999997 99.99999999999999 199.99999999999997 96.66666666666664 199.99999999999994C93.33333333333331 199.99999999999997 86.66666666666666 199.99999999999997 80 199.99999999999994C73.33333333333333 199.99999999999997 66.66666666666666 199.99999999999997 59.99999999999999 199.99999999999994C53.33333333333333 199.99999999999997 46.666666666666664 199.99999999999997 40 199.99999999999994C33.33333333333333 199.99999999999997 26.666666666666664 199.99999999999997 20 199.99999999999994C13.333333333333332 199.99999999999997 6.666666666666666 199.99999999999997 3.333333333333333 199.99999999999994C0 199.99999999999997 0 199.99999999999997 0 199.99999999999994Z"
40
+ end
41
+ it "should return correct path for interpolate=cardinal" do
42
+ @area.interpolate('cardinal')
43
+ @vis.render
44
+ doc=Nokogiri::XML(@vis.to_svg)
45
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180Q16 160 20 160C26 160 34 186 40 180S54 120 60 120S74 183 80 180Q84 178 100 100L100 200Q84 200 80 200C74 200 66 200 60 200S46 200 40 200S26 200 20 200Q16 200 0 200Z"
46
+ end
47
+ end
48
+ end
data/spec/bar_spec.rb CHANGED
@@ -65,7 +65,7 @@ require File.dirname(__FILE__)+"/spec_helper.rb"
65
65
  x=v.attributes['x'] ? v.attributes['x'].value : nil
66
66
  [x, v.attributes['y'].value, v.attributes['height'].value]
67
67
  }
68
- attribs.should==[[nil,"90","10"],["25","80","20"],[nil,"70","30"],["25","40","60"]]
68
+ attribs.should==[[nil,"90","10"], ["25","80","20"], [nil,"70","30"], ["25","40","60"]]
69
69
  end
70
70
 
71
71
  end
data/spec/line_spec.rb ADDED
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__)+"/spec_helper.rb"
2
+ describe Rubyvis::Line do
3
+ it "should have correct properties" do
4
+ props=[:antialias, :bottom, :cursor, :data, :eccentricity, :events, :fill_style, :id, :interpolate, :left, :line_join, :line_width, :reverse, :right, :segmented, :stroke_style, :tension, :title, :top, :visible]
5
+ Rubyvis::Line.properties.keys.sort.should==props
6
+ end
7
+ context "rendered" do
8
+ before do
9
+ @h=200
10
+ @w=200
11
+ @vis = Rubyvis.Panel.new.width(@w).height(@h)
12
+ @area=@vis.add(pv.Line).
13
+ data([1,2,1,4,1,5]).
14
+ bottom(lambda {|d| d*20}).
15
+ left(lambda {index*20})
16
+ end
17
+ it "should return correct default (linear) path" do
18
+ @vis.render
19
+ doc=Nokogiri::XML(@vis.to_svg)
20
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180L20 160L40 180L60 120L80 180L100 100L100 200L80 200L60 200L40 200L20 200L0 200Z"
21
+ end
22
+ it "should return correct path for interpolate=step-before" do
23
+ @area.interpolate('step-before')
24
+ @vis.render
25
+ doc=Nokogiri::XML(@vis.to_svg)
26
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0,180V160H20V180H40V120H60V180H80V100H100"
27
+ end
28
+ it "should return correct path for interpolate=step-after" do
29
+ @area.interpolate('step-after')
30
+ @vis.render
31
+ doc=Nokogiri::XML(@vis.to_svg)
32
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0,180H20V160H40V180H60V120H80V180H100V100"
33
+ end
34
+
35
+ it "should return correct path for interpolate=polar" do
36
+ @area.interpolate('polar')
37
+ @vis.render
38
+ doc=Nokogiri::XML(@vis.to_svg)
39
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0,180A14.142135623730951,14.142135623730951 0 0,1 20,160A14.142135623730951,14.142135623730951 0 0,1 40,180A31.622776601683793,31.622776601683793 0 0,1 60,120A31.622776601683793,31.622776601683793 0 0,1 80,180A41.23105625617661,41.23105625617661 0 0,1 100,100"
40
+ end
41
+
42
+
43
+ it "should return correct path for interpolate=polar-reverse" do
44
+
45
+ @area.interpolate('polar-reverse')
46
+ @vis.render
47
+ doc=Nokogiri::XML(@vis.to_svg)
48
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0,180A14.142135623730951,14.142135623730951 0 0,0 20,160A14.142135623730951,14.142135623730951 0 0,0 40,180A31.622776601683793,31.622776601683793 0 0,0 60,120A31.622776601683793,31.622776601683793 0 0,0 80,180A41.23105625617661,41.23105625617661 0 0,0 100,100"
49
+ end
50
+ it "should return correct path for interpolate=basis" do
51
+ @area.interpolate('basis')
52
+ @vis.render
53
+ doc=Nokogiri::XML(@vis.to_svg)
54
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180C0 180 0 180 3.333333333333333 176.66666666666666C6.666666666666666 173.33333333333331 13.333333333333332 166.66666666666666 20 166.66666666666666C26.666666666666664 166.66666666666666 33.33333333333333 173.33333333333331 40 166.66666666666666C46.666666666666664 160 53.33333333333333 140 60 140C66.66666666666666 140 73.33333333333333 160 80 156.66666666666666C86.66666666666666 153.33333333333331 93.33333333333331 126.66666666666666 96.66666666666666 113.33333333333331C99.99999999999999 99.99999999999999 99.99999999999999 99.99999999999999 99.99999999999997 99.99999999999997"
55
+ end
56
+ it "should return correct path for interpolate=cardinal" do
57
+ @area.interpolate('cardinal')
58
+ @vis.render
59
+ doc=Nokogiri::XML(@vis.to_svg)
60
+ doc.at_xpath("//xmlns:path").should have_path_data_close_to "M0 180Q16 160 20 160C26 160 34 186 40 180S54 120 60 120S74 183 80 180Q84 178 100 100"
61
+ end
62
+ end
63
+ end
data/spec/panel_spec.rb CHANGED
@@ -18,9 +18,7 @@ describe Rubyvis::Panel do
18
18
  it "should return valid svg" do
19
19
  @vis.render
20
20
  doc=Nokogiri::XML(@vis.to_svg)
21
- values={"font-size"=>"10px", "font-family"=>"sans-serif", "fill"=>"none", "stroke"=>"none", "stroke-width"=>"1.5", "width"=>"200.0", "height"=>"200.0"}
22
- values.each {|k,v|
23
- doc.at_xpath("//xmlns:svg").attributes[k].value.should==v
24
- }
21
+ doc.at_xpath("//xmlns:svg").should have_svg_attributes({"font-size"=>"10px", "font-family"=>"sans-serif", "fill"=>"none", "stroke"=>"none", "stroke-width"=>"1.5", "width"=>"200.0", "height"=>"200.0"})
25
22
  end
23
+
26
24
  end
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__)+"/spec_helper.rb"
2
+ describe "Ruby API for Rubyvis" do
3
+ before do
4
+ @h=200
5
+ @w=200
6
+ end
7
+ it "should create a Panel with a block" do
8
+ lambda {@vis = Rubyvis.Panel.new {
9
+ width @w
10
+ height @h
11
+ }
12
+ }.should_not raise_exception
13
+ @vis._properties.size.should==2
14
+ end
15
+ it "should create a Bar with new method" do
16
+ vis1=Rubyvis.Panel.new.width(@w).height(@h)
17
+ vis1.add(Rubyvis::Bar).
18
+ data([1,2,3]).
19
+ width(10).
20
+ height(10).
21
+ left(lambda {|x| x*10}).anchor('top').
22
+ add(Rubyvis::Label).
23
+ text(lambda {|x| x})
24
+ vis1.render
25
+ svg1=vis1.to_svg
26
+
27
+ ww=@w
28
+ hh=@h
29
+ vis2=Rubyvis.Panel.new {|pan|
30
+ pan.width ww
31
+ pan.height hh
32
+ pan.bar {
33
+ data([1,2,3])
34
+ width 10
35
+ height 10
36
+ left {|x| x*10}
37
+ label(:anchor=>'top') {
38
+ text {|x| x}
39
+ }
40
+ }
41
+ }
42
+ vis2.render
43
+ svg2=vis2.to_svg
44
+ svg1.should==svg2
45
+
46
+ end
47
+ end
@@ -107,5 +107,18 @@ describe Rubyvis::Scale::Linear do
107
107
  @y.domain().should==[0.2,1]
108
108
  end
109
109
 
110
- it "should returns correct tick_format"
110
+ it "should returns correct tick_format" do
111
+ @y.tick_format.should be_instance_of Proc
112
+ @y.tick_format.call( 2).should=='2'
113
+ @y.tick_format.call(2.0).should=='2'
114
+ @y.tick_format.call(2.1).should=='2.1'
115
+ @y.tick_format.call("a").should==''
116
+ end
117
+ it "should return correct tick_format for small numbers" do
118
+ @y.domain(0.00001,0.0001)
119
+ @y.range(0.000001,0.0001)
120
+ @y.ticks.should==[1.quo(100000), 1.quo(50000), 3.quo(100000), 1.quo(25000), 1.quo(20000), 3.quo(50000), 7.quo(100000), 1.quo(12500), 9.quo(100000), 1.quo(10000)]
121
+ @y.tick_format.call(0.2).should=='0.20000'
122
+
123
+ end
111
124
  end
data/spec/spec_helper.rb CHANGED
@@ -25,4 +25,39 @@ module Rubyvis
25
25
  @runtime = Johnson.load(*files)
26
26
  end
27
27
  end
28
+ end
29
+ # Spec matcher
30
+ Spec::Matchers.define :have_svg_attributes do |exp|
31
+ match do |obs|
32
+ exp.each {|k,v|
33
+ obs.attributes[k].value.should==v
34
+ }
35
+ end
36
+ end
37
+ Spec::Matchers.define :have_path_data_close_to do |exp|
38
+ def path_scan(path)
39
+ path.scan(/([MmCcZzLlHhVvSsQqTtAa, ])(\d+(?:\.\d+)?)/).map {|v|
40
+ v[0]="," if v[0]==" "
41
+ v[1]=v[1].to_f
42
+ v
43
+ }
44
+ end
45
+ match do |obs|
46
+ correct=true
47
+ obs_array=path_scan(obs.attributes["d"].value)
48
+
49
+ exp_array=path_scan(exp)
50
+ obs_array.each_with_index {|v,i|
51
+ if (v[0]!=exp_array[i][0]) or (v[1]-exp_array[i][1]).abs>0.001
52
+ correct=false
53
+ break
54
+ end
55
+ }
56
+ correct
57
+ end
58
+ failure_message_for_should do |obs|
59
+ obs_array=path_scan(obs.attributes["d"].value)
60
+ exp_array=path_scan(exp)
61
+ "#{obs_array} path should be equal to #{exp_array}"
62
+ end
28
63
  end
data/web/Rakefile CHANGED
@@ -22,7 +22,7 @@ file "index.html"=>["index.haml", :build_site]+EXAMPLES_BASE.map {|v| "examples/
22
22
 
23
23
  EXAMPLES.each do |v|
24
24
  e=v[1]
25
- file "examples/#{e}.html"=>["examples.haml", :build_site, v[0]]
25
+ file "examples/#{e}.html"=>["examples.haml", "examples/#{e}.svg", :build_site, v[0]]
26
26
  file "examples/#{e}.svg"=>[v[0]] do |t|
27
27
  system "ruby1.9 #{v[0]} > #{t.name}"
28
28
  end
data/web/build_site.rb CHANGED
@@ -65,7 +65,23 @@ Dir.glob(File.dirname(__FILE__)+"/../examples/**/*.rb") do |f|
65
65
  page.title=title
66
66
  page.text=text
67
67
  page.svg_file=base+".svg"
68
-
68
+ # Read svg size
69
+ width=350
70
+ height=200
71
+ if File.exists? "examples/#{page.svg_file}"
72
+ File.open("examples/#{page.svg_file}","r") {|fp|
73
+ header=fp.gets(">")
74
+ if header=~/\sheight='([^']+)'/
75
+ height=$1
76
+ end
77
+ if header=~/\swidth='([^']+)'/
78
+ width=$1
79
+ end
80
+ }
81
+ end
82
+ page.svg_width=width.to_f.ceil
83
+ page.svg_height=height.to_f.ceil
84
+
69
85
  end
70
86
 
71
87
  pages.each do |name,page|
data/web/examples.haml CHANGED
@@ -21,8 +21,8 @@
21
21
  %a{:href=>"#{next_ex}.html"} Next: #{pages[next_ex].title}
22
22
  .image
23
23
  /[if IE]
24
- %embed.svg{:src=>svg_file,:width=>"500", :height=>"300"}
25
- %object.svg{:data=>svg_file, :type=>"image/svg+xml"}
24
+ %embed.svg{:src=>svg_file,:width=>svg_width, :height=>svg_height}
25
+ %object.svg{:data=>svg_file, :type=>"image/svg+xml", :width=>svg_width, :height=>svg_height}
26
26
  .source=find_and_preserve(source)
27
27
  :javascript
28
28
  var _gaq = _gaq || [];
data/web/index.haml CHANGED
@@ -19,7 +19,7 @@
19
19
  %strong=Rubyvis::VERSION
20
20
  %p
21
21
  Protovis API Version:
22
- %strong=Rubyvis::API_VERSION
22
+ %strong=Rubyvis::PROTOVIS_API_VERSION
23
23
  %h2 Installation
24
24
  %pre gem install rubyvis
25
25
  %h2 Synopsis
@@ -28,13 +28,19 @@
28
28
  %h3 Ruby
29
29
  %pre
30
30
  :preserve
31
+ require 'rubygems'
31
32
  require 'rubyvis'
32
- vis = Rubyvis::Panel.new.width(150).height(150);
33
- vis.add(pv.Bar).data([1, 1.2, 1.7, 1.5, 0.7, 0.3]).
34
- width(20).
35
- height(lambda {|d| d * 80}).
36
- bottom(0).
37
- left(lambda {self.index * 25})
33
+ vis = Rubyvis::Panel.new do
34
+ width 150
35
+ height 150
36
+ bar do
37
+ data [1, 1.2, 1.7, 1.5, 0.7, 0.3]
38
+ width 20
39
+ height {|d| d * 80}
40
+ bottom(0)
41
+ left {index * 25}
42
+ end
43
+ end
38
44
  vis.render()
39
45
  puts vis.to_svg # Output final SVG
40
46
  #svg_code