void IntonationWidget::smoothPoints(QPainter& painter) { if (intonationPointList_.size() < 2U) return; for (unsigned int i = 0, end = intonationPointList_.size() - 1; i < end; ++i) { const auto& point1 = intonationPointList_[i]; const auto& point2 = intonationPointList_[i + 1]; double x1 = point1.absoluteTime(); double y1 = point1.semitone(); double m1 = point1.slope(); double x2 = point2.absoluteTime(); double y2 = point2.semitone(); double m2 = point2.slope(); double x12 = x1 * x1; double x13 = x12 * x1; double x22 = x2 * x2; double x23 = x22 * x2; double denominator = x2 - x1; denominator = denominator * denominator * denominator; double d = (-(y2 * x13) + 3.0 * y2 * x12 * x2 + m2 * x13 * x2 + m1 * x12 * x22 - m2 * x12 * x22 - 3.0 * x1 * y1 * x22 - m1 * x1 * x23 + y1 * x23) / denominator; double c = (-(m2 * x13) - 6.0 * y2 * x1 * x2 - 2.0 * m1 * x12 * x2 - m2 * x12 * x2 + 6.0 * x1 * y1 * x2 + m1 * x1 * x22 + 2.0 * m2 * x1 * x22 + m1 * x23) / denominator; double b = (3.0 * y2 * x1 + m1 * x12 + 2.0 * m2 * x12 - 3.0 * x1 * y1 + 3.0 * x2 * y2 + m1 * x1 * x2 - m2 * x1 * x2 - 3.0 * y1 * x2 - 2.0 * m1 * x22 - m2 * x22) / denominator; double a = (-2.0 * y2 - m1 * x1 - m2 * x1 + 2.0 * y1 + m1 * x2 + m2 * x2) / denominator; QPointF prevPoint(0.5 + timeToX(x1), 0.5 + valueToY(y1)); for (unsigned int j = static_cast<unsigned int>(x1); j <= static_cast<unsigned int>(x2); j += SMOOTH_POINTS_X_INCREMENT) { double x = j; double y = x * (x * (x * a + b) + c) + d; QPointF currPoint(0.5 + timeToX(x), 0.5 + valueToY(y)); painter.drawLine(prevPoint, currPoint); prevPoint = currPoint; } QPointF lastPoint(0.5 + timeToX(x2), 0.5 + valueToY(y2)); painter.drawLine(prevPoint, lastPoint); } }
void updateCursorPosition() { currentPositionMarker.setVisible (transportSource.isPlaying() || isMouseButtonDown()); currentPositionMarker.setRectangle (Rectangle<float> (timeToX (transportSource.getCurrentPosition()) - 0.75f, 0, 1.5f, (float) (getHeight() - scrollbar.getHeight()))); }
void IntonationWidget::mousePressEvent(QMouseEvent* event) { if (eventList_ == nullptr || eventList_->list().empty() || intonationPointList_.empty()) { return; } if (event->button() != Qt::LeftButton) return; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QPointF clickPoint = event->localPos(); #else QPointF clickPoint = event->posF(); #endif double minDist = 1.0e10; for (unsigned int i = 0, size = intonationPointList_.size(); i < size; ++i) { QPointF point( timeToX(intonationPointList_[i].absoluteTime()), valueToY(intonationPointList_[i].semitone())); // Simplified "distance". double dist = std::abs(point.x() - clickPoint.x()) + std::abs(point.y() - clickPoint.y()); if (dist < minDist) { minDist = dist; selectedPoint_ = i; } } sendSelectedPointData(); update(); }
void timerCallback() { currentPositionMarker.setVisible (transportSource.isPlaying() || isMouseButtonDown()); double currentPlayPosition = transportSource.getCurrentPosition(); currentPositionMarker.setRectangle (Rectangle<float> (timeToX (currentPlayPosition) - 0.75f, 0, 1.5f, (float) getHeight())); }
void TimelineComponent::updateClip (TimelineClip* clip) { jassert (clip); ClipRange<double> time; clip->getClipRange (time); const Range<int> hrange (trackHeight (clip->trackIndex())); clip->setBounds (timeToX (time.getStart(), clip->getTimeUnit()), hrange.getStart(), timeToWidth (time, clip->getTimeUnit()), hrange.getLength()); }
// Note: with no antialiasing, the coordinates in QPointF are rounded to the nearest integer. void TransitionWidget::paintEvent(QPaintEvent*) { if (pointList_ == nullptr) { return; } QPainter painter(this); painter.setFont(QFont("monospace")); if (dataUpdated_) { QFontMetrics fm = painter.fontMetrics(); textYOffset_ = 0.5 * fm.ascent(); leftMargin_ = MARGIN + TEXT_MARGIN + fm.width(transitionYLabels[0]); updateScales(); dataUpdated_ = false; } if (special_) { // Gray area. painter.fillRect(QRectF(QPointF(leftMargin_, MARGIN + 7.0 * yStep_), QPointF(leftMargin_ + graphWidth_, MARGIN + 14.0 * yStep_)), Qt::gray); // Y labels. for (unsigned int i = 0; i < specialTransitionYLabels.size(); ++i) { double y = MARGIN + i * yStep_ + textYOffset_; painter.drawText(QPointF(MARGIN, y), specialTransitionYLabels[i]); painter.drawText(QPointF(leftMargin_ + graphWidth_ + TEXT_MARGIN, y), specialTransitionYLabels[i]); } } else { // Gray areas. painter.fillRect(QRectF(QPointF(leftMargin_, MARGIN), QPointF(leftMargin_ + graphWidth_, MARGIN + 2.0 * yStep_)), Qt::gray); painter.fillRect(QRectF(QPointF(leftMargin_, MARGIN + 12.0 * yStep_), QPointF(leftMargin_ + graphWidth_, MARGIN + 14.0 * yStep_)), Qt::gray); // Y labels. for (unsigned int i = 0; i < transitionYLabels.size(); ++i) { double y = MARGIN + i * yStep_ + textYOffset_; painter.drawText(QPointF(MARGIN, y), transitionYLabels[i]); painter.drawText(QPointF(leftMargin_ + graphWidth_ + TEXT_MARGIN, y), transitionYLabels[i]); } } // Horizontal lines. for (int i = 0; i < 15; ++i) { double y = MARGIN + i * yStep_; painter.drawLine(QPointF(leftMargin_, y), QPointF(leftMargin_ + graphWidth_, y)); } QPen pen; QPen pen2; pen2.setWidth(2); painter.setRenderHint(QPainter::Antialiasing); // Lines. if (!pointList_->empty()) { painter.setPen(pen2); QPointF prevP(0.5 + timeToX(0.0), 0.5 + valueToY(0.0)); for (unsigned int i = 0, size = pointList_->size(); i < size; ++i) { const auto& point = (*pointList_)[i]; QPointF p(0.5 + timeToX(point.time), 0.5 + valueToY(point.value)); painter.drawLine(prevP, p); prevP = p; if (i != size - 1) { if (point.type != (*pointList_)[i + 1].type) { prevP.setY(0.5 + valueToY(0.0)); } } } QPointF p(0.5 + timeToX(ruleDuration_), 0.5 + valueToY(special_ ? 0.0 : 100.0)); painter.drawLine(prevP, p); painter.setPen(pen); } painter.setBrush(QBrush(Qt::black)); // Points. if (!pointList_->empty()) { for (auto& point : *pointList_) { double x = 0.5 + timeToX(point.time); double y = 0.5 + valueToY(point.value); switch (point.type) { case TRMControlModel::Transition::Point::TYPE_DIPHONE: drawDiPoint(painter, x, y); break; case TRMControlModel::Transition::Point::TYPE_TRIPHONE: drawTriPoint(painter, x, y); break; case TRMControlModel::Transition::Point::TYPE_TETRAPHONE: drawTetraPoint(painter, x, y); break; default: break; } } } // Posture markers. double yMarker = 0.5 + 0.5 * yStep_ + textYOffset_; switch (transitionType_) { case TRMControlModel::Transition::TYPE_DIPHONE: drawDiPoint(painter, 0.5 + timeToX(ruleDuration_), yMarker); break; case TRMControlModel::Transition::TYPE_TRIPHONE: drawDiPoint( painter, 0.5 + timeToX(mark1_) , yMarker); drawTriPoint(painter, 0.5 + timeToX(ruleDuration_), yMarker); break; case TRMControlModel::Transition::TYPE_TETRAPHONE: drawDiPoint( painter, 0.5 + timeToX(mark1_) , yMarker); drawTriPoint( painter, 0.5 + timeToX(mark2_) , yMarker); drawTetraPoint(painter, 0.5 + timeToX(ruleDuration_), yMarker); break; default: break; } painter.setBrush(QBrush()); painter.setRenderHint(QPainter::Antialiasing, false); // Posture vertical lines. double yPosture1 = special_ ? valueToY(-140.0) : valueToY(-20.0); double yPosture2 = special_ ? valueToY( 140.0) : valueToY(120.0); double x = timeToX(0.0); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); switch (transitionType_) { case TRMControlModel::Transition::TYPE_DIPHONE: x = timeToX(ruleDuration_); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); break; case TRMControlModel::Transition::TYPE_TRIPHONE: x = timeToX(mark1_); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); x = timeToX(ruleDuration_); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); break; case TRMControlModel::Transition::TYPE_TETRAPHONE: x = timeToX(mark1_); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); x = timeToX(mark2_); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); x = timeToX(ruleDuration_); painter.drawLine(QPointF(x, yPosture1), QPointF(x, yPosture2)); break; default: break; } if (!pointList_->empty()) { if (!special_) { // Slopes. double y1 = MARGIN + 15.0 * yStep_; double y2 = y1 + yStep_; double yText = y2 - 0.5 * yStep_ + textYOffset_; for (unsigned int i = 0, end = pointList_->size() - 1; i < end; ++i) { const auto& point1 = (*pointList_)[i]; if (point1.hasSlope) { const auto& point2 = (*pointList_)[i + 1]; painter.setPen(Qt::gray); painter.drawRect(QRectF(QPointF(timeToX(point1.time), y1), QPointF(timeToX(point2.time), y2))); painter.setPen(Qt::black); painter.drawText( QPointF(timeToX(point1.time) + SLOPE_TEXT_MARGIN, yText), QString("%1").arg(point1.slope, 0, 'f', 1)); } } } // Selected point. if (selectedPointIndex_ >= 0 && static_cast<std::size_t>(selectedPointIndex_) < pointList_->size()) { const auto& point = (*pointList_)[selectedPointIndex_]; double x = timeToX(point.time); double y = valueToY(point.value); painter.drawRect(QRectF( QPointF(x - SELECTION_SIZE, y - SELECTION_SIZE), QPointF(x + SELECTION_SIZE, y + SELECTION_SIZE))); } } }
// Note: with no antialiasing, the coordinates in QPointF are rounded to the nearest integer. void IntonationWidget::paintEvent(QPaintEvent*) { if (eventList_ == nullptr || eventList_->list().empty()) { return; } QPainter painter(this); painter.setFont(QFont("monospace")); if (modelUpdated_) { QFontMetrics fm = painter.fontMetrics(); textYOffset_ = 0.5 * fm.ascent(); leftMargin_ = MARGIN + TEXT_MARGIN + fm.width(yLabels[0]); modelUpdated_ = false; } totalWidth_ = std::ceil(leftMargin_ + graphWidth_ + 6.0 * MARGIN); if (totalWidth_ < MININUM_WIDTH) { totalWidth_ = MININUM_WIDTH; } totalHeight_ = std::ceil(2.0 * MARGIN + 3.0 * TRACK_HEIGHT + 15.0 * yStep_); if (totalHeight_ < MININUM_HEIGHT) { totalHeight_ = MININUM_HEIGHT; } setMinimumWidth(totalWidth_); setMinimumHeight(totalHeight_); double xStart = timeToX(0.0); double yStart = valueToY(MIN_VALUE); double xEnd = timeToX(maxTime_); double yEnd = valueToY(MAX_VALUE); // Y labels. for (unsigned int i = 0; i < yLabels.size(); ++i) { double y = MARGIN + (i + 3) * yStep_ + textYOffset_; painter.drawText(QPointF(MARGIN, y), yLabels[i]); } // Horizontal lines. painter.setPen(Qt::lightGray); for (int i = 1; i < 15; ++i) { double y = MARGIN + (i + 3) * yStep_; painter.drawLine(QPointF(xStart, y), QPointF(xEnd, y)); } painter.setPen(Qt::black); postureTimeList_.clear(); double yPosture = MARGIN + 3.0 * TRACK_HEIGHT - 0.5 * (TRACK_HEIGHT - 1.0) + textYOffset_; unsigned int postureIndex = 0; for (const TRMControlModel::Event_ptr& ev : eventList_->list()) { double x = timeToX(ev->time); if (ev->flag) { postureTimeList_.push_back(ev->time); const TRMControlModel::Posture* posture = eventList_->getPostureAtIndex(postureIndex++); if (posture) { painter.setPen(Qt::black); // Posture name. painter.drawText(QPointF(x, yPosture), posture->name().c_str()); } painter.setPen(Qt::lightGray); // Event vertical line. painter.drawLine(QPointF(x, yStart), QPointF(x, yEnd)); } } painter.setPen(Qt::black); // Frame. painter.drawRect(QRectF(QPointF(xStart, yStart), QPointF(xEnd, yEnd))); double yRuleText = MARGIN + TRACK_HEIGHT - 0.5 * (TRACK_HEIGHT - 1.0) + textYOffset_; double yRuleText2 = yRuleText + TRACK_HEIGHT; for (int i = 0; i < eventList_->numberOfRules(); ++i) { auto* ruleData = eventList_->getRuleAtIndex(i); if (ruleData) { unsigned int firstPosture = ruleData->firstPosture; unsigned int lastPosture = ruleData->lastPosture; int postureTime1, postureTime2; if (firstPosture < postureTimeList_.size()) { postureTime1 = postureTimeList_[firstPosture]; } else { postureTime1 = 0; // invalid } if (lastPosture < postureTimeList_.size()) { postureTime2 = postureTimeList_[lastPosture]; } else { postureTime2 = postureTime1 + ruleData->duration; } // Rule frame. painter.drawRect(QRectF( QPointF(timeToX(postureTime1), MARGIN), QPointF(timeToX(postureTime2), MARGIN + 2.0 * TRACK_HEIGHT))); // Rule number. painter.drawText(QPointF(timeToX(postureTime1) + TEXT_MARGIN, yRuleText), QString::number(ruleData->number)); // Rule duration. painter.drawText(QPointF(timeToX(postureTime1) + TEXT_MARGIN, yRuleText2), QString::number(ruleData->duration, 'f', 0)); } } QPen pen; QPen pen2; pen2.setWidth(2); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QBrush(Qt::black)); // Lines between intonation points. QPointF prevPoint(0.5 + xStart, 0.5 + yStart); for (unsigned int i = 0, size = intonationPointList_.size(); i < size; ++i) { QPointF currPoint( 0.5 + timeToX(intonationPointList_[i].absoluteTime()), 0.5 + valueToY(intonationPointList_[i].semitone())); painter.setPen(pen2); painter.drawLine(prevPoint, currPoint); painter.setPen(pen); drawPointMarker(painter, 0.5 + currPoint.x(), 0.5 + currPoint.y()); prevPoint = currPoint; } painter.setBrush(QBrush()); painter.setPen(pen2); smoothPoints(painter); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing, false); // Point selection. if (selectedPoint_ >= 0) { double x = timeToX(intonationPointList_[selectedPoint_].absoluteTime()); double y = valueToY(intonationPointList_[selectedPoint_].semitone()); painter.drawRect(QRectF( QPointF(x - SELECTION_SIZE, y - SELECTION_SIZE), QPointF(x + SELECTION_SIZE, y + SELECTION_SIZE))); } // Beat lines. for (unsigned int i = 0, size = intonationPointList_.size(); i < size; ++i) { double x = timeToX(intonationPointList_[i].beatTime()); painter.drawLine(QPointF(x, yStart), QPointF(x, yEnd)); } // Time scale. double yTick1 = yStart + TIME_DIVISION_MARK_SIZE; double yTick2 = yStart + 2.0 * TIME_DIVISION_MARK_SIZE; double yTimeText = yTick2 + 0.5 * TRACK_HEIGHT + textYOffset_; for (int time = 0, end = static_cast<int>(maxTime_); time <= end; time += 10) { double x = timeToX(time); if (time % 100 == 0) { painter.drawLine(QPointF(x, yStart), QPointF(x, yTick2)); painter.drawText(QPointF(x, yTimeText), QString::number(time)); } else { painter.drawLine(QPointF(x, yStart), QPointF(x, yTick1)); } } }
int TimelineComponent::timeToWidth (const Range<double>& time, const TimeUnit unit) const { return timeToX (time.getEnd(), unit) - timeToX (time.getStart(), unit); }
void TimelineComponent::paint (Graphics& g) { g.setColour (Colour (0xff454545)); g.fillAll(); // track header divider line g.setColour (Colours::black.withAlpha (0.5f)); g.drawVerticalLine (mTrackWidth + 1, 0, getHeight()); int track = heights.trackAtY (0); Rectangle<int> r; while (true) { if (r.getY() > getHeight() || track >= getNumTracks()) break; if (! heights.trackIsVisible (track)) { ++track; continue; } r.setX (mTrackWidth); r.setY (heights.trackY (track)); r.setWidth (getWidth() - mTrackWidth); r.setHeight (heights.get (track) + heights.spacing()); g.saveState(); paintTrackLane (g, track, r); g.restoreState(); ++track; } #if 0 int track = heights.trackAtY (0); Rectangle<int> r; while (true) { r.setX (0); r.setWidth (mTrackWidth); r.setY (heights.trackY (track)); r.setHeight (heights.get (track)); if (r.getY() > getHeight() || track >= getNumTracks()) break; if (! heights.trackIsVisible (track)) { ++track; continue; } if (mTrackWidth > 0) { g.saveState(); paintTrackHeader (g, track, r); g.restoreState(); } r.setX (mTrackWidth); r.setWidth (getWidth() - mTrackWidth); r.setHeight (r.getHeight() + heights.spacing()); g.saveState(); paintTrackLane (g, track, r); g.restoreState(); ++track; } #endif #if 0 // paint grid lines g.saveState(); g.setColour (Colours::white); for (int beat = scale.beatFromPixel (timeToX (timeSpan.getStart())) - 1; ++beat;) { const int pixel = scale.pixelFromBeat (beat) + mTrackWidth; if (pixel < 0) continue; if (scale.beatIsBar (beat)) { g.saveState(); g.setColour(Colours::white); g.drawText (String(beat), pixel, 0, 20, 16, Justification::left, true); g.restoreState(); g.setOpacity (0.20); } else g.setOpacity (0.10); g.drawVerticalLine (pixel, 0, getHeight()); if (pixel > getWidth()) break; } g.restoreState(); g.resetToDefaultState(); #endif }