1 /*
2 	 -------------------------------------------------------------------
3 
4 	 Copyright (C) 2014, Edwin van Leeuwen
5 
6 	 This file is part of plotd plotting library.
7 
8 	 Plotd is free software; you can redistribute it and/or modify
9 	 it under the terms of the GNU General Public License as published by
10 	 the Free Software Foundation; either version 3 of the License, or
11 	 (at your option) any later version.
12 
13 	 Plotd is distributed in the hope that it will be useful,
14 	 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 	 GNU General Public License for more details.
17 
18 	 You should have received a copy of the GNU General Public License
19 	 along with Plotd. If not, see <http://www.gnu.org/licenses/>.
20 
21 	 -------------------------------------------------------------------
22 	 */
23 
24 /**
25 	High level interface to the plotting library
26 	*/
27 module plotd.plot;
28 
29 import std.conv;
30 import std.range;
31 
32 import plotd.drawing;
33 import plotd.primitives;
34 
35 version (assert) {
36 	import std.stdio : writeln;
37 }
38 
39 /// Draw function on our plot
40 CONTEXT drawFunction(CONTEXT)( double delegate(double) func,
41 		Bounds bounds, CONTEXT context ) {
42 	auto points = iota( bounds.min_x, bounds.max_x, 
43 				bounds.width/100.0 )
44 			.map!( a => Point( a, func( a ) ) );
45 
46 	auto from = points[0];
47 	foreach( to; points[1..$] ) {
48 		context = drawLine( from, to, context );
49 		from = to;
50 	}
51 	return context;
52 }
53 
54 /// Class that holds all state to do with one figure 
55 class PlotState {
56 	Bounds plotBounds = Bounds( 0, 1, 0, 1 );
57 	Bounds marginBounds = Bounds( 70, 400, 70, 400 );
58 
59 	cairo.Surface surface;
60 	cairo.Context axesContext;
61 	cairo.Context plotContext;
62 }
63 
64 /// Instantiate a new plot
65 PlotState createPlotState( Bounds plotBounds, Bounds marginBounds ) {
66 	auto plot = new PlotState;
67 	plot.plotBounds = plotBounds;
68 	plot.marginBounds = marginBounds;
69 
70 	plot.surface = createPlotSurface( plot.marginBounds.max_x.to!int, 
71 			plot.marginBounds.max_y.to!int );
72 
73 	// setup axes
74 	plot.axesContext = axesContextFromSurface( plot.surface, 
75 			plot.plotBounds, plot.marginBounds );
76 
77 	plot.axesContext = drawAxes( plot.plotBounds, plot.axesContext );
78 
79 	plot.plotContext = plotContextFromSurface( plot.surface, 
80 			plot.plotBounds, plot.marginBounds );
81 
82 	return plot;
83 }
84 
85 /// Draw a range of points as a line
86 void drawRange(RANGE)( RANGE range, PlotState plot ) {
87 	if (!range.empty) {
88 		auto firstPoint = range.front;
89 		range.popFront;
90 		while (!range.empty) {
91 			auto nextPoint = range.front;
92 			range.popFront;
93 			plot.plotContext = 
94 				drawLine( firstPoint, nextPoint, plot.plotContext );
95 			firstPoint = nextPoint;
96 		}
97 	}
98 }
99 
100 /// Draw function on our plot
101 void drawFunction(CONTEXT)( double delegate(double) func,
102 		PlotState plot ) {
103 	iota( plot.plotBounds.min_x, plot.plotBounds.max_x, 
104 				plot.plotBounds.width/100.0 )
105 			.map!( a => Point( a, func( a ) ) ).drawRange( plot );
106 }
107 
108 /// Draw point on the plot
109 void draw( Point point, PlotState plot ) {
110 	plot.plotContext = drawPoint( point, plot.plotContext );
111 }
112 
113 /// Save plot to a file
114 void save( PlotState plot, string name = "example.png" ) {
115     (cast(cairo.ImageSurface)( plot.surface )).writeToPNG( name );
116 }