ctioga2 0.10.1 → 0.11

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.
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