andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 KeyPositionGraphDisplay.cpp: implements a graph of key position over time
|
andrewm@0
|
21 for a specific key press, which is useful for analysis of continuous key
|
andrewm@0
|
22 position.
|
andrewm@0
|
23 */
|
andrewm@0
|
24
|
andrewm@0
|
25 #include "KeyPositionGraphDisplay.h"
|
andrewm@27
|
26 #include "OpenGLJuceCanvas.h"
|
andrewm@0
|
27 #include <iostream>
|
andrewm@0
|
28 #include <cmath>
|
andrewm@0
|
29
|
andrewm@0
|
30 // Class constants
|
andrewm@0
|
31 // Display margins
|
andrewm@0
|
32 const float KeyPositionGraphDisplay::kDisplaySideMargin = 0.5;
|
andrewm@0
|
33 const float KeyPositionGraphDisplay::kDisplayBottomMargin = 0.5;
|
andrewm@0
|
34 const float KeyPositionGraphDisplay::kDisplayTopMargin = 0.5;
|
andrewm@0
|
35
|
andrewm@0
|
36 // Size of the graph area
|
andrewm@0
|
37 const float KeyPositionGraphDisplay::kDisplayGraphWidth = 20.0;
|
andrewm@0
|
38 const float KeyPositionGraphDisplay::kDisplayGraphHeight = 10.0;
|
andrewm@0
|
39
|
andrewm@27
|
40 KeyPositionGraphDisplay::KeyPositionGraphDisplay() : canvas_(0),
|
andrewm@0
|
41 displayPixelWidth_(1.0), displayPixelHeight_(1.0), totalDisplayWidth_(1.0), totalDisplayHeight_(1.0),
|
andrewm@27
|
42 xMin_(0.0), xMax_(1.0), yMin_(-0.2), yMax_(1.2)
|
andrewm@0
|
43 {
|
andrewm@0
|
44 // Initialize OpenGL settings: 2D only
|
andrewm@0
|
45
|
andrewm@0
|
46 //glMatrixMode(GL_PROJECTION);
|
andrewm@0
|
47 //glDisable(GL_DEPTH_TEST);
|
andrewm@0
|
48
|
andrewm@0
|
49 pressStartTimestamp_ = pressFinishTimestamp_ = releaseStartTimestamp_ = releaseFinishTimestamp_ = missing_value<timestamp_type>::missing();
|
andrewm@0
|
50 pressStartPosition_ = pressFinishPosition_ = releaseStartPosition_ = releaseFinishPosition_ = missing_value<key_position>::missing();
|
andrewm@0
|
51
|
andrewm@0
|
52 totalDisplayWidth_ = kDisplaySideMargin*2 + kDisplayGraphWidth;
|
andrewm@0
|
53 totalDisplayHeight_ = kDisplayTopMargin + kDisplayBottomMargin + kDisplayGraphHeight;
|
andrewm@0
|
54 }
|
andrewm@0
|
55
|
andrewm@27
|
56 // Tell the underlying canvas to repaint itself
|
andrewm@27
|
57 void KeyPositionGraphDisplay::tellCanvasToRepaint() {
|
andrewm@27
|
58 if(canvas_ != 0)
|
andrewm@27
|
59 canvas_->triggerRepaint();
|
andrewm@27
|
60 }
|
andrewm@27
|
61
|
andrewm@0
|
62 void KeyPositionGraphDisplay::setDisplaySize(float width, float height) {
|
andrewm@0
|
63 ScopedLock sl(displayMutex_);
|
andrewm@0
|
64 displayPixelWidth_ = width;
|
andrewm@0
|
65 displayPixelHeight_ = height;
|
andrewm@0
|
66 refreshViewport();
|
andrewm@0
|
67 }
|
andrewm@0
|
68
|
andrewm@0
|
69
|
andrewm@0
|
70 // Render the keyboard display
|
andrewm@0
|
71
|
andrewm@0
|
72 void KeyPositionGraphDisplay::render() {
|
andrewm@0
|
73 // Start with a light gray background
|
andrewm@0
|
74 glClearColor(0.8, 0.8, 0.8, 1.0);
|
andrewm@0
|
75 glClear(GL_COLOR_BUFFER_BIT);
|
andrewm@0
|
76 glLoadIdentity();
|
andrewm@0
|
77
|
andrewm@0
|
78 float invAspectRatio = totalDisplayWidth_ / totalDisplayHeight_;
|
andrewm@0
|
79 float scaleValue = 2.0 / totalDisplayWidth_;
|
andrewm@0
|
80
|
andrewm@0
|
81 glScalef(scaleValue, scaleValue * invAspectRatio, scaleValue);
|
andrewm@0
|
82 glTranslatef(-1.0 / scaleValue, -totalDisplayHeight_ / 2.0, 0);
|
andrewm@0
|
83 glTranslatef(kDisplaySideMargin, kDisplayBottomMargin, 0.0);
|
andrewm@0
|
84
|
andrewm@0
|
85 ScopedLock sl(displayMutex_);
|
andrewm@0
|
86
|
andrewm@0
|
87 // Draw the region where the graph will be plotted (white with black outline)
|
andrewm@0
|
88 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
andrewm@0
|
89 glColor3f(1.0, 1.0, 1.0);
|
andrewm@0
|
90
|
andrewm@0
|
91 glBegin(GL_POLYGON);
|
andrewm@0
|
92 glVertex2f(0, 0);
|
andrewm@0
|
93 glVertex2f(0, kDisplayGraphHeight);
|
andrewm@0
|
94 glVertex2f(kDisplayGraphWidth, kDisplayGraphHeight);
|
andrewm@0
|
95 glVertex2f(kDisplayGraphWidth, 0);
|
andrewm@0
|
96 glEnd();
|
andrewm@0
|
97
|
andrewm@0
|
98 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
andrewm@0
|
99 glColor3f(0.0, 0.0, 0.0);
|
andrewm@0
|
100 glLineWidth(1.0);
|
andrewm@0
|
101
|
andrewm@0
|
102 glBegin(GL_POLYGON);
|
andrewm@0
|
103 glVertex2f(0, 0);
|
andrewm@0
|
104 glVertex2f(0, kDisplayGraphHeight);
|
andrewm@0
|
105 glVertex2f(kDisplayGraphWidth, kDisplayGraphHeight);
|
andrewm@0
|
106 glVertex2f(kDisplayGraphWidth, 0);
|
andrewm@0
|
107 glEnd();
|
andrewm@0
|
108
|
andrewm@0
|
109 // Draw gray lines for the 0.0 and 1.0 key positions
|
andrewm@0
|
110 glColor3f(0.5, 0.5, 0.5);
|
andrewm@0
|
111 glBegin(GL_LINES);
|
andrewm@0
|
112 glVertex2f(0, graphToDisplayY(0.0));
|
andrewm@0
|
113 glVertex2f(kDisplayGraphWidth, graphToDisplayY(0.0));
|
andrewm@0
|
114 glEnd();
|
andrewm@0
|
115 glBegin(GL_LINES);
|
andrewm@0
|
116 glVertex2f(0, graphToDisplayY(1.0));
|
andrewm@0
|
117 glVertex2f(kDisplayGraphWidth, graphToDisplayY(1.0));
|
andrewm@0
|
118 glEnd();
|
andrewm@0
|
119
|
andrewm@0
|
120 // Draw the graph of position over time (if data exists)
|
andrewm@0
|
121 if(!keyPositions_.empty() && !keyTimestamps_.empty()) {
|
andrewm@0
|
122 glColor3f(0.0, 0.0, 0.0);
|
andrewm@0
|
123 glBegin(GL_LINE_STRIP);
|
andrewm@0
|
124 for(int index = 0; index < keyPositions_.size() && index < keyTimestamps_.size(); index++) {
|
andrewm@0
|
125 glVertex2f(graphToDisplayX(keyTimestamps_[index]), graphToDisplayY(keyPositions_[index]));
|
andrewm@0
|
126 }
|
andrewm@0
|
127 glEnd();
|
andrewm@0
|
128 }
|
andrewm@0
|
129
|
andrewm@0
|
130 // Draw the important features, if they exist
|
andrewm@0
|
131 if(!missing_value<timestamp_type>::isMissing(pressStartTimestamp_)) {
|
andrewm@0
|
132 glColor3f(0.0, 0.0, 1.0);
|
andrewm@0
|
133 glBegin(GL_LINES);
|
andrewm@0
|
134 glVertex2f(graphToDisplayX(pressStartTimestamp_), 0);
|
andrewm@0
|
135 glVertex2f(graphToDisplayX(pressStartTimestamp_), kDisplayGraphHeight);
|
andrewm@0
|
136 glEnd();
|
andrewm@0
|
137 }
|
andrewm@0
|
138 if(!missing_value<timestamp_type>::isMissing(pressFinishTimestamp_)) {
|
andrewm@0
|
139 glColor3f(1.0, 0.0, 0.0);
|
andrewm@0
|
140 glBegin(GL_LINES);
|
andrewm@0
|
141 glVertex2f(graphToDisplayX(pressFinishTimestamp_), 0);
|
andrewm@0
|
142 glVertex2f(graphToDisplayX(pressFinishTimestamp_), kDisplayGraphHeight);
|
andrewm@0
|
143 glEnd();
|
andrewm@0
|
144 }
|
andrewm@0
|
145 if(!missing_value<timestamp_type>::isMissing(releaseStartTimestamp_)) {
|
andrewm@0
|
146 glColor3f(0.0, 0.0, 1.0);
|
andrewm@0
|
147 glBegin(GL_LINES);
|
andrewm@0
|
148 glVertex2f(graphToDisplayX(releaseStartTimestamp_), 0);
|
andrewm@0
|
149 glVertex2f(graphToDisplayX(releaseStartTimestamp_), kDisplayGraphHeight);
|
andrewm@0
|
150 glEnd();
|
andrewm@0
|
151 }
|
andrewm@0
|
152 if(!missing_value<timestamp_type>::isMissing(releaseFinishTimestamp_)) {
|
andrewm@0
|
153 glColor3f(1.0, 0.0, 0.0);
|
andrewm@0
|
154 glBegin(GL_LINES);
|
andrewm@0
|
155 glVertex2f(graphToDisplayX(releaseFinishTimestamp_), 0);
|
andrewm@0
|
156 glVertex2f(graphToDisplayX(releaseFinishTimestamp_), kDisplayGraphHeight);
|
andrewm@0
|
157 glEnd();
|
andrewm@0
|
158 }
|
andrewm@0
|
159
|
andrewm@0
|
160 glFlush();
|
andrewm@0
|
161 }
|
andrewm@0
|
162
|
andrewm@0
|
163 // Mouse interaction methods
|
andrewm@0
|
164
|
andrewm@0
|
165 void KeyPositionGraphDisplay::mouseDown(float x, float y) {
|
andrewm@0
|
166 //Point mousePoint = {x, y};
|
andrewm@0
|
167 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
168 }
|
andrewm@0
|
169
|
andrewm@0
|
170 void KeyPositionGraphDisplay::mouseDragged(float x, float y) {
|
andrewm@0
|
171 //Point mousePoint = {x, y};
|
andrewm@0
|
172 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
173 }
|
andrewm@0
|
174
|
andrewm@0
|
175 void KeyPositionGraphDisplay::mouseUp(float x, float y) {
|
andrewm@0
|
176 //Point mousePoint = {x, y};
|
andrewm@0
|
177 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
178 }
|
andrewm@0
|
179
|
andrewm@0
|
180 void KeyPositionGraphDisplay::rightMouseDown(float x, float y) {
|
andrewm@0
|
181 //Point mousePoint = {x, y};
|
andrewm@0
|
182 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
183 }
|
andrewm@0
|
184
|
andrewm@0
|
185 void KeyPositionGraphDisplay::rightMouseDragged(float x, float y) {
|
andrewm@0
|
186 //Point mousePoint = {x, y};
|
andrewm@0
|
187 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
188 }
|
andrewm@0
|
189
|
andrewm@0
|
190 void KeyPositionGraphDisplay::rightMouseUp(float x, float y) {
|
andrewm@0
|
191 //Point mousePoint = {x, y};
|
andrewm@0
|
192 //Point scaledPoint = screenToInternal(mousePoint);
|
andrewm@0
|
193 }
|
andrewm@0
|
194
|
andrewm@0
|
195 float KeyPositionGraphDisplay::graphToDisplayX(float x) {
|
andrewm@0
|
196 return kDisplayGraphWidth*(x - xMin_)/(xMax_ - xMin_);
|
andrewm@0
|
197 }
|
andrewm@0
|
198
|
andrewm@0
|
199 float KeyPositionGraphDisplay::graphToDisplayY(float y) {
|
andrewm@0
|
200 return kDisplayGraphHeight*(y - yMin_)/(yMax_ - yMin_);
|
andrewm@0
|
201 }
|
andrewm@0
|
202
|
andrewm@0
|
203 void KeyPositionGraphDisplay::refreshViewport() {
|
andrewm@0
|
204 glViewport(0, 0, displayPixelWidth_, displayPixelHeight_);
|
andrewm@0
|
205 }
|
andrewm@0
|
206
|
andrewm@0
|
207 // Copy data from the circular buffer to our internal buffer for
|
andrewm@0
|
208 void KeyPositionGraphDisplay::copyKeyDataFromBuffer(Node<key_position>& keyBuffer,
|
andrewm@0
|
209 const Node<key_position>::size_type startIndex,
|
andrewm@0
|
210 const Node<key_position>::size_type endIndex) {
|
andrewm@0
|
211 // Clear existing information
|
andrewm@0
|
212 keyPositions_.clear();
|
andrewm@0
|
213 keyTimestamps_.clear();
|
andrewm@0
|
214
|
andrewm@0
|
215 Node<key_position>::size_type index = startIndex;
|
andrewm@0
|
216 if(index < keyBuffer.beginIndex())
|
andrewm@0
|
217 index = keyBuffer.beginIndex();
|
andrewm@0
|
218
|
andrewm@0
|
219 // Iterate through the buffer, copying positions and timestamps
|
andrewm@0
|
220 while(index < endIndex && index < keyBuffer.endIndex()) {
|
andrewm@0
|
221 keyPositions_.push_back(keyBuffer[index]);
|
andrewm@0
|
222 keyTimestamps_.push_back(keyBuffer.timestampAt(index));
|
andrewm@0
|
223 index++;
|
andrewm@0
|
224 }
|
andrewm@0
|
225
|
andrewm@0
|
226 // Scale the axes according to what's being displayed. The Y axis should stay
|
andrewm@0
|
227 // more or less constant (for now), but X will change depending on timestamps
|
andrewm@0
|
228 xMin_ = keyTimestamps_.front();
|
andrewm@0
|
229 xMax_ = keyTimestamps_.back();
|
andrewm@0
|
230 yMin_ = -0.2;
|
andrewm@0
|
231 yMax_ = 1.2;
|
andrewm@27
|
232 tellCanvasToRepaint();
|
andrewm@0
|
233 }
|
andrewm@0
|
234
|
andrewm@0
|
235 // Conversion from internal coordinate space to external pixel values and back
|
andrewm@0
|
236
|
andrewm@0
|
237 // Pixel values go from 0,0 (lower left) to displayPixelWidth_, displayPixelHeight_ (upper right)
|
andrewm@0
|
238 // Internal values go from -totalDisplayWidth_/2, -totalDisplayHeight_/2 (lower left)
|
andrewm@0
|
239 // to totalDisplayWidth_/2, totalDisplayHeight_/2 (upper right)
|
andrewm@0
|
240
|
andrewm@0
|
241 // Pixel value in --> OpenGL value out
|
andrewm@0
|
242 KeyPositionGraphDisplay::Point KeyPositionGraphDisplay::screenToInternal(Point& inPoint) {
|
andrewm@0
|
243 Point out;
|
andrewm@0
|
244
|
andrewm@0
|
245 out.x = -totalDisplayWidth_*0.5 + (inPoint.x/displayPixelWidth_) * totalDisplayWidth_;
|
andrewm@0
|
246 out.y = -totalDisplayHeight_*0.5 + (inPoint.y/displayPixelHeight_) * totalDisplayHeight_;
|
andrewm@0
|
247
|
andrewm@0
|
248 return out;
|
andrewm@0
|
249 }
|
andrewm@0
|
250
|
andrewm@0
|
251 // OpenGL value in --> Pixel value out
|
andrewm@0
|
252 KeyPositionGraphDisplay::Point KeyPositionGraphDisplay::internalToScreen(Point& inPoint) {
|
andrewm@0
|
253 Point out;
|
andrewm@0
|
254
|
andrewm@0
|
255 out.x = ((inPoint.x + totalDisplayWidth_*0.5)/totalDisplayWidth_) * displayPixelWidth_;
|
andrewm@0
|
256 out.y = ((inPoint.y + totalDisplayHeight_*0.5)/totalDisplayHeight_) * displayPixelHeight_;
|
andrewm@0
|
257
|
andrewm@0
|
258 return out;
|
andrewm@0
|
259 } |