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.
@@ -54,9 +54,12 @@ module CTioga2
54
54
 
55
55
  @subframe = nil # Automatic by default.
56
56
 
57
+ @prev_subframe = nil
58
+
57
59
  @style = style || Styles::PlotStyle.new
58
60
 
59
61
  @user_boundaries = {}
62
+
60
63
  end
61
64
 
62
65
  # Returns the boundaries that apply for the given _curve_ --
@@ -87,7 +90,16 @@ module CTioga2
87
90
  end
88
91
 
89
92
  def actual_subframe(t)
90
- return @subframe || @style.estimate_margins(t)
93
+ if @subframe
94
+ return @subframe
95
+ else
96
+ if @prev_subframe
97
+ @style.compute_margins(t, @prev_subframe)
98
+ else
99
+ @prev_subframe = @style.estimate_margins(t)
100
+ return @prev_subframe
101
+ end
102
+ end
91
103
  end
92
104
 
93
105
  # In general, subplot's boundaries do not count for the parent
@@ -81,6 +81,7 @@ module CTioga2
81
81
  # * a transform is set
82
82
  # * ticks are set
83
83
 
84
+ @@text_size_index = 0
84
85
 
85
86
  # Creates a new AxisStyle object at the given location with
86
87
  # the given style.
@@ -94,6 +95,9 @@ module CTioga2
94
95
  @log = false
95
96
  @ticks_side = {}
96
97
  @ticks = AxisTicks.new
98
+
99
+ @index = @@text_size_index
100
+ @@text_size_index += 1
97
101
  end
98
102
 
99
103
  # Draws the axis within the current plot. Boundaries are the
@@ -105,7 +109,9 @@ module CTioga2
105
109
  # where it should be...
106
110
  # * non-linear axes (or linear, for that matter, but with
107
111
  # a transformation)
108
- def draw_axis(t)
112
+ #
113
+ # _watcher_ is a TextSizeWatcher object.
114
+ def draw_axis(t, watcher = nil)
109
115
  spec = get_axis_specification(t)
110
116
 
111
117
  info = t.axis_information(spec)
@@ -129,7 +135,11 @@ minor_tick_length minor_tick_width)
129
135
  t.show_axis(spec)
130
136
  @axis_label.loc = @location
131
137
  default = vertical? ? 'ylabel' : 'xlabel'
132
- @axis_label.draw(t, default)
138
+ nm = "axis-label#{@index}"
139
+ @axis_label.draw(t, default, nm)
140
+ if watcher
141
+ watcher.watch(nm)
142
+ end
133
143
  end
134
144
 
135
145
  # Sets the current boundaries of the _t_ object to the _range_
@@ -67,7 +67,7 @@ module CTioga2
67
67
 
68
68
  end
69
69
 
70
- def draw_axis(t)
70
+ def draw_axis(t, watcher = nil)
71
71
  # Not beautiful at all
72
72
  size = Types::Dimension.new(:dy, extension(t),
73
73
  @location.orientation)
@@ -76,6 +76,12 @@ module CTioga2
76
76
  # mode. A Dimension.
77
77
  attr_accessor :padding
78
78
 
79
+ # Mode for auto-adjust
80
+ attr_accessor :text_auto_adjust
81
+
82
+
83
+ @@current_index = 0
84
+
79
85
  def initialize
80
86
  # Default style for the plots.
81
87
  @axes = {}
@@ -110,8 +116,20 @@ module CTioga2
110
116
  @background =
111
117
  StyleSheet.style_for(BackgroundStyle, 'background')
112
118
 
113
- # A padding of 4bp ? Why ?? Why not ?
114
- @padding = Types::Dimension.new(:bp, 4)
119
+ # A padding of 6bp ? Why ?? Why not ?
120
+ @padding = Types::Dimension.new(:bp, 6)
121
+
122
+
123
+
124
+ @text_size_index = @@current_index
125
+ @@current_index += 1
126
+
127
+ # Automatic adjustment of text sizes...
128
+ @text_sizes = TextSizeWatcher.new
129
+ @text_sizes.watch("title-#{@text_size_index}")
130
+
131
+ @text_auto_adjust = :both
132
+
115
133
  end
116
134
 
117
135
  # Apply (destructively) the current transformations to the
@@ -120,7 +138,6 @@ module CTioga2
120
138
  @transforms.transform_2d!(dataset)
121
139
  end
122
140
 
123
-
124
141
  # Whether to use log scale for the given axis.
125
142
  #
126
143
  # Now the question is: how should that affect user-defined
@@ -235,7 +252,7 @@ module CTioga2
235
252
  t.context do
236
253
  begin
237
254
  axis.set_bounds_for_axis(t, bounds[which])
238
- axis.draw_axis(t)
255
+ axis.draw_axis(t, @text_sizes)
239
256
  rescue Exception => e
240
257
  error { "Impossible to draw axis #{which}: #{e.message}" }
241
258
  debug { "Full message: #{e.inspect}\n#{e.backtrace.join("\n")}" }
@@ -243,7 +260,7 @@ module CTioga2
243
260
  end
244
261
  end
245
262
  # We draw the title last
246
- title.draw(t, 'title')
263
+ title.draw(t, 'title', "title-#{@text_size_index}")
247
264
  end
248
265
 
249
266
  # Draws all axes background lines for the plot.
@@ -302,6 +319,31 @@ module CTioga2
302
319
  return box
303
320
  end
304
321
 
322
+ # Computes the margins based on the text information.
323
+ #
324
+ # This is very different from the one above, since this one
325
+ # relies on measured texts to get it right !
326
+ def compute_margins(t, prev_margins)
327
+ margins = estimate_margins(t)
328
+ if @text_auto_adjust == :old
329
+ return margins
330
+ else
331
+ pad = if @padding
332
+ @padding.to_bp(t)
333
+ else
334
+ 4
335
+ end
336
+ nm = @text_sizes.update_margins(t, prev_margins, pad)
337
+
338
+ # We include the old margins, unless we have the :measure
339
+ # text adjust mode
340
+ if @text_auto_adjust != :measure
341
+ nm.expand_to!(t, margins)
342
+ end
343
+ return nm
344
+ end
345
+ end
346
+
305
347
  protected
306
348
 
307
349
  # Takes a string and returns a Symbol suitable for use with
@@ -394,7 +436,7 @@ EOH
394
436
  [:left, :right, :top, :bottom].each do |loc|
395
437
  style = AxisStyle.current_axis_style(plotmaker, loc)
396
438
  style.decoration = Tioga::FigureConstants::AXIS_HIDDEN
397
- style.axis_label.text = ""
439
+ style.axis_label.text = false
398
440
  end
399
441
  end
400
442
  ClearAxesCommand.
@@ -1,5 +1,5 @@
1
1
  # texts.rb: style for textual objects
2
- # copyright (c) 2009 by Vincent Fourmond
2
+ # copyright (c) 2009, 2010, 2012, 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
@@ -47,6 +47,9 @@ module CTioga2
47
47
  # The horizontal alignment
48
48
  typed_attribute :justification, 'justification'
49
49
 
50
+ # A text width
51
+ typed_attribute :text_width, "text"
52
+
50
53
  # Draw the _text_ at the given location with the given style.
51
54
  # If _y_ is _nil_, then _x_or_loc_ is taken to be a location
52
55
  # (see FigureMaker#show_text).
@@ -81,6 +84,7 @@ module CTioga2
81
84
  dim = dict['scale']
82
85
  dict['scale'] = dim.to_dy(t)
83
86
  end
87
+ dict.delete('text_width')
84
88
  return dict
85
89
  end
86
90
 
@@ -89,6 +93,10 @@ module CTioga2
89
93
  # Prepares the dictionnary for use with show_text
90
94
  def prepare_show_text_dict(t, text, x_or_loc, y = nil, measure = nil)
91
95
  dict = self.hash_for_tioga(t)
96
+ if @text_width
97
+ text = "\\parbox{#{@text_width}}{#{text}}"
98
+ end
99
+
92
100
  dict['text'] = text
93
101
 
94
102
  if y
@@ -48,8 +48,22 @@ module CTioga2
48
48
  # @todo make it possible to provide a function to generate that ?
49
49
  typed_attribute :major, 'float-list'
50
50
 
51
+ # Separation between major ticks. Overriden by the major list
52
+ typed_attribute :major_delta, "float"
53
+
54
+ # Approximate number of major ticks
55
+ typed_attribute :major_number, "integer"
56
+
51
57
  # The list of the position of minor ticks
52
58
  typed_attribute :minor, 'float-list'
59
+
60
+ # Separation between minor ticks
61
+ typed_attribute :minor_delta, "float"
62
+
63
+ # Number of minor ticks between major ticks
64
+ typed_attribute :minor_number, "integer"
65
+
66
+
53
67
 
54
68
  # The list of labels
55
69
  typed_attribute :labels, 'text-list'
@@ -71,15 +85,35 @@ module CTioga2
71
85
  end
72
86
  fmt = @format
73
87
 
88
+ # beginning or end of the axis. Not specifically x
89
+ xl, xr = * (if info['vertical']
90
+ [info['y0'], info['y1']]
91
+ else
92
+ [info['x0'], info['x1']]
93
+ end)
94
+
95
+ if xl > xr
96
+ xl, xr = xr, xl
97
+ end
98
+
74
99
  if @major
75
100
  ret['minor_ticks'] = Dobjects::Dvector.new
76
101
  ret['major_ticks'] = Dobjects::Dvector.new(@major)
77
102
 
78
103
  fmt ||= "$%g$"
104
+ elsif @major_delta || @major_number
105
+ delta = @major_delta || Utils::closest_subdivision(( (xr - xl)/@major_number))
106
+ ret['major_ticks'] = Utils::integer_subdivisions(xl, xr,
107
+ delta)
108
+ fmt ||= "$%g$"
79
109
  end
80
110
 
81
111
  if @minor
82
112
  ret['minor_ticks'] = Dobjects::Dvector.new(@minor)
113
+ elsif @minor_delta || delta
114
+ dt = @minor_delta || delta/((@minor_number || 3)+1)
115
+ ret['minor_ticks'] = Utils::integer_subdivisions(xl, xr,
116
+ dt)
83
117
  end
84
118
 
85
119
  fmt_last = @format_last || fmt
@@ -57,6 +57,41 @@ When the {command: frame-margins} is set to automatic, ctioga2 leaves
57
57
  that much space around the plot on the sides where there are no labels.
58
58
  EOH
59
59
 
60
+
61
+ TARE = {
62
+ /^\s*old/i => :old,
63
+ /^\s*both/i => :both,
64
+ /^\s*measure/i => :measure,
65
+ }
66
+
67
+ TEType =
68
+ CmdType.new('text-adjust-mode',
69
+ {:type => :re_list,
70
+ :list => TARE}, <<EOD)
71
+ Mode for text size adjustment
72
+ * @old@ for the old style heuristics
73
+ * @both@ for both the old style heuristics and the measures, taking
74
+ whichever of those is the biggest
75
+ * @measure@ for only measured text size (but watch out for axis ticks !)
76
+ EOD
77
+
78
+
79
+ TAACommand =
80
+ Cmd.new("text-adjust-mode",nil,"--text-adjust-mode",
81
+ [
82
+ CmdArg.new('text-adjust-mode'),
83
+ ]) do |plotmaker, tf|
84
+
85
+ Styles::PlotStyle.current_plot_style(plotmaker).text_auto_adjust = tf
86
+ end
87
+
88
+ TAACommand.describe('Enables or disables the automatic detection of text size',
89
+ <<EOH, SubplotsGroup)
90
+ When this is on (the default), @ctioga2@ tries to be smart about the
91
+ size of the text bits around the plot. However, this can be bothersome
92
+ at times, so you can disable that with this command.
93
+ EOH
94
+
60
95
 
61
96
  InsetCommand =
62
97
  Cmd.new("inset",nil,"--inset",
@@ -176,7 +176,27 @@ EOD
176
176
  AlignedPointType =
177
177
  CmdType.new('aligned-point', {:type => :aligned_point,
178
178
  :default => :frame}, <<EOD)
179
- A point together with alignment specifications.
179
+ A point together with alignment specifications, used to place some
180
+ elements such as legends for instance, that require alignment information.
181
+
182
+ The first two letters represent the alignment:
183
+
184
+ * @t@ for top
185
+ * @b@ for bottom
186
+ * @c@ for center
187
+ * @l@ for left and
188
+ * @r@ for right
189
+
190
+ These letters can optionally be followed by the exact location of the
191
+ point in frame coordinates. If not provided, a reasonable default
192
+ value is chosen.
193
+
194
+ Examples:
195
+
196
+ * @tl@ is a point at the top left of the frame aligned to the top
197
+ and left;
198
+ * @cl:0.1,0.6@ is vertically centered and aligned to the left, and
199
+ positioned 10% from the left and 60% from the bottom.
180
200
  EOD
181
201
 
182
202
  FrameMarginsType =
@@ -250,10 +270,16 @@ EOD
250
270
 
251
271
  BoxType =
252
272
  CmdType.new('box', :box, <<EOD)
253
- The specification for a box, such as an inset. Specifications vary for
254
- now...
273
+ The specification for a box, such as an inset. It can be a grid
274
+ specification, such as @grid:0,1@. For this to work, a grid must have
275
+ been setup beforehand using {command: setup-grid}.
276
+
277
+ It can also be an {type: aligned-point} together with a width and
278
+ optionally a height in frame coordinates, such as:
255
279
 
256
- @todo to be written later on.
280
+ * @cc:0.3@: a box in the center of size 30% width and 30% height;
281
+ * @bl:0.1,0.2:0.7,0.2@ a box starting from the point at 10% from the left
282
+ and 20% from the bottom, with a width of 70% and a height of 20%.
257
283
  EOD
258
284
 
259
285
  # Coordinate transformations
@@ -91,6 +91,19 @@ module CTioga2
91
91
  return [@left, @right, @top, @bottom]
92
92
  end
93
93
 
94
+ # Augments the margins so that they also encompass those given
95
+ # in other. Based on the current interpretation of the
96
+ # measures as bp.
97
+ def expand_to!(t, other)
98
+ for w in %w(left right top bottom)
99
+ mine = self.send(w)
100
+ theirs = other.send(w)
101
+ if mine.to_bp(t) < theirs.to_bp(t)
102
+ self.send("#{w}=", theirs)
103
+ end
104
+ end
105
+ end
106
+
94
107
  end
95
108
 
96
109
  # A box defined by an AlignedPoint and two dimensions
@@ -81,6 +81,13 @@ module CTioga2
81
81
  return fig/t.default_text_height_dy
82
82
  end
83
83
 
84
+ # Converts the dimension into big points
85
+ def to_bp(t, orientation = nil)
86
+ orientation ||= @orientation
87
+ return t.send("convert_figure_to_output_d#{orientation}",
88
+ to_figure(t, orientation)) / 10.0
89
+ end
90
+
84
91
  # Converts the Dimension to the *frame* coordinates of the
85
92
  # *current* frame in _t_.
86
93
  def to_frame(t, orientation = nil)
@@ -752,6 +752,7 @@ EOH
752
752
  {
753
753
  'oversampling' => CmdArg.new('float'),
754
754
  'scale' => CmdArg.new('float'),
755
+ 'pdftoppm' => CmdArg.new('boolean'),
755
756
  }) do |plotmaker,res, opts|
756
757
  if res =~ /^\s*(\d+)\s*x\s*(\d+)\s*$/
757
758
  size = [$1.to_i, $2.to_i]
@@ -761,6 +762,7 @@ EOH
761
762
  end
762
763
  scale = opts['scale'] || 1
763
764
  plotmaker.postprocess.png_scale = scale
765
+ plotmaker.postprocess.png_pdftoppm = opts['pdftoppm']
764
766
  page_size = size.map { |n| (n/(1.0 *scale)).to_s + "bp" }.join('x')
765
767
  plotmaker.root_object.set_page_size(page_size)
766
768
  else
@@ -43,6 +43,13 @@ module CTioga2
43
43
  # Are we converting to EPS using pdftops ?
44
44
  attr_accessor :eps
45
45
 
46
+
47
+ # @todo Maybe all the PNG stuff should be it is own class ?
48
+
49
+ # If on, we use pdftoppm rather than imagemagick (gs, used by
50
+ # pdftoppm is much slower than pdftoppm)
51
+ attr_accessor :png_pdftoppm
52
+
46
53
  # PNG resolution
47
54
  attr_accessor :png_res
48
55
 
@@ -62,6 +69,7 @@ module CTioga2
62
69
  @png_res = nil
63
70
  @png_oversampling = 2
64
71
  @png_scale = 1
72
+ @png_pdftoppm = false
65
73
 
66
74
  @processed_files = []
67
75
  end
@@ -114,9 +122,14 @@ module CTioga2
114
122
 
115
123
  # Converts to PNG if applicable
116
124
  if @png_res
117
- target = file.sub(/(\.pdf)?$/,'.png')
125
+ tbase = file.sub(/(\.pdf)?$/,'')
118
126
  info { "Converting #{file} to PNG" }
119
- spawn "convert -density #{(@png_oversampling * @png_scale * 72).to_i} #{file} -resize #{@png_res.join('x')} #{target}"
127
+
128
+ if @png_pdftoppm
129
+ spawn "pdftoppm -singlefile -png -r #{(@png_scale * 72).to_i} #{file} #{tbase}"
130
+ else
131
+ spawn "convert -density #{(@png_oversampling * @png_scale * 72).to_i} #{file} -resize #{@png_res.join('x')} #{tbase}.png"
132
+ end
120
133
  end
121
134
 
122
135
  # View produced PDF or PNG files...