motion-plot 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.repl_history +0 -0
- data/Gemfile +9 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +22 -0
- data/app/app_delegate.rb +14 -0
- data/lib/motion-plot.rb +20 -0
- data/lib/motion-plot/chart/area_plot.rb +27 -0
- data/lib/motion-plot/chart/bar_plot.rb +74 -0
- data/lib/motion-plot/chart/base.rb +194 -0
- data/lib/motion-plot/chart/delegates/bar_delegate.rb +31 -0
- data/lib/motion-plot/chart/delegates/percent_bar_delegate.rb +60 -0
- data/lib/motion-plot/chart/delegates/stack_bar_delegate.rb +55 -0
- data/lib/motion-plot/chart/line_plot.rb +65 -0
- data/lib/motion-plot/chart/pie_plot.rb +128 -0
- data/lib/motion-plot/core_ext/ui_color.rb +5 -0
- data/lib/motion-plot/series/series.rb +44 -0
- data/lib/motion-plot/theme/theme.rb +22 -0
- data/lib/motion-plot/utilities/anchor_position.rb +27 -0
- data/lib/motion-plot/utilities/area_gradient.rb +18 -0
- data/lib/motion-plot/utilities/axis.rb +37 -0
- data/lib/motion-plot/utilities/data_label.rb +36 -0
- data/lib/motion-plot/utilities/legend.rb +45 -0
- data/lib/motion-plot/utilities/plot_area.rb +43 -0
- data/lib/motion-plot/utilities/plot_options.rb +16 -0
- data/lib/motion-plot/utilities/plot_symbol.rb +51 -0
- data/lib/motion-plot/utilities/style.rb +34 -0
- data/lib/motion-plot/utilities/text_style.rb +12 -0
- data/lib/motion-plot/utilities/title.rb +17 -0
- data/lib/motion-plot/version.rb +3 -0
- data/motion-plot.gemspec +23 -0
- metadata +127 -0
data/.gitignore
ADDED
data/.repl_history
ADDED
File without changes
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Amit Kumar
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Motion::Plot
|
2
|
+
|
3
|
+
Build native charts as you are used with Highcharts/D3. This library is a wrapper on top of CorePlot
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'motion-plot'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install motion-plot
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Look at examples directory for usage options.
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
$:.unshift("/Library/RubyMotion/lib")
|
3
|
+
require 'motion/project'
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
require "bundler/setup"
|
6
|
+
Bundler.require :default
|
7
|
+
|
8
|
+
$:.unshift("./lib/")
|
9
|
+
require './lib/motion-plot'
|
10
|
+
|
11
|
+
Motion::Project::App.setup do |app|
|
12
|
+
# Use `rake config' to see complete project settings.
|
13
|
+
app.name = 'MotionPlot'
|
14
|
+
|
15
|
+
|
16
|
+
app.frameworks << "QuartzCore"
|
17
|
+
|
18
|
+
app.pods do
|
19
|
+
pod "CorePlot"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/app/app_delegate.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class AppDelegate
|
2
|
+
|
3
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
4
|
+
|
5
|
+
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
6
|
+
|
7
|
+
@chart_view = GraphHostingView.alloc.initWithFrame [[10, 10], [App.frame.size.width-10, App.frame.size.height]]
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/motion-plot.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "motion-plot/version"
|
2
|
+
require 'bubble-wrap'
|
3
|
+
require 'motion-cocoapods'
|
4
|
+
require 'cocoapods'
|
5
|
+
|
6
|
+
|
7
|
+
Motion::Project::App.setup do |app|
|
8
|
+
|
9
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'motion-plot/**/*.rb')).each do |file|
|
10
|
+
app.files.unshift(file)
|
11
|
+
end
|
12
|
+
|
13
|
+
app.files.unshift(File.join(File.dirname(__FILE__), 'motion-plot/chart/base.rb'))
|
14
|
+
|
15
|
+
app.frameworks << "QuartzCore"
|
16
|
+
|
17
|
+
app.pods do
|
18
|
+
pod "CorePlot"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module MotionPlot
|
2
|
+
class Area < Line
|
3
|
+
|
4
|
+
def plot_type
|
5
|
+
"area"
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_series
|
9
|
+
super
|
10
|
+
@plots.each do |line|
|
11
|
+
add_area_gradient(line)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_area_gradient(line)
|
16
|
+
line.areaFill = AreaGradient.new(@orientation).fill_with(line.dataLineStyle.lineColor)
|
17
|
+
line.areaBaseValue = CPTDecimalFromString("0.0")
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def plot_type
|
23
|
+
"area"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module MotionPlot
|
2
|
+
class Bar < Base
|
3
|
+
|
4
|
+
attr_reader :data_hash
|
5
|
+
|
6
|
+
CPDBarInitialX = 0.0
|
7
|
+
|
8
|
+
def add_series
|
9
|
+
@stacking = @plot_options ? @plot_options.bar[:stacking] : nil
|
10
|
+
|
11
|
+
bar_x = CPDBarInitialX
|
12
|
+
@data_hash = {}
|
13
|
+
horizontal_bar = (@orientation == "vertical") ? true : false
|
14
|
+
@delegate_object = case @stacking
|
15
|
+
when "normal"
|
16
|
+
StackBarDelegate.new(self)
|
17
|
+
when "percent"
|
18
|
+
PercentBarDelegate.new(self)
|
19
|
+
else
|
20
|
+
BarDelegate.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
@series.keys.each_with_index do |name, index|
|
24
|
+
bar = CPTBarPlot.tubularBarPlotWithColor(@series[name].color, horizontalBars: horizontal_bar)
|
25
|
+
bar.barWidth = CPTDecimalFromDouble(@series[name].width)
|
26
|
+
bar.barOffset = CPTDecimalFromDouble(bar_x) unless(@stacking)
|
27
|
+
|
28
|
+
bar.identifier = name
|
29
|
+
|
30
|
+
_style = CPTMutableLineStyle.alloc.init
|
31
|
+
_style.lineWidth = 0.5
|
32
|
+
_style.lineColor = CPTColor.lightGrayColor
|
33
|
+
|
34
|
+
bar.lineStyle = _style
|
35
|
+
bar.dataSource = @delegate_object
|
36
|
+
bar.delegate = @delegate_object
|
37
|
+
bar.barBasesVary = @stacking ? true : false
|
38
|
+
|
39
|
+
@graph.addPlot(bar)
|
40
|
+
@plots << bar
|
41
|
+
|
42
|
+
animate(bar)
|
43
|
+
|
44
|
+
bar_x += @series[name].width
|
45
|
+
@data_hash[index] = @series[name].data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def animate(bar)
|
50
|
+
bar.anchorPoint = [0.0, 0.0]
|
51
|
+
scale_direction = (@orientation == "vertical") ? "transform.scale.x" : "transform.scale.y"
|
52
|
+
scaling = CABasicAnimation.animationWithKeyPath(scale_direction)
|
53
|
+
scaling.fromValue = 0.0
|
54
|
+
scaling.toValue = 1.0
|
55
|
+
scaling.duration = 1.0
|
56
|
+
scaling.removedOnCompletion = false
|
57
|
+
scaling.fillMode = KCAFillModeBackwards
|
58
|
+
|
59
|
+
bar.addAnimation(scaling, forKey:"scaling")
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
def default_style
|
64
|
+
{
|
65
|
+
width: 0.25
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def plot_type
|
70
|
+
"bar"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module MotionPlot
|
2
|
+
class Base
|
3
|
+
|
4
|
+
attr_reader :layer_hosting_view, :graph, :series, :plot_space, :major_grid_line_style, :plots, :xaxis, :yaxis, :title
|
5
|
+
|
6
|
+
attr_accessor :legend, :plot_symbol, :axes, :theme, :data_label, :orientation, :plot_options
|
7
|
+
|
8
|
+
def bootstrap(options)
|
9
|
+
options.each_pair {|key, value|
|
10
|
+
send("#{key}=", value) if(respond_to?("#{key}="))
|
11
|
+
}
|
12
|
+
|
13
|
+
if(options[:title])
|
14
|
+
@title = Title.new(options[:title])
|
15
|
+
end
|
16
|
+
|
17
|
+
@axes = {}
|
18
|
+
if(options[:xAxis])
|
19
|
+
@axes[:x] = Axis.new(options[:xAxis].merge(type: 'xaxis'))
|
20
|
+
else
|
21
|
+
@axes[:x] = Axis.new(type: 'xaxis', enabled: false)
|
22
|
+
end
|
23
|
+
|
24
|
+
if(options[:yAxis])
|
25
|
+
@axes[:y] = Axis.new(options[:yAxis].merge(type: 'yaxis'))
|
26
|
+
else
|
27
|
+
@axes[:y] = Axis.new(type: 'yaxis', enabled: false)
|
28
|
+
end
|
29
|
+
|
30
|
+
if(@plot_options)
|
31
|
+
@plot_options = PlotOptions.new(@plot_options)
|
32
|
+
end
|
33
|
+
|
34
|
+
@series = {}
|
35
|
+
series = options[:series]
|
36
|
+
series && series.each_with_index {|hash, index|
|
37
|
+
hash.merge!({index: index, defaults: default_style, plot_options: @plot_options, type: self.plot_type})
|
38
|
+
@series[hash[:name]] = Series.new(hash)
|
39
|
+
}
|
40
|
+
|
41
|
+
if(options[:legend])
|
42
|
+
@legend = Legend.new(options[:legend])
|
43
|
+
end
|
44
|
+
|
45
|
+
if(options[:data_label])
|
46
|
+
@data_label = DataLabel.new(options[:data_label])
|
47
|
+
end
|
48
|
+
|
49
|
+
if(@plot_symbol)
|
50
|
+
@plot_symbol = PlotSymbol.new(options[:plot_symbol])
|
51
|
+
end
|
52
|
+
|
53
|
+
@plots = []
|
54
|
+
end
|
55
|
+
|
56
|
+
def initWithOptions(options, containerView:container)
|
57
|
+
bootstrap(options)
|
58
|
+
|
59
|
+
@layer_hosting_view = CPTGraphHostingView.alloc.initWithFrame([[0, 0], [container.frame.size.width, container.frame.size.height]])
|
60
|
+
|
61
|
+
bounds = @layer_hosting_view.bounds
|
62
|
+
|
63
|
+
# create and assign chart to the hosting view.
|
64
|
+
@graph = CPTXYGraph.alloc.initWithFrame(bounds)
|
65
|
+
@layer_hosting_view.hostedGraph = @graph
|
66
|
+
|
67
|
+
add_chart_title(@title) if(@title)
|
68
|
+
|
69
|
+
@graph.applyTheme(@theme || Theme.default)
|
70
|
+
|
71
|
+
default_padding
|
72
|
+
|
73
|
+
@chart_layers = [NSNumber.numberWithInt(CPTGraphLayerTypePlots), NSNumber.numberWithInt(CPTGraphLayerTypeMajorGridLines), NSNumber.numberWithInt(CPTGraphLayerTypeMinorGridLines), NSNumber.numberWithInt(CPTGraphLayerTypeAxisLines), NSNumber.numberWithInt(CPTGraphLayerTypeAxisLabels), NSNumber.numberWithInt(CPTGraphLayerTypeAxisTitles)]
|
74
|
+
@graph.topDownLayerOrder = @chart_layers
|
75
|
+
|
76
|
+
|
77
|
+
# add plot space
|
78
|
+
add_plot_space
|
79
|
+
|
80
|
+
@major_grid_line_style = CPTMutableLineStyle.lineStyle
|
81
|
+
@major_grid_line_style.lineWidth = 0.75
|
82
|
+
@major_grid_line_style.lineColor = CPTColor.grayColor.colorWithAlphaComponent(0.25)
|
83
|
+
|
84
|
+
axisSet = @graph.axisSet
|
85
|
+
|
86
|
+
# Setting up x-axis
|
87
|
+
if(@axes[:x].enabled?)
|
88
|
+
axis = @axes[:x]
|
89
|
+
@xaxis = axisSet.xAxis
|
90
|
+
@xaxis.majorGridLineStyle = @major_grid_line_style
|
91
|
+
@xaxis.minorTicksPerInterval = 1
|
92
|
+
|
93
|
+
add_axis_title(@xaxis, axis.title)
|
94
|
+
|
95
|
+
if(axis.labels)
|
96
|
+
labels = axis.labels.each_with_index.map do |l, i|
|
97
|
+
@xaxis.labelingPolicy = CPTAxisLabelingPolicyNone
|
98
|
+
label = CPTAxisLabel.alloc.initWithText(l, textStyle: axis.text_style)
|
99
|
+
label.tickLocation = CPTDecimalFromInt(i)
|
100
|
+
label.offset = 3.0
|
101
|
+
label
|
102
|
+
end
|
103
|
+
|
104
|
+
@xaxis.axisLabels = NSSet.setWithArray(labels)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Setting up y-axis
|
109
|
+
if(@axes[:y].enabled?)
|
110
|
+
@yaxis = axisSet.yAxis
|
111
|
+
@yaxis.majorGridLineStyle = @major_grid_line_style
|
112
|
+
@yaxis.minorTicksPerInterval = 1
|
113
|
+
@yaxis.labelingPolicy = CPTAxisLabelingPolicyAutomatic
|
114
|
+
|
115
|
+
axis = @axes[:y]
|
116
|
+
add_axis_title(@yaxis, axis.title)
|
117
|
+
@yaxis.setLabelTextStyle(axis.text_style)
|
118
|
+
end
|
119
|
+
|
120
|
+
@graph.axisSet = nil if(not @axes[:x].enabled? and not @axes[:y].enabled?)
|
121
|
+
|
122
|
+
add_series
|
123
|
+
|
124
|
+
add_legend if(@legend.enabled?)
|
125
|
+
|
126
|
+
add_xy_range
|
127
|
+
|
128
|
+
@layer_hosting_view
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_plot_space
|
132
|
+
@plot_space = @graph.defaultPlotSpace
|
133
|
+
@plot_space.delegate = self
|
134
|
+
@plot_space.allowsUserInteraction = true
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_plot_symbol(plot, index)
|
138
|
+
plot.plotSymbol = @plot_symbol.symbol_for(plot, atIndex: index)
|
139
|
+
plot.plotSymbolMarginForHitDetection = 5.0
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_xy_range
|
143
|
+
return if(not @axes[:x].enabled? and not @axes[:y].enabled?)
|
144
|
+
@plot_space.scaleToFitPlots(@plots)
|
145
|
+
x_range = @plot_space.xRange.mutableCopy
|
146
|
+
y_range = @plot_space.yRange.mutableCopy
|
147
|
+
|
148
|
+
x_range.expandRangeByFactor(CPTDecimalFromDouble(1.03))
|
149
|
+
y_range.expandRangeByFactor(CPTDecimalFromDouble(1.03))
|
150
|
+
|
151
|
+
@plot_space.xRange = x_range
|
152
|
+
@plot_space.yRange = y_range
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_chart_title(title)
|
156
|
+
@graph.title = title.text
|
157
|
+
@graph.titleTextStyle = title.text_style
|
158
|
+
@graph.titlePlotAreaFrameAnchor = title.position
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_axis_title(axis, title)
|
162
|
+
axis.title = title.text
|
163
|
+
axis.titleTextStyle = title.text_style
|
164
|
+
axis.titleOffset = title.style.offset
|
165
|
+
end
|
166
|
+
|
167
|
+
def add_legend
|
168
|
+
@graph.legend = @legend.cpt_legend(@graph)
|
169
|
+
@graph.legendAnchor = @legend.position
|
170
|
+
@graph.legendDisplacement = @legend.displacement
|
171
|
+
end
|
172
|
+
|
173
|
+
def default_padding
|
174
|
+
@graph.plotAreaFrame.masksToBorder = false
|
175
|
+
@graph.plotAreaFrame.borderLineStyle = nil
|
176
|
+
@graph.plotAreaFrame.paddingLeft = 50.0
|
177
|
+
@graph.plotAreaFrame.paddingTop = 10.0
|
178
|
+
@graph.plotAreaFrame.paddingRight = 20.0
|
179
|
+
@graph.plotAreaFrame.paddingBottom = 20.0
|
180
|
+
|
181
|
+
|
182
|
+
@graph.paddingLeft = 5.0
|
183
|
+
@graph.paddingRight = 0.0
|
184
|
+
@graph.paddingTop = 10.0
|
185
|
+
@graph.paddingBottom = 10.0
|
186
|
+
end
|
187
|
+
|
188
|
+
protected
|
189
|
+
def default_style
|
190
|
+
# inheriting classes should implement this
|
191
|
+
{}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module MotionPlot
|
2
|
+
class BarDelegate
|
3
|
+
|
4
|
+
# delegate :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
|
+
data = @delegated_to.series[plot.identifier].data
|
16
|
+
|
17
|
+
(field_enum == CPTBarPlotFieldBarTip) ? data[index] : index
|
18
|
+
end
|
19
|
+
|
20
|
+
def barPlot(plot, barWasSelectedAtRecordIndex:index)
|
21
|
+
if(@delegated_to.data_label and @delegated_to.data_label.annotation)
|
22
|
+
@delegated_to.graph.plotAreaFrame.plotArea.removeAnnotation(@delegated_to.data_label.annotation)
|
23
|
+
@delegated_to.data_label.annotation = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
y_value = @delegated_to.series[plot.identifier].data[index].round(2)
|
27
|
+
@delegated_to.graph.plotAreaFrame.plotArea.addAnnotation(@delegated_to.data_label.annotation_for(y_value, atCoordinate: [index+CPTDecimalFloatValue(plot.barOffset), y_value], plotSpace: @delegated_to.graph.defaultPlotSpace))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|