File indexing completed on 2024-05-05 05:46:09
0001 /*************************************************************************** 0002 * Copyright (C) 2005 by John Myers * 0003 * electronerd@electronerdia.net * 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 "rotoswitch.h" 0012 0013 #include "canvasitemparts.h" 0014 #include "ecnode.h" 0015 #include "libraryitem.h" 0016 #include "switch.h" 0017 0018 #include <KLocalizedString> 0019 #include <QPainter> 0020 #include <QPainterPath> 0021 #include <cassert> 0022 #include <cmath> 0023 0024 #include <ktechlab_debug.h> 0025 0026 // BEGIN class ECRotoSwitch 0027 Item *ECRotoSwitch::construct(ItemDocument *itemDocument, bool newItem, const char *id) 0028 { 0029 return new ECRotoSwitch(static_cast<ICNDocument *>(itemDocument), newItem, id); 0030 } 0031 0032 LibraryItem *ECRotoSwitch::libraryItem() 0033 { 0034 return new LibraryItem(QStringList(QString("ec/roto_switch")), i18n("Rotary"), i18n("Switches"), "rotary.png", LibraryItem::lit_component, ECRotoSwitch::construct); 0035 } 0036 0037 ECRotoSwitch::ECRotoSwitch(ICNDocument *icnDocument, bool newItem, const char *id) 0038 : Component(icnDocument, newItem, id ? id : "roto_switch") 0039 , m_numPositions(0) 0040 { 0041 m_name = i18n("Rotary Switch"); 0042 // Q3PointArray pa; // 2018.08.14 - see below 0043 // pa.makeArc( -_pinInnerRadius, -_pinInnerRadius, 2*_pinInnerRadius, 2*_pinInnerRadius , 0, 16*360 ); 0044 QPainterPath path; 0045 path.addEllipse(-_pinInnerRadius, -_pinInnerRadius, 2 * _pinInnerRadius, 2 * _pinInnerRadius); 0046 QPolygon pa = path.toFillPolygon().toPolygon(); 0047 0048 setItemPoints(pa); 0049 // setSize( -64, -64, 128, 128 ); 0050 0051 // half the side length of the buttons 0052 int buttonRadius = 10; 0053 addButton("go_left", QRect(-_pinOuterRadius / 3 - buttonRadius, _pinOuterRadius - 3 * buttonRadius, 2 * buttonRadius, 2 * buttonRadius), "<", false); 0054 0055 addButton("go_right", QRect(_pinOuterRadius / 3 - buttonRadius, _pinOuterRadius - 3 * buttonRadius, 2 * buttonRadius, 2 * buttonRadius), ">", false); 0056 0057 /*Variant * v = createProperty( "button_map", Variant::Type::String ); 0058 v->setCaption( i18n("Button Map") ); 0059 v->setAdvanced(true); 0060 const QString defButtonMap("SSSSSSSSSSSM"); 0061 v->setValue(defButtonMap); 0062 */ 0063 Variant *v = createProperty("num_positions", Variant::Type::Int); 0064 v->setCaption(i18n("Number of Positions")); 0065 v->setAdvanced(false); 0066 v->setValue(6); 0067 v->setMinValue(3); 0068 m_inNode = createPin(0, height() / 2, 270, "in"); 0069 0070 v = createProperty("bounce", Variant::Type::Bool); 0071 v->setCaption("Bounce"); 0072 v->setAdvanced(true); 0073 v->setValue(false); 0074 0075 v = createProperty("bounce_period", Variant::Type::Double); 0076 v->setCaption("Bounce Period"); 0077 v->setAdvanced(true); 0078 v->setUnit("s"); 0079 v->setValue(5e-3); 0080 0081 v = createProperty("cur_position", Variant::Type::Int); 0082 v->setHidden(true); 0083 v->setValue(0); 0084 0085 // v = createProperty( "left_momentary", Variant::Type::Bool ); 0086 // v->setCaption(i18n("Left Momentary" ) ); 0087 // v->setValue(false); 0088 } 0089 0090 ECRotoSwitch::~ECRotoSwitch() 0091 { 0092 } 0093 0094 void ECRotoSwitch::dataChanged() 0095 { 0096 bool bounce = dataBool("bounce"); 0097 int bouncePeriod_ms = int(dataDouble("bounce_period") * 1e3); 0098 m_curPosition = dataInt("cur_position"); 0099 setUpSwitches(); 0100 0101 if (m_positions[m_curPosition].posSwitch->state() != Switch::Closed) { 0102 m_positions[m_curPosition].posSwitch->setState(Switch::Closed); 0103 } 0104 0105 for (int i = 0; i < m_numPositions; i++) { 0106 m_positions[i].posSwitch->setBounce(bounce, bouncePeriod_ms); 0107 } 0108 } 0109 0110 inline int roundTo10(int a) 0111 { 0112 return ((a / 10) + (a % 10 < 5 ? 0 : 1)) * 10; 0113 } 0114 void ECRotoSwitch::drawShape(QPainter &p) 0115 { 0116 initPainter(p); 0117 0118 int cx = static_cast<int>(x()); 0119 int cy = static_cast<int>(y()); 0120 0121 const int rotorRadius = 5; 0122 0123 // draw the rotor 0124 p.drawEllipse(cx - rotorRadius, cy - rotorRadius, 2 * rotorRadius, 2 * rotorRadius); 0125 0126 // and its connection 0127 p.drawLine(cx, cy + rotorRadius, cx, cy + _pinInnerRadius); 0128 0129 // draw the output positions 0130 double angleBetweenPositions = (4 * M_PI / 3) / (m_numPositions - 1); 0131 0132 /// \internal \brief Round to the nearest multiple of 8 0133 #define round_8(a) (((a) > 0) ? int(((a) + 4) / 8) * 8 : int(((a)-4) / 8) * 8) 0134 for (int i = 0; i < m_numPositions; i++) { 0135 double angle = (7 * M_PI / 6) - (i * angleBetweenPositions); 0136 int contactX = static_cast<int>(_contactRingRadius * cos(angle)); 0137 int contactY = static_cast<int>(_contactRingRadius * sin(angle)); 0138 0139 p.drawEllipse(cx + contactX - _contactRadius, cy - contactY - _contactRadius, 2 * _contactRadius, 2 * _contactRadius); 0140 0141 int pinX, pinY; 0142 switch (m_positions[i].pinAngle) { 0143 case 180: 0144 pinX = _pinInnerRadius; 0145 pinY = round_8(contactY); 0146 break; 0147 case 90: 0148 pinX = round_8(contactX); 0149 pinY = _pinInnerRadius; 0150 break; 0151 case 0: 0152 pinX = -_pinInnerRadius; 0153 pinY = round_8(contactY); 0154 break; 0155 default: 0156 assert(!"Bad pin angle"); 0157 } 0158 0159 p.drawLine(cx + contactX, cy - contactY, cx + pinX, cy - pinY); 0160 } 0161 #undef round_8 0162 0163 // draw the connection to the selected position 0164 double angle = (7 * M_PI / 6) - (m_curPosition * angleBetweenPositions); 0165 int contactX = static_cast<int>(_contactRingRadius * cos(angle)); 0166 int contactY = static_cast<int>(_contactRingRadius * sin(angle)); 0167 int rotorX = static_cast<int>(rotorRadius * cos(angle)); 0168 int rotorY = static_cast<int>(rotorRadius * sin(angle)); 0169 p.drawLine(cx + rotorX, cy - rotorY, cx + contactX, cy - contactY); 0170 0171 deinitPainter(p); 0172 } 0173 0174 void ECRotoSwitch::buttonStateChanged(const QString &id, bool state) 0175 { 0176 SwitchPosition &curSP = m_positions[m_curPosition]; 0177 int nextPos = m_curPosition; 0178 if (m_numPositions < 2) { 0179 return; 0180 } 0181 0182 if (!state) // release 0183 { 0184 if (!curSP.isMomentary) 0185 return; 0186 0187 if (m_curPosition == 0) { 0188 nextPos = m_curPosition + 1; 0189 } else if (m_curPosition == m_numPositions - 1) { 0190 nextPos = m_curPosition - 1; 0191 } 0192 0193 } else // press 0194 { 0195 if (id == "go_left" && m_curPosition > 0) { 0196 nextPos = m_curPosition - 1; 0197 } else if (id == "go_right" && m_curPosition < m_numPositions - 1) { 0198 nextPos = m_curPosition + 1; 0199 } 0200 } 0201 0202 if (nextPos != m_curPosition) { 0203 SwitchPosition &nextSP = m_positions[nextPos]; 0204 0205 curSP.posSwitch->setState(Switch::Open); 0206 nextSP.posSwitch->setState(Switch::Closed); 0207 0208 m_curPosition = nextPos; 0209 0210 property("cur_position")->setValue(m_curPosition); 0211 } 0212 } 0213 0214 /*! 0215 Set up the switches according to the button_map 0216 * 0217 */ 0218 void ECRotoSwitch::setUpSwitches() 0219 { 0220 if (dataInt("num_positions") == m_numPositions) { 0221 // number of positions didn't change, so we don't have to do anything. 0222 return; 0223 } 0224 0225 // this uses the _old_ value of m_numPositions! 0226 for (int i = 0; i < m_numPositions; i++) { 0227 SwitchPosition &sp = m_positions[i]; 0228 QString pinName = QString("pin_%1").arg(i); 0229 removeNode(pinName); 0230 removeSwitch(sp.posSwitch); 0231 } 0232 0233 m_numPositions = dataInt("num_positions"); 0234 0235 if (m_curPosition >= m_numPositions) { 0236 setActivePosition(m_numPositions - 1); 0237 } 0238 0239 m_positions.clear(); 0240 m_positions.reserve(m_numPositions); 0241 double angleBetweenPositions = (4 * M_PI / 3) / (m_numPositions - 1); 0242 0243 for (int i = 0; i < m_numPositions; i++) { 0244 double angle = (7 * M_PI / 6) - (i * angleBetweenPositions); 0245 int contactX = static_cast<int>(_contactRingRadius * cos(angle)); 0246 int contactY = static_cast<int>(_contactRingRadius * sin(angle)); 0247 0248 SwitchPosition sp; 0249 0250 if (angle > 3 * M_PI / 4) { 0251 sp.pinAngle = 0; 0252 contactX = -_pinOuterRadius; 0253 } else if (angle > M_PI / 4) { 0254 sp.pinAngle = 90; 0255 contactY = _pinOuterRadius; 0256 } else { 0257 sp.pinAngle = 180; 0258 contactX = _pinOuterRadius; 0259 } 0260 // qCDebug(KTL_LOG) << contactX <<", "<< contactY; 0261 0262 sp.node = createPin(contactX, -contactY, sp.pinAngle, QString("pin_%1").arg(i)); 0263 sp.posSwitch = createSwitch(m_inNode, sp.node, true); 0264 sp.isMomentary = false; //(map[i] == 'M'); 0265 m_positions.push_back(sp); 0266 } 0267 updateAttachedPositioning(); 0268 0269 // redraw ourself 0270 setChanged(); 0271 } 0272 0273 /*! 0274 * Set the current position to \c newPosition updating the state of the switch. 0275 * \c m_curPosition must reference a valid position to switch away from 0276 * 0277 * \param newPosition the position to switch to 0278 */ 0279 void ECRotoSwitch::setActivePosition(int newPosition) 0280 { 0281 SwitchPosition &curSP = m_positions[m_curPosition]; 0282 SwitchPosition &nextSP = m_positions[newPosition]; 0283 0284 curSP.posSwitch->setState(Switch::Open); 0285 nextSP.posSwitch->setState(Switch::Closed); 0286 0287 m_curPosition = newPosition; 0288 0289 property("cur_position")->setValue(m_curPosition); 0290 } 0291 0292 // END class ECRotoSwitch