1 /*
2 * Copyright (c) 2015-2020 Meltytech, LLC
3 * Author: Brian Matherly <code@brianmatherly.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "graph.h"
21 #include <QPainterPath>
22 #include <QVector>
23 #include <math.h>
24
25 /*
26 * Apply the "bgcolor" and "angle" parameters to the QPainter
27 */
setup_graph_painter(QPainter & p,QRectF & r,mlt_properties filter_properties)28 void setup_graph_painter( QPainter& p, QRectF& r, mlt_properties filter_properties )
29 {
30 mlt_color bg_color = mlt_properties_get_color( filter_properties, "bgcolor" );
31 double angle = mlt_properties_get_double( filter_properties, "angle" );
32
33 p.setRenderHint(QPainter::Antialiasing);
34
35 // Fill background
36 if ( bg_color.r || bg_color.g || bg_color.g || bg_color.a ) {
37 QColor qbgcolor( bg_color.r, bg_color.g, bg_color.b, bg_color.a );
38 p.fillRect( 0, 0, p.device()->width(), p.device()->height(), qbgcolor );
39 }
40
41 // Apply rotation
42 if ( angle ) {
43 p.translate( r.x() + r.width() / 2, r.y() + r.height() / 2 );
44 p.rotate( angle );
45 p.translate( -(r.x() + r.width() / 2), -(r.y() + r.height() / 2) );
46 }
47 }
48
49 /*
50 * Apply the "thickness", "gorient" and "color.x" parameters to the QPainter
51 */
setup_graph_pen(QPainter & p,QRectF & r,mlt_properties filter_properties,double scale)52 void setup_graph_pen(QPainter& p, QRectF& r, mlt_properties filter_properties , double scale)
53 {
54 int thickness = mlt_properties_get_int( filter_properties, "thickness" ) * scale;
55 QString gorient = mlt_properties_get( filter_properties, "gorient" );
56 QVector<QColor> colors;
57 bool color_found = true;
58
59 QPen pen;
60 pen.setWidth( qAbs(thickness) );
61
62 // Find user specified colors for the gradient
63 while( color_found ) {
64 QString prop_name = QString("color.") + QString::number(colors.size() + 1);
65 if( mlt_properties_exists(filter_properties, prop_name.toUtf8().constData() ) ) {
66 mlt_color mcolor = mlt_properties_get_color( filter_properties, prop_name.toUtf8().constData() );
67 colors.append( QColor( mcolor.r, mcolor.g, mcolor.b, mcolor.a ) );
68 } else {
69 color_found = false;
70 }
71 }
72
73 if( !colors.size() ) {
74 // No color specified. Just use white.
75 pen.setBrush(Qt::white);
76 } else if ( colors.size() == 1 ) {
77 // Only use one color
78 pen.setBrush(colors[0]);
79 } else {
80 QLinearGradient gradient;
81 if( gorient.startsWith("h", Qt::CaseInsensitive) ) {
82 gradient.setStart ( r.x(), r.y() );
83 gradient.setFinalStop ( r.x() + r.width(), r.y() );
84 } else { // Vertical
85 gradient.setStart ( r.x(), r.y() );
86 gradient.setFinalStop ( r.x(), r.y() + r.height() );
87 }
88
89 qreal step = 1.0 / ( colors.size() - 1 );
90 for( int i = 0; i < colors.size(); i++ )
91 {
92 gradient.setColorAt( (qreal)i * step, colors[i] );
93 }
94 pen.setBrush(gradient);
95 }
96
97 p.setPen(pen);
98 }
99
fix_point(QPointF & point,QRectF & rect)100 static inline void fix_point( QPointF& point, QRectF& rect )
101 {
102 if( point.x() < rect.x() ) {
103 point.setX( rect.x() );
104 } else if ( point.x() > rect.x() + rect.width() ) {
105 point.setX( rect.x() + rect.width() );
106 }
107
108 if( point.y() < rect.y() ) {
109 point.setY( rect.y() );
110 } else if ( point.y() > rect.y() + rect.height() ) {
111 point.setY( rect.y() + rect.height() );
112 }
113 }
114
paint_line_graph(QPainter & p,QRectF & rect,int points,float * values,double tension,int fill)115 void paint_line_graph( QPainter& p, QRectF& rect, int points, float* values, double tension, int fill )
116 {
117 double width = rect.width();
118 double height = rect.height();
119 double pixelsPerPoint = width / (double)(points - 1);
120
121 // Calculate cubic control points
122 QVector<QPointF> controlPoints( (points - 1) * 2 );
123 int cpi = 0;
124 // First control point is equal to first point
125 controlPoints[cpi++] = QPointF( rect.x(), rect.y() + height - values[0] * height );
126
127 // Calculate control points between data points
128 // Based on ideas from:
129 // http://scaledinnovation.com/analytics/splines/aboutSplines.html
130 for( int i = 0; i < (points - 2); i++ ) {
131 double x0 = rect.x() + (double)i * pixelsPerPoint;
132 double x1 = rect.x() + (double)(i + 1) * pixelsPerPoint;
133 double x2 = rect.x() + (double)(i + 2) * pixelsPerPoint;
134 double y0 = rect.y() + height - values[i] * height;
135 double y1 = rect.y() + height - values[i + 1] * height;
136 double y2 = rect.y() + height - values[i + 2] * height;
137 double d01 = sqrt( pow( x1 - x0, 2 ) + pow( y1 - y0, 2) );
138 double d12 = sqrt( pow( x2 - x1, 2 ) + pow( y2 - y1, 2) );
139 double fa = tension * d01 / ( d01 + d12 );
140 double fb = tension * d12 / ( d01 + d12 );
141 double p1x = x1 - fa * ( x2 - x0 );
142 double p1y = y1 - fa * ( y2 - y0 );
143 QPointF c1( p1x, p1y );
144 fix_point( c1, rect );
145 double p2x = x1 + fb * ( x2 - x0 );
146 double p2y = y1 + fb * ( y2 - y0 );
147 QPointF c2( p2x, p2y );
148 fix_point( c2, rect );
149 controlPoints[cpi++] = c1;
150 controlPoints[cpi++] = c2;
151 }
152
153 // Last control point is equal to last point
154 controlPoints[cpi++] = QPointF( rect.x() + width, rect.y() + height - values[points - 1] * height );
155
156 // Draw a sequence of bezier curves to interpolate between points.
157 QPainterPath curvePath;
158
159 // Begin at the first data point.
160 curvePath.moveTo( rect.x(), rect.y() + height - values[0] * height );
161 cpi = 0;
162 for( int i = 1; i < points; i++ ) {
163 QPointF c1 = controlPoints[cpi++];
164 QPointF c2 = controlPoints[cpi++];
165 double endX = rect.x() + (double)i * pixelsPerPoint;
166 double endY = rect.y() + height - values[i] * height;
167 QPointF end( endX, endY );
168 curvePath.cubicTo( c1, c2, end );
169 }
170
171 if( fill ) {
172 curvePath.lineTo( rect.x() + width, rect.y() + height );
173 curvePath.lineTo( rect.x(), rect.y() + height );
174 curvePath.closeSubpath();
175 p.fillPath( curvePath, p.pen().brush() );
176 } else {
177 p.drawPath( curvePath );
178 }
179 }
180
paint_bar_graph(QPainter & p,QRectF & rect,int points,float * values)181 void paint_bar_graph( QPainter& p, QRectF& rect, int points, float* values )
182 {
183 double width = rect.width();
184 double height = rect.height();
185 double pixelsPerPoint = width / (double)points;
186 QPointF bottomPoint( rect.x() + pixelsPerPoint / 2, rect.y() + height );
187 QPointF topPoint( rect.x() + pixelsPerPoint / 2, rect.y() );
188
189 for( int i = 0; i < points; i++ ) {
190 topPoint.setY( rect.y() + height - values[i] * height );
191 p.drawLine( bottomPoint, topPoint );
192 bottomPoint.setX( bottomPoint.x() + pixelsPerPoint );
193 topPoint.setX( bottomPoint.x() );
194 }
195 }
196