motion-plot 0.4.2

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.
@@ -0,0 +1,60 @@
1
+ module MotionPlot
2
+ class PercentBarDelegate
3
+
4
+ def initialize(source)
5
+ @delegated_to = source
6
+ @number_of_plots = @delegated_to.series.keys.size
7
+ end
8
+
9
+ def numberOfRecordsForPlot(plot)
10
+ @delegated_to.series[plot.identifier].data.size
11
+ end
12
+
13
+ def numberForPlot(plot, field:field_enum, recordIndex:index)
14
+ case field_enum
15
+ when CPTBarPlotFieldBarLocation
16
+ index
17
+ when CPTBarPlotFieldBarTip
18
+ plot_index = @delegated_to.series[plot.identifier].index
19
+ record_data = @delegated_to.series[plot.identifier].data[index]
20
+ bar_tip_value(plot_index, recordIndex:index, startValue:record_data)
21
+ when CPTBarPlotFieldBarBase
22
+ plot_index = @delegated_to.series[plot.identifier].index
23
+ bar_base_value(plot_index, recordIndex:index)
24
+ end
25
+ end
26
+
27
+ def barPlot(plot, barWasSelectedAtRecordIndex:index)
28
+ if(@delegated_to.data_label and @delegated_to.data_label.annotation)
29
+ @delegated_to.graph.plotAreaFrame.plotArea.removeAnnotation(@delegated_to.data_label.annotation)
30
+ @delegated_to.data_label.annotation = nil
31
+ end
32
+
33
+ y_value = (@delegated_to.series[plot.identifier].data[index].round(2) / total_sum_at_index(index)) * 100
34
+ plot_index = @delegated_to.series[plot.identifier].index
35
+ y_pos = (0..plot_index).inject(0) {|base, i| base + (@delegated_to.data_hash[i][index] / total_sum_at_index(index)) * 100 }
36
+
37
+ @delegated_to.graph.plotAreaFrame.plotArea.addAnnotation(@delegated_to.data_label.annotation_for("#{y_value} %", atCoordinate: [index+CPTDecimalFloatValue(plot.barOffset), y_pos], plotSpace: @delegated_to.graph.defaultPlotSpace))
38
+ end
39
+
40
+ protected
41
+ def bar_base_value(plot_index, recordIndex:index)
42
+ return 0 if(plot_index == 0)
43
+
44
+ (0..plot_index-1).inject(0) {|base, i| base + (@delegated_to.data_hash[i][index] / total_sum_at_index(index))*100 }
45
+ end
46
+
47
+ def bar_tip_value(plot_index, recordIndex:index, startValue:value)
48
+ return (value / total_sum_at_index(index))*100 if(plot_index == 0)
49
+
50
+ (0..plot_index).inject(0) {|base, i| base + (@delegated_to.data_hash[i][index] / total_sum_at_index(index))*100 }
51
+ end
52
+
53
+ def total_sum_at_index(index)
54
+ total ||= (0..@number_of_plots-1).inject(0) {|total, i| total + @delegated_to.data_hash[i][index]}
55
+
56
+ total
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,55 @@
1
+ module MotionPlot
2
+ class StackBarDelegate
3
+
4
+ # consign :series, :to => :delegated_to
5
+
6
+ def initialize(source)
7
+ @delegated_to = source
8
+ end
9
+
10
+ def numberOfRecordsForPlot(plot)
11
+ @delegated_to.series[plot.identifier].data.size
12
+ end
13
+
14
+ def numberForPlot(plot, field:field_enum, recordIndex:index)
15
+ case field_enum
16
+ when CPTBarPlotFieldBarLocation
17
+ index
18
+ when CPTBarPlotFieldBarTip
19
+ plot_index = @delegated_to.series[plot.identifier].index
20
+ record_data = @delegated_to.series[plot.identifier].data[index]
21
+ bar_tip_value(plot_index, recordIndex:index, startValue:record_data)
22
+ when CPTBarPlotFieldBarBase
23
+ plot_index = @delegated_to.series[plot.identifier].index
24
+ bar_base_value(plot_index, recordIndex:index)
25
+ end
26
+ end
27
+
28
+ def barPlot(plot, barWasSelectedAtRecordIndex:index)
29
+ if(@delegated_to.data_label and @delegated_to.data_label.annotation)
30
+ @delegated_to.graph.plotAreaFrame.plotArea.removeAnnotation(@delegated_to.data_label.annotation)
31
+ @delegated_to.data_label.annotation = nil
32
+ end
33
+
34
+ y_value = @delegated_to.series[plot.identifier].data[index].round(2)
35
+ plot_index = @delegated_to.series[plot.identifier].index
36
+ y_pos = (0..plot_index).inject(0) {|base, i| base + @delegated_to.data_hash[i][index]}
37
+
38
+ @delegated_to.graph.plotAreaFrame.plotArea.addAnnotation(@delegated_to.data_label.annotation_for(y_value, atCoordinate: [index+CPTDecimalFloatValue(plot.barOffset), y_pos], plotSpace: @delegated_to.graph.defaultPlotSpace))
39
+ end
40
+
41
+ protected
42
+ def bar_base_value(plot_index, recordIndex:index)
43
+ return 0 if(plot_index == 0)
44
+
45
+ (0..plot_index-1).inject(0) {|base, i| base + @delegated_to.data_hash[i][index] }
46
+ end
47
+
48
+ def bar_tip_value(plot_index, recordIndex:index, startValue:value)
49
+ return value if(plot_index == 0)
50
+
51
+ (0..plot_index).inject(0) {|base, i| base + @delegated_to.data_hash[i][index] }
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,65 @@
1
+ module MotionPlot
2
+ class Line < Base
3
+
4
+ attr_accessor :curve_inerpolation
5
+
6
+ def add_series
7
+ @series.keys.each_with_index do |name, index|
8
+ line = CPTScatterPlot.alloc.initWithFrame(CGRectNull)
9
+ line.identifier = name
10
+
11
+ line_style = line.dataLineStyle.mutableCopy
12
+ line_style.lineWidth = @series[name].width
13
+
14
+ line_style.lineColor = @series[name].color
15
+
16
+ line.dataLineStyle = line_style
17
+ line.dataSource = self
18
+ line.delegate = self
19
+ line.interpolation = CPTScatterPlotInterpolationCurved if(@curve_inerpolation)
20
+
21
+ add_plot_symbol(line, index) if(@plot_symbol)
22
+
23
+ @graph.addPlot(line)
24
+ @plots << line
25
+ end
26
+ end
27
+
28
+ # This implementation of this method will put the line graph in a fix position so it won't be scrollable.
29
+ def plotSpace(space, willChangePlotRangeTo:new_range, forCoordinate:coordinate)
30
+ (coordinate == CPTCoordinateY) ? space.yRange : space.xRange
31
+ end
32
+
33
+ def scatterPlot(plot, plotSymbolWasSelectedAtRecordIndex:index)
34
+ if(@data_label and @data_label.annotation)
35
+ @graph.plotAreaFrame.plotArea.removeAnnotation(@data_label.annotation)
36
+ @data_label.annotation = nil
37
+ end
38
+
39
+ y_value = @series[plot.identifier].data[index].round(2)
40
+ @graph.plotAreaFrame.plotArea.addAnnotation(@data_label.annotation_for(y_value, atCoordinate: [index, y_value], plotSpace: @graph.defaultPlotSpace))
41
+ end
42
+
43
+ def numberOfRecordsForPlot(plot)
44
+ @series[plot.identifier].data.size
45
+ end
46
+
47
+ def numberForPlot(plot, field:field_enum, recordIndex:index)
48
+ data = @series[plot.identifier].data
49
+
50
+ (field_enum == CPTScatterPlotFieldY) ? data[index] : index
51
+ end
52
+
53
+ protected
54
+ def default_style
55
+ {
56
+ width: 2.0
57
+ }
58
+ end
59
+
60
+ def plot_type
61
+ "line"
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,128 @@
1
+ module MotionPlot
2
+ class Pie < Base
3
+
4
+ attr_accessor :selected_slice
5
+
6
+ def add_series
7
+ pie = CPTPieChart.alloc.init
8
+ pie.dataSource = self
9
+ pie.delegate = self
10
+ pie.pieRadius = pie_radius
11
+ pie.startAngle = Math::PI/4
12
+ pie.sliceDirection = CPTPieDirectionClockwise #CPTPieDirectionCounterClockwise
13
+ pie.identifier = plot_identifier
14
+
15
+ series_data.each_with_index {|obj, i| @selected_slice = i if(obj[:selected]) }
16
+
17
+ add_gradient(pie)
18
+
19
+ animate(pie)
20
+ @graph.addPlot(pie)
21
+ end
22
+
23
+ def default_padding
24
+ pie_series.style.paddings_for(@graph)
25
+ pie_series.style.plot_area.add_style(@graph.plotAreaFrame)
26
+ end
27
+
28
+ def numberOfRecordsForPlot(plot)
29
+ series_data.size
30
+ end
31
+
32
+ def numberForPlot(plot, field: fieldEnum, recordIndex: index)
33
+ fieldEnum == CPTPieChartFieldSliceWidth ? series_data[index][:y] : index
34
+ end
35
+
36
+ def legendTitleForPieChart(pie, recordIndex: index)
37
+ series_data[index][:name]
38
+ end
39
+
40
+ def sliceFillForPieChart(plot, recordIndex: index)
41
+ CPTFill.alloc.initWithColor Series::COLORS[index].to_color.to_cpt_color
42
+ end
43
+
44
+ def radialOffsetForPieChart(plot, recordIndex: index)
45
+ offset = 0.0
46
+
47
+ if(@selected_slice == index)
48
+ offset = pie_radius / 25.0
49
+ end
50
+
51
+ offset
52
+ end
53
+
54
+ def pieChart(plot, sliceWasSelectedAtRecordIndex: index)
55
+ @selected_slice = index
56
+ plot.reloadData
57
+ end
58
+
59
+ def dataLabelForPlot(plot, recordIndex: index)
60
+ @data_label.annotation_text_style(series_data[index][:y].round(2))
61
+ end
62
+
63
+ protected
64
+ def plot_type
65
+ "pie"
66
+ end
67
+
68
+ def add_gradient(pie)
69
+ if(pie_series.style.gradient)
70
+ overlay_gradient = CPTGradient.alloc.init
71
+ overlay_gradient.gradientType = CPTGradientTypeRadial
72
+
73
+ overlay_gradient = overlay_gradient.addColorStop(CPTColor.blackColor.colorWithAlphaComponent(0.0), atPosition:0.0)
74
+ overlay_gradient = overlay_gradient.addColorStop(CPTColor.blackColor.colorWithAlphaComponent(0.3), atPosition:0.9)
75
+ overlay_gradient = overlay_gradient.addColorStop(CPTColor.blackColor.colorWithAlphaComponent(0.7), atPosition:1.0)
76
+ pie.overlayFill = overlay_gradient
77
+ end
78
+ end
79
+
80
+ def pie_radius
81
+ [
82
+ 0.8 * (@layer_hosting_view.frame.size.height - 2 * @graph.paddingLeft) / 2.0,
83
+ 0.8 * (@layer_hosting_view.frame.size.width - 2 * @graph.paddingTop) / 2.0
84
+ ].min
85
+ end
86
+
87
+ def plot_identifier
88
+ @series.keys.select{|k| @series[k].type == plot_type}.first
89
+ end
90
+
91
+ def series_data
92
+ pie_series.data
93
+ end
94
+
95
+ def pie_series
96
+ @series[plot_identifier]
97
+ end
98
+
99
+ def animate(plot)
100
+ CATransaction.begin
101
+ CATransaction.setAnimationDuration 2.0
102
+ CATransaction.setAnimationTimingFunction CAMediaTimingFunction.functionWithName(KCAMediaTimingFunctionEaseIn)
103
+
104
+ radial_animation = CABasicAnimation.animationWithKeyPath("pieRadius")
105
+
106
+ radial_animation.fromValue = 0.0
107
+ radial_animation.toValue = pie_radius
108
+ radial_animation.duration = 1.0
109
+ radial_animation.removedOnCompletion = false
110
+ radial_animation.fillMode = KCAFillModeForwards
111
+
112
+ plot.addAnimation(radial_animation, forKey:"pieRadius")
113
+
114
+
115
+ angle_animation = CABasicAnimation.animationWithKeyPath 'angle'
116
+ angle_animation.fromValue = 0.0
117
+ angle_animation.toValue = Math::PI/4
118
+ angle_animation.duration = 1.0
119
+ angle_animation.removedOnCompletion = false
120
+ angle_animation.fillMode = KCAFillModeForwards
121
+
122
+ plot.addAnimation(angle_animation, forKey:"angle")
123
+
124
+ CATransaction.commit
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,5 @@
1
+ class UIColor
2
+ def to_cpt_color
3
+ CPTColor.alloc.initWithCGColor(self.CGColor)
4
+ end
5
+ end
@@ -0,0 +1,44 @@
1
+ module MotionPlot
2
+ class Series
3
+
4
+ COLORS = ['4572A7', 'AA4643', '89A54E', '80699B', '3D96AE', 'DB843D', '92A8CD', 'A47D7C', 'B5CA92']
5
+
6
+ attr_accessor :name, :data, :index, :type, :style
7
+
8
+ def initialize(args={})
9
+ args.each_pair {|key, value|
10
+ send("#{key}=", value) if(respond_to?("#{key}="))
11
+ }
12
+
13
+ style_attr = args[:defaults].merge!(color: COLORS[args[:index]])
14
+ merge_plot_options(style_attr, args[:plot_options])
15
+ merge_style(style_attr, args[:style])
16
+
17
+ @style = Style.new(style_attr)
18
+ end
19
+
20
+ def color
21
+ @style.color
22
+ end
23
+
24
+ def width
25
+ @style.width
26
+ end
27
+
28
+ private
29
+ def merge_plot_options(style, plot_options)
30
+ return if(plot_options.nil?)
31
+ return if(plot_options.send(type).nil?)
32
+ return if(plot_options.send(type)[:style].nil?)
33
+
34
+ merge_style(style, plot_options.send(type)[:style])
35
+ end
36
+
37
+ def merge_style(old_style, new_style)
38
+ old_style.merge!(new_style) {|key, old_val, new_val|
39
+ new_val.nil? ? old_val : new_val
40
+ } if(new_style)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module MotionPlot
2
+ class Theme
3
+ DEFAULTS = {
4
+ :dark_gradient => KCPTDarkGradientTheme,
5
+ :plain_black => KCPTPlainBlackTheme,
6
+ :plain_white => KCPTPlainWhiteTheme,
7
+ :slate => KCPTSlateTheme,
8
+ :stocks => KCPTStocksTheme
9
+ }
10
+
11
+ class << self
12
+ def method_missing(m, *args, &block)
13
+ method_name = m == :default ? :plain_white : m
14
+
15
+ raise unless(DEFAULTS.keys.include?(method_name))
16
+
17
+ CPTTheme.themeNamed(DEFAULTS[method_name])
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ module MotionPlot
2
+ class AnchorPosition
3
+ DEFAULTS = {
4
+ :top_right => CPTRectAnchorTopRight,
5
+ :bottom_left => CPTRectAnchorBottomLeft,
6
+ :bottom => CPTRectAnchorBottom,
7
+ :bottom_right => CPTRectAnchorBottomRight,
8
+ :left => CPTRectAnchorLeft,
9
+ :right => CPTRectAnchorRight,
10
+ :top_left => CPTRectAnchorTopLeft,
11
+ :top => CPTRectAnchorTop,
12
+ :center => CPTRectAnchorCenter
13
+ }
14
+
15
+ class << self
16
+
17
+ def method_missing(m, *args, &block)
18
+ method_name = m == :default ? :top : m
19
+
20
+ raise unless(DEFAULTS.keys.include?(method_name))
21
+
22
+ DEFAULTS[method_name]
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ module MotionPlot
2
+ class AreaGradient
3
+
4
+ attr_accessor :angle
5
+
6
+ def initialize(orientation)
7
+ @angle = (orientation == "vertical") ? 0.0 : -90.0
8
+ end
9
+
10
+ def fill_with(color)
11
+ gradient = CPTGradient.gradientWithBeginningColor(color, endingColor:CPTColor.clearColor)
12
+ gradient.angle = @angle
13
+
14
+ CPTFill.fillWithGradient(gradient)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,37 @@
1
+ module MotionPlot
2
+ class Axis
3
+ attr_accessor :title, :enabled, :type, :labels, :style
4
+
5
+ def initialize(args)
6
+ args.each_pair {|key, value|
7
+ send("#{key}=", value) if(respond_to?("#{key}="))
8
+ }
9
+
10
+ if(args[:title])
11
+ @title = Title.new(args[:title])
12
+ end
13
+
14
+ if(args[:style])
15
+ @style = Style.new(args[:style])
16
+ else
17
+ @style = Style.new
18
+ end
19
+ end
20
+
21
+ def text_style
22
+ TextStyle.cpt_text_style(@style)
23
+ end
24
+
25
+ def is_x?
26
+ type == "xaxis"
27
+ end
28
+
29
+ def is_y?
30
+ type == "yaxis"
31
+ end
32
+
33
+ def enabled?
34
+ enabled
35
+ end
36
+ end
37
+ end