File indexing completed on 2024-06-09 09:27:47

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "chassiscircular2.h"
0012 
0013 #include "libraryitem.h"
0014 #include "mechanicsitem.h"
0015 #include "mechanicsdocument.h"
0016 
0017 #include <KLocalizedString>
0018 #include <QPainter>
0019 #include <QPainterPath>
0020 
0021 #include <algorithm>
0022 #include <cmath>
0023 
0024 double normalizeAngle(double angle);
0025 
0026 Item *ChassisCircular2::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0027 {
0028     return new ChassisCircular2(static_cast<MechanicsDocument *>(itemDocument), newItem, id);
0029 }
0030 
0031 LibraryItem *ChassisCircular2::libraryItem()
0032 {
0033     return new LibraryItem(QStringList(QString("mech/chassis_circular_2")), i18n("Circular 2-Wheel Chassis"), i18n("Chassis'"), "chassis.png", LibraryItem::lit_mechanical, ChassisCircular2::construct);
0034 }
0035 
0036 ChassisCircular2::ChassisCircular2(MechanicsDocument *mechanicsDocument, bool newItem, const char *id)
0037     : MechanicsItem(mechanicsDocument, newItem, id ? id : "chassis_circular_2")
0038 {
0039     m_name = i18n("Circular 2-Wheel Chassis");
0040 
0041     m_theta1 = 0.0;
0042     m_theta2 = 0.0;
0043 
0044     // Q3PointArray pa;    // 2018.08.14 - ported to PainterPath
0045     // pa.makeEllipse( -25, -25, 50, 50 );
0046     QPainterPath path;
0047     path.addEllipse(-25, -25, 50, 50);
0048     QPolygon pa = path.toFillPolygon().toPolygon();
0049 
0050     QTransform m(4, 0, 0, 4, 0, 0);
0051     // m.setTransformationMode( QMatrix::Areas ); // TODO find a replacement
0052     pa = m.map(pa);
0053     setItemPoints(pa);
0054 
0055     itemResized();
0056 }
0057 
0058 ChassisCircular2::~ChassisCircular2()
0059 {
0060 }
0061 
0062 void ChassisCircular2::itemResized()
0063 {
0064     const double w = sizeRect().width();
0065     const double h = sizeRect().height();
0066 
0067     m_wheel1Pos = QRect(int(w / 5), int(h / 6), int(w / 4), int(h / 8));
0068     m_wheel2Pos = QRect(int(w / 5), int(5 * h / 6 - h / 8), int(w / 4), int(h / 8));
0069 }
0070 
0071 void ChassisCircular2::advance(int phase)
0072 {
0073     if (phase != 1)
0074         return;
0075 
0076     double speed1 = 60.;  // pixels per second
0077     double speed2 = 160.; // pixels per second
0078 
0079     m_theta1 = normalizeAngle(m_theta1 + (speed1 / 1000.) / m_wheel1Pos.width());
0080     m_theta2 = normalizeAngle(m_theta2 + (speed2 / 1000.) / m_wheel2Pos.width());
0081 
0082     const double d1 = speed1 / 1000.;
0083     const double d2 = speed2 / 1000.;
0084     const double sep = m_wheel2Pos.center().y() - m_wheel1Pos.center().y();
0085 
0086     double dtheta = std::atan((d2 - d1) / sep); // Change in orientation of chassis
0087     double moveAngle = absolutePosition().angle() + dtheta / 2;
0088     rotateBy(dtheta);
0089     moveBy(((d1 + d2) / 2.) * std::cos(moveAngle), ((d1 + d2) / 2.) * std::sin(moveAngle));
0090 }
0091 
0092 void ChassisCircular2::drawShape(QPainter &p)
0093 {
0094     const double _x = int(sizeRect().x() + x());
0095     const double _y = int(sizeRect().y() + y());
0096     const double w = sizeRect().width();
0097     const double h = sizeRect().height();
0098 
0099     initPainter(p);
0100     p.setBrush(QColor(255, 246, 210));
0101     QRect circleRect = sizeRect();
0102     circleRect.moveLeft(int(circleRect.left() + x()));
0103     circleRect.moveTop(int(circleRect.top() + y()));
0104     p.drawEllipse(circleRect);
0105 
0106     // Draw wheels
0107     // TODO get this info from m_wheel1Pos and m_wheel2Pos
0108     const double X = _x + (w / 5);          // Wheel's left pos
0109     const double H = h / 8;                 // Wheel's height
0110     const double y1 = _y + (h / 6);         // Wheel 1 y-pos
0111     const double y2 = _y + (5 * h / 6) - H; // Wheel 2 y-pos
0112 
0113     p.setPen(Qt::NoPen);
0114     const double stripeWidth = 5;
0115     const double offset2 = 1 + int(m_theta1 * m_wheel1Pos.width()) % int(2 * stripeWidth);
0116     const double offset1 = 1 + int(m_theta2 * m_wheel2Pos.width()) % int(2 * stripeWidth);
0117     p.setBrush(QColor(255, 232, 182));
0118     for (double i = -1; i < std::ceil(m_wheel1Pos.width() / stripeWidth); ++i) {
0119         p.setClipRect(QRect(int(_x + m_wheel1Pos.x() + 2), int(_y + m_wheel1Pos.y() + 2), int(m_wheel1Pos.width() - 4), int(m_wheel1Pos.height() - 4)),
0120                       /* QPainter::CoordPainter */
0121                       Qt::ReplaceClip // TODO original Qt::UniteClip
0122         );
0123         p.drawRect(int(offset1 + X + i * stripeWidth * 2), int(y1 + 1), int(stripeWidth), int(H - 2));
0124 
0125         p.setClipRect(QRect(int(_x + m_wheel2Pos.x() + 2), int(_y + m_wheel2Pos.y() + 2), int(m_wheel2Pos.width() - 4), int(m_wheel2Pos.height() - 4)),
0126                       /* QPainter::CoordPainter */
0127                       Qt::ReplaceClip // TODO original Qt::UniteClip
0128         );
0129         p.drawRect(int(offset2 + X + i * stripeWidth * 2), int(y2 + 1), int(stripeWidth), int(H - 2));
0130     }
0131     p.setClipping(false);
0132 
0133     p.setPen(Qt::black);
0134     p.setBrush(Qt::NoBrush);
0135     p.drawRoundedRect(int(X), int(y1), int(w / 4), int(H), 25, 50, Qt::RelativeSize);
0136     p.drawRoundedRect(int(X), int(y2), int(w / 4), int(H), 25, 50, Qt::RelativeSize);
0137 
0138     deinitPainter(p);
0139 }