ctioga2 0.8 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Changelog +15 -0
- data/bin/ct2-make-movie +253 -0
- data/lib/ctioga2/commands/doc/doc.rb +4 -0
- data/lib/ctioga2/commands/doc/documentation-commands.rb +23 -8
- data/lib/ctioga2/commands/doc/html.rb +111 -60
- data/lib/ctioga2/commands/doc/markup.rb +2 -2
- data/lib/ctioga2/commands/function.rb +90 -0
- data/lib/ctioga2/commands/general-commands.rb +12 -0
- data/lib/ctioga2/commands/general-functions.rb +106 -0
- data/lib/ctioga2/commands/general-types.rb +8 -3
- data/lib/ctioga2/commands/interpreter.rb +11 -0
- data/lib/ctioga2/commands/parsers/file.rb +7 -5
- data/lib/ctioga2/commands/strings.rb +25 -4
- data/lib/ctioga2/commands/variables.rb +5 -1
- data/lib/ctioga2/data/point.rb +12 -4
- data/lib/ctioga2/graphics/elements/curve2d.rb +13 -2
- data/lib/ctioga2/graphics/elements/subplot.rb +13 -1
- data/lib/ctioga2/graphics/styles/axes.rb +12 -2
- data/lib/ctioga2/graphics/styles/map-axes.rb +1 -1
- data/lib/ctioga2/graphics/styles/plot.rb +48 -6
- data/lib/ctioga2/graphics/styles/texts.rb +9 -1
- data/lib/ctioga2/graphics/styles/ticks.rb +34 -0
- data/lib/ctioga2/graphics/subplot-commands.rb +35 -0
- data/lib/ctioga2/graphics/types.rb +30 -4
- data/lib/ctioga2/graphics/types/boxes.rb +13 -0
- data/lib/ctioga2/graphics/types/dimensions.rb +7 -0
- data/lib/ctioga2/plotmaker.rb +2 -0
- data/lib/ctioga2/postprocess.rb +15 -2
- data/lib/ctioga2/utils.rb +120 -0
- data/lib/ctioga2/version.rb +2 -2
- metadata +5 -2
@@ -108,7 +108,7 @@ module CTioga2
|
|
108
108
|
attr_accessor :target
|
109
109
|
|
110
110
|
# _target_ is the name of the target, which can be of _type_
|
111
|
-
# 'group', 'command', 'backend', 'type' and 'url'
|
111
|
+
# 'group', 'command', 'backend', 'type', 'function' and 'url'
|
112
112
|
def initialize(doc, target, type)
|
113
113
|
super(doc)
|
114
114
|
if type =~ /url/
|
@@ -279,7 +279,7 @@ module CTioga2
|
|
279
279
|
protected
|
280
280
|
|
281
281
|
# A few constants to help writing out the paragraph markup
|
282
|
-
LinkRE = /\{(group|type|command|backend|url):\s*([^}]+?)\s*\}/
|
282
|
+
LinkRE = /\{(group|type|command|backend|url|function):\s*([^}]+?)\s*\}/
|
283
283
|
|
284
284
|
MarkOnceRE = /@([^@]+)@/
|
285
285
|
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# function.rb: makefile-like functions
|
2
|
+
# copyright (c) 2014 by Vincent Fourmond
|
3
|
+
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details (in the COPYING file).
|
13
|
+
|
14
|
+
require 'ctioga2/utils'
|
15
|
+
require 'ctioga2/commands/strings'
|
16
|
+
|
17
|
+
module CTioga2
|
18
|
+
|
19
|
+
module Commands
|
20
|
+
|
21
|
+
# A Function is a makefile-like "macro" or "function" that takes
|
22
|
+
# one or more arguments (no argumentless functions for now).
|
23
|
+
#
|
24
|
+
# This class provides both the definition and handling of a
|
25
|
+
# function and the global registry of functions.
|
26
|
+
class Function
|
27
|
+
|
28
|
+
# The underlying proc object. The first argument to the code is
|
29
|
+
# *always* the plotmaker object.
|
30
|
+
attr_accessor :code
|
31
|
+
|
32
|
+
# The name of the function. Probably better lowercase ?
|
33
|
+
attr_accessor :name
|
34
|
+
|
35
|
+
# A short description
|
36
|
+
attr_accessor :short_description
|
37
|
+
|
38
|
+
# Long description, ie a help text like the rest
|
39
|
+
attr_accessor :description
|
40
|
+
|
41
|
+
# Registers a function.
|
42
|
+
#
|
43
|
+
# @todo Have self-documenting capacities !
|
44
|
+
def initialize(name, short_desc, &blk)
|
45
|
+
@code = blk
|
46
|
+
@name = name
|
47
|
+
@short_description = short_desc
|
48
|
+
|
49
|
+
Function.register(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def describe(txt)
|
53
|
+
@description = txt
|
54
|
+
end
|
55
|
+
|
56
|
+
# Expands the function, and returns the corresponding string.
|
57
|
+
def expand(string, interpreter)
|
58
|
+
if @code.arity == 2
|
59
|
+
args = [string.expand_to_string(interpreter)]
|
60
|
+
else
|
61
|
+
args = string.expand_and_split(/\s+/, interpreter)
|
62
|
+
end
|
63
|
+
if (@code.arity > 0) and (args.size != (@code.arity - 1))
|
64
|
+
raise "Function #{@name} expects #{@code.arity} arguments, but was given #{args.size}"
|
65
|
+
end
|
66
|
+
return @code.call(interpreter.plotmaker_target, *args).to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
# Registers the given function definition
|
70
|
+
def self.register(func)
|
71
|
+
@functions ||= {}
|
72
|
+
@functions[func.name] = func
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the named function definition, or nil if there isn't
|
76
|
+
# such.
|
77
|
+
def self.named_function(name)
|
78
|
+
return @functions[name]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the functions hash
|
82
|
+
def self.functions
|
83
|
+
return @functions
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
@@ -85,6 +85,18 @@ EOH
|
|
85
85
|
|
86
86
|
EvalCommand.describe("Runs the given commands", <<EOH, GeneralGroup)
|
87
87
|
Runs the given strings as commands, as if given from a command file.
|
88
|
+
EOH
|
89
|
+
|
90
|
+
# Evaluate a series of commands.
|
91
|
+
SetCommand = Cmd.new("set", nil, "--set",
|
92
|
+
[ CmdArg.new('text'),
|
93
|
+
CmdArg.new('text') ]) do |plotmaker, variable, value|
|
94
|
+
plotmaker.interpreter.variables.define_variable(variable, value)
|
95
|
+
end
|
96
|
+
|
97
|
+
SetCommand.describe("Sets the value of a variable", <<EOH, GeneralGroup)
|
98
|
+
Sets the value of the variable (first argument) to the given second argument.
|
99
|
+
No parsing is done.
|
88
100
|
EOH
|
89
101
|
|
90
102
|
# Increases verbosity
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# general-functions.rb: useful function definitions
|
2
|
+
# copyright (c) 2014 by Vincent Fourmond
|
3
|
+
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details (in the COPYING file).
|
13
|
+
|
14
|
+
require 'ctioga2/utils'
|
15
|
+
require 'ctioga2/data/point'
|
16
|
+
|
17
|
+
module CTioga2
|
18
|
+
|
19
|
+
module Commands
|
20
|
+
|
21
|
+
FuncEval = Function.new("eval", "Evaluate Ruby code") do |pm, code|
|
22
|
+
eval(code)
|
23
|
+
end
|
24
|
+
|
25
|
+
FuncEval.describe <<EOD
|
26
|
+
Evaluate its argument as Ruby code
|
27
|
+
|
28
|
+
# a := $(eval 2 + 2)
|
29
|
+
# # a is now 4
|
30
|
+
|
31
|
+
Keep in mind that variables in @ctioga2@ work by plain text replacement.
|
32
|
+
They have no type. In particular, while this will work:
|
33
|
+
|
34
|
+
# a := 3
|
35
|
+
# b := $(eval $(a) * 3)
|
36
|
+
# # b is now 9
|
37
|
+
|
38
|
+
Doing the same kind of things with text will be somewhat not satisfying:
|
39
|
+
|
40
|
+
# a := "two words"
|
41
|
+
# b := $(eval $(a).split(/ /).first)
|
42
|
+
|
43
|
+
Running this will give the following syntax error:
|
44
|
+
|
45
|
+
@ [FATAL] (eval):1: syntax error, unexpected $end, expecting ')'
|
46
|
+
@ two words.split(/ /
|
47
|
+
@ ^ while processing line 2 in file 'c.ct2'
|
48
|
+
|
49
|
+
Doing it right would require the use of a decent amount of quotes.
|
50
|
+
EOD
|
51
|
+
|
52
|
+
FuncPoint = Function.new("point", "Get dataset point information") do |pm, what, spec, *rest|
|
53
|
+
dataset = if rest.first
|
54
|
+
pm.data_stack.stored_dataset(rest.first)
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
point = Data::DataPoint::from_text(pm, spec, dataset)
|
60
|
+
|
61
|
+
case what
|
62
|
+
when "x", "X"
|
63
|
+
point.x.to_s
|
64
|
+
when "y", "Y"
|
65
|
+
point.y.to_s
|
66
|
+
when "xy", "XY"
|
67
|
+
"%g,%g" % point.point
|
68
|
+
else
|
69
|
+
# The \ are not strictly speaking necessary, but they make
|
70
|
+
# ruby-mode happier
|
71
|
+
raise "\'#{what}\' unkown: which coordinate(s) of the point do you want ?"
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
FuncPoint.describe <<EOD
|
77
|
+
|
78
|
+
Returns the requested information about the given point in a
|
79
|
+
dataset. Run this way:
|
80
|
+
|
81
|
+
# $(point x @234)
|
82
|
+
|
83
|
+
The first argument, here @x@ tells what we want to know about the
|
84
|
+
given point: its @x@ value (passing @x@), its @y@ value (passing @y@),
|
85
|
+
both its @x@ and @y@ ready to be used as coordinates for drawing
|
86
|
+
commands using @xy@. For instance, to draw a circle marker right in
|
87
|
+
the middle of the last dataset plotted, just run
|
88
|
+
|
89
|
+
# draw-marker $(point xy 0.5) Circle
|
90
|
+
|
91
|
+
The second argument specifies a dataset point, just like for
|
92
|
+
{type: data-point}.
|
93
|
+
|
94
|
+
An optional third argument specifies the dataset from which one wants
|
95
|
+
the point information. Note that the dataset can also be specified
|
96
|
+
within the second argument, but it may be more readable to do it as an
|
97
|
+
optional third. It is parsed as {type: stored-dataset}
|
98
|
+
|
99
|
+
|
100
|
+
EOD
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -102,16 +102,21 @@ EOD
|
|
102
102
|
# afterwards, actually, since an access to a plotmaker object is
|
103
103
|
# necessary.
|
104
104
|
DataPointType = CmdType.new('data-point', :data_point, <<EOD)
|
105
|
-
A point from
|
105
|
+
A point from an already-loaded Dataset. You have two ways to choose the point:
|
106
106
|
|
107
|
-
|
107
|
+
* @@13@ takes the 13th point in the last dataset;
|
108
|
+
* @0.2@ takes the point the closest to 20% of the dataset.
|
109
|
+
|
110
|
+
If you need another dataset than the last one, give its number or
|
111
|
+
named within brackets: @{-2}0.2@ is the point closest to the 20% of
|
112
|
+
the one-before-last dataset.
|
108
113
|
EOD
|
109
114
|
|
110
115
|
# A LaTeX font
|
111
116
|
LaTeXFontType = CmdType.new('latex-font', :latex_font, <<EOD)
|
112
117
|
A LaTeX font.
|
113
118
|
|
114
|
-
|
119
|
+
@todo document !
|
115
120
|
EOD
|
116
121
|
|
117
122
|
# A color map
|
@@ -16,6 +16,8 @@ require 'ctioga2/commands/commands'
|
|
16
16
|
require 'ctioga2/commands/context'
|
17
17
|
require 'ctioga2/commands/variables'
|
18
18
|
require 'ctioga2/commands/strings'
|
19
|
+
require 'ctioga2/commands/function'
|
20
|
+
require 'ctioga2/commands/general-functions'
|
19
21
|
require 'ctioga2/commands/parsers/command-line'
|
20
22
|
require 'ctioga2/commands/doc/doc'
|
21
23
|
|
@@ -192,6 +194,15 @@ module CTioga2
|
|
192
194
|
@context = ParsingContext.new
|
193
195
|
end
|
194
196
|
|
197
|
+
# Calls the given function and returns the result
|
198
|
+
def call_function(name, args)
|
199
|
+
func = Function.named_function(name)
|
200
|
+
if ! func
|
201
|
+
raise "Unkown function #{name}"
|
202
|
+
end
|
203
|
+
return func.expand(args, self)
|
204
|
+
end
|
205
|
+
|
195
206
|
|
196
207
|
# Parses and run the given command-line, sending the commands to
|
197
208
|
# the #plotmaker_target.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# file.rb: new style file parser for ctioga2
|
2
|
-
# copyright (c) 2013 by Vincent Fourmond
|
2
|
+
# copyright (c) 2013, 2014 by Vincent Fourmond
|
3
3
|
|
4
4
|
# This program is free software; you can redistribute it and/or modify
|
5
5
|
# it under the terms of the GNU General Public License as published by
|
@@ -115,12 +115,14 @@ module CTioga2
|
|
115
115
|
for l in parsed_lines
|
116
116
|
idx += 1
|
117
117
|
interpreter.context.parsing_file(nil, io, lines_indices[idx])
|
118
|
-
if l =~ /^\s*([a-zA-Z0-9_-]+)\s*(=|:=)\s*(.*)/
|
118
|
+
if l =~ /^\s*([a-zA-Z0-9_-]+)\s*(\??)(=|:=)\s*(.*)/
|
119
119
|
symbol = $1
|
120
|
-
value = InterpreterString.parse_until_unquoted(StringIO.new($
|
121
|
-
|
120
|
+
value = InterpreterString.parse_until_unquoted(StringIO.new($4),"\n", false)
|
121
|
+
override = !($2 == '?')
|
122
|
+
rec = (($3 == "=") ? nil : interpreter)
|
123
|
+
|
122
124
|
|
123
|
-
interpreter.variables.define_variable(symbol, value, rec)
|
125
|
+
interpreter.variables.define_variable(symbol, value, rec, override)
|
124
126
|
elsif l =~ /^\s*#/
|
125
127
|
# comment...
|
126
128
|
else
|
@@ -137,6 +137,17 @@ module CTioga2
|
|
137
137
|
if ch == ")"
|
138
138
|
push_current_element
|
139
139
|
@state = (@state == :var ? :top : :double)
|
140
|
+
elsif ch =~ /\s/
|
141
|
+
# We don't have a variable, but a function...
|
142
|
+
@accessory = InterpreterString.parse_until_unquoted(@io, ")", true)
|
143
|
+
ch = @io.getc # Slurp the closing )
|
144
|
+
ns = (@state == :var ? :top : :double)
|
145
|
+
@state = (:var ? :funcall : :dq_funcall)
|
146
|
+
push_current_element
|
147
|
+
@state = ns
|
148
|
+
## @todo Optional: instead of having a space, use a ,
|
149
|
+
## or . or # to signify different separators ? (but
|
150
|
+
## quoting makes this more-or-less unnecessary, hey ?)
|
140
151
|
else
|
141
152
|
@current_string += ch
|
142
153
|
end
|
@@ -186,6 +197,10 @@ module CTioga2
|
|
186
197
|
@parsed << [:unquoted_variable, @current_string]
|
187
198
|
when :dq_var
|
188
199
|
@parsed << [:quoted_variable, @current_string]
|
200
|
+
when :funcall
|
201
|
+
@parsed << [:unquoted_funcall, @current_string, @accessory]
|
202
|
+
when :dq_funcall
|
203
|
+
@parsed << [:quoted_funcall, @current_string, @accessory]
|
189
204
|
when :dollar
|
190
205
|
@parsed << [:unquoted, @current_string + '$']
|
191
206
|
when :dq_dollar
|
@@ -272,12 +287,12 @@ module CTioga2
|
|
272
287
|
|
273
288
|
protected
|
274
289
|
|
275
|
-
# Returns a new InterpreterString object with all variables
|
276
|
-
# expanded. _interpreter_ is the Interpreter in
|
277
|
-
# expansion takes place.
|
290
|
+
# Returns a new InterpreterString object with all variables and
|
291
|
+
# function calls expanded. _interpreter_ is the Interpreter in
|
292
|
+
# which the expansion takes place.
|
278
293
|
def expand_all_variables(interpreter)
|
279
294
|
c = []
|
280
|
-
for type, value in @contents
|
295
|
+
for type, value, args in @contents
|
281
296
|
case type
|
282
297
|
when :quoted_variable
|
283
298
|
c << [:quoted, interpreter.variables.
|
@@ -285,6 +300,12 @@ module CTioga2
|
|
285
300
|
when :unquoted_variable
|
286
301
|
c << [:unquoted, interpreter.variables.
|
287
302
|
expand_variable(value, interpreter)]
|
303
|
+
when :quoted_funcall
|
304
|
+
c << [:quoted, interpreter.
|
305
|
+
call_function(value, args)]
|
306
|
+
when :unquoted_funcall
|
307
|
+
c << [:unquoted, interpreter.
|
308
|
+
call_function(value, args)]
|
288
309
|
else
|
289
310
|
c << [type, value]
|
290
311
|
end
|
@@ -59,7 +59,11 @@ module CTioga2
|
|
59
59
|
# expanded at the time of the definition, (immediate variable),
|
60
60
|
# whereas if it stays _nil_, the variable is defined as a
|
61
61
|
# recursively defined variable.
|
62
|
-
def define_variable(name, value, interpreter = nil)
|
62
|
+
def define_variable(name, value, interpreter = nil, override = true)
|
63
|
+
if (!override) && @variables.key?(name)
|
64
|
+
# Not redefining an already defined variable.
|
65
|
+
return
|
66
|
+
end
|
63
67
|
if value.respond_to? :expand_to_string
|
64
68
|
if interpreter
|
65
69
|
value = value.expand_to_string(interpreter)
|
data/lib/ctioga2/data/point.rb
CHANGED
@@ -40,7 +40,7 @@ module CTioga2
|
|
40
40
|
# accesses the data stack.
|
41
41
|
#
|
42
42
|
# Specification: ({_dataset_})?(_relative_|@_index_)
|
43
|
-
def self.from_text(plotmaker, text)
|
43
|
+
def self.from_text(plotmaker, text, dataset = nil)
|
44
44
|
if text =~ /^(?:\s*\{([^}]+)\})?\s*(?:([.\d]+)|@(\d+))\s*$/
|
45
45
|
which = $1 || -1
|
46
46
|
if $2
|
@@ -48,7 +48,7 @@ module CTioga2
|
|
48
48
|
else
|
49
49
|
idx = $3.to_i
|
50
50
|
end
|
51
|
-
dataset
|
51
|
+
dataset ||= plotmaker.data_stack.stored_dataset(which)
|
52
52
|
|
53
53
|
if ! dataset
|
54
54
|
raise "Invalid or empty dataset: #{which}"
|
@@ -62,9 +62,17 @@ module CTioga2
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
|
65
|
+
def x
|
66
|
+
return @dataset.x.values[@index]
|
67
|
+
end
|
68
|
+
|
69
|
+
def y
|
70
|
+
return @dataset.y.values[@index]
|
71
|
+
end
|
67
72
|
|
73
|
+
def point
|
74
|
+
return [self.x, self.y]
|
75
|
+
end
|
68
76
|
|
69
77
|
# Returns the averaged X value around the datapoint
|
70
78
|
def x_val(navg = 3)
|
@@ -74,6 +74,17 @@ module CTioga2
|
|
74
74
|
return Types::Boundaries.bounds(@function.x, @function.y)
|
75
75
|
end
|
76
76
|
|
77
|
+
def can_clip?
|
78
|
+
if @curve_style.clipped or
|
79
|
+
( @curve_style.fill && @curve_style.fill.fill?) or
|
80
|
+
( parent.is_a?(Region))
|
81
|
+
return false
|
82
|
+
else
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
77
88
|
# Creates a path for the given curve. This should be defined
|
78
89
|
# with care, as it will be used for instance for region
|
79
90
|
# coloring and stroking. The function should only append to
|
@@ -86,7 +97,7 @@ module CTioga2
|
|
86
97
|
case @curve_style.path_style
|
87
98
|
when /^splines/
|
88
99
|
for f in func.split_monotonic
|
89
|
-
new_f = if
|
100
|
+
new_f = if can_clip?
|
90
101
|
f.bound_values(*bnds.extrema)
|
91
102
|
else
|
92
103
|
f.dup
|
@@ -103,7 +114,7 @@ module CTioga2
|
|
103
114
|
|
104
115
|
# Hmmmm. This may get the wrong thing if you happen to
|
105
116
|
# draw something completely outside.
|
106
|
-
if
|
117
|
+
if can_clip?
|
107
118
|
f = func.bound_values(*bnds.extrema)
|
108
119
|
else
|
109
120
|
f = func
|