File indexing completed on 2024-05-05 05:46:06

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 "matrixdisplay.h"
0012 #include "colorutils.h"
0013 #include "diode.h"
0014 #include "ecnode.h"
0015 #include "led.h"
0016 #include "libraryitem.h"
0017 #include "simulator.h"
0018 
0019 #include <KLocalizedString>
0020 
0021 #include <QDebug>
0022 #include <QPainter>
0023 #include <QString>
0024 
0025 Item *MatrixDisplay::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0026 {
0027     return new MatrixDisplay(static_cast<ICNDocument *>(itemDocument), newItem, id);
0028 }
0029 
0030 LibraryItem *MatrixDisplay::libraryItem()
0031 {
0032     return new LibraryItem(QStringList(QString("ec/matrix_display")), i18n("Matrix Display"), i18n("Outputs"), "matrixdisplay.png", LibraryItem::lit_component, MatrixDisplay::construct);
0033 }
0034 
0035 MatrixDisplay::MatrixDisplay(ICNDocument *icnDocument, bool newItem, const char *id)
0036     : Component(icnDocument, newItem, id ? id : "matrix_display")
0037 {
0038     m_name = i18n("Matrix Display");
0039     m_bDynamicContent = true;
0040 
0041     // BEGIN Reset members
0042     for (unsigned i = 0; i < max_md_height; i++)
0043         m_pRowNodes[i] = nullptr;
0044     for (unsigned i = 0; i < max_md_width; i++)
0045         m_pColNodes[i] = nullptr;
0046 
0047     m_lastUpdatePeriod = 0.0;
0048     m_r = m_g = m_b = 0.0;
0049     m_bRowCathode = true;
0050     m_numRows = 0;
0051     m_numCols = 0;
0052     // END Reset members
0053 
0054     createProperty("0-rows", Variant::Type::Int);
0055     property("0-rows")->setCaption(i18n("Rows"));
0056     property("0-rows")->setMinValue(1);
0057     property("0-rows")->setMaxValue(max_md_height);
0058     property("0-rows")->setValue(7);
0059 
0060     createProperty("1-cols", Variant::Type::Int);
0061     property("1-cols")->setCaption(i18n("Columns"));
0062     property("1-cols")->setMinValue(1);
0063     property("1-cols")->setMaxValue(max_md_width);
0064     property("1-cols")->setValue(5);
0065 
0066     createProperty("color", Variant::Type::Color);
0067     property("color")->setCaption(i18n("Color"));
0068     property("color")->setColorScheme(ColorUtils::LED);
0069 
0070     createProperty("diode-configuration", Variant::Type::Select);
0071     property("diode-configuration")->setCaption(i18n("Configuration"));
0072     QStringMap allowed;
0073     allowed["Row Cathode"] = i18n("Row Cathode");
0074     allowed["Column Cathode"] = i18n("Column Cathode");
0075     property("diode-configuration")->setAllowed(allowed);
0076     property("diode-configuration")->setValue("Row Cathode");
0077     property("diode-configuration")->setAdvanced(true);
0078 }
0079 
0080 MatrixDisplay::~MatrixDisplay()
0081 {
0082 }
0083 
0084 void MatrixDisplay::dataChanged()
0085 {
0086     QColor color = dataColor("color");
0087     m_r = double(color.red()) / 0x100;
0088     m_g = double(color.green()) / 0x100;
0089     m_b = double(color.blue()) / 0x100;
0090 
0091     int numRows = dataInt("0-rows");
0092     int numCols = dataInt("1-cols");
0093 
0094     bool ledsChanged = (numRows != int(m_numRows)) || (numCols != int(m_numCols));
0095 
0096     if (ledsChanged)
0097         initPins(numRows, numCols);
0098 
0099     bool rowCathode = dataString("diode-configuration") == "Row Cathode";
0100     if ((rowCathode != m_bRowCathode) || ledsChanged) {
0101         m_bRowCathode = rowCathode;
0102 
0103         for (unsigned i = 0; i < m_numCols; i++) {
0104             for (unsigned j = 0; j < m_numRows; j++) {
0105                 removeElement(m_pDiodes[i][j], false);
0106                 if (rowCathode)
0107                     m_pDiodes[i][j] = createDiode(m_pColNodes[i], m_pRowNodes[j]);
0108                 else
0109                     m_pDiodes[i][j] = createDiode(m_pRowNodes[j], m_pColNodes[i]);
0110             }
0111         }
0112     }
0113 }
0114 
0115 void MatrixDisplay::initPins(unsigned numRows, unsigned numCols)
0116 {
0117     if ((numRows == m_numRows) && (numCols == m_numCols))
0118         return;
0119 
0120     if (numRows > max_md_height)
0121         numRows = max_md_height;
0122 
0123     if (numCols > max_md_width)
0124         numCols = max_md_width;
0125 
0126     m_lastUpdatePeriod = 0.0;
0127 
0128     // BEGIN Remove diodes
0129     // All the diodes are going to be readded from dataChanged (where this
0130     // function is called from), so easiest just to delete the diodes now and
0131     // resize.
0132 
0133     for (unsigned i = 0; i < m_numCols; i++) {
0134         for (unsigned j = 0; j < m_numRows; j++)
0135             removeElement(m_pDiodes[i][j], false);
0136     }
0137 
0138     m_avgBrightness.resize(numCols);
0139     m_lastBrightness.resize(numCols);
0140     m_pDiodes.resize(numCols);
0141 
0142     for (unsigned i = 0; i < numCols; i++) {
0143         m_avgBrightness[i].resize(numRows);
0144         m_lastBrightness[i].resize(numRows);
0145         m_pDiodes[i].resize(numRows);
0146 
0147         for (unsigned j = 0; j < numRows; j++) {
0148             m_avgBrightness[i][j] = 0.0;
0149             m_lastBrightness[i][j] = 255;
0150             m_pDiodes[i][j] = nullptr;
0151         }
0152     }
0153     // END Remove diodes
0154 
0155     // BEGIN Create or destroy pins
0156     if (numCols >= m_numCols) {
0157         for (unsigned i = m_numCols; i < numCols; i++)
0158             m_pColNodes[i] = createPin(0, 0, 270, colPinID(i));
0159     } else {
0160         for (unsigned i = numCols; i < m_numCols; i++) {
0161             removeNode(colPinID(i));
0162             m_pColNodes[i] = nullptr;
0163         }
0164     }
0165     m_numCols = numCols;
0166 
0167     if (numRows >= m_numRows) {
0168         for (unsigned i = m_numRows; i < numRows; i++)
0169             m_pRowNodes[i] = createPin(0, 0, 0, rowPinID(i));
0170     } else {
0171         for (unsigned i = numRows; i < m_numRows; i++) {
0172             removeNode(rowPinID(i));
0173             m_pRowNodes[i] = nullptr;
0174         }
0175     }
0176     m_numRows = numRows;
0177     // END Create or destroy pins
0178 
0179     // BEGIN Position pins et al
0180     setSize(-int(numCols + 1) * 8, -int(numRows + 1) * 8, int(numCols + 1) * 16, int(numRows + 1) * 16, true);
0181 
0182     for (int i = 0; i < int(m_numCols); i++) {
0183         m_nodeMap[colPinID(i)].x = offsetX() + 16 + 16 * i;
0184         m_nodeMap[colPinID(i)].y = offsetY() + height() + 8;
0185     }
0186 
0187     for (int i = 0; i < int(m_numRows); i++) {
0188         m_nodeMap[rowPinID(i)].x = offsetX() - 8;
0189         m_nodeMap[rowPinID(i)].y = offsetY() + 16 + 16 * i;
0190     }
0191 
0192     updateAttachedPositioning();
0193     // END Position pins et al
0194 }
0195 
0196 QString MatrixDisplay::colPinID(int col) const
0197 {
0198     return QString("col_%1").arg(QString::number(col));
0199 }
0200 QString MatrixDisplay::rowPinID(int row) const
0201 {
0202     return QString("row_%1").arg(QString::number(row));
0203 }
0204 
0205 void MatrixDisplay::stepNonLogic()
0206 {
0207     for (unsigned i = 0; i < m_numCols; i++) {
0208         for (unsigned j = 0; j < m_numRows; j++)
0209             m_avgBrightness[i][j] += LED::brightness(m_pDiodes[i][j]->current()) * LINEAR_UPDATE_PERIOD;
0210     }
0211 
0212     m_lastUpdatePeriod += LINEAR_UPDATE_PERIOD;
0213 }
0214 
0215 void MatrixDisplay::drawShape(QPainter &p)
0216 {
0217     if (isSelected())
0218         p.setPen(m_selectedCol);
0219     p.drawRect(boundingRect());
0220 
0221     initPainter(p);
0222 
0223     const int _x = int(x() + offsetX());
0224     const int _y = int(y() + offsetY());
0225 
0226     // To avoid flicker, require at least a 10 ms sample before changing
0227     // the brightness
0228     double minUpdatePeriod = 0.0099;
0229 
0230     for (int i = 0; i < int(m_numCols); i++) {
0231         for (int j = 0; j < int(m_numRows); j++) {
0232             if (m_lastUpdatePeriod > minUpdatePeriod)
0233                 m_lastBrightness[i][j] = unsigned(m_avgBrightness[i][j] / m_lastUpdatePeriod);
0234 
0235             double _b = m_lastBrightness[i][j];
0236 
0237             QColor brush = QColor(uint(255 - (255 - _b) * (1 - m_r)), uint(255 - (255 - _b) * (1 - m_g)), uint(255 - (255 - _b) * (1 - m_b)));
0238             p.setBrush(brush);
0239             p.setPen(Qt::NoPen);
0240             p.drawEllipse(_x + 10 + i * 16, _y + 10 + j * 16, 12, 12);
0241         }
0242     }
0243 
0244     if (m_lastUpdatePeriod > minUpdatePeriod) {
0245         m_lastUpdatePeriod = 0.0;
0246 
0247         for (unsigned i = 0; i < m_numCols; i++) {
0248             for (unsigned j = 0; j < m_numRows; j++)
0249                 m_avgBrightness[i][j] = 0.0;
0250         }
0251     }
0252 
0253     deinitPainter(p);
0254 }