File indexing completed on 2024-05-19 04:36:37

0001 /* This file is part of the TikZKit project.
0002  *
0003  * Copyright (C) 2014 Dominik Haumann <dhaumann@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU Library General Public License as published
0007  * by the Free Software Foundation, either version 2 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, see
0017  * <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "DiamondShape.h"
0021 #include "NodeItem.h"
0022 
0023 #include <tikz/core/Style.h>
0024 #include <tikz/core/tikz.h>
0025 
0026 #include <cmath>
0027 
0028 namespace tikz {
0029 namespace ui {
0030 
0031 class DiamondShapePrivate
0032 {
0033     public:
0034 };
0035 
0036 DiamondShape::DiamondShape(NodeItem * node)
0037     : AbstractShape(node)
0038     , d(new DiamondShapePrivate())
0039 {
0040 }
0041 
0042 DiamondShape::~DiamondShape()
0043 {
0044     delete d;
0045 }
0046 
0047 tikz::Shape DiamondShape::type() const
0048 {
0049     return tikz::Shape::ShapeDiamond;
0050 }
0051 
0052 void DiamondShape::adjustShapeRect(const QRectF & textRect, QRectF & shapeRect) const
0053 {
0054     // see pgflibraryshapes.geometric.code.tex
0055 
0056     // calculate radius of textRect
0057     const qreal s = node()->style()->innerSep().toPoint();
0058     const qreal w = textRect.width();
0059     const qreal h = textRect.height();
0060 
0061     // FIXME: /pgf/aspect is not supported right now
0062     const qreal d = w + h + 4 * s;
0063     QRectF r(-d/2, -d/2, d, d);
0064 
0065     if (r.width() > shapeRect.width()) {
0066         shapeRect.setWidth(r.width());
0067     }
0068     if (r.height() > shapeRect.height()) {
0069         shapeRect.setHeight(r.height());
0070     }
0071 }
0072 
0073 QPainterPath DiamondShape::shape() const
0074 {
0075     const QRectF rect = node()->shapeRect();
0076     QPainterPath path;
0077     path.moveTo(rect.left(), 0);
0078     path.lineTo(0, rect.top());
0079     path.lineTo(rect.right(), 0);
0080     path.lineTo(0, rect.bottom());
0081     path.closeSubpath();
0082     return path;
0083 }
0084 
0085 QPainterPath DiamondShape::outline() const
0086 {
0087     const QRectF rect = outlineRect();
0088     const qreal w = rect.width() / 2.0;
0089     const qreal h = rect.height() / 2.0;
0090 
0091     QPainterPath path;
0092     path.moveTo(-w, 0);
0093     path.lineTo(0, h);
0094     path.lineTo(w, 0);
0095     path.lineTo(0, -h);
0096     path.closeSubpath();
0097     return path;
0098 }
0099 
0100 QStringList DiamondShape::supportedAnchors() const
0101 {
0102     // by default, just return NoAnchor
0103     const QStringList anchors = QStringList()
0104         << QString()
0105         << QStringLiteral("center")
0106         << QStringLiteral("north")
0107         << QStringLiteral("north east")
0108         << QStringLiteral("east")
0109         << QStringLiteral("south east")
0110         << QStringLiteral("south")
0111         << QStringLiteral("south west")
0112         << QStringLiteral("west")
0113         << QStringLiteral("north west");
0114     return anchors;
0115 }
0116 
0117 QPointF DiamondShape::anchorPos(const QString & anchor) const
0118 {
0119     if (anchor.isEmpty()) {
0120         return QPointF(0, 0);
0121     }
0122 
0123     const QRectF rect = outlineRect();
0124     const qreal rx = rect.width() / 2.0;
0125     const qreal ry = rect.height() / 2.0;
0126 
0127     if (anchor == QStringLiteral("center")) {
0128         return QPointF(0, 0);
0129     } else if (anchor == QStringLiteral("north")) {
0130         return QPointF(0, ry);
0131     } else if (anchor == QStringLiteral("north east")) {
0132         return QPointF(rx, ry) * 0.5;
0133     } else if (anchor == QStringLiteral("east")) {
0134         return QPointF(rx, 0);
0135     } else if (anchor == QStringLiteral("south east")) {
0136         return QPointF(rx, -ry) * 0.5;
0137     } else if (anchor == QStringLiteral("south")) {
0138         return QPointF(0, -ry);
0139     } else if (anchor == QStringLiteral("south west")) {
0140         return QPointF(-rx, -ry) * 0.5;
0141     } else if (anchor == QStringLiteral("west")) {
0142         return QPointF(-rx, 0);
0143     } else if (anchor == QStringLiteral("north west")) {
0144         return QPointF(-rx, ry) * 0.5;
0145     }
0146 
0147     tikz::warn("The shape 'diamond' does not support anchor '" + anchor + "'.");
0148 
0149     return QPointF(0, 0);
0150 }
0151 
0152 QPointF DiamondShape::contactPoint(const QString & anchor, qreal rad) const
0153 {
0154     if (! anchor.isEmpty()) {
0155         return anchorPos(anchor);
0156     }
0157 
0158     const QRectF rect = outlineRect();
0159     const qreal rx = rect.width() / 2.0;
0160     const qreal ry = rect.height() / 2.0;
0161     const qreal len = qMax(rx, ry);
0162 
0163     // create line to intersect with
0164     const qreal x = std::cos(rad);
0165     const qreal y = std::sin(rad);
0166     const QLineF radLine(0, 0, len * x, len * y);
0167 
0168     // create 4 lines of the rect and find intersection
0169     const QLineF l1(-rx, 0, 0, ry);
0170     const QLineF l2(0, ry, rx, 0);
0171     const QLineF l3(rx, 0, 0, -ry);
0172     const QLineF l4(0, -ry, -rx, 0);
0173 
0174     QPointF intersectionPoint;
0175     if ((l1.intersects(radLine, &intersectionPoint) == QLineF::BoundedIntersection) ||
0176         (l2.intersects(radLine, &intersectionPoint) == QLineF::BoundedIntersection) ||
0177         (l3.intersects(radLine, &intersectionPoint) == QLineF::BoundedIntersection) ||
0178         (l4.intersects(radLine, &intersectionPoint) == QLineF::BoundedIntersection)
0179     ) {
0180         return intersectionPoint;
0181     }
0182 
0183     Q_ASSERT(false);
0184     return intersectionPoint;
0185 }
0186 
0187 QRectF DiamondShape::outlineRect() const
0188 {
0189     const QRectF rect = node()->shapeRect();
0190     const qreal rx = rect.width() / 2.0;
0191     const qreal ry = rect.height() / 2.0;
0192     Q_ASSERT(rx + ry > 0);
0193 
0194     // height of triangle
0195     const qreal hypothenuse = std::sqrt(rx * rx + ry * ry);
0196     const qreal height = rx * ry / hypothenuse;
0197 
0198     // scale by new height
0199     const qreal d = height + node()->style()->outerSep().toPoint();
0200     const qreal dy = ry * d / height;
0201     const qreal dx = rx * d / height;
0202 
0203     return QRectF(-dx, -dy, 2 * dx, 2 * dy);
0204 }
0205 
0206 }
0207 }
0208 
0209 // kate: indent-width 4; replace-tabs on;