ctioga2 0.0 → 0.1
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.
- 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
|
+
|