dyi 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -213,7 +213,7 @@ module DYI
213
213
  @receive_event
214
214
  end
215
215
 
216
- # Registers a script with the image.
216
+ # Registers a script.
217
217
  # @param [String, Script::SimpleScript] script_body a string that is a
218
218
  # script body or a script object that is registered
219
219
  # @param [String] content_type a content-type of the script. If parameter
@@ -264,6 +264,15 @@ module DYI
264
264
  end
265
265
  end
266
266
 
267
+ # @since 1.3.0
268
+ def to_reused_source
269
+ options = {}
270
+ options[:css_class] = css_class
271
+ template = Shape::GraphicalTemplate.new(width, height, preserve_aspect_ratio, options)
272
+ template.instance_variable_set('@child_elements', child_elements)
273
+ template
274
+ end
275
+
267
276
  private
268
277
 
269
278
  def get_formatter(format=nil, options={})
@@ -36,6 +36,8 @@ module DYI
36
36
  attr_reader :axis_back_canvas, :chart_back_canvas, :scale_canvas, :chart_front_canvas, :axis_front_canvas, :legend_canvas
37
37
  # @since 1.2.0
38
38
  attr_reader :x_scale_canvas, :y_scale_canvas, :guid_front_canvas, :chart_region
39
+ # @since 1.3.0
40
+ attr_reader :back_canvas, :data_label_canvas
39
41
 
40
42
  opt_accessor :chart_margins, {:type => :hash, :default => {}, :keys => [:top,:right,:bottom,:left], :item_type => :length}
41
43
  opt_accessor :chart_colors, {:type => :array, :item_type => :color}
@@ -75,6 +77,16 @@ module DYI
75
77
  # @since 1.2.0
76
78
  opt_accessor :marker_size, {:type => :float, :default => 2.5}
77
79
 
80
+ # @since 1.3.0
81
+ opt_accessor :display_range, :type => :range, :default => (0..-1)
82
+
83
+ # @since 1.3.0
84
+ opt_accessor :show_data_label, :type => :boolean
85
+ # @since 1.3.0
86
+ opt_accessor :data_label_font, :type => :font
87
+ # @since 1.3.0
88
+ opt_accessor :data_label_format, :type => :string
89
+
78
90
  def margin_top
79
91
  chart_margins[:top] || Length.new(16)
80
92
  end
@@ -180,8 +192,8 @@ module DYI
180
192
  sub_series_data = []
181
193
  @bar_series = []
182
194
  data.values_size.times do |i|
183
- main_series_data.push(*data.series(i)) unless use_y_second_axis?(i)
184
- sub_series_data.push(*data.series(i)) if use_y_second_axis?(i)
195
+ main_series_data.push(*data.series(i)[display_range]) unless use_y_second_axis?(i)
196
+ sub_series_data.push(*data.series(i)[display_range]) if use_y_second_axis?(i)
185
197
  @bar_series.push(i) if chart_type(i) == :bar
186
198
  end
187
199
  settings =
@@ -218,6 +230,8 @@ module DYI
218
230
  @axis_back_canvas = Shape::ShapeGroup.draw_on(@canvas)
219
231
  # @chart_front_canvas = Shape::ShapeGroup.draw_on(@canvas, :mask => "url(##{mask.id})") unless @chart_front_canvas
220
232
  chart_clip = Drawing::Clipping.new(Shape::Rectangle.new([margin_left, margin_top], width - margin_left - margin_right, height - margin_top - margin_bottom))
233
+ @chart_region = Shape::Rectangle.new([margin_left, margin_top], width - margin_left - margin_right, height - margin_top - margin_bottom, :painting => {:stroke_width => 0})
234
+ @chart_region.draw_on(canvas)
221
235
  unless @chart_back_canvas
222
236
  clip_container = Shape::ShapeGroup.draw_on(@canvas)
223
237
  @chart_back_canvas = Shape::ShapeGroup.draw_on(clip_container)
@@ -226,12 +240,12 @@ module DYI
226
240
  @scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @scale_canvas
227
241
  unless @chart_front_canvas
228
242
  clip_container = Shape::ShapeGroup.draw_on(@canvas)
229
- @chart_front_canvas = Shape::ShapeGroup.draw_on(clip_container)
230
243
  clip_container.set_clipping(chart_clip)
231
- @chart_region = Shape::Rectangle.new([margin_left, margin_top], width - margin_left - margin_right, height - margin_top - margin_bottom, :painting => {:stroke_width => 0})
232
- @chart_region.draw_on(clip_container)
233
- @guid_front_canvas = Shape::ShapeGroup.draw_on(clip_container)
244
+ @chart_front_canvas = Shape::ShapeGroup.draw_on(clip_container)
234
245
  end
246
+ @guid_front_canvas = Shape::ShapeGroup.draw_on(@canvas)
247
+ @guid_front_canvas.set_clipping(chart_clip)
248
+ @data_label_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @data_label_canvas
235
249
  @x_scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @x_scale_canvas
236
250
  @y_scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @y_scale_canvas
237
251
  @axis_front_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @axis_front_canvas
@@ -350,29 +364,33 @@ module DYI
350
364
  pen.draw_line(@scale_canvas, left_point, right_point)
351
365
  end
352
366
 
353
- def needs_x_scale?(i)
354
- return true if data.records_size < max_x_label_count
355
- i % ((data.records_size - 1) / [max_x_label_count - 1, 1].max) == 0
367
+ def needs_x_scale?(i, display_records_size)
368
+ return true if display_records_size <= max_x_label_count
369
+ i % ((display_records_size - 1) / [max_x_label_count - 1, 1].max) == 0
356
370
  end
357
371
 
358
372
  def draw_x_axis(main_pen, sub_pen, text_pen, text_margin)
359
373
  main_pen.draw_line(represent_3d? ? @axis_back_canvas : @axis_front_canvas, [margin_left, height - margin_bottom], [width - margin_right, height - margin_bottom])
374
+ display_records_size = data.records[display_range].size
375
+ non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : data.records_size)
360
376
 
361
- data.records_size.times do |i|
362
- next unless needs_x_scale?(i)
363
- text_x = order_position_on_chart(margin_left, chart_width, data.records_size, i, x_axis_type)
364
- scale_x = x_axis_type == :range ? order_position_on_chart(margin_left, chart_width, data.records_size + 1, i) : text_x
377
+ display_records_size.times do |i|
378
+ next unless needs_x_scale?(i, display_records_size)
379
+ text_x = order_position_on_chart(margin_left, chart_width, display_records_size, i, x_axis_type)
380
+ scale_x = x_axis_type == :range ? order_position_on_chart(margin_left, chart_width, display_records_size + 1, i) : text_x
365
381
  text_pen.draw_text(
366
382
  @x_scale_canvas,
367
383
  [text_x, height - margin_bottom + text_margin],
368
- format_x_label(data.name_values[i]),
384
+ format_x_label(data.name_values[i + non_display_size]),
369
385
  :text_anchor => 'middle', :alignment_baseline => 'top') if show_x_labels?
370
386
 
371
- sub_pen.draw_line_on_direction(
372
- @guid_front_canvas,
373
- [scale_x, height - margin_bottom],
374
- 0,
375
- -(axis_font ? axis_font.draw_size : Font::DEFAULT_SIZE) * 0.5) if i > 0 && i < data.records_size - (x_axis_type == :range ? 0 : 1)
387
+ if x_axis_type == :point || display_records_size <= max_x_label_count
388
+ sub_pen.draw_line_on_direction(
389
+ @guid_front_canvas,
390
+ [scale_x, height - margin_bottom],
391
+ 0,
392
+ -(axis_font ? axis_font.draw_size : Font::DEFAULT_SIZE) * 0.5) if i > 0 && i < display_records_size - (x_axis_type == :range ? 0 : 1)
393
+ end
376
394
  end
377
395
  end
378
396
 
@@ -388,19 +406,29 @@ module DYI
388
406
  def draw_line(id, color, settings)
389
407
  values = data.series(id)
390
408
  return if values.compact.size == 0
409
+ display_records_size = values[display_range].size
410
+ non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
391
411
  first_index = values.each_with_index {|value, i| break i if value}
392
412
  pen_options = {:color => color, :width => line_width}
393
413
  pen = line_chart_pen(color)
394
414
 
395
- x = order_position_on_chart(margin_left, chart_width, values.size, first_index, x_axis_type)
415
+ x = order_position_on_chart(margin_left, chart_width, display_records_size, first_index - non_display_size, x_axis_type)
396
416
  y = value_position_on_chart(margin_top, settings, values[first_index], true)
417
+ if show_data_label?
418
+ label_pen = Drawing::Pen.black_pen(:font => data_label_font)
419
+ font_size = label_pen.font ? label_pen.font.draw_size : Font::DEFAULT_SIZE
420
+ label_pen.draw_text(data_label_canvas, [x, y - font_size * 0.25], data_label_format ? values[first_index].strfnum(data_label_format) : values[first_index].to_s, :text_anchor => 'middle')
421
+ end
397
422
  pen.linejoin = 'bevel'
398
423
  polyline = pen.draw_polyline(@chart_front_canvas, [x, y], @chart_options) {|polyline|
399
424
  ((first_index + 1)...values.size).each do |i|
400
- x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type)
425
+ x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type)
401
426
  y = value_position_on_chart(margin_top, settings, values[i], true)
402
427
  polyline.line_to([x, y])
403
428
  end
429
+ if show_data_label?
430
+ label_pen.draw_text(data_label_canvas, [x, y - font_size * 0.25], data_label_format ? values[i].strfnum(data_label_format) : values[i].to_s, :text_anchor => 'middle')
431
+ end
404
432
  }
405
433
  if show_markers?
406
434
  marker_type = (markers && markers[id % markers.size]) || DEFAULT_MARKERS[id % DEFAULT_MARKERS.size]
@@ -412,16 +440,25 @@ module DYI
412
440
  def draw_area(id, color, settings)
413
441
  values = data.series(id)
414
442
  return if values.compact.size == 0
443
+ display_records_size = values[display_range].size
444
+ non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
415
445
  first_index = values.each_with_index {|value, i| break i if value}
416
446
  brush = area_chart_brush(color)
417
447
 
418
- x = order_position_on_chart(margin_left, chart_width, values.size, first_index, x_axis_type)
448
+ x = order_position_on_chart(margin_left, chart_width, display_records_size, first_index - non_display_size, x_axis_type)
419
449
  y = value_position_on_chart(margin_top, settings, settings[:min], true)
450
+ if show_data_label?
451
+ label_pen = Drawing::Pen.black_pen(:font => data_label_font)
452
+ font_size = label_pen.font ? label_pen.font.draw_size : Font::DEFAULT_SIZE
453
+ end
420
454
  polygone = brush.draw_polygon(@chart_front_canvas, [x, y], @chart_options) {|polygon|
421
455
  (first_index...values.size).each do |i|
422
- x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type)
456
+ x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type)
423
457
  y = value_position_on_chart(margin_top, settings, values[i], true)
424
458
  polygon.line_to([x, y])
459
+ if show_data_label?
460
+ label_pen.draw_text(data_label_canvas, [x, y - font_size * 0.25], data_label_format ? values[i].strfnum(data_label_format) : values[i].to_s, :text_anchor => 'middle')
461
+ end
425
462
  end
426
463
  y = value_position_on_chart(margin_top, settings, settings[:min], true)
427
464
  polygon.line_to([x, y])
@@ -433,16 +470,25 @@ module DYI
433
470
  bar_group = Shape::ShapeGroup.new(@chart_options).draw_on(@chart_front_canvas)
434
471
  values = data.series(id)
435
472
  return if values.compact.size == 0
436
- bar_width = chart_width * bar_width_ratio / values.size / (@bar_series.size + (@bar_series.size - 1) * bar_seriese_interval)
473
+ display_records_size = values[display_range].size
474
+ non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
475
+ bar_width = chart_width * bar_width_ratio / display_records_size / (@bar_series.size + (@bar_series.size - 1) * bar_seriese_interval)
437
476
 
438
477
  brush = bar_chart_brush(color, bar_width)
439
478
 
479
+ if show_data_label?
480
+ label_pen = Drawing::Pen.black_pen(:font => data_label_font)
481
+ font_size = label_pen.font ? label_pen.font.draw_size : Font::DEFAULT_SIZE
482
+ end
440
483
  values.each_with_index do |value, i|
441
484
  next if value.nil?
442
- x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type, bar_width_ratio) + bar_width * (1 + bar_seriese_interval) * @bar_series.index(id)
485
+ x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type, bar_width_ratio) + bar_width * (1 + bar_seriese_interval) * @bar_series.index(id)
443
486
  y = value_position_on_chart(margin_top, settings, value, true)
444
487
  brush = bar_chart_brush(data.has_field?(:color) ? data.records[i].color : color, bar_width) if data.has_field?(:color)
445
488
  brush.draw_rectangle(bar_group, [x, y], bar_width, height - margin_bottom - y)
489
+ if show_data_label?
490
+ label_pen.draw_text(data_label_canvas, [x + bar_width * 0.5, y - font_size * 0.4], data_label_format ? value.strfnum(data_label_format) : value.to_s, :text_anchor => 'middle')
491
+ end
446
492
  end
447
493
  bar_group.translate(back_translate_value[:dx] / 2, back_translate_value[:dy] / 2) if represent_3d?
448
494
  end
@@ -452,7 +498,9 @@ module DYI
452
498
 
453
499
  values = data.series(id)
454
500
  return if values.compact.size == 0
455
- bar_width = chart_width * bar_width_ratio / values.size
501
+ display_records_size = values[display_range].size
502
+ non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
503
+ bar_width = chart_width * bar_width_ratio / display_records_size
456
504
 
457
505
  brush = bar_chart_brush(color, bar_width)
458
506
 
@@ -461,7 +509,7 @@ module DYI
461
509
  values.each_with_index do |value, i|
462
510
  @stacked_values[i] ||= 0
463
511
  next if value.nil?
464
- x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type, bar_width_ratio)
512
+ x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type, bar_width_ratio)
465
513
  y = value_position_on_chart(margin_top, settings, (@stacked_values[i] += value), true)
466
514
  bar_height = value_position_on_chart(margin_top, settings, (@stacked_values[i] - value), true) - y
467
515
  # brush.color = data[:$color][i] if data[:$color]
@@ -93,6 +93,123 @@ module DYI
93
93
  end
94
94
  end
95
95
 
96
+ # Class representing a radial gradient.
97
+ # @since 1.3.0
98
+ class RadialGradient
99
+
100
+ # Array of strings that can be indicated as the property spread method.
101
+ SPREAD_METHODS = ['pad', 'reflect', 'repeat']
102
+
103
+ # @return [Coordinate] a center point of largest circle for the radial
104
+ # gradient
105
+ attr_reader :center_point
106
+ # @return [Length] a radius of largest circle for the radial gradient
107
+ attr_reader :radius
108
+ # @return [Coordinate] a focal point of largest circle for the radial
109
+ # gradient
110
+ attr_reader :focal_point
111
+ # @return [String] a string that mean what happens if the gradient
112
+ # starts or ends inside the bounds of the objects being painted by the
113
+ # gradient
114
+ attr_reader :spread_method
115
+
116
+ # @param [Array<Numeric>] center_point a relative position of the center
117
+ # point to the shape that is drawn
118
+ # @param [Numeric] radius a relative length of largest circle for the
119
+ # radial gradient to the shape that is drawn
120
+ # @param [Array<Numeric>] focal_point a relative position of the focal
121
+ # point of largest circle for the radial gradient to the shape that is
122
+ # drawn. If this paramater is nil, this paramater is considered to
123
+ # indicate the same value as center_point
124
+ # @param [String] spread_method a string that mean what happens if the
125
+ # gradient starts or ends inside the bounds of the objects being
126
+ # painted by the gradient
127
+ def initialize(center_point, radius, focal_point=nil, spread_method=nil)
128
+ @center_point = Coordinate.new(center_point)
129
+ @radius = Length.new(radius)
130
+ @focal_point = focal_point ? Coordinate.new(focal_point) : @center_point
131
+ self.spread_method = spread_method
132
+ @child_elements = []
133
+ end
134
+
135
+ # Adds a color to the gradient.
136
+ # @param [Numeirc] offset a ratio of distance from focal point to the
137
+ # edge of the largest circle
138
+ # @param [Color] color a color at the offset point
139
+ def add_color(offset, color)
140
+ @child_elements.push(GradientStop.new(offset, :color => color))
141
+ end
142
+
143
+ # Adds a opacity to the gradient.
144
+ # @param [Numeirc] offset a ratio of distance from focal point to the
145
+ # edge of the largest circle
146
+ # @param [Numeric] opacity a opecity at the offset point
147
+ def add_opacity(offset, opacity)
148
+ @child_elements.push(GradientStop.new(offset, :opacity => opacity))
149
+ end
150
+
151
+ # Adds a color and a opacity to the gradient.
152
+ # @param [Numeirc] offset a ratio of distance from focal point to the
153
+ # edge of the largest circle
154
+ # @param [Color] color a color at the offset point
155
+ # @param [Numeric] opacity a opecity at the offset point
156
+ def add_color_opacity(offset, color, opacity)
157
+ @child_elements.push(GradientStop.new(offset, :color => color, :opacity => opacity))
158
+ end
159
+
160
+ # Sets value to the spread method property.
161
+ # @param [String] spread_method a string that mean what happens if the
162
+ # gradient starts or ends inside the bounds of the objects being
163
+ # painted by the gradient
164
+ def spread_method=(value)
165
+ raise ArgumentError, "\"#{value}\" is invalid spreadMethod" if value && !SPREAD_METHODS.include?(value)
166
+ @spread_method = value
167
+ end
168
+
169
+ # Returns the array of the gradient colors.
170
+ # @return [Array<GradientStop>] the array of the gradient colors
171
+ def child_elements
172
+ @child_elements.clone
173
+ end
174
+
175
+ # Returns whether this object is a color.
176
+ # @return [Boolean] always false
177
+ def color?
178
+ true
179
+ end
180
+
181
+ # Writes the gradient on io object.
182
+ # @param [Formatter::Base] formatter an object that defines the image
183
+ # format
184
+ # @param [IO] io an io to be written
185
+ def write_as(formatter, io=$>)
186
+ formatter.write_radial_gradient(self, io)
187
+ end
188
+
189
+ class << self
190
+
191
+ public
192
+
193
+ # Create a gradient on which spaced the specified colors.
194
+ # @param [Color] colors a color that the gradient uses
195
+ # @return [RadialGradient] a gradient on which spaced the colors
196
+ def simple_gradation(*colors)
197
+ case count = colors.size
198
+ when 0
199
+ nil
200
+ when 1
201
+ Color.new(colors.first)
202
+ else
203
+ obj = new(['50%','50%'], '50%', ['50%','50%'])
204
+ colors.each_with_index do |color, i|
205
+ obj.add_color(i.quo(count - 1), color)
206
+ end
207
+ obj
208
+ end
209
+ end
210
+ end
211
+ end
212
+
96
213
  class GradientStop
97
214
  attr_reader :offset, :color, :opacity
98
215
 
@@ -312,7 +312,9 @@ module DYI
312
312
  # @return [Shape::Path] a drawn path
313
313
  # @see Shape::Path
314
314
  def draw_path(canvas, point, options={}, &block)
315
- Shape::Path.draw(point, merge_option(options), &block).draw_on(canvas)
315
+ path = Shape::Path.new(point, merge_option(options)).draw_on(canvas)
316
+ yield path
317
+ path
316
318
  end
317
319
 
318
320
  # Draws a free-form line or curve, and closes path finally. See methods of
@@ -324,7 +326,9 @@ module DYI
324
326
  # @return (see #draw_path)
325
327
  # @see (see #draw_path)
326
328
  def draw_closed_path(canvas, point, options={}, &block)
327
- Shape::Path.draw_and_close(point, merge_option(options), &block).draw_on(canvas)
329
+ path = draw_path(canvas, point, options, &block)
330
+ path.close_path unless path.close?
331
+ path
328
332
  end
329
333
 
330
334
  # Draws a circle to specify the center point and the radius.
@@ -139,7 +139,7 @@ module DYI
139
139
  if css_classes.include?(css_class.to_s)
140
140
  return nil
141
141
  end
142
- @css_class = css_classes.push(scc_class).join(' ')
142
+ @css_class = css_classes.push(css_class).join(' ')
143
143
  css_class
144
144
  end
145
145
 
@@ -172,14 +172,6 @@ module DYI
172
172
  publish_id
173
173
  end
174
174
 
175
- # Registers event listeners on this element.
176
- # @param [String] event_name an event name for which the user is registering
177
- # @param [Script::EcmaScript::EventListener, ] event_listener an event name for which the user is registering
178
- # @return [String] id for this element
179
- def add_event_listener(event_name, event_listener)
180
-
181
- end
182
-
183
175
  # Returns whether an event is set to the element.
184
176
  # @return [Boolean] true if an event is set to the element, false otherwise
185
177
  def event_target?
@@ -188,9 +180,12 @@ module DYI
188
180
 
189
181
  # Registers event listeners on this element.
190
182
  # @param [Symbol] event_name an event name for which the user is registering
191
- # @param [Script::SimpleScript] listener an event listener which contains
183
+ # @param [Script::EcmaScript::EventListener, #to_s] listener an event listener which contains
192
184
  # the methods to be called when the event occurs.
193
185
  def add_event_listener(event_name, listener)
186
+ unless listener.respond_to?(:related_to)
187
+ listener = DYI::Script::EcmaScript::EventListener.new(listener.to_s)
188
+ end
194
189
  listener.related_to(DYI::Event.new(event_name, self))
195
190
  if event_listeners.key?(event_name)
196
191
  unless event_listeners[event_name].include?(listener)
@@ -211,5 +206,11 @@ module DYI
211
206
  event_listeners[event_name].delete(listener)
212
207
  end
213
208
  end
209
+
210
+ # @since 1.3.0
211
+ def to_reused_source
212
+ publish_id
213
+ self
214
+ end
214
215
  end
215
216
  end
@@ -34,8 +34,11 @@ module DYI
34
34
  # output by SVG format.
35
35
  # @since 1.0.0
36
36
  class Event
37
+ =begin
38
+ # IMPLEMENT_EVENTS abolished at version 1.3.0
37
39
  IMPLEMENT_EVENTS = [:focusin,:focusout,:click,:mousedown,:mouseup,
38
40
  :mouseover,:mousemove,:mouseout,:load]
41
+ =end
39
42
 
40
43
  # @return [Symbol] event name
41
44
  attr_reader :event_name
@@ -50,9 +53,6 @@ module DYI
50
53
  # @raise [ArgumentError] unknown event name is given
51
54
  def initialize(event_name, target)
52
55
  event_name = event_name.to_sym
53
- unless IMPLEMENT_EVENTS.include?(event_name)
54
- raise ArgumentError, "`#{event_name}' is unknown event"
55
- end
56
56
  @event_name = event_name
57
57
  (@target = target).set_event(self)
58
58
  end