ctioga2 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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...