qskinny/src/nodes/QskBoxMetrics.cpp

200 lines
6.2 KiB
C++

/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskBoxMetrics.h"
#include "QskBoxShapeMetrics.h"
#include "QskBoxBorderMetrics.h"
#include "QskVertex.h"
#include "QskFunctions.h"
QskBoxMetrics::QskBoxMetrics( const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border )
: outerRect( rect )
{
isOutsideRounded = !shape.isRectangle();
if ( !isOutsideRounded )
{
isInsideRounded = false;
isOutsideSymmetric = true;
stepSymmetries = Qt::Vertical | Qt::Horizontal;
preferredOrientation = Qt::Vertical;
const auto bw = border.widths();
hasBorder = bw.width() > 0.0 || bw.height() > 0.0;
innerRect = qskValidOrEmptyInnerRect( rect, bw );
return;
}
isOutsideSymmetric = shape.isRectellipse();
{
const auto tl = shape.topLeft();
const auto tr = shape.topRight();
const auto bl = shape.bottomLeft();
const auto br = shape.bottomRight();
if ( tl.isEmpty() || tr.isEmpty() || ( tl.height() == tr.height() ) )
{
if ( bl.isEmpty() || br.isEmpty() || ( bl.height() == br.height() ) )
stepSymmetries |= Qt::Vertical;
}
if ( tl.isEmpty() || bl.isEmpty() || ( tl.width() == bl.width() ) )
{
if ( tr.isEmpty() || br.isEmpty() || ( tr.width() == br.width() ) )
stepSymmetries |= Qt::Horizontal;
}
}
for ( int i = 0; i < 4; i++ )
{
auto& c = corners[ i ];
const auto radius = shape.radius( static_cast< Qt::Corner >( i ) );
c.radiusX = qBound( 0.0, radius.width(), 0.5 * outerRect.width() );
c.radiusY = qBound( 0.0, radius.height(), 0.5 * outerRect.height() );
c.stepCount = QskVertex::ArcIterator::segmentHint( qMax( c.radiusX, c.radiusY ) );
switch ( i )
{
case Qt::TopLeftCorner:
c.centerX = outerRect.left() + c.radiusX;
c.centerY = outerRect.top() + c.radiusY;
c.sx = -1.0;
c.sy = -1.0;
break;
case Qt::TopRightCorner:
c.centerX = outerRect.right() - c.radiusX;
c.centerY = outerRect.top() + c.radiusY;
c.sx = +1.0;
c.sy = -1.0;
break;
case Qt::BottomLeftCorner:
c.centerX = outerRect.left() + c.radiusX;
c.centerY = outerRect.bottom() - c.radiusY;
c.sx = -1.0;
c.sy = +1.0;
break;
case Qt::BottomRightCorner:
c.centerX = outerRect.right() - c.radiusX;
c.centerY = outerRect.bottom() - c.radiusY;
c.sx = +1.0;
c.sy = +1.0;
break;
}
}
{
const auto cleft = qMax( corners[ Qt::TopLeftCorner ].centerX,
corners[ Qt::BottomLeftCorner ].centerX );
const auto cright = qMin( corners[ Qt::TopRightCorner ].centerX,
corners[ Qt::BottomRightCorner ].centerX );
const auto ctop = qMax( corners[ Qt::TopLeftCorner ].centerY,
corners[ Qt::TopRightCorner ].centerY );
const auto cbottom = qMin( corners[ Qt::BottomLeftCorner ].centerY,
corners[ Qt::BottomRightCorner ].centerY );
// now the bounding rectangle of the fill area
const auto bw = border.widths();
hasBorder = bw.width() > 0.0 || bw.height() > 0.0;
qreal l = outerRect.left() + bw.left();
qreal t = outerRect.top() + bw.top();
qreal r = outerRect.right() - bw.right();
qreal b = outerRect.bottom() - bw.bottom();
l = qMin( l, cright );
r = qMax( r, cleft );
t = qMin( t, cbottom );
b = qMax( b, ctop );
if ( l > r )
l = r = r + 0.5 * ( l - r );
if ( t > b )
t = b = b + 0.5 * ( t - b );
innerRect.setCoords( l, t, r, b );
}
const QskMargins margins(
innerRect.left() - outerRect.left(),
innerRect.top() - outerRect.top(),
outerRect.right() - innerRect.right(),
outerRect.bottom() - innerRect.bottom() );
isBorderRegular = margins.isEquidistant();
isInsideRounded = false;
for ( int i = 0; i < 4; i++ )
{
auto& c = corners[ i ];
if ( c.sx < 0.0 )
c.radiusInnerX = c.radiusX - margins.left();
else
c.radiusInnerX = c.radiusX - margins.right();
if ( c.sy < 0.0 )
c.radiusInnerY = c.radiusY - margins.top();
else
c.radiusInnerY = c.radiusY - margins.bottom();
if ( c.radiusInnerX > 0.0 && c.radiusInnerY > 0.0 )
{
c.centerInnerX = c.centerX;
c.centerInnerY = c.centerY;
isInsideRounded = true;
}
else
{
/*
not enough space for a rounded border -> the inner side
becomes rectangular
*/
c.radiusInnerX = c.radiusInnerY = 0.0;
c.centerInnerX = ( c.sx < 0.0 ) ? innerRect.left() : innerRect.right();
c.centerInnerY = ( c.sy < 0.0 ) ? innerRect.top() : innerRect.bottom();
}
}
{
const auto tl = corners[ Qt::TopLeftCorner ].innerStepCount();
const auto tr = corners[ Qt::TopRightCorner ].innerStepCount();
const auto bl = corners[ Qt::BottomLeftCorner ].innerStepCount();
const auto br = corners[ Qt::BottomRightCorner ].innerStepCount();
if ( qMax( tl, tr ) + qMax( bl, br ) >= qMax( tl, bl ) + qMax( tr, br ) )
preferredOrientation = Qt::Vertical;
else
preferredOrientation = Qt::Horizontal;
}
}
int QskBoxMetrics::outerStepCount() const
{
return corners[0].stepCount + corners[1].stepCount
+ corners[2].stepCount + corners[3].stepCount;
}
int QskBoxMetrics::innerStepCount() const
{
return corners[0].innerStepCount() + corners[1].innerStepCount()
+ corners[2].innerStepCount() + corners[3].innerStepCount();
}