ctioga 1.11.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|