ctioga2 0.0 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +25 -1
- data/lib/ctioga2/commands/commands.rb +13 -2
- data/lib/ctioga2/commands/doc/doc.rb +13 -17
- data/lib/ctioga2/commands/doc/documentation-commands.rb +14 -1
- data/lib/ctioga2/commands/doc/help.rb +136 -25
- data/lib/ctioga2/commands/doc/html.rb +56 -4
- data/lib/ctioga2/commands/doc/introspection.rb +45 -9
- data/lib/ctioga2/commands/doc/man.rb +7 -5
- data/lib/ctioga2/commands/doc/markup.rb +39 -12
- data/lib/ctioga2/commands/doc/wordwrap.rb +70 -0
- data/lib/ctioga2/commands/general-commands.rb +7 -4
- data/lib/ctioga2/commands/general-types.rb +27 -12
- data/lib/ctioga2/commands/interpreter.rb +2 -2
- data/lib/ctioga2/commands/parsers/command-line.rb +9 -5
- data/lib/ctioga2/commands/parsers/file.rb +5 -3
- data/lib/ctioga2/commands/type.rb +10 -3
- data/lib/ctioga2/commands/variables.rb +2 -2
- data/lib/ctioga2/data/backends/backend.rb +17 -15
- data/lib/ctioga2/data/backends/backends.rb +2 -2
- data/lib/ctioga2/data/backends/backends/gnuplot.rb +20 -5
- data/lib/ctioga2/data/backends/backends/math.rb +2 -2
- data/lib/ctioga2/data/backends/backends/text.rb +112 -17
- data/lib/ctioga2/data/backends/description.rb +10 -11
- data/lib/ctioga2/data/datacolumn.rb +73 -14
- data/lib/ctioga2/data/dataset.rb +305 -9
- data/lib/ctioga2/data/filters.rb +49 -1
- data/lib/ctioga2/data/indexed-dtable.rb +137 -0
- data/lib/ctioga2/data/point.rb +98 -7
- data/lib/ctioga2/data/stack.rb +98 -21
- data/lib/ctioga2/graphics/coordinates.rb +19 -2
- data/lib/ctioga2/graphics/elements.rb +12 -2
- data/lib/ctioga2/graphics/elements/containers.rb +14 -2
- data/lib/ctioga2/graphics/elements/contour.rb +67 -0
- data/lib/ctioga2/graphics/elements/curve2d.rb +103 -42
- data/lib/ctioga2/graphics/elements/element.rb +12 -2
- data/lib/ctioga2/graphics/elements/gradient-region.rb +94 -0
- data/lib/ctioga2/graphics/elements/parametric2d.rb +172 -0
- data/lib/ctioga2/graphics/elements/primitive.rb +37 -21
- data/lib/ctioga2/graphics/elements/region.rb +143 -0
- data/lib/ctioga2/graphics/elements/subplot.rb +92 -32
- data/lib/ctioga2/graphics/elements/tangent.rb +99 -0
- data/lib/ctioga2/graphics/elements/xyz-map.rb +126 -0
- data/lib/ctioga2/graphics/generator.rb +91 -6
- data/lib/ctioga2/graphics/legends.rb +26 -21
- data/lib/ctioga2/graphics/legends/area.rb +8 -8
- data/lib/ctioga2/graphics/legends/items.rb +5 -5
- data/lib/ctioga2/graphics/legends/storage.rb +4 -2
- data/lib/ctioga2/graphics/root.rb +24 -2
- data/lib/ctioga2/graphics/styles.rb +8 -0
- data/lib/ctioga2/graphics/styles/axes.rb +49 -23
- data/lib/ctioga2/graphics/styles/base.rb +2 -2
- data/lib/ctioga2/graphics/styles/carrays.rb +9 -2
- data/lib/ctioga2/graphics/styles/colormap.rb +272 -0
- data/lib/ctioga2/graphics/styles/curve.rb +64 -4
- data/lib/ctioga2/graphics/styles/drawable.rb +68 -9
- data/lib/ctioga2/graphics/styles/errorbar.rb +73 -0
- data/lib/ctioga2/graphics/styles/factory.rb +133 -17
- data/lib/ctioga2/graphics/styles/gradients.rb +60 -0
- data/lib/ctioga2/graphics/styles/location.rb +64 -0
- data/lib/ctioga2/graphics/styles/map-axes.rb +164 -0
- data/lib/ctioga2/graphics/styles/plot.rb +165 -62
- data/lib/ctioga2/graphics/styles/sets.rb +14 -1
- data/lib/ctioga2/graphics/styles/texts.rb +44 -34
- data/lib/ctioga2/graphics/subplot-commands.rb +94 -6
- data/lib/ctioga2/graphics/types.rb +113 -35
- data/lib/ctioga2/graphics/types/bijection.rb +3 -3
- data/lib/ctioga2/graphics/types/boundaries.rb +120 -1
- data/lib/ctioga2/graphics/types/dimensions.rb +8 -1
- data/lib/ctioga2/graphics/types/grid.rb +196 -0
- data/lib/ctioga2/graphics/types/location.rb +228 -0
- data/lib/ctioga2/graphics/types/point.rb +2 -2
- data/lib/ctioga2/log.rb +18 -18
- data/lib/ctioga2/metabuilder/type.rb +15 -3
- data/lib/ctioga2/metabuilder/types.rb +2 -2
- data/lib/ctioga2/metabuilder/types/coordinates.rb +13 -1
- data/lib/ctioga2/metabuilder/types/data.rb +50 -0
- data/lib/ctioga2/metabuilder/types/generic.rb +60 -0
- data/lib/ctioga2/metabuilder/types/lists.rb +53 -16
- data/lib/ctioga2/metabuilder/types/styles.rb +26 -45
- data/lib/ctioga2/plotmaker.rb +91 -20
- data/lib/ctioga2/postprocess.rb +8 -8
- data/lib/ctioga2/utils.rb +23 -4
- metadata +107 -75
- data/lib/ctioga2/data/merge.rb +0 -43
@@ -16,7 +16,7 @@ require 'ctioga2/log'
|
|
16
16
|
|
17
17
|
module CTioga2
|
18
18
|
|
19
|
-
Version::register_svn_info('$Revision:
|
19
|
+
Version::register_svn_info('$Revision: 155 $', '$Date: 2010-06-21 21:41:32 +0200 (Mon, 21 Jun 2010) $')
|
20
20
|
|
21
21
|
module Graphics
|
22
22
|
|
@@ -61,10 +61,10 @@ module CTioga2
|
|
61
61
|
# separated by :: -- or only one block in the case of an
|
62
62
|
# involution (very common, actually, all 1/x transforms).
|
63
63
|
#
|
64
|
-
#
|
64
|
+
# \todo few things around here to change... in particular,
|
65
65
|
# I should try to find a way to include Math...
|
66
66
|
#
|
67
|
-
#
|
67
|
+
# \todo add very common cases ?
|
68
68
|
def self.from_text(spec)
|
69
69
|
blocks = spec.split(/::/).map do |code|
|
70
70
|
eval("proc do |x|\n#{code}\nend")
|
@@ -16,7 +16,7 @@ require 'ctioga2/log'
|
|
16
16
|
|
17
17
|
module CTioga2
|
18
18
|
|
19
|
-
Version::register_svn_info('$Revision:
|
19
|
+
Version::register_svn_info('$Revision: 125 $', '$Date: 2010-01-12 00:56:15 +0100 (Tue, 12 Jan 2010) $')
|
20
20
|
|
21
21
|
# This module contains all graphical elements of CTioga2
|
22
22
|
module Graphics
|
@@ -25,7 +25,108 @@ module CTioga2
|
|
25
25
|
# with Tioga
|
26
26
|
module Types
|
27
27
|
|
28
|
+
# A range of coordinates.
|
29
|
+
class SimpleRange
|
30
|
+
|
31
|
+
attr_accessor :first, :last
|
32
|
+
|
33
|
+
# Create a new SimpleRange object that runs from _first_ to
|
34
|
+
# _last_ (_last_ can be less than _first_). A _nil_,
|
35
|
+
# _false_ or NaN in one of those means *unspecified*.
|
36
|
+
#
|
37
|
+
# Alternatively, _first_ can be an object that responds to
|
38
|
+
# #first and #last.
|
39
|
+
def initialize(first, last = nil)
|
40
|
+
if first.respond_to?(:first)
|
41
|
+
@first = first.first
|
42
|
+
@last = first.last
|
43
|
+
else
|
44
|
+
@first = first
|
45
|
+
@last = last
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Minimum value
|
50
|
+
def min
|
51
|
+
@first < @last ? @first : @last
|
52
|
+
end
|
53
|
+
|
54
|
+
# Maximum value
|
55
|
+
def max
|
56
|
+
@first > @last ? @first : @last
|
57
|
+
end
|
58
|
+
|
59
|
+
# Algebraic distance
|
60
|
+
def distance
|
61
|
+
return @last - @first
|
62
|
+
end
|
63
|
+
|
64
|
+
# This function makes sures that the SimpleRange object is big
|
65
|
+
# enough to encompass what it currently does and the _range_
|
66
|
+
# SimpleRange object.
|
67
|
+
#
|
68
|
+
# \todo this does not work correctly in the case of reversed
|
69
|
+
# boundaries. I don't think it can anyway.
|
70
|
+
#
|
71
|
+
# Actually, it even works with normal Range elements !
|
72
|
+
def extend(range)
|
73
|
+
# Left/right
|
74
|
+
if (! @first.is_a? Float) or @first.nan? or
|
75
|
+
(@first > range.first)
|
76
|
+
@first = range.first
|
77
|
+
end
|
78
|
+
|
79
|
+
if (! @last.is_a? Float) or @last.nan? or
|
80
|
+
(@last < range.last)
|
81
|
+
@last = range.last
|
82
|
+
end
|
83
|
+
|
84
|
+
return self
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# Override the Boundaries with the contents of _override_. All
|
89
|
+
# elements which are not _nil_ or NaN from _override_
|
90
|
+
# precisely override those in _self_.
|
91
|
+
def override(override)
|
92
|
+
for el in [ :first, :last]
|
93
|
+
val = override.send(el)
|
94
|
+
if val and (val == val) # Strip NaN on the property that NaN != NaN
|
95
|
+
self.send("#{el}=", val)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Apply a fixed margin on the Boundaries.
|
101
|
+
def apply_margin!(margin)
|
102
|
+
d = self.distance
|
103
|
+
@first = @first - margin * d
|
104
|
+
@last = @last + margin * d
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns a SimpleRange object that is large enough to exactly
|
108
|
+
# contain all _values_
|
109
|
+
def self.bounds(values)
|
110
|
+
return SimpleRange.new(values.min, values.max)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Takes an array of Boundaries and returns a Boundaries object
|
114
|
+
# that precisely encompasses them all. Invalid floats are simply
|
115
|
+
# ignored.
|
116
|
+
def self.overall_range(ranges)
|
117
|
+
retval = SimpleRange.new(nil, nil)
|
118
|
+
for r in ranges
|
119
|
+
retval.extend(b)
|
120
|
+
end
|
121
|
+
return retval
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
28
126
|
# An object representing boundaries for a plot.
|
127
|
+
#
|
128
|
+
# \todo Should be converted to using two SimpleRange
|
129
|
+
# objects. Will be more clear anyway.
|
29
130
|
class Boundaries
|
30
131
|
|
31
132
|
# Boundaries
|
@@ -65,6 +166,18 @@ module CTioga2
|
|
65
166
|
@bottom > @top ? @bottom : @top
|
66
167
|
end
|
67
168
|
|
169
|
+
# Returns a SimpleRange object corresponding to the horizontal
|
170
|
+
# range
|
171
|
+
def horizontal
|
172
|
+
return SimpleRange.new(@left, @right)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns a SimpleRange object corresponding to the vertical
|
176
|
+
# range
|
177
|
+
def vertical
|
178
|
+
return SimpleRange.new(@bottom, @top)
|
179
|
+
end
|
180
|
+
|
68
181
|
|
69
182
|
# Converts to an [xmin, xmax, ymin, ymax] array
|
70
183
|
def extrema
|
@@ -161,6 +274,12 @@ module CTioga2
|
|
161
274
|
return retval
|
162
275
|
end
|
163
276
|
|
277
|
+
# Creates a Boundaries object from two SimpleRange objects.
|
278
|
+
def self.from_ranges(horiz, vert)
|
279
|
+
return Boundaries.new(horiz.first, horiz.last,
|
280
|
+
vert.last, vert.first)
|
281
|
+
end
|
282
|
+
|
164
283
|
end
|
165
284
|
|
166
285
|
end
|
@@ -16,7 +16,7 @@ require 'ctioga2/log'
|
|
16
16
|
|
17
17
|
module CTioga2
|
18
18
|
|
19
|
-
Version::register_svn_info('$Revision:
|
19
|
+
Version::register_svn_info('$Revision: 198 $', '$Date: 2010-11-30 00:48:23 +0100 (Tue, 30 Nov 2010) $')
|
20
20
|
|
21
21
|
module Graphics
|
22
22
|
|
@@ -85,6 +85,13 @@ module CTioga2
|
|
85
85
|
to_figure(t, orientation))
|
86
86
|
end
|
87
87
|
|
88
|
+
# Express the Dimension in units of text height (dy)
|
89
|
+
def to_text_height(t, orientation = nil)
|
90
|
+
orientation ||= @orientation
|
91
|
+
return self.to_figure(t, orientation)/
|
92
|
+
t.send("default_text_height_d#{orientation}")
|
93
|
+
end
|
94
|
+
|
88
95
|
# Replace this Dimension by _dimension_ if the latter is
|
89
96
|
# bigger. Conserves the current orientation.
|
90
97
|
def replace_if_bigger(t, dimension)
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# grib.rb: setup and use of a "graph grid"
|
2
|
+
# copyright (c) 2009,2010 by Vincent Fourmond
|
3
|
+
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details (in the COPYING file).
|
13
|
+
|
14
|
+
require 'ctioga2/utils'
|
15
|
+
require 'ctioga2/log'
|
16
|
+
|
17
|
+
require 'ctioga2/graphics/types/dimensions'
|
18
|
+
require 'ctioga2/graphics/types/boxes'
|
19
|
+
|
20
|
+
module CTioga2
|
21
|
+
|
22
|
+
Version::register_svn_info('$Revision: 147 $', '$Date: 2010-04-06 21:13:28 +0200 (Tue, 06 Apr 2010) $')
|
23
|
+
|
24
|
+
module Graphics
|
25
|
+
|
26
|
+
module Types
|
27
|
+
|
28
|
+
# The position of a single element in a GridLayout
|
29
|
+
#
|
30
|
+
# \todo add the possibility to override one element of the
|
31
|
+
# final positions.
|
32
|
+
class GridBox < Box
|
33
|
+
|
34
|
+
OptionHashRE = /([\w-]+)\s*=\s*([^,]+),?\s*/
|
35
|
+
|
36
|
+
GridBoxRE = /^\s*grid:(\d+)\s*,\s*(\d+)(?:,(#{OptionHashRE}+))?\s*$/
|
37
|
+
|
38
|
+
# This hash helps to convert from a hash-based representation
|
39
|
+
# of frame coordinates to the array-based one.
|
40
|
+
#
|
41
|
+
# \todo I should either use existing code or refactor into
|
42
|
+
# something globally useful.
|
43
|
+
FrameCoordsOverride = { 'xl' => 0,
|
44
|
+
'yt' => 1,
|
45
|
+
'xr' => 2,
|
46
|
+
'yb' => 3
|
47
|
+
}
|
48
|
+
|
49
|
+
def self.from_text(txt)
|
50
|
+
if txt =~ GridBoxRE
|
51
|
+
return GridBox.new(GridLayout.current_grid, $1.to_i, $2.to_i,
|
52
|
+
$3) # The latter being to remove
|
53
|
+
# the initial comma
|
54
|
+
else
|
55
|
+
raise "#{txt} is not a grid box."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(grid, x, y, options = {})
|
60
|
+
if options.is_a? String
|
61
|
+
str = options
|
62
|
+
options = {}
|
63
|
+
str.split(/\s*,\s*/).map { |s|
|
64
|
+
s =~ OptionHashRE
|
65
|
+
options[$1] =
|
66
|
+
BaseCoordinate.from_text($2,if FrameCoordsOverride[$1] % 2 == 0
|
67
|
+
:x
|
68
|
+
else
|
69
|
+
:y
|
70
|
+
end, :frame)
|
71
|
+
}
|
72
|
+
end
|
73
|
+
@x = x.to_i
|
74
|
+
@grid = grid
|
75
|
+
@y = y.to_i
|
76
|
+
@overrides = options || {}
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_frame_coordinates(t)
|
80
|
+
a = @grid.frame_coordinates(t, @x, @y)
|
81
|
+
## \todo write a framework for manipulating this !
|
82
|
+
for k,v in @overrides
|
83
|
+
next unless FrameCoordsOverride.key?(k)
|
84
|
+
a[FrameCoordsOverride[k]] = v.to_frame(t)
|
85
|
+
end
|
86
|
+
return a
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# This class provides a grid-like layout through the use of a grid
|
91
|
+
# setup command and a grid box specification.
|
92
|
+
class GridLayout
|
93
|
+
|
94
|
+
# The margins (left, right, top, bottom) around the whole grid
|
95
|
+
attr_accessor :outer_margins
|
96
|
+
|
97
|
+
# The X offset to go from the right-hand side of one element to
|
98
|
+
# the left-hand-side of the next
|
99
|
+
attr_accessor :delta_x
|
100
|
+
|
101
|
+
# The Y offset to go from the bottom of one element to
|
102
|
+
# the top of the next.
|
103
|
+
attr_accessor :delta_y
|
104
|
+
|
105
|
+
# The nup: an array nb horizontal, nb vertical
|
106
|
+
attr_accessor :nup
|
107
|
+
|
108
|
+
# Horizontal scales
|
109
|
+
attr_accessor :hscales
|
110
|
+
|
111
|
+
# Vertical scales
|
112
|
+
attr_accessor :vscales
|
113
|
+
|
114
|
+
def initialize(nup = "2x2")
|
115
|
+
if nup.respond_to?(:split)
|
116
|
+
if nup =~ /,/
|
117
|
+
@hscales, @vscales = nup.split(/\s*x\s*/).map { |x|
|
118
|
+
x.split(/\s*,\s*/).map { |y| y.to_f }
|
119
|
+
}
|
120
|
+
@nup = [@hscales.size, @vscales.size]
|
121
|
+
else
|
122
|
+
@nup = nup.split(/\s*x\s*/).map { |s| s.to_i }
|
123
|
+
end
|
124
|
+
else
|
125
|
+
@nup = nup.dup
|
126
|
+
end
|
127
|
+
|
128
|
+
# Initialize with the given
|
129
|
+
@outer_margins = {
|
130
|
+
'left' => Dimension.new(:dy, 2.5, :x),
|
131
|
+
'right' => Dimension.new(:bp, 6, :x),
|
132
|
+
'bottom' => Dimension.new(:dy, 2.5, :y),
|
133
|
+
'top' => Dimension.new(:dy, 2.5, :y)
|
134
|
+
}
|
135
|
+
@delta_x = Dimension.new(:dy, 2.5, :x)
|
136
|
+
@delta_y = Dimension.new(:dy, 2.5, :y)
|
137
|
+
|
138
|
+
@hscales ||= [1] * @nup[0]
|
139
|
+
@vscales ||= [1] * @nup[1]
|
140
|
+
end
|
141
|
+
|
142
|
+
# The grid currently in use.
|
143
|
+
@current_grid = nil
|
144
|
+
|
145
|
+
def self.current_grid=(grid)
|
146
|
+
@current_grid = grid
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.current_grid
|
150
|
+
return @current_grid
|
151
|
+
end
|
152
|
+
|
153
|
+
# Compute the frame coordinates fo the x,y element of the
|
154
|
+
# grid. They are numbered from the top,left element.
|
155
|
+
def frame_coordinates(t, x, y)
|
156
|
+
compute_lengths(t)
|
157
|
+
xo = if x > 0
|
158
|
+
@hscales[0..(x-1)].inject(0,:+) * @wbase
|
159
|
+
else
|
160
|
+
0
|
161
|
+
end
|
162
|
+
xl = @outer_margins['left'].to_frame(t, :x) + xo +
|
163
|
+
x * @delta_x.to_frame(t, :x)
|
164
|
+
yo = if y > 0
|
165
|
+
@vscales[0..(y-1)].inject(0,:+) * @hbase
|
166
|
+
else
|
167
|
+
0
|
168
|
+
end
|
169
|
+
yt = 1 - (@outer_margins['top'].to_frame(t, :y) + yo +
|
170
|
+
y * @delta_y.to_frame(t, :y))
|
171
|
+
return [xl, yt,
|
172
|
+
xl + @wbase * @hscales[x],
|
173
|
+
yt - @hbase * @vscales[y]]
|
174
|
+
end
|
175
|
+
|
176
|
+
protected
|
177
|
+
|
178
|
+
# Compute the necessary variables in frame coordinates
|
179
|
+
def compute_lengths(t)
|
180
|
+
return if (@wbase && @hbase)
|
181
|
+
@wbase = (1 -
|
182
|
+
(@outer_margins['left'].to_frame(t, :x) +
|
183
|
+
@outer_margins['right'].to_frame(t, :x) +
|
184
|
+
@delta_x.to_frame(t, :x) * (@nup[0]-1)))/@hscales.inject(0,:+)
|
185
|
+
@hbase = (1 -
|
186
|
+
(@outer_margins['top'].to_frame(t, :y) +
|
187
|
+
@outer_margins['bottom'].to_frame(t, :y) +
|
188
|
+
@delta_y.to_frame(t, :y) * (@nup[1]-1)))/@vscales.inject(0,:+)
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# location.rb: handling the concept of "location" (for an axis especially)
|
2
|
+
# copyright (c) 2009 by Vincent Fourmond
|
3
|
+
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful, but
|
10
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# General Public License for more details (in the COPYING file).
|
13
|
+
|
14
|
+
require 'ctioga2/utils'
|
15
|
+
require 'ctioga2/log'
|
16
|
+
|
17
|
+
module CTioga2
|
18
|
+
|
19
|
+
Version::register_svn_info('$Revision: 223 $', '$Date: 2011-01-11 01:07:48 +0100 (Tue, 11 Jan 2011) $')
|
20
|
+
|
21
|
+
module Graphics
|
22
|
+
|
23
|
+
module Types
|
24
|
+
|
25
|
+
|
26
|
+
# Location of an object (especially axes) in a plot, in terms of
|
27
|
+
# the side of the plot or the X and Y axis.
|
28
|
+
class PlotLocation
|
29
|
+
|
30
|
+
# Conversion between the #base_location attribute and the real
|
31
|
+
# constant used for Tioga
|
32
|
+
LocationToTiogaLocation = {
|
33
|
+
:left => Tioga::FigureConstants::LEFT,
|
34
|
+
:right => Tioga::FigureConstants::RIGHT,
|
35
|
+
:bottom => Tioga::FigureConstants::BOTTOM,
|
36
|
+
:top => Tioga::FigureConstants::TOP,
|
37
|
+
:at_x_origin => Tioga::FigureConstants::AT_X_ORIGIN,
|
38
|
+
:at_y_origin => Tioga::FigureConstants::AT_Y_ORIGIN
|
39
|
+
}
|
40
|
+
|
41
|
+
# Horizontal or vertical
|
42
|
+
LocationVertical = {
|
43
|
+
:left => true,
|
44
|
+
:right => true,
|
45
|
+
:bottom => false,
|
46
|
+
:top => false,
|
47
|
+
:at_x_origin => true,
|
48
|
+
:at_y_origin => false
|
49
|
+
}
|
50
|
+
|
51
|
+
# A few helper hashes to convert from sides to margins
|
52
|
+
# @todo won't work for origins.
|
53
|
+
LocationBaseMargins = {
|
54
|
+
:left => [0,1,0,0],
|
55
|
+
:right => [1,0,0,0],
|
56
|
+
:bottom => [0,0,1,0],
|
57
|
+
:top => [0,0,0,1]
|
58
|
+
}
|
59
|
+
|
60
|
+
# Multiply this by the frame dimension in the correct
|
61
|
+
# direction to get the frame margins.
|
62
|
+
LocationMarginMultiplier = {
|
63
|
+
:left => [-1,0,0,0],
|
64
|
+
:right => [0,-1,0,0],
|
65
|
+
:bottom => [0,0,0,-1],
|
66
|
+
:top => [0,0,-1,0]
|
67
|
+
}
|
68
|
+
|
69
|
+
LocationsReorientMargins = {
|
70
|
+
:left => [1,0,3,2],
|
71
|
+
:right => [0,1,2,3],
|
72
|
+
:top => [2,3,1,0],
|
73
|
+
:bottom => [3,2,0,1]
|
74
|
+
}
|
75
|
+
|
76
|
+
# The position of the object, one of :left, :right, :top,
|
77
|
+
# :bottom, :at_y_origin or :at_x_origin.
|
78
|
+
#
|
79
|
+
# @todo This will have to be extended to allow possibly
|
80
|
+
# arbitrary frame/figure placement.
|
81
|
+
attr_accessor :base_location
|
82
|
+
|
83
|
+
# The shift away from the position given by #base_location.
|
84
|
+
#
|
85
|
+
# This will be a Dimension object.
|
86
|
+
#
|
87
|
+
# @todo This is not currently implemented
|
88
|
+
attr_accessor :shift
|
89
|
+
|
90
|
+
|
91
|
+
# Creates a new PlotLocation object, either copying the one
|
92
|
+
# given as argument or from scratch specifying at least the
|
93
|
+
# base location.
|
94
|
+
def initialize(location, shift = nil)
|
95
|
+
if location.respond_to? :shift
|
96
|
+
@base_location = location.base_location
|
97
|
+
@shift = shift || location.shift
|
98
|
+
else
|
99
|
+
@base_location = location
|
100
|
+
@shift = shift
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the tioga location (ie that suitable for sending to
|
105
|
+
# show_axis for instance)
|
106
|
+
def tioga_location
|
107
|
+
return LocationToTiogaLocation[@base_location]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Whether the given location is vertical or horizontal
|
111
|
+
def vertical?
|
112
|
+
return LocationVertical[@base_location]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the orientation away from the graph
|
116
|
+
def orientation
|
117
|
+
if vertical?
|
118
|
+
return :x
|
119
|
+
else
|
120
|
+
return :y
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Extra extension that should be reserved for a label on the
|
125
|
+
# given side based on simple heuristics. Value is returned in
|
126
|
+
# text height units.
|
127
|
+
def label_extra_space(t)
|
128
|
+
case @base_location
|
129
|
+
when :bottom, :right
|
130
|
+
extra = 0.5 # To account for baseline ?
|
131
|
+
when :top, :left
|
132
|
+
extra = 1
|
133
|
+
else # We take the safe side !
|
134
|
+
extra = 1
|
135
|
+
end
|
136
|
+
if @shift
|
137
|
+
## @todo Here add the shift
|
138
|
+
end
|
139
|
+
return extra
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns whether the location is on the given side.
|
143
|
+
def is_side?(which)
|
144
|
+
return @base_location == which
|
145
|
+
end
|
146
|
+
|
147
|
+
# Takes a set of margins, expressed in relative terms, ie
|
148
|
+
# * _close_ (the margins on the side next to the graph),
|
149
|
+
# * _away_ (on the other side),
|
150
|
+
# * _aleft_ (on the left going away from the graph) and
|
151
|
+
# * _aright_ (on the right going away from the graph)
|
152
|
+
# into a left,right,top,bottom suitable for standards margins calls.
|
153
|
+
def reorient_margins(close, away, aleft, aright)
|
154
|
+
a = [close, away, aleft, aright]
|
155
|
+
return LocationsReorientMargins[@base_location].map do |i|
|
156
|
+
a[i]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns the margins argument suitable for sending to
|
161
|
+
# set_subframe to paint within the region defined by the given
|
162
|
+
# size at the given position.
|
163
|
+
#
|
164
|
+
# _size_ is a Dimension object.
|
165
|
+
def frame_margins_for_size(t, size)
|
166
|
+
margins = Dobjects::Dvector[*LocationBaseMargins[@base_location]]
|
167
|
+
## @todo handle the case of at Y and at X
|
168
|
+
dim = size.to_frame(t, orientation)
|
169
|
+
|
170
|
+
add = Dobjects::Dvector[*LocationMarginMultiplier[@base_location]]
|
171
|
+
add.mul!(dim)
|
172
|
+
margins += add
|
173
|
+
return margins
|
174
|
+
end
|
175
|
+
|
176
|
+
def do_sub_frame(t, size)
|
177
|
+
margins = frame_margins_for_size(t, size)
|
178
|
+
|
179
|
+
## @todo This is should integrate some common class.
|
180
|
+
left = t.convert_frame_to_page_x(margins[0])
|
181
|
+
right = t.convert_frame_to_page_x(1 - margins[1])
|
182
|
+
top = t.convert_frame_to_page_y(1 - margins[2])
|
183
|
+
bottom = t.convert_frame_to_page_y(margins[3])
|
184
|
+
|
185
|
+
# Ensure that we don't have coords outside of the page range
|
186
|
+
# because of rounding problems:
|
187
|
+
left = 0.0 if left < 0
|
188
|
+
bottom = 0.0 if bottom < 0
|
189
|
+
right = 1.0 if right > 1
|
190
|
+
top = 1.0 if top > 1
|
191
|
+
|
192
|
+
t.context do
|
193
|
+
t.set_frame_sides(left, right, top, bottom)
|
194
|
+
yield
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Creates a location from the given text
|
199
|
+
#
|
200
|
+
# So far, no real parsing
|
201
|
+
def self.from_text(str)
|
202
|
+
str.gsub!(/-/,"_")
|
203
|
+
return PlotLocation.new(str.to_sym)
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
# Something meant to be fed to PlotStyle#get_axis_style
|
210
|
+
LocationType = CmdType.new('location', { :type => :function_based,
|
211
|
+
:class => Graphics::Types::PlotLocation
|
212
|
+
}, <<EOD)
|
213
|
+
A position on the plot, referenced with respect to the sides. Can be:
|
214
|
+
* @left@
|
215
|
+
* @right@
|
216
|
+
* @top@
|
217
|
+
* @bottom@
|
218
|
+
|
219
|
+
In addition, there will one day be the possibility to specify an
|
220
|
+
offset from these locations. But that is still something to do.
|
221
|
+
EOD
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|