Revision control

++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "shapemanager.h"
++ #include <QtQuick3DParticles/private/qquick3dparticleshapedatautils_p.h>
++
++ #include <QtCore/qdir.h>
++ #include <QCborStreamWriter>
++ #include <QCborStreamReader>
++ #include <QRandomGenerator>
++
++ ShapeManager::ShapeManager(QObject *parent) : QObject(parent)
++ {
++
++ }
++
++ void ShapeManager::setImage(const QString &filename)
++ {
++ if (m_imageFilename == filename)
++ return;
++
++ m_imageFilename = filename;
++ loadImage();
++ }
++
++ void ShapeManager::setDepth(float depth)
++ {
++ m_depth = depth;
++ }
++
++ void ShapeManager::setScale(float scale)
++ {
++ m_scale = scale;
++ }
++
++ // Set the amount of positions CBOR should contain.
++ // When -1, then amount is same as the amount of pixels in the image.
++ // Default value -1.
++ void ShapeManager::setAmount(int amount)
++ {
++ m_amount = amount;
++ }
++
++ void ShapeManager::setSortingMode(SortingMode mode)
++ {
++ m_sortingMode = mode;
++ }
++
++ void ShapeManager::setSortingPosition(const QVector3D &position)
++ {
++ m_sortingPosition = position;
++ }
++
++ bool ShapeManager::loadImage()
++ {
++ QFileInfo fileInfo(m_imageFilename);
++
++ // Is this a real file?
++ if (!fileInfo.exists()) {
++ qWarning() << "Imagefile not found:" << qPrintable(m_imageFilename);
++ return false;
++ }
++
++ if (!m_image.load(m_imageFilename)) {
++ qWarning() << "Not able to load image:" << qPrintable(m_imageFilename);
++ return false;
++ }
++
++ // Make sure image is in proper format
++ if (m_image.format() != QImage::Format_ARGB32 && m_image.format() != QImage::Format_ARGB32_Premultiplied)
++ m_image = m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
++
++ // Flip the image
++ m_image.flip();
++
++ return true;
++ }
++
++ bool ShapeManager::generateData()
++ {
++ if (m_image.isNull()) {
++ qWarning() << "Can't generate data, no image loaded";
++ return false;
++ }
++
++ QRect r(0, 0, m_image.width(), m_image.height());
++ // resample on the fly using 16-bit
++ int sx = (m_image.width() << 16) / r.width();
++ int sy = (m_image.height() << 16) / r.height();
++ int w = r.width();
++ int h = r.height();
++ for (int y = 0; y < h; ++y) {
++ const uint *sl = (const uint *) m_image.constScanLine((y * sy) >> 16);
++ for (int x = 0; x < w; ++x) {
++ if (sl[(x * sx) >> 16] & 0xff000000) {
++ QVector3D pos(x - (w * 0.5f), y - (h * 0.5f), 0.0f);
++ pos *= m_scale;
++ // Get random z based on depth
++ float posZ = float(QRandomGenerator::global()->generateDouble() - 0.5f) * m_depth * m_scale;
++ pos.setZ(posZ);
++ m_data << pos;
++ }
++ }
++ }
++ // Shuffle the data into random order
++ auto rd = std::random_device{};
++ std::shuffle(m_data.begin(),
++ m_data.end(),
++ std::default_random_engine(rd()));
++
++ return true;
++ }
++
++ bool ShapeManager::saveShapeData(const QString &filename)
++ {
++ m_outputData.clear();
++
++ QFile dataFile(filename);
++ if (!dataFile.open(QIODevice::WriteOnly)) {
++ // Invalid file
++ qWarning() << "Unable to open file:" << filename;
++ return false;
++ }
++
++ QCborStreamWriter writer(&dataFile);
++ QQuick3DParticleShapeDataUtils::writeShapeHeader(writer);
++
++ // Start positions array
++ writer.startArray();
++
++ // Collect correct amount of positions
++ if (m_amount < 0) {
++ m_outputData = m_data;
++ } else {
++ // m_data is shuffled so we can just pick m_amount from there
++ int index = 0;
++ while (m_outputData.size() < m_amount) {
++ m_outputData << m_data[index];
++ index = index < (m_data.size() - 1) ? index + 1 : 0;
++ }
++ }
++
++ // Sort
++ if (m_sortingMode == SortingMode::DistanceClosestFirst) {
++ std::sort(m_outputData.begin(),
++ m_outputData.end(),
++ [this](const QVector3D& lhs, const QVector3D& rhs) {
++ return m_sortingPosition.distanceToPoint(lhs) < m_sortingPosition.distanceToPoint(rhs);
++ });
++ } else if (m_sortingMode == SortingMode::DistanceClosestLast) {
++ std::sort(m_outputData.begin(),
++ m_outputData.end(),
++ [this](const QVector3D& lhs, const QVector3D& rhs) {
++ return m_sortingPosition.distanceToPoint(rhs) < m_sortingPosition.distanceToPoint(lhs);
++ });
++ }
++
++ // Write positions
++ for (auto position : m_outputData)
++ QQuick3DParticleShapeDataUtils::writeValue(writer, position);
++
++ // Leave positions array
++ writer.endArray();
++
++ // Leave root array
++ writer.endArray();
++
++ return true;
++ }
++
++ void ShapeManager::dumpOutput()
++ {
++ qDebug() << "Particle Shape";
++ qDebug() << m_outputData;
++ qDebug() << "Image positions:" << m_data.size();
++ qDebug() << "Generated positions:" << m_outputData.size();
++ }