Files
qelectrotech-source-mirror/sources/factory/elementpicturefactory.h
T
ispyisail d2e0532b12 Cache the drawing of element definitions that have no uuid
ElementPictureFactory caches the QPicture it builds for an element
definition, keyed by that definition's uuid. Definitions saved before uuids
were written do not have one, and every one of them presented the same null
uuid. getPictures() spotted that and took an uncached path, so the drawing
was rebuilt from the XML for every instance the project placed.

Counted on the shipped examples:

  examples/m_000.qet           831 builds for 97 definitions
  examples/affuteuse_250h.qet  256 builds for 106 definitions
  examples/industrial.qet      65 builds, 553 cache hits (has uuids)

13 of the 23 example projects carry definitions without a uuid, so this is
not a rare shape.

Derive a key from the location when the definition has no uuid of its own.
ElementsLocation::toString() qualifies an embedded path with the id of the
project owning it, and QETApp hands out project ids from an ever-increasing
counter and never reuses them, so the derived key cannot collide with an
element of another project.

Measured with callgrind, which counts instructions and so does not depend
on what else the machine is doing, opening examples/affuteuse_250h.qet:

  4,801,381,735 -> 4,285,411,908 instructions  (-10.7 %)
  ElementPictureFactory::build    995 M -> 478 M
  ElementPictureFactory::getPictures  1289 M -> 774 M

The halving of build() matches the counters independently: 106 definitions
against 256 instances is 41 %, and the cost falls to 48 %.

This also retires a latent aliasing bug rather than a measured one:
build() inserted into m_primitives_H under the same null uuid for every
definition lacking one, and getPrimitives() read back through that shared
key. Its only caller is the image export dialog, which the command line
does not reach, so no wrong output could be demonstrated here -- but the
entries could only ever have belonged to whichever element was built last.

--info stays byte identical on all 23 example projects, and the SVG export
of affuteuse_250h.qet -- a project whose definitions all lack uuids -- is
byte identical too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6MRq2Ach1ogvnGcbuqNLr
2026-09-12 18:32:14 +12:00

116 lines
3.5 KiB
C++

/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ELEMENTPICTUREFACTORY_H
#define ELEMENTPICTUREFACTORY_H
#include <QMutex>
#include <QSharedPointer>
#include <QHash>
class ElementsLocation;
class QPicture;
class QUuid;
class QDomElement;
class QPainter;
class QGraphicsSimpleTextItem;
/**
@brief The ElementPictureFactory class
This class is singleton factory, use
to create and get the picture use by elements
*/
class ElementPictureFactory
{
public :
struct primitives
{
QList<QLineF> m_lines;
QList<QRectF> m_rectangles;
QList<QRectF> m_circles;
QList<QVector<QPointF>> m_polygons;
QList<QVector<qreal>> m_arcs;
QList<QGraphicsSimpleTextItem*> m_texts;
};
/**
@brief instance
@return The instance of the factory
*/
static ElementPictureFactory* instance()
{
static QMutex mutex;
if (!m_factory)
{
mutex.lock();
if (!m_factory) {
m_factory = new ElementPictureFactory();
}
mutex.unlock();
}
return m_factory;
}
/**
@brief dropInstance
Drop the instance of factory
*/
static void dropInstance()
{
static QMutex mutex;
if (m_factory)
{
mutex.lock();
delete m_factory;
m_factory = nullptr;
mutex.unlock();
}
}
void getPictures(const ElementsLocation &location, QPicture &picture, QPicture &low_picture);
QPixmap pixmap(const ElementsLocation &location);
ElementPictureFactory::primitives getPrimitives(const ElementsLocation &location);
private:
ElementPictureFactory() {}
ElementPictureFactory (const ElementPictureFactory &);
ElementPictureFactory operator= (const ElementPictureFactory &);
~ElementPictureFactory();
static QUuid cacheKey(const ElementsLocation &location);
bool build(const ElementsLocation &location, QPicture *picture=nullptr, QPicture *low_picture=nullptr);
void parseElement(const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parseLine (const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parseRect (const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parseEllipse(const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parseCircle (const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parseArc (const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parsePolygon(const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parseText (const QDomElement &dom, QPainter &painter, primitives &prim) const;
void parsePlcTable(const QDomElement &dom, const QDomElement &plc_data, QPainter &painter) const;
void setPainterStyle(const QDomElement &dom, QPainter &painter) const;
QHash<QUuid, QPicture> m_pictures_H;
QHash<QUuid, QPicture> m_low_pictures_H;
QHash<QUuid, QPixmap> m_pixmap_H;
QHash<QUuid, primitives> m_primitives_H;
static ElementPictureFactory* m_factory;
};
#endif // ELEMENTPICTUREFACTORY_H