ctioga2 0.10.1 → 0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Changelog +18 -0
- data/bin/ctioga2 +28 -0
- data/lib/ctioga2/commands/commands.rb +1 -0
- data/lib/ctioga2/commands/doc/documentation-commands.rb +13 -0
- data/lib/ctioga2/commands/doc/help.rb +3 -2
- data/lib/ctioga2/commands/doc/html.rb +48 -0
- data/lib/ctioga2/commands/doc/introspection.rb +2 -2
- data/lib/ctioga2/commands/general-types.rb +10 -0
- data/lib/ctioga2/commands/parsers/file.rb +23 -2
- data/lib/ctioga2/data/backends/backends.rb +1 -2
- data/lib/ctioga2/data/backends/backends/smath.rb +129 -0
- data/lib/ctioga2/data/backends/backends/text.rb +1 -0
- data/lib/ctioga2/data/dataset.rb +1 -1
- data/lib/ctioga2/data/stack.rb +13 -3
- data/lib/ctioga2/graphics/elements.rb +2 -0
- data/lib/ctioga2/graphics/elements/containers.rb +3 -1
- data/lib/ctioga2/graphics/elements/element.rb +194 -5
- data/lib/ctioga2/graphics/elements/gradient-region.rb +5 -2
- data/lib/ctioga2/graphics/elements/histogram.rb +7 -2
- data/lib/ctioga2/graphics/elements/plot-elements.rb +88 -0
- data/lib/ctioga2/graphics/elements/primitive.rb +28 -12
- data/lib/ctioga2/graphics/elements/region.rb +6 -1
- data/lib/ctioga2/graphics/elements/style-lists.rb +2 -2
- data/lib/ctioga2/graphics/elements/subplot.rb +3 -3
- data/lib/ctioga2/graphics/elements/tangent.rb +5 -8
- data/lib/ctioga2/graphics/generator.rb +10 -0
- data/lib/ctioga2/graphics/geometry.rb +96 -0
- data/lib/ctioga2/graphics/legends.rb +4 -2
- data/lib/ctioga2/graphics/legends/area.rb +12 -4
- data/lib/ctioga2/graphics/root.rb +16 -14
- data/lib/ctioga2/graphics/styles.rb +5 -2
- data/lib/ctioga2/graphics/styles/arrows.rb +5 -0
- data/lib/ctioga2/graphics/styles/axes.rb +1 -1
- data/lib/ctioga2/graphics/styles/base.rb +95 -14
- data/lib/ctioga2/graphics/styles/curve.rb +8 -0
- data/lib/ctioga2/graphics/styles/drawable.rb +35 -48
- data/lib/ctioga2/graphics/styles/factory.rb +23 -23
- data/lib/ctioga2/graphics/styles/fill.rb +268 -0
- data/lib/ctioga2/graphics/styles/plot.rb +90 -46
- data/lib/ctioga2/graphics/styles/sets.rb +3 -0
- data/lib/ctioga2/graphics/styles/{sheet.rb → styles.rb} +70 -160
- data/lib/ctioga2/graphics/styles/stylesheet.rb +355 -0
- data/lib/ctioga2/graphics/styles/texts.rb +4 -2
- data/lib/ctioga2/graphics/styles/ticks.rb +44 -4
- data/lib/ctioga2/graphics/subplot-commands.rb +84 -9
- data/lib/ctioga2/graphics/types.rb +1 -1
- data/lib/ctioga2/graphics/types/dimensions.rb +40 -0
- data/lib/ctioga2/graphics/types/grid.rb +21 -5
- data/lib/ctioga2/graphics/types/point.rb +2 -1
- data/lib/ctioga2/log.rb +5 -1
- data/lib/ctioga2/metabuilder/types/styles.rb +11 -7
- data/lib/ctioga2/plotmaker.rb +2 -0
- data/lib/ctioga2/utils.rb +21 -6
- data/lib/ctioga2/version.rb +2 -2
- metadata +105 -108
@@ -0,0 +1,355 @@
|
|
1
|
+
# stylesheet.rb: handling of style sheets
|
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/log'
|
16
|
+
|
17
|
+
require 'ctioga2/graphics/coordinates'
|
18
|
+
|
19
|
+
# This module contains all the classes used by ctioga
|
20
|
+
module CTioga2
|
21
|
+
|
22
|
+
module Graphics
|
23
|
+
|
24
|
+
module Styles
|
25
|
+
|
26
|
+
# A StyleSheet is a simple implementation of CSS-like facilities
|
27
|
+
# in ctioga2. As in CSS, it recognizes id and classes, and type.
|
28
|
+
class StyleSheet
|
29
|
+
|
30
|
+
# An element in a XPath
|
31
|
+
class XPathElement
|
32
|
+
|
33
|
+
# The type -- or nil if not selecting on type
|
34
|
+
attr_accessor :obj_type
|
35
|
+
|
36
|
+
# The class -- or nil if not selecting on class
|
37
|
+
attr_accessor :obj_class
|
38
|
+
|
39
|
+
# The ID, or nil if not selecting on id
|
40
|
+
attr_accessor :obj_id
|
41
|
+
|
42
|
+
# If this flag is on, the object has to be the direct parent
|
43
|
+
# of the child below.
|
44
|
+
attr_accessor :direct_parent
|
45
|
+
|
46
|
+
# A XPathElement is a series of elements (\w and - allowed),
|
47
|
+
# optionnally followed by a > sign. No space allowed,
|
48
|
+
# excepted before the > sign
|
49
|
+
def parse_string(txt)
|
50
|
+
|
51
|
+
|
52
|
+
rest = txt.gsub(/([.#]?)([\w-]+)/) do |x|
|
53
|
+
if $1 == "."
|
54
|
+
@obj_class = $2
|
55
|
+
elsif $1 == "#"
|
56
|
+
@obj_id = $2
|
57
|
+
else
|
58
|
+
@obj_type = $2
|
59
|
+
end
|
60
|
+
""
|
61
|
+
end
|
62
|
+
|
63
|
+
if rest =~ /^\s*\*?\s*(>)?$/
|
64
|
+
if $1 == ">"
|
65
|
+
@direct_parent = true
|
66
|
+
end
|
67
|
+
else
|
68
|
+
raise "Incorrect XPath element: #{txt}"
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.from_text(txt)
|
74
|
+
a = XPathElement.new
|
75
|
+
a.parse_string(txt)
|
76
|
+
return a
|
77
|
+
end
|
78
|
+
|
79
|
+
def matches?(obj)
|
80
|
+
if @obj_type && (obj.style_name != @obj_type)
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
if @obj_class && !obj.object_classes.include?(@obj_class)
|
84
|
+
return false
|
85
|
+
end
|
86
|
+
if @obj_id && (obj.object_id != @obj_id)
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
return true
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
a = @obj_type || ""
|
94
|
+
if @obj_id
|
95
|
+
a += "##{@obj_id}"
|
96
|
+
end
|
97
|
+
if @obj_class
|
98
|
+
a += ".#{@obj_class}"
|
99
|
+
end
|
100
|
+
if a.size == 0
|
101
|
+
a = "*"
|
102
|
+
end
|
103
|
+
if @direct_parent
|
104
|
+
a += " >"
|
105
|
+
end
|
106
|
+
return a
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
# p self.from_text("*").to_s
|
111
|
+
# p self.from_text("bidule").to_s
|
112
|
+
# p self.from_text(".bidule").to_s
|
113
|
+
# p self.from_text("#bidule").to_s
|
114
|
+
# p self.from_text("b#a.cls").to_s
|
115
|
+
# p self.from_text(".cs").to_s
|
116
|
+
# p self.from_text(".cs#truc").to_s
|
117
|
+
# p self.from_text("b#a.cls >").to_s
|
118
|
+
|
119
|
+
# p self.from_text("$sdf")
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
# An XPath, ie a series of XPathElement from outermost to
|
124
|
+
# innermost.
|
125
|
+
class XPath
|
126
|
+
|
127
|
+
# From the innermost to outermost
|
128
|
+
attr_accessor :elements
|
129
|
+
|
130
|
+
def parse_string(txt)
|
131
|
+
@elements = txt.gsub(/\s*>/, '>').split(/\s+/).reverse.map do |x|
|
132
|
+
XPathElement.from_text(x)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.from_text(txt)
|
137
|
+
a = XPath.new
|
138
|
+
a.parse_string(txt)
|
139
|
+
return a
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns true if the innermost element has a type
|
143
|
+
def typed?
|
144
|
+
if @elements.first.obj_type
|
145
|
+
return true
|
146
|
+
else
|
147
|
+
return false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def matches?(obj)
|
152
|
+
return match_chain(obj, @elements)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns a normalized version of the XPATH, that can be
|
156
|
+
# used as a hash key.
|
157
|
+
def to_s
|
158
|
+
return @elements.reverse.map { |x| x.to_s }.join(" ")
|
159
|
+
end
|
160
|
+
|
161
|
+
protected
|
162
|
+
|
163
|
+
def match_chain(obj, elems)
|
164
|
+
if ! elems.first.matches?(obj)
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
|
168
|
+
if elems.size <= 1
|
169
|
+
return true
|
170
|
+
end
|
171
|
+
|
172
|
+
np = obj.object_parent
|
173
|
+
if ! np
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
if elems[1].direct_parent
|
177
|
+
return match_chain(np, elems[1..-1])
|
178
|
+
else
|
179
|
+
while np
|
180
|
+
if match_chain(np, elems[1..-1])
|
181
|
+
return true
|
182
|
+
else
|
183
|
+
np = np.object_parent
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
return false
|
188
|
+
end
|
189
|
+
|
190
|
+
# p self.from_text("b#a.cls #stuff").to_s
|
191
|
+
# p self.from_text("b.cls#a > foo").to_s
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
# A style bucket, a hash 'key' => 'value' associated with a
|
196
|
+
# unique xpath
|
197
|
+
class Bucket
|
198
|
+
|
199
|
+
# The style information (a string->string hash).
|
200
|
+
#
|
201
|
+
# Not that it can actually be a string->typed stuff, since
|
202
|
+
# most types accept that !
|
203
|
+
attr_accessor :style
|
204
|
+
|
205
|
+
# All the XPath associated with this style information
|
206
|
+
attr_accessor :xpath
|
207
|
+
|
208
|
+
# The xpath text initially used
|
209
|
+
attr_accessor :xname
|
210
|
+
|
211
|
+
def initialize(xp)
|
212
|
+
@xname = xp
|
213
|
+
@xpath = XPath.from_text(xp)
|
214
|
+
@style = {}
|
215
|
+
end
|
216
|
+
|
217
|
+
def matches?(obj)
|
218
|
+
if @xpath.matches?(obj)
|
219
|
+
return true
|
220
|
+
else
|
221
|
+
return false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns the style, but with all the options normalized to
|
226
|
+
# lowercase and without
|
227
|
+
def normalized_style
|
228
|
+
stl = {}
|
229
|
+
for k,v in @style
|
230
|
+
stl[k.gsub(/-/,"_").downcase] = v
|
231
|
+
end
|
232
|
+
return stl
|
233
|
+
end
|
234
|
+
|
235
|
+
# # Returns the style for the given object. DOES NOT CHECK
|
236
|
+
# # that the object belongs to this Bucket.
|
237
|
+
# def style_for(obj)
|
238
|
+
# @cache ||= {}
|
239
|
+
# if ! @cache.key?(obj.style_name)
|
240
|
+
# @cache[obj.style_name] = obj.style_class.from_hash(@style)
|
241
|
+
# end
|
242
|
+
# return @cache[obj.style_name]
|
243
|
+
# end
|
244
|
+
end
|
245
|
+
|
246
|
+
# OK, so now we begin the StyleSheet class per se.
|
247
|
+
#
|
248
|
+
# The stylesheet class is but an ordered list of buckets.
|
249
|
+
|
250
|
+
# The list of buckets
|
251
|
+
attr_accessor :buckets
|
252
|
+
|
253
|
+
# A hash "full xpath" -> bucket name, so that one can update a
|
254
|
+
# bucket instead of just adding to it.
|
255
|
+
attr_accessor :buckets_by_xpath
|
256
|
+
|
257
|
+
def initialize()
|
258
|
+
@buckets = []
|
259
|
+
@buckets_by_xpath = {}
|
260
|
+
end
|
261
|
+
|
262
|
+
def set_style(xpath, style)
|
263
|
+
for f in xpath.split(/\s*,\s*/)
|
264
|
+
bkt = get_bucket(f)
|
265
|
+
bkt.style = style
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# @todo Maybe update and set should just add a new bucket at
|
270
|
+
# the end, so that it overrides the previous ones anyway ?
|
271
|
+
def update_style(xpath, style, default_type = nil)
|
272
|
+
for f in xpath.split(/\s*,\s*/)
|
273
|
+
xp = XPath.from_text(f)
|
274
|
+
if default_type
|
275
|
+
xp.elements.first.obj_type ||= default_type
|
276
|
+
end
|
277
|
+
bkt = get_bucket(xp)
|
278
|
+
bkt.style.merge!(style)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def style_hash_for(obj)
|
283
|
+
stl = {}
|
284
|
+
# p [:cls, obj.class, obj.object_id, obj.object_classes]
|
285
|
+
for bkt in @buckets
|
286
|
+
# p [bkt.xpath, bkt.matches?(obj), bkt.style]
|
287
|
+
if bkt.matches?(obj)
|
288
|
+
stl.merge!(bkt.normalized_style)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# p [:s, stl]
|
293
|
+
cls = obj.style_class
|
294
|
+
# p cls.options_hash.keys
|
295
|
+
rv = cls.convert_string_hash(stl)
|
296
|
+
# p [:t, rv]
|
297
|
+
return rv
|
298
|
+
end
|
299
|
+
|
300
|
+
def style_for(obj)
|
301
|
+
return obj.style_class.from_hash(style_hash_for(obj))
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.style_sheet
|
305
|
+
@style_sheet ||= StyleSheet.new
|
306
|
+
@style_sheet
|
307
|
+
end
|
308
|
+
|
309
|
+
def self.style_hash_for(obj)
|
310
|
+
return self.style_sheet.style_hash_for(obj)
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.style_for(obj)
|
314
|
+
return self.style_sheet.style_for(obj)
|
315
|
+
end
|
316
|
+
|
317
|
+
def update_from_file(file)
|
318
|
+
end
|
319
|
+
|
320
|
+
def update_from_string(str)
|
321
|
+
# First, strip all comments from the string
|
322
|
+
str = str.gsub(/\/\*.*?\*\//m, '')
|
323
|
+
|
324
|
+
str.gsub(/^\s*((?:[.#]?[,\w-]+\s*>?\s*)+)\s*\{([^}]+)\}/m) do |x|
|
325
|
+
xpath = $1
|
326
|
+
smts = $2.split(/\s*;\s*/)
|
327
|
+
|
328
|
+
stl = {}
|
329
|
+
for s in smts
|
330
|
+
if s =~ /\s*([\w-]+)\s*:\s*(.*)/m
|
331
|
+
stl[$1] = $2
|
332
|
+
else
|
333
|
+
error { "Style not understood: #{s}" }
|
334
|
+
end
|
335
|
+
end
|
336
|
+
update_style(xpath, stl)
|
337
|
+
end
|
338
|
+
# p self
|
339
|
+
end
|
340
|
+
|
341
|
+
protected
|
342
|
+
|
343
|
+
def get_bucket(xp)
|
344
|
+
xpath = xp.to_s
|
345
|
+
if ! @buckets_by_xpath.key? xpath
|
346
|
+
@buckets << Bucket.new(xpath)
|
347
|
+
@buckets_by_xpath[xpath] = @buckets.last
|
348
|
+
end
|
349
|
+
return @buckets_by_xpath[xpath]
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
@@ -44,12 +44,12 @@ module CTioga2
|
|
44
44
|
# The vertical alignment
|
45
45
|
typed_attribute :alignment, 'alignment'
|
46
46
|
|
47
|
-
alias_for :valign, :alignment
|
47
|
+
alias_for :valign, :alignment, true
|
48
48
|
|
49
49
|
# The horizontal alignment
|
50
50
|
typed_attribute :justification, 'justification'
|
51
51
|
|
52
|
-
alias_for :halign, :justification
|
52
|
+
alias_for :halign, :justification, true
|
53
53
|
|
54
54
|
# A text width
|
55
55
|
typed_attribute :text_width, "dimension-or-no"
|
@@ -110,6 +110,8 @@ module CTioga2
|
|
110
110
|
# Prepares the dictionnary for use with show_text
|
111
111
|
def prepare_show_text_dict(t, text, x_or_loc, y = nil, measure = nil)
|
112
112
|
dict = self.hash_for_tioga(t)
|
113
|
+
# get rid of uncommented stuff problems
|
114
|
+
text = "#{text}%\n"
|
113
115
|
|
114
116
|
loc = nil
|
115
117
|
|
@@ -26,6 +26,9 @@ module CTioga2
|
|
26
26
|
|
27
27
|
# This class describes where to place ticks on the target axis
|
28
28
|
# and how to label them.
|
29
|
+
#
|
30
|
+
# I really should drop the call to Tioga altogether. It makes
|
31
|
+
# everything too complicated.
|
29
32
|
class AxisTicks < BasicStyle
|
30
33
|
|
31
34
|
include Log
|
@@ -51,6 +54,11 @@ module CTioga2
|
|
51
54
|
# Separation between major ticks. Overriden by the major list
|
52
55
|
typed_attribute :major_delta, "float"
|
53
56
|
|
57
|
+
# Or physical separation between ticks
|
58
|
+
#
|
59
|
+
# @todo Make a major_min_sep ?
|
60
|
+
typed_attribute :major_sep, 'dimension'
|
61
|
+
|
54
62
|
# Approximate number of major ticks
|
55
63
|
typed_attribute :major_number, "integer"
|
56
64
|
|
@@ -63,6 +71,11 @@ module CTioga2
|
|
63
71
|
# Number of minor ticks between major ticks
|
64
72
|
typed_attribute :minor_number, "integer"
|
65
73
|
|
74
|
+
# This needs a little more careful thinking. Or just a min
|
75
|
+
# separation ?
|
76
|
+
# Physical separation between the minor ticks
|
77
|
+
typed_attribute :minor_sep_min, 'dimension'
|
78
|
+
|
66
79
|
|
67
80
|
|
68
81
|
# The list of labels
|
@@ -96,13 +109,22 @@ module CTioga2
|
|
96
109
|
xl, xr = xr, xl
|
97
110
|
end
|
98
111
|
|
112
|
+
mn = if @major_number
|
113
|
+
@major_number
|
114
|
+
elsif @major_sep
|
115
|
+
dx = @major_sep.to_figure(t, info['vertical'] ? :y : :x)
|
116
|
+
mn = (xr - xl)/dx
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
99
121
|
if @major
|
100
122
|
ret['minor_ticks'] = Dobjects::Dvector.new
|
101
123
|
ret['major_ticks'] = Dobjects::Dvector.new(@major)
|
102
124
|
|
103
125
|
fmt ||= "$%g$"
|
104
|
-
elsif @major_delta ||
|
105
|
-
delta = @major_delta || Utils::closest_subdivision(( (xr - xl)
|
126
|
+
elsif @major_delta || mn
|
127
|
+
delta = @major_delta || Utils::closest_subdivision(( (xr - xl)/mn))
|
106
128
|
ret['major_ticks'] = Utils::integer_subdivisions(xl, xr,
|
107
129
|
delta)
|
108
130
|
fmt ||= "$%g$"
|
@@ -110,8 +132,26 @@ module CTioga2
|
|
110
132
|
|
111
133
|
if @minor
|
112
134
|
ret['minor_ticks'] = Dobjects::Dvector.new(@minor)
|
113
|
-
elsif @minor_delta || delta
|
114
|
-
|
135
|
+
elsif @minor_delta || @minor_sep || delta
|
136
|
+
|
137
|
+
dt = if @minor_delta
|
138
|
+
@minor_delta
|
139
|
+
else
|
140
|
+
nb = if @minor_number
|
141
|
+
@minor_number
|
142
|
+
elsif @minor_sep_min
|
143
|
+
dx = @minor_sep_min.to_figure(t, info['vertical'] ? :y : :x)
|
144
|
+
mx = ((delta/dx).round - 1)
|
145
|
+
if mx > 3
|
146
|
+
3
|
147
|
+
else
|
148
|
+
mx
|
149
|
+
end
|
150
|
+
else
|
151
|
+
3
|
152
|
+
end
|
153
|
+
delta/(nb+1)
|
154
|
+
end
|
115
155
|
ret['minor_ticks'] = Utils::integer_subdivisions(xl, xr,
|
116
156
|
dt)
|
117
157
|
end
|