tchart 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/tchart +5 -0
- data/lib/tchart.rb +27 -0
- data/lib/tchart/lang/tchart_error.rb +9 -0
- data/lib/tchart/model/bar.rb +24 -0
- data/lib/tchart/model/chart.rb +25 -0
- data/lib/tchart/model/command_line_args.rb +18 -0
- data/lib/tchart/model/coordinate.rb +29 -0
- data/lib/tchart/model/grid_line.rb +25 -0
- data/lib/tchart/model/label.rb +43 -0
- data/lib/tchart/model/layout.rb +85 -0
- data/lib/tchart/model/separator.rb +28 -0
- data/lib/tchart/model/settings.rb +68 -0
- data/lib/tchart/model/y_item.rb +44 -0
- data/lib/tchart/process/chart_builder.rb +63 -0
- data/lib/tchart/process/command_line_parser.rb +73 -0
- data/lib/tchart/process/data_parser.rb +76 -0
- data/lib/tchart/process/data_reader.rb +14 -0
- data/lib/tchart/process/items_parser.rb +156 -0
- data/lib/tchart/process/layout_builder.rb +106 -0
- data/lib/tchart/process/settings_parser.rb +63 -0
- data/lib/tchart/process/tex_builder.rb +85 -0
- data/lib/tchart/process/tex_writer.rb +13 -0
- data/lib/tchart/version.rb +3 -0
- data/test/integration_test.rb +82 -0
- data/test/tchart/model/bar_test.rb +17 -0
- data/test/tchart/model/coordinate_test.rb +10 -0
- data/test/tchart/model/grid_line_test.rb +17 -0
- data/test/tchart/model/label_test.rb +31 -0
- data/test/tchart/model/layout_test.rb +17 -0
- data/test/tchart/model/separator_test.rb +19 -0
- data/test/tchart/model/settings_test.rb +48 -0
- data/test/tchart/model/y_item_test.rb +24 -0
- data/test/tchart/process/chart_builder_test.rb +33 -0
- data/test/tchart/process/command_line_parser_test.rb +89 -0
- data/test/tchart/process/data_parser_test.rb +60 -0
- data/test/tchart/process/data_reader_test.rb +23 -0
- data/test/tchart/process/items_parser_test.rb +154 -0
- data/test/tchart/process/layout_builder_test.rb +189 -0
- data/test/tchart/process/settings_parser_test.rb +75 -0
- data/test/tchart/process/tex_builder_test.rb +120 -0
- data/test/tchart/process/tex_writer_test.rb +38 -0
- data/test/tchart_test.rb +47 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b23ad54a382777cdf1e500dbcf5539779a0a3d3b
|
4
|
+
data.tar.gz: e484d3aed917693c74abd2557e362b08bbc39de9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4ba8ddc34005fba31ddf15d9a1a32225fb9d60077434a66498d2d76f2686b5dcd47ab2b853d1100bff5c74273c18978ff49c8b5982b29d0788b6158acc03fa3
|
7
|
+
data.tar.gz: 2b74530475bc379d20fc6706c750372177ea31f5e8123d7062c50ca0f224da2b04bec43633823df2947ec33099c2abe0df2b44a077cb9ecdd2f8cfae1e286fd7
|
data/bin/tchart
ADDED
data/lib/tchart.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# 'require' all files except ourselves.
|
2
|
+
me = File.absolute_path(__FILE__)
|
3
|
+
Dir.glob(File.dirname(me) + '/**/*.rb') {|fn| require fn unless fn == me }
|
4
|
+
|
5
|
+
module TChart
|
6
|
+
|
7
|
+
#
|
8
|
+
# Program entry point. Responsible for running the various steps
|
9
|
+
# required to generate TikZ code that renders a chart. Also
|
10
|
+
# responsible for reporting errors.
|
11
|
+
#
|
12
|
+
def self.run(argv)
|
13
|
+
args, errors = CommandLineParser.parse(argv) ; abort_if errors
|
14
|
+
settings, items, errors = DataReader.read(args.data_filename) ; abort_if errors
|
15
|
+
layout, errors = LayoutBuilder.build(settings, items) ; abort_if errors
|
16
|
+
chart = ChartBuilder.build(layout, items)
|
17
|
+
tex = chart.render
|
18
|
+
TeXWriter.write(args.tex_filename, tex)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.abort_if errors
|
24
|
+
abort(errors.join("\n")) unless errors.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# A bar that represents a date range on the chart. Responsible
|
5
|
+
# for generating TikZ code to render the bar.
|
6
|
+
#
|
7
|
+
class Bar
|
8
|
+
|
9
|
+
attr_reader :from
|
10
|
+
attr_reader :to
|
11
|
+
attr_reader :style # TikZ style, must be defined in encompasing TeX document.
|
12
|
+
|
13
|
+
def initialize(from, to, style)
|
14
|
+
@from = from
|
15
|
+
@to = to
|
16
|
+
@style = style
|
17
|
+
end
|
18
|
+
|
19
|
+
def render(tex)
|
20
|
+
tex.bar @from, @to, @style
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# The plots of zero or more date ranges. Includes bars representing the
|
5
|
+
# date ranges, x and y axes lines, gridlines, and labels. Has overall
|
6
|
+
# responsibility for generating all of the TikZ code to render the chart.
|
7
|
+
#
|
8
|
+
class Chart
|
9
|
+
|
10
|
+
attr_reader :elements # Labels, gridlines and bars that make up the chart.
|
11
|
+
|
12
|
+
def initialize(elements)
|
13
|
+
@elements = elements
|
14
|
+
end
|
15
|
+
|
16
|
+
def render # => String
|
17
|
+
tex = TeXBuilder.new
|
18
|
+
tex.begin_chart
|
19
|
+
@elements.each { |element| element.render(tex) }
|
20
|
+
tex.end_chart
|
21
|
+
tex.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# Responsible for storing all command line arguments for use
|
5
|
+
# throughout the lifecycle of the application.
|
6
|
+
#
|
7
|
+
class CommandLineArgs
|
8
|
+
|
9
|
+
attr_reader :data_filename # input file
|
10
|
+
attr_reader :tex_filename # output file
|
11
|
+
|
12
|
+
def initialize(data_filename, tex_filename)
|
13
|
+
@data_filename = data_filename
|
14
|
+
@tex_filename = tex_filename
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# An (x,y) location on the chart. x and y can be
|
5
|
+
# in any units, e.g. millimeters, pixels, etc.
|
6
|
+
#
|
7
|
+
class Coordinate
|
8
|
+
|
9
|
+
attr_reader :x
|
10
|
+
attr_reader :y
|
11
|
+
|
12
|
+
def initialize(x, y)
|
13
|
+
@x = x
|
14
|
+
@y = y
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
module Kernel
|
22
|
+
|
23
|
+
#
|
24
|
+
# Shorthand for TChart::Coordinate.new(x, t).
|
25
|
+
#
|
26
|
+
def xy(x, y) # => Coordinate
|
27
|
+
TChart::Coordinate.new(x, y)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# A horizontal or vertical line on the chart that spans the
|
5
|
+
# entire width or height of the chart and serves as a reading
|
6
|
+
# aid. The x and y axes, and the top and and right frame of
|
7
|
+
# the chart are also grid lines. Responsible for generating
|
8
|
+
# TikZ code to render the grid line.
|
9
|
+
#
|
10
|
+
class GridLine
|
11
|
+
|
12
|
+
attr_reader :from
|
13
|
+
attr_reader :to
|
14
|
+
|
15
|
+
def initialize(from, to)
|
16
|
+
@from = from
|
17
|
+
@to = to
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(tex)
|
21
|
+
tex.gridline @from, @to, "gridline"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# An x or y axis label on the chart. X axis labels will be years, e.g.
|
5
|
+
# "2014", "2015", etc., and y axis labels will be descriptions of the
|
6
|
+
# items being plotted, e.g. "Ruby", "C", "C++", etc. Responsible for
|
7
|
+
# generating TikZ code to render the label.
|
8
|
+
#
|
9
|
+
class Label
|
10
|
+
|
11
|
+
attr_reader :coord # Horizontal and vertical mid-point of label location.
|
12
|
+
attr_reader :width # Required for text justification.
|
13
|
+
attr_reader :style # TikZ style, must be defined in encompasing TeX document.
|
14
|
+
attr_reader :text
|
15
|
+
|
16
|
+
#
|
17
|
+
# The difference between an x axis label and a y axis label is the TikZ style;
|
18
|
+
# x axis labels are generated with the style "xlabel", y axis labels use
|
19
|
+
# "ylabel". In the TeX document that embeds the chart, the x axis labels will
|
20
|
+
# usually be styled with centered text, whereas y axis labels will be left
|
21
|
+
# justified.
|
22
|
+
#
|
23
|
+
def self.build_xlabel(coord, width, text) # => Label
|
24
|
+
Label.new(coord, width, "xlabel", text)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.build_ylabel(coord, width, text) # => Label
|
28
|
+
Label.new(coord, width, "ylabel", text)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(coord, width, style, text)
|
32
|
+
@coord = coord
|
33
|
+
@width = width
|
34
|
+
@style = style
|
35
|
+
@text = text
|
36
|
+
end
|
37
|
+
|
38
|
+
def render(tex)
|
39
|
+
tex.label @coord, @width, @style, @text
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# Responsible for storing various chart metrics such as the length of the
|
5
|
+
# axes, the width of the axes labels, the coordinates of the axes labels,
|
6
|
+
# and so on. All metrics are unitless, although they should all be in
|
7
|
+
# the same unit. Also responsible for converting a date range to its
|
8
|
+
# equivalent start and end coordinates on the chart.
|
9
|
+
#
|
10
|
+
class Layout
|
11
|
+
|
12
|
+
#
|
13
|
+
# The length of the x axis from the leftmost to the rightmost label.
|
14
|
+
#
|
15
|
+
attr_accessor :x_axis_length
|
16
|
+
|
17
|
+
#
|
18
|
+
# The amount of horizontal space to allocate for each x axis label.
|
19
|
+
# Used to determine the width of the margins to the left and right of
|
20
|
+
# the x axis, and also passed to TikZ/TeX.
|
21
|
+
#
|
22
|
+
attr_accessor :x_axis_label_width
|
23
|
+
|
24
|
+
#
|
25
|
+
# The distance of the mid point of the x axis labels from the x axis.
|
26
|
+
#
|
27
|
+
attr_accessor :x_axis_label_y_coordinate
|
28
|
+
|
29
|
+
#
|
30
|
+
# The x coordinates of the x axis labels and associated vertical
|
31
|
+
# grid lines.
|
32
|
+
#
|
33
|
+
attr_accessor :x_axis_tick_x_coordinates
|
34
|
+
|
35
|
+
#
|
36
|
+
# The dates to be used for the x axis labels.
|
37
|
+
#
|
38
|
+
attr_accessor :x_axis_tick_dates
|
39
|
+
|
40
|
+
#
|
41
|
+
# The length of the y axis from the topmost to the bottommost item.
|
42
|
+
#
|
43
|
+
attr_accessor :y_axis_length
|
44
|
+
|
45
|
+
#
|
46
|
+
# The width of the y axis labels. Used to calculate the amount of
|
47
|
+
# horizontal space to leave for the labels, and also passed in the
|
48
|
+
# generated TikX code.
|
49
|
+
#
|
50
|
+
attr_accessor :y_axis_label_width
|
51
|
+
|
52
|
+
#
|
53
|
+
# The distance of the mid point of the y axis labels to the left
|
54
|
+
# of the y axis.
|
55
|
+
#
|
56
|
+
attr_accessor :y_axis_label_x_coordinate
|
57
|
+
|
58
|
+
#
|
59
|
+
# The y coordinates of the y axis labels and their associated bars.
|
60
|
+
#
|
61
|
+
attr_accessor :y_axis_tick_y_coordinates
|
62
|
+
|
63
|
+
#
|
64
|
+
# Convert a date range, e.g. Date.new(2000,1,1)..Date.new(2002,10,3), to
|
65
|
+
# its equivalent start and end coordinates on the chart.
|
66
|
+
#
|
67
|
+
def date_range_to_x_coordinates(date_range) # => [ Numeric, Numeric ]
|
68
|
+
x_from = date_to_x_coordinate(date_range.begin)
|
69
|
+
x_to = date_to_x_coordinate(date_range.end + 1) # +1 bumps the time to end-of-day of the end date
|
70
|
+
[x_from, x_to]
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
#
|
76
|
+
# ratio is: x_coordinate / x_axis_length = ( date - date_range.begin ) / date_range_length
|
77
|
+
#
|
78
|
+
def date_to_x_coordinate(date) # => Numeric
|
79
|
+
date_range_begin, date_range_end = @x_axis_tick_dates.first.jd, @x_axis_tick_dates.last.jd
|
80
|
+
date_range_length = date_range_end - date_range_begin
|
81
|
+
( @x_axis_length * ( date.jd - date_range_begin ) * 1.0 ) / date_range_length
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# The input data file specifies settings, data lines, and separators.
|
5
|
+
# A separator renders as a horizontal grid line and serves to separate
|
6
|
+
# the charted items into sections. Responsible for building the grid
|
7
|
+
# line element.
|
8
|
+
#
|
9
|
+
class Separator
|
10
|
+
|
11
|
+
#
|
12
|
+
# This is part of the charted item interface. Separators
|
13
|
+
# have no date ranges so this array will always be empty.
|
14
|
+
#
|
15
|
+
attr_reader :date_ranges
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@date_ranges = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def build(layout, y) # => [ GridLine ]
|
22
|
+
from = xy(0, y)
|
23
|
+
to = xy(layout.x_axis_length, y)
|
24
|
+
[ GridLine.new(from, to) ]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# The input data file specifies settings, data lines, and separators.
|
5
|
+
# This class stores the settings from the data file. Responsible for
|
6
|
+
# providing default values for those settings that are not specified
|
7
|
+
# in the input file. Also responsible for answering whether a setting
|
8
|
+
# name is known or not, and for providing a list of all setting names.
|
9
|
+
#
|
10
|
+
# All setting values are in millimeters. The Settings and TeXBuilder
|
11
|
+
# are the only two classes that know what units are being used. All
|
12
|
+
# other classes are unit agnostic.
|
13
|
+
#
|
14
|
+
class Settings
|
15
|
+
|
16
|
+
#
|
17
|
+
# The amount of horizontal space available for the chart. It must be
|
18
|
+
# large enough to accomodate the width of the y-axis labels, the width
|
19
|
+
# of the x axis, and the margins to the left and right of the x axis.
|
20
|
+
#
|
21
|
+
attr_accessor :chart_width
|
22
|
+
|
23
|
+
#
|
24
|
+
# The amount of vertical space to allocate for each line of the chart.
|
25
|
+
# It must be large enough to accomodate the larger of the y axis label
|
26
|
+
# height and the bar height.
|
27
|
+
#
|
28
|
+
attr_accessor :line_height
|
29
|
+
|
30
|
+
#
|
31
|
+
# The amount of horizontal space to allocate for each x axis label.
|
32
|
+
# Used to determine the width of the margins to the left and right of
|
33
|
+
# the x axis, and also passed to TikZ/TeX.
|
34
|
+
#
|
35
|
+
attr_accessor :x_axis_label_width
|
36
|
+
|
37
|
+
#
|
38
|
+
# The distance of the mid point of the x axis labels from the x axis.
|
39
|
+
#
|
40
|
+
attr_accessor :x_axis_label_y_coordinate
|
41
|
+
|
42
|
+
#
|
43
|
+
# The width of the y axis labels. Used to calculate the amount of
|
44
|
+
# horizontal space to leave for the labels, and also passed in the
|
45
|
+
# generated TikX code.
|
46
|
+
#
|
47
|
+
attr_accessor :y_axis_label_width
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@chart_width = 164.99
|
51
|
+
@line_height = 4.6
|
52
|
+
@x_axis_label_width = 10
|
53
|
+
@x_axis_label_y_coordinate = -3
|
54
|
+
@y_axis_label_width = 24
|
55
|
+
end
|
56
|
+
|
57
|
+
def has_setting?(setting_name)
|
58
|
+
setting_names.include?(setting_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def setting_names # => [ "chart_width", "line_height", ... ]
|
62
|
+
methods # => [ "has_setting?", "chart_width", "chart_width=", ... ]
|
63
|
+
.grep(/\w=$/) # => [ "chart_width=", ... ]
|
64
|
+
.map {|name| name.to_s.chomp('=')} # => [ "chart_width", ... ]
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module TChart
|
2
|
+
|
3
|
+
#
|
4
|
+
# The input data file specifies settings, data lines, and separators. A
|
5
|
+
# data line consists of a description, which becomes a y axis label, zero
|
6
|
+
# or more date ranges to be plotted on the chart as horizontal bars, and
|
7
|
+
# a TikZ style for the bars. YItem is reponsible for capturing all of
|
8
|
+
# that information and for building the label and bar elements.
|
9
|
+
#
|
10
|
+
class YItem
|
11
|
+
|
12
|
+
attr_reader :description # Used for the y-label.
|
13
|
+
attr_reader :bar_style # TikZ style, must be defined in encompasing TeX document.
|
14
|
+
attr_reader :date_ranges # Can be >= 0; drawn as bars on the chart.
|
15
|
+
|
16
|
+
def initialize(description, bar_style, date_ranges)
|
17
|
+
@description = description
|
18
|
+
@bar_style = bar_style
|
19
|
+
@date_ranges = date_ranges
|
20
|
+
end
|
21
|
+
|
22
|
+
def build(layout, y) # => [ Label, Bar, Bar, ... ]
|
23
|
+
[ new_y_label(layout, y) ].concat new_bars(layout, y)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def new_y_label(layout, y) # => Label
|
29
|
+
Label.build_ylabel(xy(layout.y_axis_label_x_coordinate, y), layout.y_axis_label_width, @description)
|
30
|
+
end
|
31
|
+
|
32
|
+
def new_bars(layout, y) # => [ Bar, Bar, ... ]
|
33
|
+
@date_ranges.map do |date_range|
|
34
|
+
x_from, x_to = layout.date_range_to_x_coordinates(date_range)
|
35
|
+
new_bar(x_from, x_to, y)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def new_bar(x_from, x_to, y) # => Bar
|
40
|
+
Bar.new(xy(x_from, y), xy(x_to, y), @bar_style)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|