ctioga 1.11.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/COPYING +340 -0
- data/ctioga/bin/ctable +28 -0
- data/ctioga/bin/ctioga +37 -0
- data/ctioga/doc/ctable.1 +156 -0
- data/ctioga/doc/ctioga.1 +2363 -0
- data/ctioga/examples/README +46 -0
- data/ctioga/examples/ctioga.gnuplot +4 -0
- data/ctioga/examples/ctioga_within_tioga.rb +53 -0
- data/ctioga/examples/ctiogarc.rb +24 -0
- data/ctioga/examples/include_1.rb +15 -0
- data/ctioga/examples/noise.dat +100 -0
- data/ctioga/examples/noise.rb +13 -0
- data/ctioga/examples/trig.csv +100 -0
- data/ctioga/examples/trig.dat +100 -0
- data/ctioga/examples/trig.rb +14 -0
- data/ctioga/examples/trigh.dat +100 -0
- data/ctioga/examples/trigh.rb +10 -0
- data/ctioga/examples/tutorial +763 -0
- data/ctioga/examples/tutorial.sh +269 -0
- data/ctioga/tests/README +14 -0
- data/ctioga/tests/axes.sh +40 -0
- data/ctioga/tests/basic.sh +11 -0
- data/ctioga/tests/draw.sh +24 -0
- data/ctioga/tests/histograms.sh +14 -0
- data/ctioga/tests/insets.sh +41 -0
- data/ctioga/tests/layouts.sh +29 -0
- data/ctioga/tests/legends.sh +113 -0
- data/ctioga/tests/styles.sh +43 -0
- data/ctioga/tests/test_style.sh +8 -0
- data/ctioga/tests/tests.sh +24 -0
- data/ctioga/tests/text_backend.sh +83 -0
- data/ctioga/tests/tioga_defaults.rb +18 -0
- data/lib/CTioga/axes.rb +904 -0
- data/lib/CTioga/backends.rb +88 -0
- data/lib/CTioga/boundaries.rb +224 -0
- data/lib/CTioga/ctable.rb +134 -0
- data/lib/CTioga/curve_style.rb +246 -0
- data/lib/CTioga/debug.rb +199 -0
- data/lib/CTioga/dimension.rb +133 -0
- data/lib/CTioga/elements.rb +17 -0
- data/lib/CTioga/elements/base.rb +84 -0
- data/lib/CTioga/elements/containers.rb +578 -0
- data/lib/CTioga/elements/curves.rb +368 -0
- data/lib/CTioga/elements/tioga_primitives.rb +440 -0
- data/lib/CTioga/layout.rb +595 -0
- data/lib/CTioga/legends.rb +29 -0
- data/lib/CTioga/legends/cmdline.rb +187 -0
- data/lib/CTioga/legends/item.rb +164 -0
- data/lib/CTioga/legends/style.rb +257 -0
- data/lib/CTioga/log.rb +73 -0
- data/lib/CTioga/movingarrays.rb +131 -0
- data/lib/CTioga/partition.rb +271 -0
- data/lib/CTioga/plot_style.rb +230 -0
- data/lib/CTioga/plotmaker.rb +1677 -0
- data/lib/CTioga/shortcuts.rb +69 -0
- data/lib/CTioga/structures.rb +82 -0
- data/lib/CTioga/styles.rb +140 -0
- data/lib/CTioga/themes.rb +581 -0
- data/lib/CTioga/themes/classical.rb +82 -0
- data/lib/CTioga/themes/demo.rb +63 -0
- data/lib/CTioga/themes/fits.rb +91 -0
- data/lib/CTioga/themes/mono.rb +33 -0
- data/lib/CTioga/tioga.rb +32 -0
- data/lib/CTioga/utils.rb +173 -0
- data/lib/MetaBuilder/Parameters/dates.rb +38 -0
- data/lib/MetaBuilder/Parameters/lists.rb +132 -0
- data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
- data/lib/MetaBuilder/Parameters/strings.rb +86 -0
- data/lib/MetaBuilder/Parameters/styles.rb +75 -0
- data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
- data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
- data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
- data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
- data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
- data/lib/MetaBuilder/descriptions.rb +603 -0
- data/lib/MetaBuilder/factory.rb +101 -0
- data/lib/MetaBuilder/group.rb +57 -0
- data/lib/MetaBuilder/metabuilder.rb +10 -0
- data/lib/MetaBuilder/parameter.rb +374 -0
- data/lib/MetaBuilder/parameters.rb +11 -0
- data/lib/MetaBuilder/qt4.rb +8 -0
- data/lib/SciYAG/Backends/backend.rb +379 -0
- data/lib/SciYAG/Backends/binner.rb +168 -0
- data/lib/SciYAG/Backends/cache.rb +102 -0
- data/lib/SciYAG/Backends/dataset.rb +158 -0
- data/lib/SciYAG/Backends/descriptions.rb +469 -0
- data/lib/SciYAG/Backends/filters.rb +25 -0
- data/lib/SciYAG/Backends/filters/average.rb +134 -0
- data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
- data/lib/SciYAG/Backends/filters/filter.rb +70 -0
- data/lib/SciYAG/Backends/filters/norm.rb +39 -0
- data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
- data/lib/SciYAG/Backends/filters/sort.rb +43 -0
- data/lib/SciYAG/Backends/filters/strip.rb +34 -0
- data/lib/SciYAG/Backends/filters/trim.rb +64 -0
- data/lib/SciYAG/Backends/gnuplot.rb +131 -0
- data/lib/SciYAG/Backends/math.rb +108 -0
- data/lib/SciYAG/Backends/mdb.rb +462 -0
- data/lib/SciYAG/Backends/multitext.rb +96 -0
- data/lib/SciYAG/Backends/source.rb +64 -0
- data/lib/SciYAG/Backends/text.rb +339 -0
- data/lib/SciYAG/backends.rb +16 -0
- metadata +191 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# This file contains some 'visual tests' for ctioga to make sure that when
|
|
3
|
+
# I add some features to ctioga, I don't break everything existing.
|
|
4
|
+
# As such, it also stands as a 'feature list' for ctioga.
|
|
5
|
+
|
|
6
|
+
# Test for legends
|
|
7
|
+
|
|
8
|
+
. ./test_style.sh
|
|
9
|
+
|
|
10
|
+
ctioga -t 'Normal legends' 'sin(x)' 'cos(x)'
|
|
11
|
+
|
|
12
|
+
ctioga -t 'Normal legends + line' 'sin(x)' --legend-line \
|
|
13
|
+
'One line between' 'cos(x)'
|
|
14
|
+
|
|
15
|
+
# Legends of the second axes must show where the first axes are
|
|
16
|
+
ctioga -t 'Legends and second axes' 'sin(x)' 'cos(x)' \
|
|
17
|
+
--y2 -l '$x^2$ (2nd)' 'x**2'
|
|
18
|
+
|
|
19
|
+
# Legends and insets
|
|
20
|
+
ctioga -N -t 'Legends and insets' -l '$x^2$' 'x**2' \
|
|
21
|
+
--inset .5,.5:.4 -l '$\sin x$' 'sin(x)' \
|
|
22
|
+
-l '$\cos x$' 'cos(x)'
|
|
23
|
+
|
|
24
|
+
# Legends and markers
|
|
25
|
+
ctioga -N -t 'Legends and markers' --marker auto \
|
|
26
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
27
|
+
-l 'No line' 'x**2 + 2*x + 1'
|
|
28
|
+
|
|
29
|
+
# Legends inside
|
|
30
|
+
ctioga -N -t 'Legends inside' \
|
|
31
|
+
--legend-inside 0.5,0.7:0.3 \
|
|
32
|
+
--marker auto \
|
|
33
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
34
|
+
-l 'No line' 'x**2 + 2*x + 1'
|
|
35
|
+
|
|
36
|
+
# A (bad) use case of --legend-dy
|
|
37
|
+
ctioga -N -t 'Crammed legends inside' \
|
|
38
|
+
--legend-inside 0.5,0.7:0.3 \
|
|
39
|
+
--marker auto --legend-dy 1.0 \
|
|
40
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
41
|
+
--legend-line \
|
|
42
|
+
'One (crammed) line between' \
|
|
43
|
+
'cos(x)' \
|
|
44
|
+
-l 'No line' 'x**2 + 2*x + 1'
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# New position scheme
|
|
48
|
+
ctioga -N -t 'Top left in the middle' \
|
|
49
|
+
--legend-inside tl:0.5,0.5 \
|
|
50
|
+
--marker auto \
|
|
51
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
52
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
ctioga -N -t 'Bottom right in the middle' \
|
|
56
|
+
--legend-inside br:0.5,0.5 \
|
|
57
|
+
--marker auto \
|
|
58
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
59
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
60
|
+
|
|
61
|
+
ctioga -N -t 'Center in the middle' \
|
|
62
|
+
--legend-inside cc:0.5,0.5 \
|
|
63
|
+
--marker auto \
|
|
64
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
65
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
66
|
+
|
|
67
|
+
ctioga -N -t 'Center in the middle, simpler spec' \
|
|
68
|
+
--legend-inside cc --marker auto \
|
|
69
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
70
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
71
|
+
|
|
72
|
+
# Frames around legends...
|
|
73
|
+
ctioga -N -t 'Bottom right in the middle, framed (square)' \
|
|
74
|
+
--legend-inside br:0.5,0.5 \
|
|
75
|
+
--marker auto \
|
|
76
|
+
--legend-frame square \
|
|
77
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
78
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
79
|
+
|
|
80
|
+
ctioga -N -t 'Bottom right in the middle, framed (round)' \
|
|
81
|
+
--legend-inside br:0.5,0.5 \
|
|
82
|
+
--marker auto \
|
|
83
|
+
--legend-frame round \
|
|
84
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
85
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
86
|
+
|
|
87
|
+
# Colors...
|
|
88
|
+
ctioga -N -t 'Colored frames' \
|
|
89
|
+
--legend-inside cc:0.5,0.5 \
|
|
90
|
+
--legend-color Green --legend-background 0.9,1.0,0.9 \
|
|
91
|
+
--marker auto \
|
|
92
|
+
--legend-frame round \
|
|
93
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
94
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
95
|
+
|
|
96
|
+
ctioga -N -t 'Transparent background' \
|
|
97
|
+
--legend-inside cc:0.5,0.5 --math-samples 1000 \
|
|
98
|
+
--legend-color Green --legend-background White \
|
|
99
|
+
--legend-transparency 0.4 \
|
|
100
|
+
--marker auto \
|
|
101
|
+
--legend-frame round \
|
|
102
|
+
-l '$\cos 2x$' 'cos(2*x)' \
|
|
103
|
+
-l '$\sin 2x$ (still decently long)' 'sin(2*x)'
|
|
104
|
+
|
|
105
|
+
ctioga -N -t 'Fancy frames' \
|
|
106
|
+
--legend-inside cc:0.5,0.5 --math-samples 100 \
|
|
107
|
+
--legend-line-width 0.7 \
|
|
108
|
+
--legend-line-style Dots \
|
|
109
|
+
--legend-background Cornsilk \
|
|
110
|
+
--marker auto \
|
|
111
|
+
--legend-frame round \
|
|
112
|
+
-l '$x^2$' 'x**2' --line-style none \
|
|
113
|
+
-l 'Intentionally long legend' 'x**2 + 2*x + 1'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# This file contains some 'visual tests' for ctioga to make sure that when
|
|
3
|
+
# I add some features to ctioga, I don't break everything existing.
|
|
4
|
+
# As such, it also stands as a 'feature list' for ctioga.
|
|
5
|
+
|
|
6
|
+
. ./test_style.sh
|
|
7
|
+
|
|
8
|
+
# Then, we try various styles:
|
|
9
|
+
ctioga -N -t 'Transparency and fills' \
|
|
10
|
+
--fill y-axis --fill-transparency 0.6 'sin(x)' 'cos(x)'
|
|
11
|
+
ctioga -N -t 'Markers' \
|
|
12
|
+
--marker auto 'sin(x)**2' 'cos(x)**2'
|
|
13
|
+
ctioga -N -t 'Dot clouds' \
|
|
14
|
+
--marker auto --marker-scale 0.2 --line-style no 'x**2' '1 - x**2'
|
|
15
|
+
|
|
16
|
+
# Special sets
|
|
17
|
+
ctioga -N -t 'Unusual set specifications' \
|
|
18
|
+
--marker auto --color-set 'Red|Blue|Pink' \
|
|
19
|
+
--line-style-set Dashes 'x**2 + 10*0##9'
|
|
20
|
+
|
|
21
|
+
# Style manipulations
|
|
22
|
+
ctioga -N -t 'Reset override' \
|
|
23
|
+
--marker auto --marker-scale 0.2 --line-style no 'x**2' '1 - x**2' \
|
|
24
|
+
--reset-override '10 - x**2'
|
|
25
|
+
# And override manipulations
|
|
26
|
+
ctioga -N -t 'Override manipulations' \
|
|
27
|
+
--marker auto --marker-scale 0.2 --line-style no 'x**2' \
|
|
28
|
+
--save-override cloud --reset-override \
|
|
29
|
+
'1 - x**2' '10 - x**2' --use-override cloud 'x**2 - 10'
|
|
30
|
+
|
|
31
|
+
# Now testing background colors
|
|
32
|
+
|
|
33
|
+
ctioga -N -t 'Turquoise background' \
|
|
34
|
+
--background PaleTurquoise 'sin(x)**2' 'cos(x)**2'
|
|
35
|
+
|
|
36
|
+
ctioga -N -t 'Backgrounds with --grid' \
|
|
37
|
+
--grid column=2 \
|
|
38
|
+
--background PaleTurquoise 'sin(x)**2' 'cos(x)**2' \
|
|
39
|
+
--next 'tan(x)' --next --background Chiffon \
|
|
40
|
+
'x**2' 'x**2 + 10'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# This file contains some 'visual tests' for ctioga to make sure that when
|
|
3
|
+
# I add some features to ctioga, I don't break everything existing.
|
|
4
|
+
# As such, it also stands as a 'feature list' for ctioga.
|
|
5
|
+
|
|
6
|
+
. ./test_style.sh
|
|
7
|
+
|
|
8
|
+
# First, some basic stuff
|
|
9
|
+
ctioga -t 'Basic curve' 'sin(x)'
|
|
10
|
+
ctioga -N -t 'Several styles' 'sin(x)' 'cos(x)' 'tan(x)'
|
|
11
|
+
|
|
12
|
+
# Then, we try various styles:
|
|
13
|
+
ctioga -N -t 'Transparency and fills' \
|
|
14
|
+
--fill y-axis --fill-transparency 0.6 'sin(x)' 'cos(x)'
|
|
15
|
+
ctioga -N -t 'Markers' \
|
|
16
|
+
--marker auto 'sin(x)**2' 'cos(x)**2'
|
|
17
|
+
ctioga -N -t 'Dot clouds' \
|
|
18
|
+
--marker auto --marker-scale 0.2 --line-style no 'x**2' '1 - x**2'
|
|
19
|
+
|
|
20
|
+
# Then, insets:
|
|
21
|
+
|
|
22
|
+
ctioga -N -t 'Inset (with title)' 'x**2' --inset .5,.5:.4 \
|
|
23
|
+
-t 'Title inset' 'cos(x)'
|
|
24
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# This file contains some 'visual tests' for ctioga to make sure that when
|
|
3
|
+
# I add some features to ctioga, I don't break everything existing.
|
|
4
|
+
# As such, it also stands as a 'feature list' for ctioga.
|
|
5
|
+
|
|
6
|
+
. ./test_style.sh
|
|
7
|
+
|
|
8
|
+
# We first create several data files using ctable
|
|
9
|
+
|
|
10
|
+
tmpfile=`mktemp -t "ctioga-test-XXXXXXX"`
|
|
11
|
+
|
|
12
|
+
echo "Generating temporary data file ${tmpfile}"
|
|
13
|
+
|
|
14
|
+
# Here: warning; density is quite high (500 points)
|
|
15
|
+
ruby -e 'include Math; srand(Time.now.to_f); 0.upto(500) do |i|
|
|
16
|
+
x = 3.1415 * (1- 2*rand)
|
|
17
|
+
puts "#{i}\t#{x}\t#{sin(x)}\t#{cos(x) + 0.1*rand}\t#{0.2*sin(x)}"
|
|
18
|
+
end
|
|
19
|
+
' > $tmpfile
|
|
20
|
+
|
|
21
|
+
ctioga -N -t 'Raw data' --text --marker auto --line-style Dashes \
|
|
22
|
+
$tmpfile@2:3
|
|
23
|
+
|
|
24
|
+
ctioga -N -t 'Without lines' --text --marker auto --line-style No \
|
|
25
|
+
$tmpfile@2:3
|
|
26
|
+
|
|
27
|
+
ctioga -N -t '20\% error bars' --text --marker auto --line-style No \
|
|
28
|
+
$tmpfile@2:3:5
|
|
29
|
+
|
|
30
|
+
ctioga -N -t 'Noisy cosine' --text --marker auto --line-style No \
|
|
31
|
+
$tmpfile@2:4
|
|
32
|
+
|
|
33
|
+
# Two small exercises:
|
|
34
|
+
# * why did I draw the y = 1 line ?
|
|
35
|
+
# * compute the envelope of the obtained dot cloud ;-)...
|
|
36
|
+
|
|
37
|
+
ctioga -N -t 'Nearly one !' --text --marker auto --line-style No \
|
|
38
|
+
--draw 'line: -3.14,1 3.14,1 line_style=Dashes color=Gray' \
|
|
39
|
+
$tmpfile@'$2:$3**2 + $4**2'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Now, demonstrating the --sort filter:
|
|
43
|
+
|
|
44
|
+
ctioga -N -t 'Sorted data' --text --sort --marker auto \
|
|
45
|
+
--line-style Dashes $tmpfile@2:3
|
|
46
|
+
|
|
47
|
+
ctioga -N -t 'Sorted data with error bars' --text --sort --marker auto \
|
|
48
|
+
--line-style Dashes $tmpfile@2:3:5
|
|
49
|
+
|
|
50
|
+
ctioga -N -t 'Cosine' --text --marker auto --line-style No \
|
|
51
|
+
--legend-inside 'tl' \
|
|
52
|
+
-l "Original" $tmpfile@2:4 \
|
|
53
|
+
--smooth 7 -l 'Smoothed before sorting' $tmpfile@2:4 \
|
|
54
|
+
--filter-clear --sort \
|
|
55
|
+
--smooth 7 -l 'Smoothed after sorting' $tmpfile@2:4
|
|
56
|
+
|
|
57
|
+
# rm -f $tmpfile
|
|
58
|
+
|
|
59
|
+
echo "Generating temporary (nearly) redundant data ${tmpfile}"
|
|
60
|
+
|
|
61
|
+
# Here: warning; density is quite high (500 points)
|
|
62
|
+
ruby -e 'include Math; srand(Time.now.to_f); 0.upto(50) do |i|
|
|
63
|
+
x = 3.1415 * (1- 2*rand)
|
|
64
|
+
20.times { puts "#{x}\t#{sin(x) + (1.6*(rand - 0.5))**3}" }
|
|
65
|
+
end
|
|
66
|
+
puts "0\t0"
|
|
67
|
+
' > $tmpfile
|
|
68
|
+
|
|
69
|
+
ctioga -N -t 'Redundant data' --text --marker auto --line-style Dashes \
|
|
70
|
+
$tmpfile
|
|
71
|
+
|
|
72
|
+
ctioga -N -t 'Sorted redundant data' --text --marker auto \
|
|
73
|
+
--sort --line-style Dashes \
|
|
74
|
+
$tmpfile
|
|
75
|
+
|
|
76
|
+
ctioga -N -t 'After averageing duplicates' --text --marker auto \
|
|
77
|
+
--line-style Dashes --avgdup $tmpfile
|
|
78
|
+
|
|
79
|
+
ctioga -N -t 'Computing standard deviation' --text --marker auto \
|
|
80
|
+
--sort --stddev \
|
|
81
|
+
--line-style Dashes --avgdup $tmpfile
|
|
82
|
+
|
|
83
|
+
rm -f $tmpfile
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# tioga_defaults.rb: a small Ruby script displaying Tioga's default parameters
|
|
2
|
+
# Copyright 2008 by Vincent Fourmond
|
|
3
|
+
# You are free to do whatever you want with this file, provided you don't
|
|
4
|
+
# remove the copyright notice nor this notice
|
|
5
|
+
|
|
6
|
+
require 'Tioga/FigureMaker'
|
|
7
|
+
|
|
8
|
+
def display_value(t, element)
|
|
9
|
+
puts "#{element} -> #{t.send(element).inspect}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
t = Tioga::FigureMaker.new
|
|
13
|
+
display_value(t, 'legend_line_dy')
|
|
14
|
+
display_value(t, 'legend_line_x0')
|
|
15
|
+
display_value(t, 'legend_line_x1')
|
|
16
|
+
display_value(t, 'legend_text_dy')
|
|
17
|
+
display_value(t, 'legend_text_xstart')
|
|
18
|
+
display_value(t, 'legend_text_ystart')
|
data/lib/CTioga/axes.rb
ADDED
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
# axes.rb: A module to deal with axes customization.
|
|
2
|
+
# Copyright (c) 2007, 2008 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 'CTioga/utils'
|
|
15
|
+
require 'CTioga/layout'
|
|
16
|
+
require 'CTioga/log'
|
|
17
|
+
require 'CTioga/boundaries'
|
|
18
|
+
require 'CTioga/partition'
|
|
19
|
+
|
|
20
|
+
module CTioga
|
|
21
|
+
|
|
22
|
+
Version::register_svn_info('$Revision: 828 $', '$Date: 2008-08-07 11:29:21 +0200 (Thu, 07 Aug 2008) $')
|
|
23
|
+
|
|
24
|
+
# A module to be included by the main PlotMaker instance. It
|
|
25
|
+
# deals with various aspects of axes manipulations.
|
|
26
|
+
module Axes
|
|
27
|
+
|
|
28
|
+
LabelSpecification = {
|
|
29
|
+
/^x(label)?$/i => :xlabel,
|
|
30
|
+
/^y(label)?$/i => :ylabel,
|
|
31
|
+
/^t(itle)?$/i => :title,
|
|
32
|
+
/^xticks?$/i => :xticks,
|
|
33
|
+
/^yticks?$/i => :yticks,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
AxisSpecification = {
|
|
37
|
+
/^x$/i => 'xaxis',
|
|
38
|
+
/^y?$/i => 'yaxis',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
LabelSide = {
|
|
42
|
+
/bottom/i => Tioga::FigureConstants::BOTTOM,
|
|
43
|
+
/top/i => Tioga::FigureConstants::TOP,
|
|
44
|
+
/left/i => Tioga::FigureConstants::LEFT,
|
|
45
|
+
/right/i => Tioga::FigureConstants::RIGHT,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Alignment = {
|
|
49
|
+
/B/ => Tioga::FigureConstants::ALIGNED_AT_BASELINE,
|
|
50
|
+
/baseline/i => Tioga::FigureConstants::ALIGNED_AT_BASELINE,
|
|
51
|
+
/b/ => Tioga::FigureConstants::ALIGNED_AT_BOTTOM,
|
|
52
|
+
/bottom/i => Tioga::FigureConstants::ALIGNED_AT_BOTTOM,
|
|
53
|
+
/t(op)?/i => Tioga::FigureConstants::ALIGNED_AT_TOP,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Justification = {
|
|
57
|
+
/c(enter)?/i => Tioga::FigureConstants::CENTERED,
|
|
58
|
+
/l(eft)?/i => Tioga::FigureConstants::LEFT_JUSTIFIED,
|
|
59
|
+
/r(ight)?/i => Tioga::FigureConstants::RIGHT_JUSTIFIED,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# TODO: all the following attributes should go to PlotStyle!
|
|
64
|
+
# This would allow to remove the dirty ugly funcall hack for
|
|
65
|
+
# log values...
|
|
66
|
+
|
|
67
|
+
# Whether to have X or Y log axes
|
|
68
|
+
attr_accessor :x_log, :y_log
|
|
69
|
+
# Scaling of data axes
|
|
70
|
+
attr_accessor :x_factor, :y_factor
|
|
71
|
+
# Offsets
|
|
72
|
+
attr_accessor :x_offset, :y_offset
|
|
73
|
+
# Decimal separator
|
|
74
|
+
attr_accessor :decimal_separator
|
|
75
|
+
|
|
76
|
+
# Initializes variables pertaining to axes
|
|
77
|
+
def init_axes
|
|
78
|
+
# TODO: same as above
|
|
79
|
+
@x_log = false
|
|
80
|
+
@y_log = false
|
|
81
|
+
@x_factor = false
|
|
82
|
+
@y_factor = false
|
|
83
|
+
@x_offset = false
|
|
84
|
+
@y_offset = false
|
|
85
|
+
@decimal_separator = false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Turns a String into a [ left, right, top, bottom ]
|
|
89
|
+
# edge visibility specification
|
|
90
|
+
def parse_edge_visibility(str)
|
|
91
|
+
# First and simplest : a series of v (for visible) and i
|
|
92
|
+
# (for invisible)
|
|
93
|
+
if str =~ /^\s*([vi]{4})\s*$/i
|
|
94
|
+
val = $1.split(//).map { |s| s =~ /v/i ? true : false }
|
|
95
|
+
return val
|
|
96
|
+
end
|
|
97
|
+
# By default, everything is visible
|
|
98
|
+
return [ true, true, true, true ]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Runs a block safely with a label specification
|
|
102
|
+
def run_with_label(name)
|
|
103
|
+
w = Utils::interpret_arg(name, LabelSpecification) {false}
|
|
104
|
+
if w
|
|
105
|
+
yield current_object.plot_style.send(w)
|
|
106
|
+
elsif current_plot_style.edges.axes.key? name
|
|
107
|
+
yield current_plot_style.edges.axes[name].ticks
|
|
108
|
+
else
|
|
109
|
+
error "The label/axis specification #{w} was not understood, ignoring"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Runs a block safely with an axis specification.
|
|
114
|
+
#
|
|
115
|
+
# This function relies on the fact that it is being used
|
|
116
|
+
# from within a PlotMaker instance !!
|
|
117
|
+
def run_with_axis(name)
|
|
118
|
+
w = Utils::interpret_arg(name, AxisSpecification) {name}
|
|
119
|
+
if current_plot_style.edges.axes.key? w
|
|
120
|
+
yield current_plot_style.edges.axes[w]
|
|
121
|
+
else
|
|
122
|
+
error "The axis specification #{w} was not understood, ignoring"
|
|
123
|
+
error "Valid axis specifications are " +
|
|
124
|
+
current_plot_style.edges.axes.keys.join(', ')
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Prepare the option parser for axes options
|
|
129
|
+
def axes_options(parser)
|
|
130
|
+
|
|
131
|
+
parser.separator "\nVarious axes manipulations:"
|
|
132
|
+
parser.on("--xfact FACT",
|
|
133
|
+
"Multiply all x values by FACT") do |f|
|
|
134
|
+
@x_factor = safe_float(f)
|
|
135
|
+
end
|
|
136
|
+
parser.on("--yfact FACT",
|
|
137
|
+
"Multiply all y values by FACT") do |f|
|
|
138
|
+
@y_factor = safe_float(f)
|
|
139
|
+
end
|
|
140
|
+
parser.on("--xoffset OFFSET",
|
|
141
|
+
"Add OFFSET to all X values (after multiplication)") do |f|
|
|
142
|
+
@x_offset = safe_float(f)
|
|
143
|
+
end
|
|
144
|
+
parser.on("--yoffset OFFSET",
|
|
145
|
+
"Add OFFSET to all X values (after multiplication)") do |f|
|
|
146
|
+
@y_offset = safe_float(f)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
parser.on("--[no-]xlog",
|
|
150
|
+
"Uses logarithmic scale for X axis") do |v|
|
|
151
|
+
@x_log = v
|
|
152
|
+
add_elem_funcall(:xaxis_log_values=, v)
|
|
153
|
+
end
|
|
154
|
+
parser.on("--[no-]ylog",
|
|
155
|
+
"Uses logarithmic scale for Y axis") do |v|
|
|
156
|
+
@y_log = v
|
|
157
|
+
add_elem_funcall(:yaxis_log_values=, v)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
parser.on("--reset-transformations",
|
|
161
|
+
"Reset axes transformations") do |v|
|
|
162
|
+
@y_log = false
|
|
163
|
+
@x_log = false
|
|
164
|
+
# TODO: remove that when this is move to PlotStyle:
|
|
165
|
+
# it should be setup automatically by the Container
|
|
166
|
+
# at the beginning of a plot...
|
|
167
|
+
add_elem_funcall(:xaxis_log_values=, false)
|
|
168
|
+
add_elem_funcall(:yaxis_log_values=, false)
|
|
169
|
+
@x_factor = nil
|
|
170
|
+
@y_factor = nil
|
|
171
|
+
@x_offset = nil
|
|
172
|
+
@y_offset = nil
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
parser.on("--comma",
|
|
178
|
+
"Uses a comma for the decimal separator") do
|
|
179
|
+
@decimal_separator = ','
|
|
180
|
+
end
|
|
181
|
+
parser.on("--decimal SEP",
|
|
182
|
+
"Uses SEP for the decimal separator") do |s|
|
|
183
|
+
@decimal_separator = s
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
parser.separator "\nLabels and titles"
|
|
187
|
+
parser.on("-x","--[no-]xlabel [LABEL]",
|
|
188
|
+
"Label of the x axis") do |l|
|
|
189
|
+
current_object.plot_style.xlabel.label = l
|
|
190
|
+
end
|
|
191
|
+
parser.on("-y","--[no-]ylabel [LABEL]",
|
|
192
|
+
"Label of the y axis") do |l|
|
|
193
|
+
current_object.plot_style.ylabel.label = l
|
|
194
|
+
end
|
|
195
|
+
parser.on('-t', "--[no-]title [TITLE]",
|
|
196
|
+
"Sets the title of the plot") do |l|
|
|
197
|
+
current_object.plot_style.title.label = l
|
|
198
|
+
end
|
|
199
|
+
parser.on("--side WHAT ALIGN",
|
|
200
|
+
"Sets the side for the WHAT label, ",
|
|
201
|
+
"where WHAT = x(label),y(label) or t(itle)") do |w|
|
|
202
|
+
run_with_label(w) do |label|
|
|
203
|
+
a = Utils::interpret_arg(@args.shift, LabelSide) {false}
|
|
204
|
+
label.side = a if a
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
parser.on("--lcolor WHAT COLOR",
|
|
209
|
+
"Sets the color for the WHAT label, ",
|
|
210
|
+
"where WHAT = x(label),y(label) or t(itle)") do |w|
|
|
211
|
+
run_with_label(w) do |label|
|
|
212
|
+
a = CTioga.get_tioga_color(@args.shift)
|
|
213
|
+
label.color = a if a
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
parser.on("--position WHAT WHERE",
|
|
218
|
+
"Sets the position for the WHAT label, ",
|
|
219
|
+
"where WHAT = x(label),y(label) or t(itle)",
|
|
220
|
+
"and WHERE a number between 0 and 1, 0.5 = centered") do |w|
|
|
221
|
+
run_with_label(w) do |label|
|
|
222
|
+
a = safe_float(@args.shift)
|
|
223
|
+
label.position = a if a
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
parser.on("--angle WHAT ANGLE",
|
|
228
|
+
"Sets the angle for the WHAT label, ",
|
|
229
|
+
"where WHAT = x(label),y(label) or t(itle).") do |w|
|
|
230
|
+
run_with_label(w) do |label|
|
|
231
|
+
a = safe_float(@args.shift)
|
|
232
|
+
label.angle = a if a
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
parser.on("--scale WHAT SCALE",
|
|
237
|
+
"Sets the scale for the WHAT label, ",
|
|
238
|
+
"where WHAT = x(label),y(label) or t(itle).") do |w|
|
|
239
|
+
run_with_label(w) do |label|
|
|
240
|
+
a = safe_float(@args.shift)
|
|
241
|
+
label.scale = a if a
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
parser.on("--shift WHAT SHIFT",
|
|
246
|
+
"Sets the shift for the WHAT label, ",
|
|
247
|
+
"where WHAT = x(label),y(label) or t(itle).",
|
|
248
|
+
"The shift is the distance of the label from the plot") do |w|
|
|
249
|
+
run_with_label(w) do |label|
|
|
250
|
+
a = safe_float(@args.shift)
|
|
251
|
+
label.shift = a if a
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
parser.on("--align WHAT ALIGN",
|
|
256
|
+
"Sets the 'vertical' alignment for the WHAT label, ",
|
|
257
|
+
"where WHAT = x(label),y(label) or t(itle).") do |w|
|
|
258
|
+
run_with_label(w) do |label|
|
|
259
|
+
a = Utils::interpret_arg(@args.shift, Alignment) {false}
|
|
260
|
+
label.alignment = a if a
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
parser.on("--just WHAT JUST",
|
|
265
|
+
"Sets the 'horizontal' alignment for the WHAT label, ",
|
|
266
|
+
"where WHAT = x(label),y(label) or t(itle).") do |w|
|
|
267
|
+
run_with_label(w) do |label|
|
|
268
|
+
a = Utils::interpret_arg(@args.shift, Justification) {false}
|
|
269
|
+
label.justification = a if a
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
parser.separator "\nEdges and axes look:"
|
|
274
|
+
|
|
275
|
+
parser.on("--edges-visibility SPEC",
|
|
276
|
+
"Chooses which edges of the current graph will be",
|
|
277
|
+
"visible. Will not act on edges that are also axes.") do |w|
|
|
278
|
+
current_object.plot_style.edges.edge_visibility =
|
|
279
|
+
parse_edge_visibility(w)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
parser.on("--axis-pos AXIS POS",
|
|
283
|
+
"Sets the position of AXIS to POS") do |w|
|
|
284
|
+
run_with_axis(w) do |axis|
|
|
285
|
+
axis.loc = EdgesAndAxes.parse_axis_position(@args.shift)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
parser.on("--xaxis STYLE",
|
|
291
|
+
"Sets the style of the X axis") do |w|
|
|
292
|
+
current_object.plot_style.set_axis_style(:x, w)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
parser.on("--yaxis STYLE",
|
|
296
|
+
"Sets the style of the Y axis") do |w|
|
|
297
|
+
current_object.plot_style.set_axis_style(:y, w)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
parser.on("--edge-style WHICH STYLE",
|
|
301
|
+
"Sets the style of the X axis") do |w|
|
|
302
|
+
current_object.plot_style.set_axis_style(w.to_sym,
|
|
303
|
+
@args.shift)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
parser.on("--no-axes",
|
|
307
|
+
"No title, labels and axes") do
|
|
308
|
+
ps = current_plot_style
|
|
309
|
+
ps.set_axis_style(:x, "none")
|
|
310
|
+
ps.set_axis_style(:y, "none")
|
|
311
|
+
ps.xlabel.label = false
|
|
312
|
+
ps.ylabel.label = false
|
|
313
|
+
ps.title.label = false
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
parser.on("--lines-color AXIS COLOR",
|
|
317
|
+
"Sets the color for lines perpendicular to AXIS",
|
|
318
|
+
"'none' for no lines") do |w|
|
|
319
|
+
run_with_axis(w) do |axis|
|
|
320
|
+
color = @args.shift
|
|
321
|
+
if color =~ /no(ne)?/
|
|
322
|
+
color = false
|
|
323
|
+
else
|
|
324
|
+
color = CTioga.get_tioga_color(color)
|
|
325
|
+
end
|
|
326
|
+
axis.lines_color = color
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
parser.on("--new-axis NAME POSITION",
|
|
331
|
+
"Creates a new axis named NAME for this plot") do |name|
|
|
332
|
+
current_plot_style.edges.axes[name] =
|
|
333
|
+
EdgesAndAxes::Axis.new(@args.shift)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
parser.on("--axis-function AXIS TO FROM",
|
|
337
|
+
"Sets AXIS to use a non-linear mapping represented",
|
|
338
|
+
"by 2 mathematical functions (of x): TO (to convert from",
|
|
339
|
+
"real to axis) and FROM that does the opposite") do |w|
|
|
340
|
+
run_with_axis(w) do |axis|
|
|
341
|
+
axis.real_to_axis = eval "proc { |x| #{@args.shift} }"
|
|
342
|
+
axis.axis_to_real = eval "proc { |x| #{@args.shift} }"
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# A class that describes the various attributes of a label,
|
|
353
|
+
# either for the X or Y axis, or even the plot title.
|
|
354
|
+
class Label
|
|
355
|
+
|
|
356
|
+
include Log
|
|
357
|
+
|
|
358
|
+
# Include the figure constants, else it gets really painful...
|
|
359
|
+
include Tioga::FigureConstants
|
|
360
|
+
|
|
361
|
+
# The various attributes
|
|
362
|
+
Attributes = [:alignment, :angle, :color, :justification,
|
|
363
|
+
:position, :scale, :shift, :side, :visible]
|
|
364
|
+
|
|
365
|
+
attr_accessor *Attributes
|
|
366
|
+
|
|
367
|
+
# Which axis ?
|
|
368
|
+
attr_accessor :which
|
|
369
|
+
|
|
370
|
+
# The actual text to be displayed
|
|
371
|
+
attr_accessor :label
|
|
372
|
+
|
|
373
|
+
# Creates a label specification for the _which_ axis.
|
|
374
|
+
# _other_values_ is a hash that can be used to provide default
|
|
375
|
+
# values for the various Attributes.
|
|
376
|
+
#
|
|
377
|
+
# _which_ can be :xlabel, :ylabel or :title
|
|
378
|
+
def initialize(which, label = nil, other_values = {})
|
|
379
|
+
@which = which
|
|
380
|
+
@label = label
|
|
381
|
+
for key,val in other_values
|
|
382
|
+
self.send("#{key}=", val)
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# Shows the given label on the figure _t_
|
|
387
|
+
def show(t)
|
|
388
|
+
# We don't do anything unless we have a label to display !
|
|
389
|
+
return unless @label
|
|
390
|
+
# We wrap the call in a context so we don't pollute subpictures
|
|
391
|
+
t.context do
|
|
392
|
+
for attr in Attributes
|
|
393
|
+
if instance_variables.include?("@#{attr}")
|
|
394
|
+
val = instance_variable_get("@#{attr}")
|
|
395
|
+
t.send("#{which}_#{attr}=", val)
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
t.send("show_#{which}", @label)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Guesses the extension of the label on the side of the plot.
|
|
403
|
+
# The output is only garanteed when called with _t_ exactly in the
|
|
404
|
+
# context that will be used for the plot. An external _scale_ factor
|
|
405
|
+
# can be given in the case we have more information about the actual
|
|
406
|
+
# overall scale. Of course, just multiplying the result by it does
|
|
407
|
+
# give the same thing.
|
|
408
|
+
#
|
|
409
|
+
# The value returned is an array left,right, top, bottom
|
|
410
|
+
# containing only zeroes but for the place where the axis
|
|
411
|
+
# does extend.
|
|
412
|
+
def extension(t, scale = 1)
|
|
413
|
+
ret_val = [0,0,0,0]
|
|
414
|
+
if @label
|
|
415
|
+
ext = (@scale || tioga_value(t,:scale)) *
|
|
416
|
+
(1 + (@shift || tioga_value(t,:shift))) *
|
|
417
|
+
t.default_text_scale * t.default_font_size * scale
|
|
418
|
+
# Set to 0 if label doesn't extend.
|
|
419
|
+
ext = 0 if ext < 0
|
|
420
|
+
case (@side || tioga_value(t,:side))
|
|
421
|
+
when LEFT
|
|
422
|
+
ret_val[0] = ext
|
|
423
|
+
when BOTTOM
|
|
424
|
+
ret_val[3] = ext
|
|
425
|
+
when TOP
|
|
426
|
+
ret_val[2] = ext
|
|
427
|
+
when RIGHT
|
|
428
|
+
ret_val[1] = ext
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
debug "Axis #{self.inspect} extension #{ret_val.inspect}"
|
|
432
|
+
return ret_val
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
# Returns the value of the _what_ attribute
|
|
437
|
+
# corresponding to the value of which.
|
|
438
|
+
def tioga_value(t, what)
|
|
439
|
+
return t.send("#{@which}_#{what}")
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# A tick label is basically the same thing as a label
|
|
444
|
+
class TickLabels < Label
|
|
445
|
+
|
|
446
|
+
# Extension currently does not work really well.
|
|
447
|
+
def extension(t)
|
|
448
|
+
return [0,0,0,0]
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def show(t)
|
|
452
|
+
for attr in Attributes
|
|
453
|
+
if instance_variables.include?("@#{attr}")
|
|
454
|
+
val = instance_variable_get("@#{attr}")
|
|
455
|
+
t.send("#{which}_#{attr}=", val)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
undef :color=
|
|
461
|
+
def color=(*a)
|
|
462
|
+
error "There is no attribute color for tick labels"
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# A class that holds the information about how to plot the edges.
|
|
469
|
+
# And the size they potentially take.
|
|
470
|
+
#
|
|
471
|
+
# TODO: make sure the TickLabels and EdgesAndAxes
|
|
472
|
+
# talk to each other (for the #extension, essentially). They should be
|
|
473
|
+
# connected from the SubPlot class.
|
|
474
|
+
class EdgesAndAxes
|
|
475
|
+
|
|
476
|
+
# This function parses an axes position specification
|
|
477
|
+
# from the command-line.
|
|
478
|
+
def self.parse_axis_position(str)
|
|
479
|
+
case str
|
|
480
|
+
when /xori?g(in)?/i, /x=0/i
|
|
481
|
+
return Tioga::FigureConstants::AT_X_ORIGIN
|
|
482
|
+
when /yori?g(in)?/i, /y=0/i
|
|
483
|
+
return Tioga::FigureConstants::AT_Y_ORIGIN
|
|
484
|
+
when /l(eft)?/i
|
|
485
|
+
return Tioga::FigureConstants::LEFT
|
|
486
|
+
when /r(ight)?/i
|
|
487
|
+
return Tioga::FigureConstants::RIGHT
|
|
488
|
+
when /t(op)?/i
|
|
489
|
+
return Tioga::FigureConstants::TOP
|
|
490
|
+
when /b(ottom)?/i
|
|
491
|
+
return Tioga::FigureConstants::BOTTOM
|
|
492
|
+
else
|
|
493
|
+
raise "Unkown axis position: #{str}"
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# A class that represents an axis.
|
|
498
|
+
class Axis
|
|
499
|
+
|
|
500
|
+
include Log
|
|
501
|
+
|
|
502
|
+
# Include the figure constants, else it gets really painful...
|
|
503
|
+
include Tioga::FigureConstants
|
|
504
|
+
|
|
505
|
+
# For an easy use of Boundaries:
|
|
506
|
+
include Utils
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
# The various attributes
|
|
510
|
+
Attributes = [:line_width, :loc, :log_values, :type,
|
|
511
|
+
:ticks_inside, :ticks_outside, :visible]
|
|
512
|
+
|
|
513
|
+
attr_accessor *Attributes
|
|
514
|
+
|
|
515
|
+
# Which axis ?
|
|
516
|
+
attr_accessor :which
|
|
517
|
+
|
|
518
|
+
# The corresponding TickLabels
|
|
519
|
+
attr_accessor :ticks
|
|
520
|
+
|
|
521
|
+
# The color of perpendicular lines, or false if no lines are
|
|
522
|
+
# to be drawn
|
|
523
|
+
attr_accessor :lines_color
|
|
524
|
+
|
|
525
|
+
# The line style of perpendicular lines
|
|
526
|
+
attr_accessor :lines_style
|
|
527
|
+
|
|
528
|
+
# The line width of perpendicular lines
|
|
529
|
+
attr_accessor :lines_width
|
|
530
|
+
|
|
531
|
+
# The coordinate conversions blocks
|
|
532
|
+
attr_accessor :real_to_axis, :axis_to_real
|
|
533
|
+
|
|
534
|
+
# _which_ can be :x or :y
|
|
535
|
+
def initialize(which, ticks = nil)
|
|
536
|
+
@which = which
|
|
537
|
+
case which
|
|
538
|
+
when :x
|
|
539
|
+
@loc = BOTTOM
|
|
540
|
+
when :y
|
|
541
|
+
@loc = LEFT
|
|
542
|
+
when String
|
|
543
|
+
# We have a named position, in which case a little
|
|
544
|
+
# more tweaking needs to be done.
|
|
545
|
+
if which =~ /(\w+)([+-].*)?/
|
|
546
|
+
# OK, $1 is the position, and $2 is a position shift
|
|
547
|
+
loc = EdgesAndAxes::parse_axis_position($1)
|
|
548
|
+
if $2
|
|
549
|
+
dim = Dimension.new($2)
|
|
550
|
+
else
|
|
551
|
+
dim = false
|
|
552
|
+
end
|
|
553
|
+
if dim
|
|
554
|
+
@loc = [loc, dim]
|
|
555
|
+
else
|
|
556
|
+
@loc = loc
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
@ticks = ticks || TickLabels.new(:none)
|
|
561
|
+
|
|
562
|
+
# Background lines.
|
|
563
|
+
@lines_color = false
|
|
564
|
+
@lines_style = Styles::Dashes
|
|
565
|
+
@lines_width = false
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# Setup attributes for the axis.
|
|
569
|
+
def setup(t, container)
|
|
570
|
+
# We readjust the position of the axis if the X or Y origin is
|
|
571
|
+
# out of bounds
|
|
572
|
+
case @loc
|
|
573
|
+
when AT_X_ORIGIN
|
|
574
|
+
bounds = Boundaries.new(container.internal_get_boundaries)
|
|
575
|
+
case bounds.where_x?(0)
|
|
576
|
+
when :left
|
|
577
|
+
@loc = LEFT
|
|
578
|
+
when :right
|
|
579
|
+
@loc = RIGHT
|
|
580
|
+
end
|
|
581
|
+
when AT_Y_ORIGIN
|
|
582
|
+
bounds = Boundaries.new(container.internal_get_boundaries)
|
|
583
|
+
case bounds.where_y?(0)
|
|
584
|
+
when :top
|
|
585
|
+
@loc = TOP
|
|
586
|
+
when :bottom
|
|
587
|
+
@loc = BOTTOM
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
for attr in Attributes
|
|
591
|
+
if instance_variables.include?("@#{attr}")
|
|
592
|
+
# The visible attribute cannot be explicitly set to true.
|
|
593
|
+
next if attr == :visible && @visible
|
|
594
|
+
val = instance_variable_get("@#{attr}")
|
|
595
|
+
t.send("#{which}axis_#{attr}=", val)
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
# Returns the boundaries of the axis corresponding to the
|
|
601
|
+
# location of the axis
|
|
602
|
+
def axis_coordinate_boundaries(t, container)
|
|
603
|
+
loc = (@loc.is_a?(Array) ? @loc.first : @loc)
|
|
604
|
+
case loc
|
|
605
|
+
when TOP, BOTTOM, AT_Y_ORIGIN
|
|
606
|
+
return [t.bounds_left, t.bounds_right]
|
|
607
|
+
when LEFT, RIGHT, AT_X_ORIGIN
|
|
608
|
+
return [t.bounds_bottom, t.bounds_top]
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Prepares the major ticks/tick labels, in the case of a
|
|
613
|
+
# non-linear axis. These are returned as a
|
|
614
|
+
# [ [tick position], [tick values]]
|
|
615
|
+
#
|
|
616
|
+
# Returns nil if no tick changes are applicable
|
|
617
|
+
def prepare_major_ticks(t, container)
|
|
618
|
+
if @real_to_axis
|
|
619
|
+
# First, the naive version:
|
|
620
|
+
|
|
621
|
+
x1, x2 = axis_coordinate_boundaries(t, container)
|
|
622
|
+
|
|
623
|
+
y1 = @real_to_axis.call(x1)
|
|
624
|
+
y2 = @real_to_axis.call(x2)
|
|
625
|
+
|
|
626
|
+
# Now the y1, y2 segment is our mapping.
|
|
627
|
+
# We make sure y1 < y2:
|
|
628
|
+
if y1 > y2
|
|
629
|
+
y1, y2 = y2, y1
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
# We get information about what the axis would have been
|
|
633
|
+
loc = (@loc.is_a?(Array) ? @loc.first : @loc)
|
|
634
|
+
info = t.axis_information(loc)
|
|
635
|
+
|
|
636
|
+
# First: we divide relatively naively
|
|
637
|
+
number = info['major'].size
|
|
638
|
+
|
|
639
|
+
values = Utils::partition_nonlinear(@real_to_axis, @axis_to_real,
|
|
640
|
+
x1, x2, number + 1)
|
|
641
|
+
|
|
642
|
+
# Now, we have the positions (or nearly so), we need to
|
|
643
|
+
# convert that back to original
|
|
644
|
+
|
|
645
|
+
positions = values.map(&@axis_to_real)
|
|
646
|
+
|
|
647
|
+
return [positions, values]
|
|
648
|
+
|
|
649
|
+
else
|
|
650
|
+
return nil
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
# Gets the argument to give to FigureMaker#show_axis and
|
|
655
|
+
# FigureMaker#axis_information.
|
|
656
|
+
def axis_argument(t, container)
|
|
657
|
+
if @which == :x or @which == :y
|
|
658
|
+
return @loc
|
|
659
|
+
end
|
|
660
|
+
spec = {}
|
|
661
|
+
if @loc.is_a? Array
|
|
662
|
+
spec['location'] = @loc.first
|
|
663
|
+
else
|
|
664
|
+
spec['location'] = @loc
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
spec['type'] = @type if @type
|
|
668
|
+
if val = prepare_major_ticks(t, container)
|
|
669
|
+
spec['major_ticks'] = val[0]
|
|
670
|
+
spec['labels'] = val[1].map { |v| v.to_s }
|
|
671
|
+
end
|
|
672
|
+
return spec
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# Draws the background lines associated with this axis, if
|
|
676
|
+
# applicable.
|
|
677
|
+
#
|
|
678
|
+
# This function requires Tioga SVN -r 482
|
|
679
|
+
def draw_lines(t, container)
|
|
680
|
+
|
|
681
|
+
return unless @lines_color
|
|
682
|
+
# First, getting major ticks location from tioga
|
|
683
|
+
info = t.axis_information(axis_argument(t, container))
|
|
684
|
+
|
|
685
|
+
if info['vertical']
|
|
686
|
+
x0 = t.bounds_left
|
|
687
|
+
x1 = t.bounds_right
|
|
688
|
+
else
|
|
689
|
+
y0 = t.bounds_bottom
|
|
690
|
+
y1 = t.bounds_top
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
t.context do
|
|
694
|
+
t.stroke_color = @lines_color
|
|
695
|
+
t.line_width = @lines_width || info['major_tick_width']
|
|
696
|
+
t.line_type = @lines_style
|
|
697
|
+
for val in info['major']
|
|
698
|
+
if info['vertical']
|
|
699
|
+
t.stroke_line(x0, val, x1, val)
|
|
700
|
+
else
|
|
701
|
+
t.stroke_line(val, y0, val, y1)
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def label_shift(t)
|
|
708
|
+
shift = nil
|
|
709
|
+
if @ticks
|
|
710
|
+
shift = @ticks.shift
|
|
711
|
+
end
|
|
712
|
+
shift = t.send("#{@which}axis_numeric_label_shift") unless shift
|
|
713
|
+
return shift
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
def label_scale(t)
|
|
717
|
+
scale = nil
|
|
718
|
+
if @ticks
|
|
719
|
+
scale = @ticks.scale
|
|
720
|
+
end
|
|
721
|
+
scale = t.send("#{@which}axis_numeric_label_scale") unless scale
|
|
722
|
+
return scale
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
def extension(t, scale = 1)
|
|
726
|
+
ext = 0
|
|
727
|
+
f = [0,0,0,0]
|
|
728
|
+
# First, we estimate the size:
|
|
729
|
+
case @type
|
|
730
|
+
when AXIS_WITH_MAJOR_TICKS_AND_NUMERIC_LABELS,
|
|
731
|
+
AXIS_WITH_TICKS_AND_NUMERIC_LABELS
|
|
732
|
+
ext = (1 + (label_shift(t))) * label_scale(t) *
|
|
733
|
+
t.default_text_scale * t.default_font_size
|
|
734
|
+
else
|
|
735
|
+
ext = 0
|
|
736
|
+
end
|
|
737
|
+
loc_idx = Utils::location_index(@loc)
|
|
738
|
+
if loc_idx # That is, left,right, top, bottom
|
|
739
|
+
f[loc_idx] = ext
|
|
740
|
+
end
|
|
741
|
+
return f
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# Manually shows the given element.
|
|
745
|
+
def show(t, container)
|
|
746
|
+
t.show_axis(axis_argument(t, container))
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
include Tioga::FigureConstants
|
|
752
|
+
|
|
753
|
+
include Log
|
|
754
|
+
|
|
755
|
+
Edges = [:left, :right, :top, :bottom]
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
# A hash containing the edge type for each edge.
|
|
759
|
+
attr_accessor :edge_specs
|
|
760
|
+
|
|
761
|
+
# A hash containing the default visibility for
|
|
762
|
+
# the edges.
|
|
763
|
+
attr_reader :edge_visibility
|
|
764
|
+
|
|
765
|
+
# The default values for the edges
|
|
766
|
+
Defaults = {
|
|
767
|
+
:left => AXIS_WITH_TICKS_AND_NUMERIC_LABELS,
|
|
768
|
+
:right => AXIS_WITH_TICKS_ONLY,
|
|
769
|
+
:top => AXIS_WITH_TICKS_ONLY,
|
|
770
|
+
:bottom => AXIS_WITH_TICKS_AND_NUMERIC_LABELS
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
# The main axes
|
|
774
|
+
attr_accessor :xaxis, :yaxis
|
|
775
|
+
|
|
776
|
+
# Named axes:
|
|
777
|
+
attr_accessor :axes
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def initialize(xticks = nil, yticks = nil)
|
|
781
|
+
@edge_specs = Defaults.dup
|
|
782
|
+
@xaxis = Axis.new(:x, xticks)
|
|
783
|
+
@xaxis.type = AXIS_WITH_TICKS_AND_NUMERIC_LABELS
|
|
784
|
+
@yaxis = Axis.new(:y, yticks)
|
|
785
|
+
@yaxis.type = AXIS_WITH_TICKS_AND_NUMERIC_LABELS
|
|
786
|
+
|
|
787
|
+
# A handy shortcut
|
|
788
|
+
@axis = {:x => @xaxis, :y => @yaxis}
|
|
789
|
+
|
|
790
|
+
@edge_visibility = {}
|
|
791
|
+
|
|
792
|
+
@axes = {
|
|
793
|
+
'xaxis' => @xaxis,
|
|
794
|
+
'yaxis' => @yaxis,
|
|
795
|
+
}
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
def set_edges(value, *which)
|
|
799
|
+
which = Edges if which.empty?
|
|
800
|
+
for edge in which
|
|
801
|
+
@edge_specs[edge] = value
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
# Disable the given edges, or all if no arguments
|
|
806
|
+
def disable(*which)
|
|
807
|
+
set_edges(AXIS_HIDDEN, *which)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# Show only one line for the given edges.
|
|
811
|
+
def line_only(*which)
|
|
812
|
+
set_edges(AXIS_LINE_ONLY, *which)
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
# Returns either #xaxis or #yaxis depending on the value of
|
|
816
|
+
# which.
|
|
817
|
+
def axis(which)
|
|
818
|
+
return @axis[which]
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
# Sets up the edges for the given plot. Should probably be called
|
|
823
|
+
# just before the exit of the show_plot_with_legend block argument
|
|
824
|
+
def setup(t, container)
|
|
825
|
+
# Send axes informations.
|
|
826
|
+
@xaxis.setup(t, container)
|
|
827
|
+
@yaxis.setup(t, container)
|
|
828
|
+
for edge in Edges
|
|
829
|
+
t.send("#{edge}_edge_type=", @edge_specs[edge]) if
|
|
830
|
+
@edge_specs.key? edge
|
|
831
|
+
# Can only disable edges
|
|
832
|
+
if (@edge_visibility.key?(edge) && !@edge_visibility[edge])
|
|
833
|
+
t.send("#{edge}_edge_visible=", false)
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
# Sets the style for both the axis and the edges of the given
|
|
841
|
+
# side.
|
|
842
|
+
def set_axis_and_edges_style(axis, style)
|
|
843
|
+
case axis
|
|
844
|
+
when :x
|
|
845
|
+
@xaxis.type = style
|
|
846
|
+
set_edges(style, :top,:bottom)
|
|
847
|
+
when :y
|
|
848
|
+
@yaxis.type = style
|
|
849
|
+
set_edges(style, :left,:right)
|
|
850
|
+
else # for :left, :right, :top, :bottom
|
|
851
|
+
set_edges(style, axis)
|
|
852
|
+
end
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
# This does not take ticks into account yet. No need !
|
|
856
|
+
def extension(t, scale = 1)
|
|
857
|
+
f = [0,0,0,0]
|
|
858
|
+
Dimension.update_extensions(f, @xaxis.extension(t))
|
|
859
|
+
Dimension.update_extensions(f, @yaxis.extension(t))
|
|
860
|
+
debug "Edges and axis #{self.inspect} extension #{f.inspect}"
|
|
861
|
+
return f
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
# Choosing which edges are visible
|
|
865
|
+
def edge_visibility=(ar)
|
|
866
|
+
if ar.is_a?(Array) and ar.size == 4
|
|
867
|
+
4.times do |i|
|
|
868
|
+
@edge_visibility[Edges[i]] = ar[i]
|
|
869
|
+
end
|
|
870
|
+
else
|
|
871
|
+
if ar
|
|
872
|
+
@edge_visibility = h.dup
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
# Sets the edge visibility for the given sides
|
|
878
|
+
def set_edges_visibility(which, what)
|
|
879
|
+
case which
|
|
880
|
+
when :y
|
|
881
|
+
@edge_visibility[:left] = @edge_visibility[:right] = what
|
|
882
|
+
when :x
|
|
883
|
+
@edge_visibility[:top] = @edge_visibility[:bottom] = what
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
# Called from PlotStyle#show_background to display
|
|
888
|
+
# axis lines.
|
|
889
|
+
def show_axis_lines(t, container)
|
|
890
|
+
for axis in @axes.keys.sort
|
|
891
|
+
@axes[axis].draw_lines(t, container)
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
# Draw all named non-standard axes
|
|
896
|
+
def show_additional_axes(t, container)
|
|
897
|
+
for name in @axes.keys - %w(xaxis yaxis)
|
|
898
|
+
@axes[name].show(t, container)
|
|
899
|
+
end
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
end
|
|
903
|
+
end
|
|
904
|
+
|