MuseScore/libmscore/skyline.cpp

286 lines
8.2 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2016 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "skyline.h"
#include "segment.h"
namespace Ms {
static const qreal MAXIMUM_Y = 1000000.0;
static const qreal MINIMUM_Y = -1000000.0;
// #define SKL_DEBUG
#ifdef SKL_DEBUG
#define DP(...) printf(__VA_ARGS__)
#else
#define DP(...)
#endif
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Skyline::add(const QRectF& r)
{
_north.add(r.x(), r.top(), r.width());
_south.add(r.x(), r.bottom(), r.width());
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void SkylineLine::add(const Shape& s)
{
for (const auto& r : s)
add(r);
}
void SkylineLine::add(const QRectF& r)
{
if (north)
add(r.x(), r.top(), r.width());
else
add(r.x(), r.bottom(), r.width());
}
void Skyline::add(const Shape& s)
{
for (const auto& r : s)
add(r);
}
void SkylineLine::add(qreal x, qreal y, qreal w)
{
// Q_ASSERT(w >= 0.0);
if (x < 0.0)
return;
DP("===add %f %f %f\n", x, y, w);
qreal cx = 0.0;
for (auto i = begin(); i != end(); ++i) {
qreal cy = i->y;
if ((x + w) <= cx) // A
return; // break;
if (x > (cx + i->w)) { // B
cx += i->w;
continue;
}
if ((north && (cy <= y)) || (!north && (cy >= y))) {
cx += i->w;
continue;
}
if ((x >= cx) && ((x+w) < (cx+i->w))) { // (E) insert segment
DP(" insert at %f %f x:%f w:%f\n", cx, i->w, x, w);
qreal w1 = x - cx;
qreal w2 = w;
qreal w3 = i->w - (w1 + w2);
if (w1 > 0.0000001) {
i->w = w1;
++i;
i = insert(i, SkylineSegment(y, w2));
DP(" A w1 %f w2 %f\n", w1, w2);
}
else {
i->w = w2;
i->y = y;
DP(" B w2 %f\n", w2);
}
if (w3 > 0.0000001) {
++i;
DP(" C w3 %f\n", w3);
insert(i, SkylineSegment(cy, w3));
}
return;
}
else if ((x <= cx) && ((x + w) >= (cx + i->w))) { // F
DP(" change(F) cx %f y %f\n", cx, y);
i->y = y;
}
else if (x < cx) { // C
qreal w1 = x + w - cx;
i->w -= w1;
DP(" add(C) cx %f y %f w %f w1 %f\n", cx, y, w1, i->w);
insert(i, SkylineSegment(y, w1));
return;
}
else { // D
qreal w1 = x - cx;
qreal w2 = i->w - w1;
if (w2 > 0.0000001) {
i->w = w1;
cx += w1;
DP(" add(D) %f %f\n", y, w2);
++i;
i = insert(i, SkylineSegment(y, w2));
}
}
cx += i->w;
}
if (x >= cx) {
if (x > cx) {
qreal cy = north ? MAXIMUM_Y : MINIMUM_Y;
DP(" append1 %f %f\n", cy, x - cx);
push_back(SkylineSegment(cy, x - cx));
}
DP(" append2 %f %f\n", y, w);
push_back(SkylineSegment(y, w));
}
else if (x + w > cx)
push_back(SkylineSegment(y, x + w - cx));
}
//---------------------------------------------------------
// clear
//---------------------------------------------------------
void Skyline::clear()
{
_north.clear();
_south.clear();
}
//-------------------------------------------------------------------
// minDistance
// a is located below this skyline.
// Calculates the minimum distance between two skylines
//-------------------------------------------------------------------
qreal Skyline::minDistance(const Skyline& s) const
{
return south().minDistance(s.north());
}
qreal SkylineLine::minDistance(const SkylineLine& sl) const
{
qreal dist = MINIMUM_Y;
qreal x1 = 0.0;
qreal x2 = 0.0;
auto k = sl.begin();
for (auto i = begin(); i != end(); ++i) {
while (k != sl.end() && (x2 + k->w) < x1) {
x2 += k->w;
++k;
}
if (k == sl.end())
break;
for (;;) {
if ((x1 + i->w > x2) && (x1 < x2 + k->w))
dist = qMax(dist, i->y - k->y);
if (x2 + k->w < x1 + i->w) {
x2 += k->w;
++k;
if (k == sl.end())
break;
}
else
break;
}
if (k == sl.end())
break;
x1 += i->w;
}
return dist;
}
//---------------------------------------------------------
// paint
//---------------------------------------------------------
void Skyline::paint(QPainter& p) const
{
p.save();
p.setBrush(Qt::NoBrush);
QMatrix matrix = p.matrix();
p.setPen(QPen(QBrush(Qt::darkYellow), 2.0 / matrix.m11()));
_north.paint(p);
p.setPen(QPen(QBrush(Qt::green), 2.0 / matrix.m11()));
_south.paint(p);
p.restore();
}
void SkylineLine::paint(QPainter& p) const
{
qreal x1 = 0.0;
qreal x2;
qreal y = 0.0;
bool pvalid = false;
for (const SkylineSegment& s : *this) {
x2 = x1 + s.w;
if (valid(s)) {
if (pvalid)
p.drawLine(QLineF(x1, y, x1, s.y));
y = s.y;
p.drawLine(QLineF(x1, y, x2, y));
pvalid = true;
}
else
pvalid = false;
x1 = x2;
}
}
bool SkylineLine::valid(const SkylineSegment& s) const
{
return north ? (s.y != MAXIMUM_Y) : (s.y != MINIMUM_Y);
}
//---------------------------------------------------------
// dump
//---------------------------------------------------------
void Skyline::dump(const char* p, bool n) const
{
printf("Skyline dump: %p %s\n", this, p);
if (n)
_north.dump();
else
_south.dump();
}
void SkylineLine::dump() const
{
qreal x = 0.0;
for (const SkylineSegment& s : *this) {
printf(" x %f y %f w %f\n", x, s.y, s.w);
x += s.w;
}
}
//---------------------------------------------------------
// max
//---------------------------------------------------------
qreal SkylineLine::max() const
{
qreal val;
if (north) {
val = MAXIMUM_Y;
for (const SkylineSegment& s : *this)
val = qMin(val, s.y);
}
else {
val = MINIMUM_Y;
for (const SkylineSegment& s : *this)
val = qMax(val, s.y);
}
return val;
}
} // namespace Ms