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