ctioga2 0.10.1 → 0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog +18 -0
  3. data/bin/ctioga2 +28 -0
  4. data/lib/ctioga2/commands/commands.rb +1 -0
  5. data/lib/ctioga2/commands/doc/documentation-commands.rb +13 -0
  6. data/lib/ctioga2/commands/doc/help.rb +3 -2
  7. data/lib/ctioga2/commands/doc/html.rb +48 -0
  8. data/lib/ctioga2/commands/doc/introspection.rb +2 -2
  9. data/lib/ctioga2/commands/general-types.rb +10 -0
  10. data/lib/ctioga2/commands/parsers/file.rb +23 -2
  11. data/lib/ctioga2/data/backends/backends.rb +1 -2
  12. data/lib/ctioga2/data/backends/backends/smath.rb +129 -0
  13. data/lib/ctioga2/data/backends/backends/text.rb +1 -0
  14. data/lib/ctioga2/data/dataset.rb +1 -1
  15. data/lib/ctioga2/data/stack.rb +13 -3
  16. data/lib/ctioga2/graphics/elements.rb +2 -0
  17. data/lib/ctioga2/graphics/elements/containers.rb +3 -1
  18. data/lib/ctioga2/graphics/elements/element.rb +194 -5
  19. data/lib/ctioga2/graphics/elements/gradient-region.rb +5 -2
  20. data/lib/ctioga2/graphics/elements/histogram.rb +7 -2
  21. data/lib/ctioga2/graphics/elements/plot-elements.rb +88 -0
  22. data/lib/ctioga2/graphics/elements/primitive.rb +28 -12
  23. data/lib/ctioga2/graphics/elements/region.rb +6 -1
  24. data/lib/ctioga2/graphics/elements/style-lists.rb +2 -2
  25. data/lib/ctioga2/graphics/elements/subplot.rb +3 -3
  26. data/lib/ctioga2/graphics/elements/tangent.rb +5 -8
  27. data/lib/ctioga2/graphics/generator.rb +10 -0
  28. data/lib/ctioga2/graphics/geometry.rb +96 -0
  29. data/lib/ctioga2/graphics/legends.rb +4 -2
  30. data/lib/ctioga2/graphics/legends/area.rb +12 -4
  31. data/lib/ctioga2/graphics/root.rb +16 -14
  32. data/lib/ctioga2/graphics/styles.rb +5 -2
  33. data/lib/ctioga2/graphics/styles/arrows.rb +5 -0
  34. data/lib/ctioga2/graphics/styles/axes.rb +1 -1
  35. data/lib/ctioga2/graphics/styles/base.rb +95 -14
  36. data/lib/ctioga2/graphics/styles/curve.rb +8 -0
  37. data/lib/ctioga2/graphics/styles/drawable.rb +35 -48
  38. data/lib/ctioga2/graphics/styles/factory.rb +23 -23
  39. data/lib/ctioga2/graphics/styles/fill.rb +268 -0
  40. data/lib/ctioga2/graphics/styles/plot.rb +90 -46
  41. data/lib/ctioga2/graphics/styles/sets.rb +3 -0
  42. data/lib/ctioga2/graphics/styles/{sheet.rb → styles.rb} +70 -160
  43. data/lib/ctioga2/graphics/styles/stylesheet.rb +355 -0
  44. data/lib/ctioga2/graphics/styles/texts.rb +4 -2
  45. data/lib/ctioga2/graphics/styles/ticks.rb +44 -4
  46. data/lib/ctioga2/graphics/subplot-commands.rb +84 -9
  47. data/lib/ctioga2/graphics/types.rb +1 -1
  48. data/lib/ctioga2/graphics/types/dimensions.rb +40 -0
  49. data/lib/ctioga2/graphics/types/grid.rb +21 -5
  50. data/lib/ctioga2/graphics/types/point.rb +2 -1
  51. data/lib/ctioga2/log.rb +5 -1
  52. data/lib/ctioga2/metabuilder/types/styles.rb +11 -7
  53. data/lib/ctioga2/plotmaker.rb +2 -0
  54. data/lib/ctioga2/utils.rb +21 -6
  55. data/lib/ctioga2/version.rb +2 -2
  56. 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 || @major_number
105
- delta = @major_delta || Utils::closest_subdivision(( (xr - xl)/@major_number))
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
- dt = @minor_delta || delta/((@minor_number || 3)+1)
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