tools/hhtracer/tracer.cpp
changeset 16084 2d65bd46c92f
equal deleted inserted replaced
16083:629d5123a979 16084:2d65bd46c92f
       
     1 #include "tracer.h"
       
     2 
       
     3 #include <QRandomGenerator>
       
     4 #include <QSvgGenerator>
       
     5 
       
     6 Tracer::Tracer(QObject* parent)
       
     7     : QObject{parent},
       
     8       palette_{{Qt::black,
       
     9                 Qt::white,
       
    10                 {"#f29ce7"},
       
    11                 {"#9f086e"},
       
    12                 {"#54a2fa"},
       
    13                 {"#2c78d2"}}} {}
       
    14 
       
    15 QList<QColor> Tracer::palette() const { return palette_; }
       
    16 
       
    17 void Tracer::setPalette(const QList<QColor>& newPalette) {
       
    18   if (palette_ == newPalette) return;
       
    19   palette_ = newPalette;
       
    20   emit paletteChanged();
       
    21 }
       
    22 
       
    23 double Tracer::bestSolution() const { return bestSolution_; }
       
    24 
       
    25 void Tracer::start(const QString& fileName) {
       
    26   qDebug() << "Starting using" << fileName;
       
    27 
       
    28   bestSolution_ = 0;
       
    29   solutions_.clear();
       
    30   generation_.clear();
       
    31   image_ = QImage{};
       
    32 
       
    33   if (palette_.isEmpty()) {
       
    34     qDebug("Empty palette");
       
    35     return;
       
    36   }
       
    37 
       
    38   image_.load(QUrl(fileName).toLocalFile());
       
    39 
       
    40   if (image_.isNull()) {
       
    41     qDebug("Failed to load image");
       
    42     return;
       
    43   }
       
    44 
       
    45   for (int i = 0; i < 100; ++i) {
       
    46     generation_.append(Solution{{32, 32}, palette_});
       
    47   }
       
    48 }
       
    49 
       
    50 void Tracer::step() {
       
    51   solutions_.clear();
       
    52 
       
    53   for (auto& solution : generation_) {
       
    54     const auto fileName = newFileName();
       
    55     solutions_.append(fileName);
       
    56 
       
    57     solution.render(fileName);
       
    58   }
       
    59 
       
    60   qDebug() << solutions_;
       
    61 
       
    62   emit solutionsChanged();
       
    63 }
       
    64 
       
    65 QStringList Tracer::solutions() const { return solutions_; }
       
    66 
       
    67 QString Tracer::newFileName() {
       
    68   static qlonglong counter{0};
       
    69   counter += 1;
       
    70   return tempDir_.filePath(
       
    71       QStringLiteral("hedgehog_%1.svg").arg(counter, 3, 32, QChar(u'_')));
       
    72 }
       
    73 
       
    74 Solution::Solution(QSizeF size, const QList<QColor>& palette) : size{size} {
       
    75   fitness = 0;
       
    76   primitives = {Primitive(size, palette)};
       
    77 }
       
    78 
       
    79 void Solution::render(const QString& fileName) const {
       
    80   const auto imageSize = size.toSize();
       
    81 
       
    82   QSvgGenerator generator;
       
    83   generator.setFileName(fileName);
       
    84   generator.setSize(imageSize);
       
    85   generator.setViewBox(QRect(0, 0, imageSize.width(), imageSize.height()));
       
    86   generator.setTitle("Hedgehog");
       
    87   generator.setDescription("Approximation of a target image using primitives");
       
    88 
       
    89   QPainter painter;
       
    90   painter.begin(&generator);
       
    91   painter.setRenderHint(QPainter::Antialiasing, true);
       
    92 
       
    93   for (const auto& primitive : primitives) {
       
    94     painter.setPen(primitive.pen);
       
    95     painter.setBrush(primitive.brush);
       
    96     painter.resetTransform();
       
    97     painter.translate(primitive.origin);
       
    98     painter.rotate(primitive.rotation);
       
    99 
       
   100     switch (primitive.type) {
       
   101       case Polygon: {
       
   102         QPolygonF polygon;
       
   103         polygon.append({0, 0});
       
   104         polygon.append(primitive.points);
       
   105 
       
   106         painter.drawPolygon(polygon);
       
   107         break;
       
   108       }
       
   109       case Circle:
       
   110         painter.drawEllipse({0, 0}, primitive.radius1, primitive.radius2);
       
   111         break;
       
   112     }
       
   113   }
       
   114 
       
   115   painter.end();
       
   116 }
       
   117 
       
   118 double Solution::cost() const {
       
   119   return std::accumulate(primitives.constBegin(), primitives.constEnd(), 0,
       
   120                          [](auto a, auto p) { return a + p.cost(); });
       
   121 }
       
   122 
       
   123 Primitive::Primitive(QSizeF size, const QList<QColor>& palette) {
       
   124   auto rg = QRandomGenerator::global();
       
   125   auto randomPoint = [&]() -> QPointF {
       
   126     return {rg->bounded(size.width()), rg->bounded(size.height())};
       
   127   };
       
   128 
       
   129   if (rg->bounded(2) == 0) {
       
   130     type = Polygon;
       
   131 
       
   132     points.append(randomPoint());
       
   133     points.append(randomPoint());
       
   134   } else {
       
   135     type = Circle;
       
   136 
       
   137     radius1 = rg->bounded(size.width());
       
   138     radius2 = rg->bounded(size.width());
       
   139     rotation = rg->bounded(90);
       
   140   }
       
   141 
       
   142   pen = QPen(palette[rg->bounded(palette.length())]);
       
   143   pen.setWidthF(rg->bounded(size.width() * 0.1));
       
   144   brush = QBrush(palette[rg->bounded(palette.length())]);
       
   145 
       
   146   origin = randomPoint();
       
   147 }
       
   148 
       
   149 double Primitive::cost() const { return 1.0 + 0.1 * points.length(); }