1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2010-2019 Werner Schweer and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2.
9 //
10 // This program 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
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19
20 #include "easeinoutcanvas.h"
21 #include "libmscore/easeInOut.h"
22 #include "preferences.h"
23
24 namespace Ms {
25
26 //---------------------------------------------------------
27 // EaseInOutCanvas
28 //---------------------------------------------------------
29
EaseInOutCanvas(QWidget * parent)30 EaseInOutCanvas::EaseInOutCanvas(QWidget* parent)
31 : QFrame(parent),
32 m_easeIn(0.0),
33 m_easeOut(0.0),
34 m_pitchDelta(0)
35 {
36 setFrameStyle(QFrame::NoFrame);
37 }
38
39 //---------------------------------------------------------
40 // paintEvent
41 //---------------------------------------------------------
42
paintEvent(QPaintEvent * ev)43 void EaseInOutCanvas::paintEvent(QPaintEvent* ev)
44 {
45 // not qreal here, even though elsewhere yes,
46 // because width and height return a number of pixels,
47 // hence integers.
48 const int w = width();
49 const int h = height();
50 const int border = 1;
51 const int nPitches = std::abs(m_pitchDelta) + 1;
52
53 const qreal graphWidth = static_cast<qreal>(w - 2 * border);
54 const qreal graphHeight = static_cast<qreal>(h - 2 * border);
55 const qreal nbEvents = static_cast<qreal>(m_events.size());
56 const qreal pitchDelta = static_cast<qreal>(nPitches);
57
58 // let half a column of margin around
59 const qreal leftPos = static_cast<qreal>(border); // also left margin
60 const qreal topPos = leftPos; // also top margin
61 const qreal bottomPos = static_cast<qreal>(h - border); // bottom end position of graph
62 const qreal rightPos = static_cast<qreal>(w - border); // right end position of graph
63
64 EaseInOut eio(static_cast<qreal>(m_easeIn) / 100.0, static_cast<qreal>(m_easeOut) / 100.0);
65
66 char noteNames[] = ("C D EF G A B");
67
68 QPainter painter(this);
69 painter.setRenderHint(QPainter::Antialiasing, preferences.getBool(PREF_UI_CANVAS_MISC_ANTIALIASEDDRAWING));
70
71 painter.fillRect(rect(), QApplication::palette().color(QPalette::Window).lighter());
72 QPen pen = painter.pen();
73 pen.setWidth(1);
74
75 QColor eventLinesColor(Qt::gray);
76 eventLinesColor.setAlphaF(0.5);
77 QColor pitchLinesColor(Qt::gray);
78 pitchLinesColor.setAlphaF(0.25);
79
80 // Color scheme based on the MuseScore blue.
81 QColor borderLinesColor, warpLineColor, pitchFillColor, pitchGraphColor, pitchNameColor;
82 if (preferences.isThemeDark()) {
83 pitchFillColor.setRgbF(0.078, 0.284, 0.463);
84 pitchGraphColor.setRgbF(0.125, 0.455, 0.741);
85 warpLineColor.setRgbF(0.621, 0.315, 0.132);
86 borderLinesColor.setRgbF(0.891, 0.932, 0.968);
87 pitchNameColor.setRgbF(0.935, 0.782, 0.691);
88 }
89 else {
90 pitchFillColor.setRgbF(0.891, 0.932, 0.968);
91 pitchGraphColor.setRgbF(0.125, 0.455, 0.741);
92 warpLineColor.setRgbF(0.914, 0.710, 0.588);
93 borderLinesColor.setRgbF(0.016, 0.057, 0.093);
94 pitchNameColor.setRgbF(0.310, 0.157, 0.066);
95 }
96
97 // this lambda takes as input a pitch value, and determines where what are its x and y coordinates
98 auto getPosition = [graphWidth, graphHeight, leftPos, bottomPos](const QPointF& p) -> QPointF {
99 return { leftPos + p.x() * graphWidth, bottomPos - p.y() * graphHeight };
100 };
101
102 std::vector<QPointF> pitchPoints;
103 qreal offset = m_pitchDelta < 0 ? 1.0 : 0.0;
104 QPointF prevPoint, currPoint;
105 for (size_t i = 0; i < m_events.size(); i++) {
106 currPoint = getPosition({ eio.XfromY(static_cast<qreal>(i) / nbEvents), offset + static_cast<qreal>(m_events[i]) / static_cast<qreal>(pitchDelta) });
107 pitchPoints.push_back(currPoint);
108 }
109
110 // Draw the pitches barchart graph in the background first
111 if (pitchPoints.size() > 1) {
112 prevPoint = pitchPoints[0];
113 for (size_t i = 1; i < pitchPoints.size(); i++) {
114 currPoint = pitchPoints[i];
115 painter.fillRect(prevPoint.x(), bottomPos, (currPoint.x() - prevPoint.x()) + 1, prevPoint.y() - bottomPos, pitchFillColor);
116 prevPoint = currPoint;
117 }
118 painter.fillRect(prevPoint.x(), bottomPos, (rightPos - prevPoint.x()) + 1, prevPoint.y() - bottomPos, pitchFillColor);
119
120 // draw time-warped vertical lines in lighter gray.
121 // These lines will move as ease-in and ease-out are adjusted.
122 pen.setWidth(0);
123 pen.setColor(eventLinesColor);
124 painter.setPen(pen);
125 for (size_t i = 1; i < pitchPoints.size(); ++i) {
126 qreal xPos = pitchPoints[i].x();
127 painter.drawLine(xPos, topPos, xPos, bottomPos);
128 }
129 }
130
131 // draw half step horigontal lines in even lighter gray
132 pen.setWidth(0);
133 pen.setColor(pitchLinesColor);
134 painter.setPen(pen);
135 for (int i = 1; i < nPitches; ++i) {
136 qreal yPos = topPos + (static_cast<qreal>(i) / pitchDelta) * graphHeight;
137 painter.drawLine(leftPos, yPos, rightPos, yPos);
138 }
139
140 // draw note names
141 pen.setColor(pitchNameColor);
142 painter.setPen(pen);
143 QFont font;
144 qreal fontHeight = std::min(12.0, (graphHeight * 0.875) / pitchDelta);
145 font.setPixelSize(fontHeight);
146 painter.setFont(font);
147 int curPitch = m_bottomPitch;
148 for (int i = 0; i <= nPitches; ++i) {
149 QString pitchName(noteNames[(curPitch - 60) % 12]);
150 QPointF pos = { 4, topPos + fontHeight * 0.3 + (1.0 - (static_cast<qreal>(i) / pitchDelta)) * graphHeight };
151 painter.drawText(pos, pitchName);
152 curPitch++;
153 }
154
155 if (m_events.size()) {
156 // Not a portamento style glissando.
157 // Draw the Bezier transfer curve only in ease-in or ease-out are not zero. This warps
158 // the event times so this curve always go from lower left corner to upper-right corner.
159 if (m_easeIn != 0 || m_easeOut != 0) {
160 pen.setWidth(2);
161 pen.setColor(warpLineColor);
162 painter.setPen(pen);
163 prevPoint = { leftPos, bottomPos };
164 for (int i = 1; i <= 33; i++) {
165 currPoint = getPosition(eio.Eval(static_cast<qreal>(i) / 33.0));
166 painter.drawLine(prevPoint, currPoint);
167 prevPoint = currPoint;
168 }
169 }
170 }
171 else {
172 // Draw the portamento style glissando curve instead of the transfer curve
173 pen.setWidth(3);
174 pen.setColor(pitchGraphColor);
175 painter.setPen(pen);
176 prevPoint = { leftPos, m_pitchDelta > 0 ? bottomPos : topPos };
177 for (int i = 1; i <= 33; i++) {
178 currPoint = eio.Eval(static_cast<qreal>(i) / 33.0);
179 if (m_pitchDelta < 0)
180 currPoint.setY(1.0 - currPoint.y());
181 currPoint = getPosition(currPoint);
182 painter.drawLine(prevPoint, currPoint);
183 prevPoint = currPoint;
184 }
185 }
186
187 // Draw the pitches level lines next so they cover the Bezier transfer curve.
188 if (pitchPoints.size() > 1) {
189 pen.setWidth(3);
190 pen.setCapStyle(Qt::FlatCap);
191 pen.setColor(pitchGraphColor);
192 painter.setPen(pen);
193 prevPoint = pitchPoints[0];
194 for (size_t i = 1; i < pitchPoints.size(); i++) {
195 currPoint = pitchPoints[i];
196 painter.drawLine(prevPoint, { currPoint.x(), prevPoint.y() });
197 prevPoint = currPoint;
198 }
199 painter.drawLine(prevPoint, { rightPos, prevPoint.y() });
200 }
201
202 // draw the graph frame after all the other graphics elements to cover them
203 pen.setColor(borderLinesColor);
204 pen.setWidth(1);
205 painter.setPen(pen);
206 painter.drawRect(border, border, w - 2 * border, h - 2 * border);
207
208 QFrame::paintEvent(ev);
209 }
210
211 } // namespace Ms
212