Core Plot Design Overview
This document describes the main classes of Core Plot, and how they work together.
Design Considerations
Before delving into the classes that make up Core Plot, it is worth considering the design goals of the framework. Core Plot has been developed to run on both Mac OS X and iOS. This places some restrictions on the technologies that can be used: AppKit drawing is not possible, and view classes like NSView and UIView can only be used as host views. Drawing is instead performed using the low-level Quartz 2D API, and Core Animation layers are used to build up the various different aspects of a graph.
It's not all bad news, because utilizing Core Animation also opens up a whole range of possibilities for introducing 'eye-candy'. Graphs can be animated, with transitions and effects. The objective is to have Core Plot be capable of not only producing publication quality still images, but also stunning graphical effects and interactivity.
Another objective that is influential in the design of Core Plot is that it should behave as much as possible from a developer's perspective as a built-in framework. Design patterns and technologies used in Apple's own frameworks, such as the data source pattern, delegation, and bindings, are all supported in Core Plot.
Anatomy of a Graph
This diagram shows a standard bar graph with two data sets plotted. Below, the chart has been annotated to show the various components of the the chart, and the naming scheme used in Core Plot to identify them.
Class Diagram
This standard UML class diagram gives a static view of the main classes in the framework. The cardinality of relationships is given by a label, with a '1' indicating a to-one relationship, and an asterisk (*) representing a to-many relationship.
Objects and Layers
This diagram shows run time relationships between objects (right) together with layers in the Core Animation layer tree (left). Color coding shows the correspondence between objects and their corresponding layers.
Layers
Core Animation's layer class, CALayer, is not very suitable for producing vector images, as required for publication quality graphics, and provides no support for event handling. For these reasons, Core Plot layers derive from a class called CPTLayer, which itself is a subclass of CALayer. CPTLayer includes drawing methods that make it possible to produce high quality vector graphics, as well as event handling methods to facilitate interaction.
The drawing methods include
-(void)renderAsVectorInContext:(CGContextRef)context;
-(void)recursivelyRenderInContext:(CGContextRef)context;
-(NSData *)dataForPDFRepresentationOfLayer;
When subclassing CPTLayer, it is important that you don't just override the standard drawInContext: method, but instead override renderAsVectorInContext:. That way, the layer will draw properly when vector graphics are generated, as well as when drawn to the screen.
Graphs
The central class of Core Plot is CPTGraph. In Core Plot, the term 'graph' refers to the complete diagram, which includes axes, labels, a title, and one or more plots (eg histogram, line plot). CPTGraph is an abstract class from which all graph classes derive.
A graph class is fundamentally a factory: It is responsible for creating the various objects that make up the graphic, and for setting up the appropriate relationships. The CPTGraph class holds references to objects of other high level classes, such as CPTAxisSet, CPTPlotArea, and CPTPlotSpace. It also keeps track of the plots (CPTPlot instances) that are displayed on the graph.
@interface CPTGraph : CPTBorderedLayer {
@private
CPTPlotAreaFrame *plotAreaFrame;
NSMutableArray *plots;
NSMutableArray *plotSpaces;
NSString *title;
CPTTextStyle *titleTextStyle;
CPTRectAnchor titlePlotAreaFrameAnchor;
CGPoint titleDisplacement;
CPTLayerAnnotation *titleAnnotation;
CPTLegend *legend;
CPTLayerAnnotation *legendAnnotation;
CPTRectAnchor legendAnchor;
CGPoint legendDisplacement;
}
@property (nonatomic, readwrite, copy) NSString *title;
@property (nonatomic, readwrite, copy) CPTTextStyle *titleTextStyle;
@property (nonatomic, readwrite, assign) CGPoint titleDisplacement;
@property (nonatomic, readwrite, assign) CPTRectAnchor titlePlotAreaFrameAnchor;
@property (nonatomic, readwrite, retain) CPTAxisSet *axisSet;
@property (nonatomic, readwrite, retain) CPTPlotAreaFrame *plotAreaFrame;
@property (nonatomic, readonly, retain) CPTPlotSpace *defaultPlotSpace;
@property (nonatomic, readwrite, retain) NSArray *topDownLayerOrder;
@property (nonatomic, readwrite, retain) CPTLegend *legend;
@property (nonatomic, readwrite, assign) CPTRectAnchor legendAnchor;
@property (nonatomic, readwrite, assign) CGPoint legendDisplacement;
-(void)reloadData;
-(void)reloadDataIfNeeded;
-(NSArray *)allPlots;
-(CPTPlot *)plotAtIndex:(NSUInteger)index;
-(CPTPlot *)plotWithIdentifier:(id <NSCopying>)identifier;
-(void)addPlot:(CPTPlot *)plot;
-(void)addPlot:(CPTPlot *)plot toPlotSpace:(CPTPlotSpace *)space;
-(void)removePlot:(CPTPlot *)plot;
-(void)removePlotWithIdentifier:(id <NSCopying>)identifier;
-(void)insertPlot:(CPTPlot *)plot atIndex:(NSUInteger)index;
-(void)insertPlot:(CPTPlot *)plot atIndex:(NSUInteger)index intoPlotSpace:(CPTPlotSpace *)space;
-(NSArray *)allPlotSpaces;
-(CPTPlotSpace *)plotSpaceAtIndex:(NSUInteger)index;
-(CPTPlotSpace *)plotSpaceWithIdentifier:(id <NSCopying>)identifier;
-(void)addPlotSpace:(CPTPlotSpace *)space;
-(void)removePlotSpace:(CPTPlotSpace *)plotSpace;
-(void)applyTheme:(CPTTheme *)theme;
@endCPTGraph is an abstract superclass; subclasses like CPTXYGraph are actually responsible for doing most of creation and organization of graph components. Each subclass is usually associated with particular subclasses of the various layers that make up the graph. For example, the CPTXYGraph creates an instance of CPTXYAxisSet, and CPTXYPlotSpace. This is a classic example of the Factory design pattern, as described in the GoF Design Patterns book (Gamma, et al).
Plot Area
The plot area is that part of a graph where data is plotted. It is typically bordered by axes, and grid lines may also appear in the plot area. There is only one plot area for each graph, and it is represented by the class CPTPlotArea. The plot area is surrounded by a CPTPlotAreaFrame, which can be used to add a border to the area.
Plot Spaces
Plot spaces define the mapping between the coordinate space, in which a set of data exists, and the drawing space inside the plot area.
For example, if you were to plot the speed of a train versus time, the data space would have time along the horizontal axis, and speed on the vertical axis. The data space may range from 0 to 150 km/hr for the speed, and 0 to 180 minutes for the time. The drawing space, on the other hand, is dictated by the bounds of the plot area. A plot space, represented by a descendant of the CPTPlotSpace class, defines the mapping between a coordinate in the data space, and the corresponding point in the plot area.
It is tempting to use the built in support for affine transformations to perform the mapping between the data and drawing spaces, but this would be very limiting, because the mapping does not have to be linear. For example, it is not uncommon to use a logarithmic scale for the data space.
To facilitate as wide a range of data sets as possible, values in the data space can be stored internally as NSDecimalNumber instances. It makes no sense to store values in the drawing space in this way, because drawing coordinates are represented in Cocoa by floating point numbers (CGFloat), and any extra precision would be lost.
A CPTPlotSpace subclass must implement methods for transforming from drawing coordinates to data coordinates, and for converting from data coordinates to drawing coordinates.
-(CGPoint)plotAreaViewPointForPlotPoint:(NSDecimal *)plotPoint;
-(CGPoint)plotAreaViewPointForDoublePrecisionPlotPoint:(double *)plotPoint;
-(void)plotPoint:(NSDecimal *)plotPoint forPlotAreaViewPoint:(CGPoint)point;
-(void)doublePrecisionPlotPoint:(double *)plotPoint forPlotAreaViewPoint:(CGPoint)point;
Data coordinates --- represented here by the 'plot point' --- are passed as an C array of NSDecimals or doubles. Drawing coordinates --- represented here by the 'view point' --- are passed as standard CGPoint instances.
Whenever an object needs to perform the transform from data to drawing coordinates, or vice versa, it should query the plot space to which it corresponds. For example, instances of CPTPlot (discussed below) are each associated with a particular plot space, and use that plot space to determine where in the plot area they should draw.
It is important to realize that a single graph may contain multiple plots, and that these plots may be plotted on different scales. For example, one plot may need to be drawn with a logarithmic scale, and a separate plot may be drawn on a linear scale. There is nothing to prevent both plots appearing in a single graph.
For this reason, a single CPTGraph instance can have multiple instances of CPTPlotSpace. In the most common cases, there will only be a single instance of CPTPlotSpace, but the flexibility exists within the framework to support multiple spaces in a single graph.
Plots
A particular representation of data in a graph is known as a 'plot'. For example, data could be shown as a line or scatter plot, with a symbol at each data point. The same data could be represented by a bar plot/histogram.
A graph can have multiple plots. Each plot can derive from a single data set, or different data sets: they are completely independent of one another.
Although it may not seem like it at first glance, a plot is analogous to a table view. For example, to present a simple line plot of the speed of a train versus time, you need a value for the speed at different points in time. This data could be stored in two columns of a table view, or represented as a scatter plot. In effect, the plot and the table view are just different views of the same model data.
What this means is that the same design patterns used to populate table views with data can be used to provide data to plots. In particular, you can either use the data source design pattern, or you can use bindings. To provide a plot with data using the data source approach, you set the dataSource outlet of the CPTPlot object, and then implement the data source methods.
@protocol CPTPlotDataSource <NSObject>
-(NSUInteger)numberOfRecords;
@optional
// Implement one of the following
-(NSArray *)numbersForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndexRange:(NSRange)indexRange;
-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index;
@end
You can think of the field as being analogous to a column identifier in a table view, and the record index being analogous to the row index. Each type of plot has a fixed number of fields. For example, a scatter plot has two: the value of for the horizontal axis (x) and the value for the vertical axis (y). An enumerator in the CPTScatterPlot class defines these fields.
typedef enum _CPTScatterPlotField {
CPTScatterPlotFieldX,
CPTScatterPlotFieldY
} CPTScatterPlotField;A record is analogous to the row of a table view. For a scatter plot, it corresponds to a single point on the graph.
Plot classes not only support the data source design pattern, but also Cocoa bindings, as a means of supplying data. This is again very similar to the approach taken with table views: each field of the plot --- analogous to a table column --- gets bound to a key path via an NSArrayController.
CPTGraph *graph = ...;
CPTScatterPlot *boundLinePlot = [[[CPTScatterPlot alloc] initWithFrame:CGRectZero] autorelease];
boundLinePlot.identifier = @"Bindings Plot";
boundLinePlot.dataLineStyle.lineWidth = 2.f;
[graph addPlot:boundLinePlot];
[boundLinePlot bind:CPTScatterPlotBindingXValues toObject:self withKeyPath:@"arrangedObjects.x" options:nil];
[boundLinePlot bind:CPTScatterPlotBindingYValues toObject:self withKeyPath:@"arrangedObjects.y" options:nil];
The superclass of all plot classes is CPTPlot. This is an abstract base class; each subclass of CPTPlot represents a particular variety of plot. For example, the CPTScatterPlot class is used to draw line and scatter plots, while the CPTBarPlot class is used for bar and histogram plots.
A plot object has a close relationship to the CPTPlotSpace class discussed earlier. In order to draw itself, the plot class needs to transform the values it receives from the data source into drawing coordinates. The plot space serves this purpose.
Axes
Axes describe the scale of the plotting coordinate space to the viewer. A basic graph will have just two axes, one for the horizontal direction (x) and one for the vertical direction (y), but this is not a constraint in Core Plot --- you can add as many axes as you like. Axes can appear at the sides of the plot area, but also on top of it. Axes can have different scales, and can include major and/or minor ticks, as well as labels and a title.
Each axis on a graph is represented by an object of class descendant from CPTAxis. CPTAxis is responsible for drawing itself, and accessories like ticks and labels. To do this it needs to know how to map data coordinates into drawing coordinates. For this reason, each axis is associated with a single instance of CPTPlotSpace.
A graph can have multiple axes, but all axes get grouped together in a single CPTAxisSet object. An axis set is a container for all the axes belonging to a graph, as well as a factory for creating standard sets of axes (eg CPTXYAxisSet creates two axes, one for x and one for y).
Axis labels are usually textual, but there is support in Core Plot for custom labels: any core animation layer can be used as an axis label by wrapping it in an instance of the CPTAxisLabel class.
Animations
A unique aspect of Core Plot is the integration of animation, which facilitates dynamic effects and interactivity. The Core Animation framework provides the mechanisms for positioning and transforming layers in time. In general you can access layers directly to apply animation. Be aware that transforming some layers, such as plots or the plot area, could invalidate the correspondence of data and axes.
Core Plot is actually easy to implement after going over 1-2 examples. The design overview here reflexes the designer's insight and vision. I did enjoy reading it. But for someone like me who is under the gun to deliver, I have the first impression of too complex to implement.
I prefer to have the overview to tie directly to coding using iPhone's jargon. For example, initially I have trouble to see how all the plot space, plot and graph concept manifest into specific coding. Now I see a plot in coder's term as follows:
A plot is similar to a view with a transparent background showing an xy scatter data, pie chart or bar chart without the axises and legend. You can add multiple plot "views" to a graph's "subviews" so that multiple data plots can be shown simultaneously in the graph. To add a plot to a graph, call addPlot:a plot in the graph (similar to addsubview).
Each plot has its data source just like an UITableView. To assign a data source for a plot, use CPPlotDataSource delegate (as to UITableViewDataSource for UITableView).
Dear Concerned, Currently am developing the line-graph using the core-plot. Well am bit confuse that how to add a tag or label on the particular data-plot point. Please guide me Regards Zahur
I am so stupid and confused that whether the LZ(Post's writer) is Chinese guy. The sentences is so hard to understand. "This places some restrictions on the technologies that can be used" is too simple to understand.
What does the mean by the world "host views" in the sentence "View classes like NSView and UIView can only be used as host views". Thanks for your answer.
@infomato May I ask you what does the mean by "iPhone's jargon". THX.
@nnnwjs, the writer was trying to emphasize the fact that Core Plot is designed to run on both MacOS and iOS. This limits the framework to use technologies (e.g., bindings, KVO, Core Animation, etc.) that Apple supports on both platforms. This limitation only applies to Core Plot itself; your program is limited only by what your chosen platform (Mac or iOS) supports.
The comment about NSView and UIView explains one of the consequences of choosing Core Animation as the basis for Core Plot. The two view classes are different enough that it would be very difficult to use them as the foundation of a cross-platform tool like Core Plot. This was one of the factors that drove the decision to use Core Animation. We have created a custom view class that is designed to "host" a Core Plot graph. This makes it easy to integrate a graph into an application that uses views for its interface and doesn't necessarily use Core Animation for anything else.
I believe the reference to "iPhone jargon" was a request to express the description of Core Plot components in terms of familiar Cocoa concepts like the view hierarchy and delegates.
@eskroch
Thanks for your patience and explanations. And I am a newbie on iPhone development and want to learn some form Core plot. THX.
"the writer was trying to emphasize the fact that Core Plot is designed to run on both MacOS and iOS. This limits the framework to use technologies (e.g., bindings, KVO, Core Animation, etc.) that Apple supports on both platforms. This limitation only applies to Core Plot itself; your program is limited only by what your chosen platform (Mac or iOS) supports." You mean Core plot used some technologies or frameworks(Such as Quartz 2D API and Layer in Core Animation) for crossing platform(MAC OS and iOS), right?
Maybe I got the mean of "host views". "Host view" is just a owner or container which graph of Core plot can be added or attach, right?
Today morning I was in the subway before I saw your comment, I had realized the word "jargon" is just a kind of allegory. Maybe the writer just uses the frameworks or technologies provided by apple to develope app instead of doing from scratch such as core plot. am I right?
Now, I begin to read the source code of CorePlot?. However, I found that that the code is hard to understand, because it's pool documented. Although there are some comments before the variables and methods, they are useless. Because these comments are valueless and only can be understood by code owner. For example the code segment "/// @name Bindings /// @{ +(void)exposeBinding:(NSString )binding; -(void)bind:(NSString )binding toObject:(id)observable withKeyPath:(NSString )keyPath options:(NSDictionary )options; -(void)unbind:(NSString )binding; -(Class)valueClassForBinding:(NSString )binding; /// @}" in class CPLayer.h What does the NSString binding mean in CorePlot?? But, I guess the string "bidning" may be a event name. Why did't use the pattern of Target/Action. Because we should follow the usual practice of iPhone and MAC developers'.
How do you guys think about?
@nnnwjs You are correct. The discussion was about the technologies and Apple frameworks used to build Core Plot. It also tried to explain the choice of technologies was driven by the desire to have Core Plot work on both the Mac and iOS.
Host view == container for a Core Plot graph
The methods in your example are all part of Apple's NSKeyValueBindingCreation protocol. That's why they're not documented more fully in the Core Plot code.
If you only looking at the header files for documentation, you're missing a large part of it. One of the coding style decisions made early on was to keep as much documentation as possible in the implementation files to keep the header files clean. We use Doxygen to create documentation for the framework (see http://code.google.com/p/core-plot/wiki/DocumentationPolicy). Some documentation comments have to be in the headers for Doxygen to pick them up, e.g., protocols, enums, and categories as well as the grouping commands ("@name") which is why you see some documentation in the headers.
@eskroch
Thanks for your patience and explanations. I got what did you explained. So sorry to misunderstand you and your team.
Recently, I got a plan to design and develop a framework for data visualization in only iPhone platform. Maybe I should reference some design of CorePlot?. Maybe you will ask me why I can't use CorePlot? directly? Yes, CorePlot? is excellent, however, the framework I want is like FusionChart?, OpenFlashChart? and RoamBi?. I like FusionChart? and OpenFlashChart? for RESTful datasource with XML and JSON data format. I like RoamBi? for the cool UI and amazing Animations.
So, buddy, How do you think about?
I don't have any direct experience with any of those products. From what I can see on the website, most of of the charting in RoamBi? could be done with Core Plot as it exists right now. Drawing the charts is secondary to that program. The power comes from being able to hook up and manipulate the data easily. It helps that they have some nice appearance themes, but that's not their primary feature.
It would be possible to create new CPTheme classes that use XML and JSON inputs instead of hard-coded settings to configure the appearance of the graph. It should also be possible and not too hard to create some datasource providers that implement the datasource protocols and take XML and JSON as inputs.
Please add enhancement requests to the issue tracker for these features. Since this is a volunteer effort, there's no guarantee if or when any specific features will be implemented. If you'd like to take a shot at adding one or more of these things to Core Plot, we'd welcome the effort.
@eskroch
Well, firstly, thankds for your invitation.
However, I am so stupid and confused with your opinions about RoamBi?, what's your point? Maybe your point is "The power comes from being able to hook up and manipulate the data easily", if were this, I agree with you.
Next, Maybe I should explain what the XML and JSON datasource meaning I metioned. Firstly I agree with you about the theme can be customized by XML and JSON data(Did your know the HA project OpenRemote? for iPhone client, we render the UI like that what you thought). However, I mean that the purpose of XML and JSON is storing some business data(such as the sale records about CorePlot?) transfered from some RESTful sevices and the CorePlot? just be the DATA VISULIZATION CLIENT and render the business data.
@eskroch
Maybe the DATA VISUALIZATION framework I want conflicts with CorePlot?'s purpose.
I don't see a conflict. It's just that Core Plot provides only one part of what a "Data Visualization" or BI tool should do. Core Plot is focused on graphing data of all sorts. It doesn't care what it means or where it came from. "Data Visualization" and BI includes a graphing component but is more than that.
@eskroch
Good explanations and take it easy.
Obviously, I didn't understand the concept of "Data Visualization" with CorePlot?, and didn't know BI either. So, I think I should spend some time in Data Visualization with CorePlot? and BI. THX.
I want to use this to draw stock candle chart, how can I do? does anyone give me some suggestion or hint?
Best to ask this on the mailing list. There is an example of plotting stock data in the examples folder of the source code distribution.
Mailing list is http://groups.google.com/group/coreplot-discuss