MuseScore/libmscore/shape.cpp
2020-05-28 09:50:45 +02:00

378 lines
9.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//=============================================================================
// 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 "shape.h"
#include "segment.h"
namespace Ms {
//---------------------------------------------------------
// addHorizontalSpacing
// Currently implemented by adding rectangles of zero
// height to the Y position corresponding to the type.
// This is a simple solution but has its drawbacks too.
//---------------------------------------------------------
void Shape::addHorizontalSpacing(HorizontalSpacingType type, qreal leftEdge, qreal rightEdge)
{
constexpr qreal eps = 100 * std::numeric_limits<qreal>::epsilon();
const qreal y = eps * int(type);
if (leftEdge == rightEdge) { // HACK zero-width shapes collide with everything currently.
rightEdge += eps;
}
add(QRectF(leftEdge, y, rightEdge - leftEdge, 0));
}
//---------------------------------------------------------
// translate
//---------------------------------------------------------
void Shape::translate(const QPointF& pt)
{
for (QRectF& r : *this) {
r.translate(pt);
}
}
void Shape::translateX(qreal xo)
{
for (QRectF& r : *this) {
r.setLeft(r.left() + xo);
r.setRight(r.right() + xo);
}
}
void Shape::translateY(qreal yo)
{
for (QRectF& r : *this) {
r.setTop(r.top() + yo);
r.setBottom(r.bottom() + yo);
}
}
//---------------------------------------------------------
// translated
//---------------------------------------------------------
Shape Shape::translated(const QPointF& pt) const
{
Shape s;
for (const ShapeElement& r : *this)
#ifndef NDEBUG
{
s.add(r.translated(pt), r.text);
}
#else
{
s.add(r.translated(pt));
}
#endif
return s;
}
//-------------------------------------------------------------------
// minHorizontalDistance
// a is located right of this shape.
// Calculates the minimum horizontal distance between the two shapes
// so they dont touch.
//-------------------------------------------------------------------
qreal Shape::minHorizontalDistance(const Shape& a) const
{
qreal dist = -1000000.0; // min real
for (const QRectF& r2 : a) {
qreal by1 = r2.top();
qreal by2 = r2.bottom();
for (const QRectF& r1 : *this) {
qreal ay1 = r1.top();
qreal ay2 = r1.bottom();
if (Ms::intersects(ay1, ay2, by1, by2)
|| ((r1.height() == 0.0) && (r2.height() == 0.0) && (ay1 == by1))
|| ((r1.width() == 0.0) || (r2.width() == 0.0))) {
dist = qMax(dist, r1.right() - r2.left());
}
}
}
return dist;
}
//-------------------------------------------------------------------
// minVerticalDistance
// a is located below this shape.
// Calculates the minimum distance between two shapes.
//-------------------------------------------------------------------
qreal Shape::minVerticalDistance(const Shape& a) const
{
qreal dist = -1000000.0; // min real
for (const QRectF& r2 : a) {
if (r2.height() <= 0.0) {
continue;
}
qreal bx1 = r2.left();
qreal bx2 = r2.right();
for (const QRectF& r1 : *this) {
if (r1.height() <= 0.0) {
continue;
}
qreal ax1 = r1.left();
qreal ax2 = r1.right();
if (Ms::intersects(ax1, ax2, bx1, bx2)) {
dist = qMax(dist, r1.bottom() - r2.top());
}
}
}
return dist;
}
//---------------------------------------------------------
// left
// compute left border
//---------------------------------------------------------
qreal Shape::left() const
{
qreal dist = 0.0;
for (const QRectF& r : *this) {
if (r.height() != 0.0 && r.left() < dist) {
// if (r.left() < dist)
dist = r.left();
}
}
return -dist;
}
//---------------------------------------------------------
// right
// compute right border
//---------------------------------------------------------
qreal Shape::right() const
{
qreal dist = 0.0;
for (const QRectF& r : *this) {
if (r.right() > dist) {
dist = r.right();
}
}
return dist;
}
//---------------------------------------------------------
// top
//---------------------------------------------------------
qreal Shape::top() const
{
qreal dist = 1000000.0;
for (const QRectF& r : *this) {
if (r.top() < dist) {
dist = r.top();
}
}
return dist;
}
//---------------------------------------------------------
// bottom
//---------------------------------------------------------
qreal Shape::bottom() const
{
qreal dist = -1000000.0;
for (const QRectF& r : *this) {
if (r.bottom() > dist) {
dist = r.bottom();
}
}
return dist;
}
//---------------------------------------------------------
// topDistance
// p is on top of shape
// returns negative values if there is an overlap
//---------------------------------------------------------
qreal Shape::topDistance(const QPointF& p) const
{
qreal dist = 1000000.0;
for (const QRectF& r : *this) {
if (p.x() >= r.left() && p.x() < r.right()) {
dist = qMin(dist, r.top() - p.y());
}
}
return dist;
}
//---------------------------------------------------------
// bottomDistance
// p is below the shape
// returns negative values if there is an overlap
//---------------------------------------------------------
qreal Shape::bottomDistance(const QPointF& p) const
{
qreal dist = 1000000.0;
for (const QRectF& r : *this) {
if (p.x() >= r.left() && p.x() < r.right()) {
dist = qMin(dist, p.y() - r.bottom());
}
}
return dist;
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Shape::remove(const QRectF& r)
{
for (auto i = begin(); i != end(); ++i) {
if (*i == r) {
erase(i);
return;
}
}
// qWarning("Shape::remove: QRectF not found in Shape");
qFatal("Shape::remove: QRectF not found in Shape");
}
void Shape::remove(const Shape& s)
{
for (const QRectF& r : s) {
remove(r);
}
}
//---------------------------------------------------------
// contains
//---------------------------------------------------------
bool Shape::contains(const QPointF& p) const
{
for (const QRectF& r : *this) {
if (r.contains(p)) {
return true;
}
}
return false;
}
//---------------------------------------------------------
// intersects
//---------------------------------------------------------
bool Shape::intersects(const QRectF& rr) const
{
for (const QRectF& r : *this) {
if (r.intersects(rr)) {
return true;
}
}
return false;
}
//---------------------------------------------------------
// intersects
//---------------------------------------------------------
bool Shape::intersects(const Shape& other) const
{
for (const QRectF& r : other) {
if (intersects(r)) {
return true;
}
}
return false;
}
//---------------------------------------------------------
// paint
//---------------------------------------------------------
void Shape::paint(QPainter& p) const
{
for (const QRectF& r : *this) {
p.drawRect(r);
}
}
#ifndef NDEBUG
//---------------------------------------------------------
// dump
//---------------------------------------------------------
void Shape::dump(const char* p) const
{
qDebug("Shape dump: %p %s size %zu", this, p, size());
for (const ShapeElement& r : *this) {
r.dump();
}
}
void ShapeElement::dump() const
{
qDebug(" %s: %f %f %f %f", text ? text : "", x(), y(), width(), height());
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Shape::add(const QRectF& r, const char* t)
{
push_back(ShapeElement(r, t));
}
#endif
#ifdef DEBUG_SHAPES
//---------------------------------------------------------
// testShapes
//---------------------------------------------------------
void testShapes()
{
printf("======test shapes======\n");
//=======================
// minDistance()
//=======================
Shape a;
Shape b;
a.add(QRectF(-10, -10, 20, 20));
qreal d = a.minHorizontalDistance(b); // b is empty
printf(" minHDistance (0.0): %f", d);
if (d != 0.0) {
printf(" =====error");
}
printf("\n");
b.add(QRectF(0, 0, 10, 10));
d = a.minHorizontalDistance(b);
printf(" minHDistance (10.0): %f", d);
if (d != 10.0) {
printf(" =====error");
}
printf("\n");
d = a.minVerticalDistance(b);
printf(" minVDistance (10.0): %f", d);
if (d != 10.0) {
printf(" =====error");
}
printf("\n");
}
#endif // DEBUG_SHAPES
} // namespace Ms