bio-graphics 1.0

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 (63) hide show
  1. data/doc/classes/Bio.html +135 -0
  2. data/doc/classes/Bio/Graphics.html +247 -0
  3. data/doc/classes/Bio/Graphics/Panel.html +344 -0
  4. data/doc/classes/Bio/Graphics/Panel.src/M000005.html +29 -0
  5. data/doc/classes/Bio/Graphics/Panel.src/M000006.html +19 -0
  6. data/doc/classes/Bio/Graphics/Panel.src/M000007.html +67 -0
  7. data/doc/classes/Bio/Graphics/Panel/Ruler.html +238 -0
  8. data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000008.html +20 -0
  9. data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000009.html +28 -0
  10. data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000010.html +54 -0
  11. data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000013.html +20 -0
  12. data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000014.html +28 -0
  13. data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000015.html +59 -0
  14. data/doc/classes/Bio/Graphics/Panel/Track.html +342 -0
  15. data/doc/classes/Bio/Graphics/Panel/Track.src/M000008.html +23 -0
  16. data/doc/classes/Bio/Graphics/Panel/Track.src/M000009.html +42 -0
  17. data/doc/classes/Bio/Graphics/Panel/Track.src/M000010.html +285 -0
  18. data/doc/classes/Bio/Graphics/Panel/Track.src/M000011.html +23 -0
  19. data/doc/classes/Bio/Graphics/Panel/Track.src/M000012.html +43 -0
  20. data/doc/classes/Bio/Graphics/Panel/Track.src/M000013.html +259 -0
  21. data/doc/classes/Bio/Graphics/Panel/Track/Feature.html +292 -0
  22. data/doc/classes/Bio/Graphics/Panel/Track/Feature.src/M000011.html +65 -0
  23. data/doc/classes/Bio/Graphics/Panel/Track/Feature.src/M000014.html +65 -0
  24. data/doc/classes/Bio/Graphics/Panel/Track/Feature/PixelRange.html +155 -0
  25. data/doc/classes/Bio/Graphics/Panel/Track/Feature/PixelRange.src/M000012.html +18 -0
  26. data/doc/classes/Bio/Graphics/Panel/Track/Feature/PixelRange.src/M000015.html +18 -0
  27. data/doc/classes/ImageMap.html +185 -0
  28. data/doc/classes/ImageMap.src/M000001.html +18 -0
  29. data/doc/classes/ImageMap.src/M000002.html +24 -0
  30. data/doc/classes/ImageMap/ImageMapElement.html +187 -0
  31. data/doc/classes/ImageMap/ImageMapElement.src/M000003.html +19 -0
  32. data/doc/classes/ImageMap/ImageMapElement.src/M000004.html +20 -0
  33. data/doc/created.rid +1 -0
  34. data/doc/files/README_DEV.html +432 -0
  35. data/doc/files/TUTORIAL.html +358 -0
  36. data/doc/files/lib/bio-graphics_rb.html +121 -0
  37. data/doc/files/lib/bio/graphics/feature_rb.html +113 -0
  38. data/doc/files/lib/bio/graphics/image_map_rb.html +113 -0
  39. data/doc/files/lib/bio/graphics/panel_rb.html +113 -0
  40. data/doc/files/lib/bio/graphics/ruler_rb.html +113 -0
  41. data/doc/files/lib/bio/graphics/track_rb.html +113 -0
  42. data/doc/fr_class_index.html +35 -0
  43. data/doc/fr_file_index.html +34 -0
  44. data/doc/fr_method_index.html +41 -0
  45. data/doc/images/example.png +0 -0
  46. data/doc/images/glyph_showcase.png +0 -0
  47. data/doc/images/terms.png +0 -0
  48. data/doc/images/terms.svg +166 -0
  49. data/doc/index.html +24 -0
  50. data/images/example.png +0 -0
  51. data/images/glyph_showcase.png +0 -0
  52. data/images/terms.png +0 -0
  53. data/images/terms.svg +166 -0
  54. data/lib/bio-graphics.rb +18 -0
  55. data/lib/bio/graphics/feature.rb +136 -0
  56. data/lib/bio/graphics/image_map.rb +37 -0
  57. data/lib/bio/graphics/panel.rb +205 -0
  58. data/lib/bio/graphics/ruler.rb +96 -0
  59. data/lib/bio/graphics/track.rb +387 -0
  60. data/samples/arkdb_features.rb +37 -0
  61. data/samples/data.txt +32 -0
  62. data/samples/glyph_showcase.rb +29 -0
  63. metadata +137 -0
@@ -0,0 +1,18 @@
1
+ #
2
+ # = bio-graphics.rb - Loading all Bio::Graphics modules
3
+ #
4
+ # Copyright:: Copyright (C) 2007
5
+ # Jan Aerts <jan.aerts@bbsrc.ac.uk>
6
+ # License:: The Ruby License
7
+ #
8
+ begin
9
+ require 'bio'
10
+ require 'cairo'
11
+ rescue nil
12
+ end
13
+
14
+ require File.dirname(__FILE__) + '/bio/graphics/image_map.rb'
15
+ require File.dirname(__FILE__) + '/bio/graphics/panel.rb'
16
+ require File.dirname(__FILE__) + '/bio/graphics/track.rb'
17
+ require File.dirname(__FILE__) + '/bio/graphics/feature.rb'
18
+ require File.dirname(__FILE__) + '/bio/graphics/ruler.rb'
@@ -0,0 +1,136 @@
1
+ #
2
+ # = bio/graphics/feature.rb - feature class
3
+ #
4
+ # Copyright:: Copyright (C) 2007
5
+ # Jan Aerts <jan.aerts@bbsrc.ac.uk>
6
+ # License:: The Ruby License
7
+ #
8
+ module Bio
9
+ module Graphics
10
+ class Panel
11
+ class Track
12
+ # The Bio::Graphics::Track::Feature class describes features to be
13
+ # placed on the graph. See Bio::Graphics documentation for explanation
14
+ # of interplay between different classes.
15
+ #
16
+ # The position of the Feature is a Bio::Locations object to make it possible
17
+ # to transparently work with simple and spliced features.
18
+ class Feature
19
+ # !!Not to be used directly. Use
20
+ # Bio::Graphics::Panel::Track.add_feature instead!!
21
+ # A feature can not exist except within the confines of a
22
+ # Bio::Graphics::Panel::Track object.
23
+ #
24
+ #--
25
+ # This is necessary because the feature needs to know the colour and glyph,
26
+ # both of which are defined within the panel.
27
+ #++
28
+ #
29
+ # ---
30
+ # *Arguments*:
31
+ # * _panel_ (required) :: Bio::Graphics::Panel::Track object that this
32
+ # feature belongs to
33
+ # * _name_ (required) :: Name of the feature
34
+ # * _location_ :: Bio::Locations object. Default = whole panel, forward strand
35
+ # * _link_ :: URL for clickable images
36
+ # *Returns*:: Bio::Graphics::Track::Feature object
37
+ def initialize(track, name, location = Bio::Locations.new('1..' + track.panel.length.to_s), link = nil)
38
+ @track = track
39
+ @name = name
40
+ @location = location
41
+ @start = location.collect{|l| l.from}.min.to_i
42
+ @stop = location.collect{|l| l.to}.max.to_i
43
+ @strand = location[0].strand.to_i
44
+ @link = link
45
+ @pixel_range_collection = Array.new
46
+ @chopped_at_start = false
47
+ @chopped_at_stop = false
48
+ @hidden_subfeatures_at_start = false
49
+ @hidden_subfeatures_at_stop = false
50
+
51
+ # Get all pixel ranges for the subfeatures
52
+ location.each do |l|
53
+ # xxxxxx [ ]
54
+ if l.to < track.panel.display_start
55
+ @hidden_subfeatures_at_start = true
56
+ next
57
+ # [ ] xxxxx
58
+ elsif l.from > track.panel.display_stop
59
+ @hidden_subfeatures_at_stop = true
60
+ next
61
+ # xxxx[xxx ]
62
+ elsif l.from < track.panel.display_start and l.to > track.panel.display_start
63
+ start_pixel = 1
64
+ stop_pixel = ( l.to - track.panel.display_start ).to_f / track.panel.rescale_factor
65
+ @chopped_at_start = true
66
+ # [ xxxx]xxxx
67
+ elsif l.from < track.panel.display_stop and l.to > track.panel.display_stop
68
+ start_pixel = ( l.from - track.panel.display_start ).to_f / track.panel.rescale_factor
69
+ stop_pixel = track.panel.width
70
+ @chopped_at_stop = true
71
+ # xxxx[xxxxxxxxxx]xxxx
72
+ elsif l.from < track.panel.display_start and l.to > track.panel.display_stop
73
+ start_pixel = 1
74
+ stop_pixel = track.panel.width
75
+ @chopped_at_start = true
76
+ @chopped_at_stop = true
77
+ # [ xxxxx ]
78
+ else
79
+ start_pixel = ( l.from - track.panel.display_start ).to_f / track.panel.rescale_factor
80
+ stop_pixel = ( l.to - track.panel.display_start ).to_f / track.panel.rescale_factor
81
+ end
82
+
83
+ @pixel_range_collection.push(PixelRange.new(start_pixel, stop_pixel))
84
+
85
+ end
86
+ end
87
+
88
+ # The track that this feature belongs to
89
+ attr_accessor :track
90
+
91
+ # The name of the feature
92
+ attr_accessor :name
93
+
94
+ # The location of the feature (which is a Bio::Locations object)
95
+ attr_accessor :location
96
+
97
+ # The start position of the feature (in bp)
98
+ attr_accessor :start
99
+
100
+ # The stop position of the feature (in bp)
101
+ attr_accessor :stop
102
+
103
+ # The strand of the feature
104
+ attr_accessor :strand
105
+
106
+ # The URL to be followed when the glyph for this feature is clicked
107
+ attr_accessor :link
108
+
109
+ # The array keeping the pixel ranges for the sub-features. Unspliced
110
+ # features will just have one element, while spliced features will
111
+ # have more than one.
112
+ attr_accessor :pixel_range_collection
113
+
114
+ # Is the first subfeature incomplete?
115
+ attr_accessor :chopped_at_start
116
+
117
+ # Is the last subfeature incomplete?
118
+ attr_accessor :chopped_at_stop
119
+
120
+ # Are there subfeatures out of view at the left side of the picture?
121
+ attr_accessor :hidden_subfeatures_at_start
122
+
123
+ # Are there subfeatures out of view at the right side of the picture?
124
+ attr_accessor :hidden_subfeatures_at_stop
125
+
126
+ class PixelRange
127
+ def initialize(start_pixel, stop_pixel)
128
+ @start_pixel, @stop_pixel = start_pixel, stop_pixel
129
+ end
130
+ attr_accessor :start_pixel, :stop_pixel
131
+ end
132
+ end #Feature
133
+ end #Track
134
+ end #Panel
135
+ end #Graphics
136
+ end #Bio
@@ -0,0 +1,37 @@
1
+ #
2
+ # = bio/graphics/image_map.rb - create the image map for clickable images
3
+ #
4
+ # Copyright:: Copyright (C) 2007
5
+ # Jan Aerts <jan.aerts@bbsrc.ac.uk>
6
+ # License:: Ruby's
7
+ class ImageMap
8
+ def initialize
9
+ @elements = Array.new
10
+ end
11
+ attr_accessor :elements
12
+
13
+ def to_s
14
+ output = Array.new
15
+ output.push('<map name="image_map" border="1">')
16
+ @elements.each do |e|
17
+ output.push(e.to_s)
18
+ end
19
+ output.push('</map>')
20
+ return output.join("\n")
21
+ end
22
+
23
+
24
+ class ImageMapElement
25
+ def initialize(left, top, right, bottom, url = nil)
26
+ @left, @top, @right, @bottom = left, top, right, bottom
27
+ @url = ( url.nil? ) ? '' : url
28
+ end
29
+ attr_accessor :left, :top, :right, :bottom, :url
30
+
31
+ def to_s
32
+ unless @url == ''
33
+ return '<area shape="rect" coords="' + @left.to_s + ' ' + @top.to_s + ' ' + @right.to_s + ' ' + @bottom.to_s + '" href="' + @url + '"/>'
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,205 @@
1
+ #
2
+ # = bio/graphics/panel - panel class
3
+ #
4
+ # Copyright:: Copyright (C) 2007
5
+ # Jan Aerts <jan.aerts@bbsrc.ac.uk>
6
+ # License:: The Ruby License
7
+ #
8
+ module Bio
9
+ # = DESCRIPTION
10
+ # The Bio::Graphics set of objects allow for creating simple images that
11
+ # display features on a linear map. A picture consists of:
12
+ # * one *panel*: container of all tracks
13
+ # * one or more *tracks*: container of the features. Multiple tracks
14
+ # can exist in the same graphic to allow for differential visualization of
15
+ # different feature types (e.g. genes as blue rectangles and polymorphisms
16
+ # as red triangles)
17
+ # * one or more *features* in each track: these are the actual features that
18
+ # you want to display (e.g. 'gene 1', 'SNP 123445')
19
+ # * a *ruler* on top of the panel: is added automatically
20
+ #
21
+ # Schematically:
22
+ # panel
23
+ # +-> track 1
24
+ # | +-> feature 1
25
+ # | +-> feature 2
26
+ # | +-> feature 3
27
+ # +-> track 2
28
+ # | +-> feature 4
29
+ # | +-> feature 5
30
+ # +-> ruler
31
+ #
32
+ # = USAGE
33
+ # # Create a panel for something with a length of 653. This could be a
34
+ # # sequence of 653 bp, but also a genetic map of 653 cM.
35
+ # g = Bio::Graphics::Panel.new(653)
36
+ #
37
+ # # Add the first track (e.g. 'genes')
38
+ # track1 = g.add_track('genes')
39
+ #
40
+ # # And put features in that track
41
+ # track1.add_feature('gene1',250,375)
42
+ # track1.add_feature('gene2',54,124)
43
+ # track1.add_feature('gene3',100,500)
44
+ #
45
+ # # Add a second track (e.g. 'polymorphisms')
46
+ # track2 = g.add_track('polymorphisms','red','triangle')
47
+ #
48
+ # # And put features on this one
49
+ # track2.add_feature('polymorphism 1',56,56)
50
+ # track2.add_feature('polymorphism 2',103,103)
51
+ #
52
+ # # Create the actual image as SVG text
53
+ # g.draw('my_picture.png')
54
+ #
55
+ # = FUTURE PROSPECTS
56
+ # Any help from other developers is welcomed to work on these features:
57
+ # * Would be nice if this module would be easily accessible from any object
58
+ # that implements bioruby's Bio::Map::ActsAsMap.
59
+ #
60
+ module Graphics
61
+
62
+ # The defaults
63
+ DEFAULT_PANEL_WIDTH = 800 # How many pixels wide do we want the picture to be?
64
+ TRACK_HEADER_HEIGHT = 12 # The track header will contain the title.
65
+ FEATURE_HEIGHT = 10 # The height in pixels of a glyph.
66
+ FEATURE_V_DISTANCE = 5 # The vertical distance in pixels between glyphs
67
+ FEATURE_ARROW_LENGTH = 5 # In pixels again.
68
+ RULER_TEXT_HEIGHT = 10 # And again...
69
+ RULER_MIN_DISTANCE_TICKS_PIXEL = 5 # There should be at least 5 pixels between
70
+ # consecutive ticks. This is used for the
71
+ # calculation of tick distance.
72
+
73
+ # The Bio::Graphics::Panel class describes the complete graph and contains
74
+ # all tracks. See Bio::Graphics documentation for explanation of interplay
75
+ # between different classes.
76
+ class Panel
77
+ # Create a new Bio::Graphics::Panel object
78
+ #
79
+ # g = Bio::Graphics::Panel.new(456)
80
+ #
81
+ # The height of the image is calculated automatically depending on how many
82
+ # tracks and features it contains. The width of the image defaults to 800 pt
83
+ # but can be set manually by using a second argument:
84
+ #
85
+ # g = Bio::Graphics::Panel.new(456, 400)
86
+ #
87
+ #
88
+ # See also: Bio::Graphics::Track, BioExt::Graphics::Feature
89
+ # ---
90
+ # *Arguments*:
91
+ # * _length_ :: length of the thing you want to visualize, e.g for
92
+ # visualizing a sequence that is 3.24 kb long, use 324.
93
+ # * _width_ :: width of the resulting image in pt. This should be a string
94
+ # and not an integer. Default = '800' (Notice the quotes...).
95
+ # * _clickable_ :: whether the picture should have clickable glyphs or not
96
+ # (default: false) If set to true, a html file will be created with
97
+ # the map.
98
+ # * _display_start_ :: start coordinate to be displayed (default: 1)
99
+ # * _display_stop_ :: stop coordinate to be displayed (default: length of sequence)
100
+ # *Returns*:: Bio::Graphics::Panel object
101
+ def initialize(length, width = DEFAULT_PANEL_WIDTH, clickable = false, display_start = nil, display_stop = nil)
102
+ @length = length.to_i
103
+ @width = width.to_i
104
+ @tracks = Array.new
105
+ @number_of_times_bumped = 0
106
+ @clickable = clickable
107
+ @image_map = ( clickable ) ? ImageMap.new : nil
108
+ @display_start = ( display_start.nil? ) ? 0 : display_start
109
+ @display_stop = ( display_stop.nil? ) ? @length : display_stop
110
+ if @display_stop <= @display_start
111
+ raise "[ERROR] Start coordinate to be displayed has to be smaller than stop coordinate."
112
+ end
113
+ @rescale_factor = (@display_stop - @display_start).to_f / @width
114
+ end
115
+ attr_accessor :length, :width, :height, :rescale_factor, :tracks, :number_of_times_bumped, :clickable, :image_map, :display_start, :display_stop
116
+
117
+ # Adds a Bio::Graphics::Track container to this panel. A panel contains a
118
+ # logical grouping of features, e.g. (for sequence annotation:) genes,
119
+ # polymorphisms, ESTs, etc.
120
+ #
121
+ # est_track = g.add_track('ESTs')
122
+ # gene_track = g.add_track('genes')
123
+ #
124
+ # ---
125
+ # *Arguments*:
126
+ # * _name_ (required) :: Name of the track to be displayed (e.g. 'genes')
127
+ # * _colour_ :: Colour to be used to draw the features within the track.
128
+ # Default = 'blue'
129
+ # * _glyph_ :: Glyph to use for drawing the features. Options are:
130
+ # 'generic', 'directed_generic', 'spliced', 'directed_spliced' and
131
+ # 'triangle'. Triangles can be used
132
+ # for features whose start and stop positions are the same (e.g. SNPs).
133
+ # If you try to draw a feature that is longer with triangles, an error
134
+ # will be shown.
135
+ # *Returns*:: Bio::Graphics::Track object that has just been created
136
+ def add_track(name, feature_colour = [0,0,1], feature_glyph = 'generic')
137
+ @tracks.push(Bio::Graphics::Panel::Track.new(self, name, feature_colour, feature_glyph))
138
+ return @tracks[-1]
139
+ end
140
+
141
+ # Create the drawing
142
+ #--
143
+ # The fact that display_start and display_stop can be set has two
144
+ # consequences:
145
+ # 1. not all features are drawn
146
+ # 2. the x-coordinate of all glyphs has to be corrected
147
+ #++
148
+ def draw(file_name)
149
+ # Create a panel that is huge vertically
150
+ huge_height = 2000
151
+
152
+ huge_panel_drawing = nil
153
+ huge_panel_drawing = Cairo::ImageSurface.new(1, @width, huge_height)
154
+
155
+ background = Cairo::Context.new(huge_panel_drawing)
156
+ background.set_source_rgb(1,1,1)
157
+ background.rectangle(0,0,@width,huge_height).fill
158
+
159
+ # Add ruler
160
+ vertical_offset = 0
161
+ ruler = Bio::Graphics::Panel::Ruler.new(self)
162
+ ruler.draw(huge_panel_drawing, vertical_offset)
163
+ vertical_offset += ruler.height
164
+
165
+ # Add tracks
166
+ @tracks.each do |track|
167
+ track.draw(huge_panel_drawing, vertical_offset)
168
+ @number_of_times_bumped += track.number_of_times_bumped
169
+ vertical_offset += ( track.number_of_times_bumped*(FEATURE_HEIGHT+FEATURE_V_DISTANCE+5)) + 10 # '10' is for the header
170
+ end
171
+
172
+ # And create a smaller version of the panel
173
+ height = ruler.height
174
+ @number_of_times_bumped.times do
175
+ height += 20
176
+ end
177
+ @tracks.length.times do #To correct for the track headers
178
+ height += 10
179
+ end
180
+
181
+ resized_panel_drawing = nil
182
+ resized_panel_drawing = Cairo::ImageSurface.new(1, @width, height)
183
+ resizing_context = Cairo::Context.new(resized_panel_drawing)
184
+ resizing_context.set_source(huge_panel_drawing, 0,0)
185
+ resizing_context.rectangle(0,0,@width, height).fill
186
+
187
+ # And print to file
188
+ resized_panel_drawing.write_to_png(file_name)
189
+ if @clickable # create png and map
190
+ html_filename = file_name.sub(/\.[^.]+$/, '.html')
191
+ html = File.open(html_filename,'w')
192
+ html.puts "<html>"
193
+ html.puts "<body>"
194
+ html.puts @image_map.to_s
195
+ html.puts "<img border='1' src='" + file_name + "' usemap='#image_map' />"
196
+ html.puts "</body>"
197
+ html.puts "</html>"
198
+ end
199
+ end
200
+
201
+
202
+
203
+ end #Panel
204
+ end #Graphics
205
+ end #Bio
@@ -0,0 +1,96 @@
1
+ #
2
+ # = bio/graphics/ruler - ruler class
3
+ #
4
+ # Copyright:: Copyright (C) 2007
5
+ # Jan Aerts <jan.aerts@bbsrc.ac.uk>
6
+ # License:: The Ruby License
7
+ #
8
+ module Bio
9
+ module Graphics
10
+ class Panel
11
+ # The Bio::Graphics::Ruler class describes the ruler to be drawn on the
12
+ # graph. This is created automatically when creating the picture by using
13
+ # Bio::Graphics::Panel.to_svg. See BioExt::Graphics documentation for
14
+ # explanation of interplay between different classes.
15
+ #--
16
+ # TODO: the ruler might be implemented as a special case of a track, so
17
+ # it would inherit from it (class Ruler < Bio::Graphics::Panel::Track).
18
+ # But I haven't really thought this through yet.
19
+ #++
20
+ class Ruler
21
+ # Creates a new Bio::Graphics::Panel::Ruler object.
22
+ # ---
23
+ # *Arguments*:
24
+ # * _panel_ (required) :: Bio::Graphics::Panel object that this ruler
25
+ # belongs to
26
+ # * _colour_ :: colour of the ruler. Default = 'black'
27
+ # *Returns*:: Bio::Graphics::Ruler object
28
+ def initialize(panel, colour = [0,0,0])
29
+ @panel = panel
30
+ @name = 'ruler'
31
+ @colour = colour
32
+ end
33
+ attr_accessor :panel, :name, :colour, :height, :minor_tick_distance, :major_tick_distance
34
+
35
+ def calculate_tick_distance
36
+ min_tick_distance_requirement_met = false
37
+ self.minor_tick_distance = 1 # in basepairs.
38
+ while ! min_tick_distance_requirement_met
39
+ if self.minor_tick_distance/panel.rescale_factor >= RULER_MIN_DISTANCE_TICKS_PIXEL
40
+ min_tick_distance_requirement_met = true
41
+ else
42
+ self.minor_tick_distance = self.minor_tick_distance*5
43
+ end
44
+ end
45
+
46
+ self.major_tick_distance = self.minor_tick_distance * 10
47
+ end
48
+
49
+ def draw(panel_drawing, vertical_offset)
50
+ ruler_drawing = Cairo::Context.new(panel_drawing)
51
+
52
+ self.calculate_tick_distance
53
+
54
+ # Draw line
55
+ ruler_drawing.move_to(0,10)
56
+ ruler_drawing.line_to(panel.width, 10)
57
+ ruler_drawing.stroke
58
+
59
+ # Draw ticks
60
+ # * Find position of first tick.
61
+ # Most of the time, we don't want the first tick on the very first
62
+ # basepair of the view. Suppose that would be position 333 in the
63
+ # sequence. Then the numbers under the major tickmarks would be:
64
+ # 343, 353, 363, 373 and so on. Instead, we want 350, 360, 370, 380.
65
+ # So we want to find the position of the first tick.
66
+ first_tick_position = panel.display_start
67
+ while first_tick_position.modulo(minor_tick_distance) != 0
68
+ first_tick_position += 1
69
+ end
70
+
71
+ # * And start drawing the rest.
72
+ first_tick_position.step(panel.display_stop, minor_tick_distance) do |tick|
73
+ tick_pixel_position = (tick - panel.display_start) / panel.rescale_factor
74
+ ruler_drawing.move_to(tick_pixel_position.floor, 5)
75
+ if tick.modulo(major_tick_distance) == 0
76
+ ruler_drawing.rel_line_to(0, 15)
77
+
78
+ # Draw tick number
79
+ ruler_drawing.select_font_face('Georgia', 1, 1)
80
+ ruler_drawing.set_font_size(RULER_TEXT_HEIGHT)
81
+ ruler_drawing.move_to(tick_pixel_position.floor, 20 + RULER_TEXT_HEIGHT)
82
+ ruler_drawing.show_text(tick.to_i.to_s)
83
+ else
84
+ ruler_drawing.rel_line_to(0, 5)
85
+
86
+ end
87
+ ruler_drawing.stroke
88
+ end
89
+
90
+
91
+ @height = 25 + RULER_TEXT_HEIGHT
92
+ end
93
+ end #Ruler
94
+ end #Panel
95
+ end #Graphics
96
+ end #Bio