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

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 "Grid.h"
0021 
0022 #include <tikz/core/Value.h>
0023 #include <QPainter>
0024 
0025 namespace tikz {
0026 namespace ui {
0027 
0028 class GridPrivate
0029 {
0030 public:
0031     bool dirty = true;
0032     qreal zoom = 1.0;
0033     QRectF rect;
0034     tikz::Unit unit = tikz::Unit::Centimeter;
0035 
0036     QVarLengthArray<QLineF, 256> majorLines;
0037     QVarLengthArray<QLineF, 256> minorLines;
0038     QVarLengthArray<QLineF, 256> tenthLines;
0039 
0040     /**
0041      * Returns the number of lines that are visible per unit:
0042      * -  1 equals only major lines
0043      * -  2 means each unit is subdivided once
0044      * - 10 means each unit is subdivided 10 times line
0045      * The return value is guaranteed to be greater or equal to 1.
0046      */
0047     int linesPerUnit() const
0048     {
0049         // how much space does one unit have on screen [cm] ?
0050         const tikz::Value oneUnitOnScreen = tikz::Value(zoom, unit);
0051 
0052         // we want a line each 5 mm
0053         int lpu = 1;
0054         while ((oneUnitOnScreen / lpu) >= tikz::Value(10, tikz::Unit::Millimeter)) {
0055             if (lpu == 1) {
0056                 lpu *= 2;
0057             } else if (lpu == 2) {
0058                 lpu *= 5;
0059             } else {
0060                 break;
0061             }
0062         }
0063 
0064         Q_ASSERT(lpu > 0);
0065         return lpu;
0066     }
0067 
0068     /**
0069      * Update the cached major and minor lines.
0070      */
0071     void updateCache(const QRectF & r)
0072     {
0073         if (rect != r || dirty) {
0074             dirty = false;
0075             rect = r;
0076 
0077             // we want a line each 5 mm
0078             const auto lpu = linesPerUnit();
0079 //             qDebug() << "lines per unit" << lpu;
0080 
0081             majorLines.clear();
0082             minorLines.clear();
0083             tenthLines.clear();
0084 
0085             const Value one(1, unit);
0086 
0087             tikz::Value left = tikz::Value(tikz::Value(rect.left()).convertTo(unit).value());
0088             left = tikz::Value(std::floor(left.value()), unit);
0089 
0090             tikz::Value top = tikz::Value(tikz::Value(rect.top()).convertTo(unit).value());
0091             top = tikz::Value(std::ceil(top.value()), unit);
0092 
0093             //
0094             // vertical lines
0095             //
0096             int i = 0;
0097             for (tikz::Value x = left; x.toPoint() < rect.right(); x += one) {
0098                 QLineF l(x.toPoint(), rect.top(), x.toPoint(), rect.bottom());
0099                 majorLines.append(l);
0100 
0101                 if (lpu >= 2) {
0102                     auto tx = x + (one / 2);
0103                     minorLines.append(QLineF(tx.toPoint(), rect.top(), tx.toPoint(), rect.bottom()));
0104                 }
0105 
0106                 if (lpu == 10) {
0107                     const qreal tenth = (one / 10).toPoint();
0108                     l.translate(tenth, 0);
0109                     tenthLines.append(l);
0110                     l.translate(tenth, 0);
0111                     tenthLines.append(l);
0112                     l.translate(tenth, 0);
0113                     tenthLines.append(l);
0114                     l.translate(tenth, 0);
0115                     tenthLines.append(l);
0116 
0117                     l.translate(2 * tenth, 0);
0118                     tenthLines.append(l);
0119                     l.translate(tenth, 0);
0120                     tenthLines.append(l);
0121                     l.translate(tenth, 0);
0122                     tenthLines.append(l);
0123                     l.translate(tenth, 0);
0124                     tenthLines.append(l);
0125                 }
0126                 ++i;
0127             }
0128 
0129             //
0130             // horizontal lines
0131             //
0132             i = 0;
0133             for (tikz::Value y = top; y.toPoint() < rect.bottom(); y += one) {
0134                 QLineF l(rect.left(), y.toPoint(), rect.right(), y.toPoint());
0135                 majorLines.append(l);
0136 
0137                 if (lpu >= 2) {
0138                     auto ty = y + (one / 2);
0139                     minorLines.append(QLineF(rect.left(), ty.toPoint(), rect.right(), ty.toPoint()));
0140                 }
0141 
0142                 if (lpu == 10) {
0143                     const qreal tenth = (one / 10).toPoint();
0144                     l.translate(0, tenth);
0145                     tenthLines.append(l);
0146                     l.translate(0, tenth);
0147                     tenthLines.append(l);
0148                     l.translate(0, tenth);
0149                     tenthLines.append(l);
0150                     l.translate(0, tenth);
0151                     tenthLines.append(l);
0152 
0153                     l.translate(0, 2 * tenth);
0154                     tenthLines.append(l);
0155                     l.translate(0, tenth);
0156                     tenthLines.append(l);
0157                     l.translate(0, tenth);
0158                     tenthLines.append(l);
0159                     l.translate(0, tenth);
0160                     tenthLines.append(l);
0161                 }
0162                 ++i;
0163             }
0164 
0165         }
0166     }
0167 };
0168 
0169 Grid::Grid(QObject * parent)
0170     : QObject(parent)
0171     , d(new GridPrivate())
0172 {
0173 }
0174 
0175 Grid::~Grid()
0176 {
0177     delete d;
0178 }
0179 
0180 void Grid::draw(QPainter * p, const QRectF & rect)
0181 {
0182     d->updateCache(rect);
0183 
0184     p->save();
0185     p->setRenderHints(QPainter::Antialiasing, false);
0186 
0187     QPen pen(QColor(230, 230, 230));
0188     pen.setWidth(0);
0189     p->setPen(pen);
0190     p->drawLines(d->majorLines.data(), d->majorLines.size());
0191 
0192     pen.setDashPattern(QVector<qreal>() << 6 << 3);
0193     p->setPen(pen);
0194     p->drawLines(d->minorLines.data(), d->minorLines.size());
0195 
0196     pen.setDashPattern(QVector<qreal>() << 1 << 2);
0197     p->setPen(pen);
0198     p->drawLines(d->tenthLines.data(), d->tenthLines.size());
0199 
0200     p->restore();
0201 }
0202 
0203 void Grid::setUnit(tikz::Unit unit) noexcept
0204 {
0205     if (d->unit != unit) {
0206         d->unit = unit;
0207         d->dirty = true;
0208     }
0209 }
0210 
0211 tikz::Unit Grid::unit() const noexcept
0212 {
0213     return d->unit;
0214 }
0215 
0216 void Grid::setZoom(qreal zoomFactor) noexcept
0217 {
0218     if (d->zoom != zoomFactor) {
0219         d->zoom = zoomFactor;
0220         d->dirty = true;
0221     }
0222 }
0223 
0224 qreal Grid::zoom() const noexcept
0225 {
0226     return d->zoom;
0227 }
0228 
0229 tikz::Value Grid::snapValue(const tikz::Value & value) const
0230 {
0231     const int lpu = d->linesPerUnit();
0232     const Value one(1.0 / lpu, d->unit);
0233 
0234     // snap to the grid and return a Value using the Grid's d->unit
0235     const auto snappedValue = tikz::Value(qRound(value.convertTo(d->unit).value() / one.value()) * one.value(), d->unit);
0236 //     qDebug() << value << "converts to:" << snappedValue;
0237     return snappedValue;
0238 }
0239 
0240 tikz::Pos Grid::snapPos(const tikz::Pos & pos) const
0241 {
0242     return tikz::Pos(snapValue(pos.x()), snapValue(pos.y()));
0243 }
0244 
0245 }
0246 }
0247 
0248 // kate: indent-width 4; replace-tabs on;