dyi 1.2.1 → 1.3.0

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