Modernize: use unique_ptr and array

This commit is contained in:
Robert Griebl
2026-02-28 18:35:25 +01:00
parent b8785d7ccc
commit dec3e09ed8
8 changed files with 163 additions and 247 deletions
+6 -6
View File
@@ -28,7 +28,7 @@ namespace BrickLink {
PictureCache *Picture::s_cache = nullptr;
Picture::Picture(const Item *item, const Color *color)
Picture::Picture(Private, const Item *item, const Color *color)
: m_item(item)
, m_color(color)
{ }
@@ -320,11 +320,11 @@ Picture *PictureCache::picture(const Item *item, const Color *color, bool highPr
bool needToLoad = !pic || (!pic->isValid() && (pic->updateStatus() == UpdateStatus::UpdateFailed));
if (!pic) {
pic = new Picture(item, color);
int cost = pic->cost();
if (!d->m_cache.insert(key, pic, cost)) {
qCWarning(LogCache, "Can not add picture to cache (cache max/cur: %d/%d, item cost/id: %d/%s)",
int(d->m_cache.maxCost()), int(d->m_cache.totalCost()), int(cost), item->id().constData());
auto newPic = std::make_unique<Picture>(Picture::Private { }, item, color);
pic = d->m_cache.insert(key, std::move(newPic), 1 /* start with roughly 1KB cost */);
if (!pic) {
qCWarning(LogCache, "Can not add picture to cache (cache max/cur: %d/%d, item id: %s)",
int(d->m_cache.maxCost()), int(d->m_cache.totalCost()), item->id().constData());
return nullptr;
}
AppStatistics::inst()->update(d->m_cacheStatId, d->m_cache.size());
+3 -3
View File
@@ -29,6 +29,8 @@ class Picture : public QObject, protected Ref
Q_PROPERTY(BrickLink::UpdateStatus updateStatus READ updateStatus NOTIFY updateStatusChanged FINAL)
Q_PROPERTY(QImage image READ image NOTIFY imageChanged FINAL)
struct Private { };
public:
const Item *item() const { return m_item; }
const Color *color() const { return m_color; }
@@ -44,7 +46,7 @@ public:
int cost() const;
Picture(std::nullptr_t) : Picture(nullptr, nullptr) { } // for scripting only!
Picture(Private, const Item *item, const Color *color);
~Picture() override;
Q_DISABLE_COPY_MOVE(Picture)
@@ -76,8 +78,6 @@ private:
static PictureCache *s_cache;
private:
Picture(const Item *item, const Color *color);
void setIsValid(bool valid);
void setUpdateStatus(UpdateStatus status);
void setLastUpdated(const QDateTime &dt);
+6 -5
View File
@@ -35,7 +35,7 @@ namespace BrickLink {
PriceGuideCache *PriceGuide::s_cache = nullptr;
PriceGuide::PriceGuide(const Item *item, const Color *color, VatType vatType)
PriceGuide::PriceGuide(Private, const Item *item, const Color *color, VatType vatType)
: m_item(item)
, m_color(color)
, m_vatType(vatType)
@@ -725,10 +725,11 @@ PriceGuide *PriceGuideCache::priceGuide(const Item *item, const Color *color, Va
bool needToLoad = !pg || (!pg->isValid() && (pg->updateStatus() == UpdateStatus::UpdateFailed));
if (!pg) {
pg = new PriceGuide(item, color, vatType);
if (!d->m_cache.insert(key, pg)) {
qCWarning(LogCache, "Can not add price guide to cache (cache max/cur: %d/%d, cost: %d)",
int(d->m_cache.maxCost()), int(d->m_cache.totalCost()), 1);
auto newPg = std::make_unique<PriceGuide>(PriceGuide::Private { }, item, color, vatType);
pg = d->m_cache.insert(key, std::move(newPg));
if (!pg) {
qCWarning(LogCache, "Can not add price guide to cache (cache max/cur: %d/%d, item id: %s)",
int(d->m_cache.maxCost()), int(d->m_cache.totalCost()), item->id().constData());
return nullptr;
}
AppStatistics::inst()->update(d->m_cacheStatId, d->m_cache.size());
+3 -3
View File
@@ -29,6 +29,8 @@ class PriceGuide : public QObject, public Ref
Q_PROPERTY(QDateTime lastUpdated READ lastUpdated NOTIFY lastUpdatedChanged FINAL)
Q_PROPERTY(BrickLink::UpdateStatus updateStatus READ updateStatus NOTIFY updateStatusChanged FINAL)
struct Private { };
public:
const Item *item() const { return m_item; }
const Color *color() const { return m_color; }
@@ -45,7 +47,7 @@ public:
Q_INVOKABLE int lots(BrickLink::Time t, BrickLink::Condition c) const { return m_data.lots[int(t)][int(c)]; }
Q_INVOKABLE double price(BrickLink::Time t, BrickLink::Condition c, BrickLink::Price p) const { return m_data.prices[int(t)][int(c)][int(p)]; }
PriceGuide(std::nullptr_t) : PriceGuide(nullptr, nullptr, VatType::Excluded) { } // for scripting only!
PriceGuide(Private, const Item *item, const Color *color, VatType vatType);
~PriceGuide() override;
Q_DISABLE_COPY_MOVE(PriceGuide)
@@ -84,8 +86,6 @@ private:
static PriceGuideCache *s_cache;
private:
PriceGuide(const Item *item, const Color *color, VatType vatType);
void setIsValid(bool valid);
void setUpdateStatus(UpdateStatus status);
void setLastUpdated(const QDateTime &dt);
+69 -125
View File
@@ -10,27 +10,18 @@
namespace LDraw {
template <typename T, const int N> static T *parseVectors(const QStringList &list)
template <typename T> static T *parseVectors(const QStringList &list)
{
QVector3D v[N];
std::array<QVector3D, T::PointCount> v;
for (int i = 0; i < N; ++i)
for (int i = 0; i < T::PointCount; ++i)
v[i] = QVector3D(list[3*i + 1].toFloat(), list[3*i + 2].toFloat(), list[3*i + 3].toFloat());
return T::create(list[0].toInt(), v);
}
Element *Element::fromString(const QString &line, const QString &dir)
std::unique_ptr<Element> Element::fromString(const QString &line, const QString &dir)
{
Element *e = nullptr;
static const int element_count_lut[] = {
0,
14,
7,
10,
13,
13,
};
std::unique_ptr<Element> e;
auto list = line.simplified().split(u' ');
@@ -39,40 +30,29 @@ Element *Element::fromString(const QString &line, const QString &dir)
list.removeFirst();
if (t >= 0 && t <= 5) {
int count = element_count_lut[t];
if ((count == 0) || (list.size() == count)) {
switch (t) {
case 0: {
const QString cmd = line.mid(1).trimmed();
if (cmd.startsWith(u"PE_TEX_")) // Stud.io textures do not have fallbacks
break;
e = CommentElement::create(cmd);
switch (t) {
case 0: {
const QString cmd = line.mid(1).trimmed();
if (cmd.startsWith(u"PE_TEX_")) // Stud.io textures do not have fallbacks
break;
}
case 1: {
QMatrix4x4 m {
list[4].toFloat(), list[5].toFloat(), list[6].toFloat(), list[1].toFloat(),
list[7].toFloat(), list[8].toFloat(), list[9].toFloat(), list[2].toFloat(),
list[10].toFloat(), list[11].toFloat(), list[12].toFloat(), list[3].toFloat(),
0, 0, 0, 1
};
m.optimize();
e = PartElement::create(list[0].toInt(), m, list[13], dir);
break;
}
case 2:
e = parseVectors<LineElement, 2>(list);
break;
case 3:
e = parseVectors<TriangleElement, 3>(list);
break;
case 4:
e = parseVectors<QuadElement, 4>(list);
break;
case 5:
e = parseVectors<CondLineElement, 4>(list);
break;
}
e = CommentElement::create(cmd);
break;
}
case 1:
e = PartElement::create(list, dir);
break;
case 2:
e = LineElement::create<LineElement>(list);
break;
case 3:
e = TriangleElement::create<TriangleElement>(list);
break;
case 4:
e = QuadElement::create<QuadElement>(list);
break;
case 5:
e = CondLineElement::create<CondLineElement>(list);
break;
}
}
}
@@ -81,7 +61,7 @@ Element *Element::fromString(const QString &line, const QString &dir)
CommentElement::CommentElement(const QString &text)
CommentElement::CommentElement(Private, const QString &text)
: CommentElement(Type::Comment, text)
{ }
@@ -90,16 +70,16 @@ CommentElement::CommentElement(Element::Type t, const QString &text)
, m_comment(text)
{ }
CommentElement *CommentElement::create(const QString &text)
std::unique_ptr<CommentElement> CommentElement::create(const QString &text)
{
if (text.startsWith(u"BFC "))
return new BfcCommandElement(text);
return BfcCommandElement::create(text);
else
return new CommentElement(text);
return std::make_unique<CommentElement>(Private { }, text);
}
BfcCommandElement::BfcCommandElement(const QString &text)
BfcCommandElement::BfcCommandElement(Private, const QString &text)
: CommentElement(Type::BfcCommand, text)
{
auto c = text.split(u' ');
@@ -117,64 +97,13 @@ BfcCommandElement::BfcCommandElement(const QString &text)
}
}
BfcCommandElement *BfcCommandElement::create(const QString &text)
std::unique_ptr<BfcCommandElement> BfcCommandElement::create(const QString &text)
{
return new BfcCommandElement(text);
return std::make_unique<BfcCommandElement>(Private { }, text);
}
LineElement::LineElement(int color, const QVector3D *v)
: Element(Type::Line), m_color(color)
{
memcpy(m_points, v, sizeof(m_points));
}
LineElement *LineElement::create(int color, const QVector3D *v)
{
return new LineElement(color, v);
}
CondLineElement::CondLineElement(int color, const QVector3D *v)
: Element(Type::CondLine), m_color(color)
{
memcpy(m_points, v, sizeof(m_points));
}
CondLineElement *CondLineElement::create(int color, const QVector3D *v)
{
return new CondLineElement(color, v);
}
TriangleElement::TriangleElement(int color, const QVector3D *v)
: Element(Type::Triangle), m_color(color)
{
Q_ASSERT(color >= 0);
memcpy(m_points, v, sizeof(m_points));
}
TriangleElement *TriangleElement::create(int color, const QVector3D *v)
{
return new TriangleElement(color, v);
}
QuadElement::QuadElement(int color, const QVector3D *v)
: Element(Type::Quad), m_color(color)
{
Q_ASSERT(color >= 0);
memcpy(m_points, v, sizeof(m_points));
}
QuadElement *QuadElement::create(int color, const QVector3D *v)
{
return new QuadElement(color, v);
}
PartElement::PartElement(int color, const QMatrix4x4 &matrix, Part *p)
PartElement::PartElement(Private, int color, const QMatrix4x4 &matrix, Part *p)
: Element(Type::Part), m_matrix(matrix), m_part(p), m_color(color)
{
if (m_part)
@@ -187,24 +116,30 @@ PartElement::~PartElement()
m_part->release();
}
PartElement *PartElement::create(int color, const QMatrix4x4 &matrix,
const QString &filename, const QString &parentdir)
std::unique_ptr<PartElement> PartElement::create(const QStringList &list, const QString &parentdir)
{
PartElement *e = nullptr;
if (list.size() != 14)
return { };
QMatrix4x4 m {
list[4].toFloat(), list[5].toFloat(), list[6].toFloat(), list[1].toFloat(),
list[7].toFloat(), list[8].toFloat(), list[9].toFloat(), list[2].toFloat(),
list[10].toFloat(), list[11].toFloat(), list[12].toFloat(), list[3].toFloat(),
0, 0, 0, 1
};
m.optimize();
const QString filename = list[13];
const int color = list[0].toInt();
if (Part *p = library()->findPart(filename, parentdir))
e = new PartElement(color, matrix, p);
return e;
return std::make_unique<PartElement>(Private { }, color, m, p);
return nullptr;
}
Part::~Part()
std::unique_ptr<Part> Part::parse(const QByteArray &data, const QString &dir)
{
qDeleteAll(m_elements);
}
Part *Part::parse(const QByteArray &data, const QString &dir)
{
Part *p = new Part();
auto p = std::make_unique<Part>(Private { });
QTextStream ts(data);
QString line;
@@ -214,23 +149,32 @@ Part *Part::parse(const QByteArray &data, const QString &dir)
lineno++;
if (line.isEmpty())
continue;
if (Element *e = Element::fromString(line, dir)) {
p->m_elements.append(e);
if (std::unique_ptr<Element> e = Element::fromString(line, dir)) {
p->m_cost += int(e->size());
p->m_elements.emplace_back(std::move(e));
} else {
qCWarning(LogLDraw) << "Could not parse line" << lineno << ":" << line;
delete p;
return nullptr;
p.reset();
return { };
}
}
if (p->m_elements.isEmpty()) {
delete p;
p = nullptr;
if (p->m_elements.empty()) {
p.reset();
return { };
}
return p;
}
QVector<const Element *> Part::elements() const
{
QVector<const Element *> v { qsizetype(m_elements.size()) };
int i = 0;
for (const auto &ue : m_elements)
v[i++] = ue.get();
return v;
}
int Part::cost() const
{
return m_cost;
+59 -89
View File
@@ -18,24 +18,26 @@ class Element;
class PartElement;
class Part : public Ref
class Part final : public Ref
{
struct Private { };
public:
~Part() override;
Part(Private) { };
~Part() override = default;
inline const QVector<Element *> &elements() const { return m_elements; }
QVector<const Element *> elements() const;
int cost() const;
protected:
Part() = default;
static std::unique_ptr<Part> parse(const QByteArray &data, const QString &dir);
private:
static Part *parse(const QByteArray &data, const QString &dir);
friend class PartElement;
friend class Library;
static void calculateBoundingBox(const Part *part, const QMatrix4x4 &matrix, QVector3D &vmin, QVector3D &vmax);
QVector<Element *> m_elements;
std::vector<std::unique_ptr<Element>> m_elements;
int m_cost = 0;
Q_DISABLE_COPY_MOVE(Part)
@@ -55,7 +57,7 @@ public:
CondLine
};
static Element *fromString(const QString &line, const QString &dir);
static std::unique_ptr<Element> fromString(const QString &line, const QString &dir);
inline Type type() const { return m_type; }
virtual ~Element() = default;
virtual uint size() const = 0;
@@ -65,31 +67,35 @@ protected:
: m_type(t) { }
private:
Q_DISABLE_COPY(Element)
Q_DISABLE_COPY_MOVE(Element)
Type m_type;
};
class CommentElement : public Element
{
struct Private { };
public:
QString comment() const { return m_comment; }
uint size() const override { return int(sizeof(*this)) + uint(m_comment.size() * 2); }
static CommentElement *create(const QString &text);
static std::unique_ptr<CommentElement> create(const QString &text);
CommentElement(Private, const QString &text);
~CommentElement() override = default;
protected:
CommentElement(Type t, const QString &text);
CommentElement(const QString &);
private:
Q_DISABLE_COPY(CommentElement)
Q_DISABLE_COPY_MOVE(CommentElement)
QString m_comment;
};
class BfcCommandElement : public CommentElement
{
struct Private { };
public:
bool certify() const { return m_certify; }
bool noCertify() const { return m_nocertify; }
@@ -99,13 +105,13 @@ public:
bool cw() const { return m_cw; }
bool invertNext() const { return m_invertNext; }
static BfcCommandElement *create(const QString &text);
static std::unique_ptr<BfcCommandElement> create(const QString &text);
protected:
BfcCommandElement(const QString &);
BfcCommandElement(Private, const QString &);
~BfcCommandElement() override = default;
private:
Q_DISABLE_COPY(BfcCommandElement)
Q_DISABLE_COPY_MOVE(BfcCommandElement)
bool m_certify : 1 = false;
bool m_nocertify : 1 = false;
bool m_clip : 1 = false;
@@ -117,103 +123,67 @@ private:
};
class LineElement : public Element
template<Element::Type ELEMENT_TYPE, int POINT_COUNT>
class PointElement : public Element
{
struct Private { };
public:
static constexpr int PointCount = POINT_COUNT;
int color() const { return m_color; }
const QVector3D *points() const { return m_points;}
uint size() const override { return sizeof(*this); }
const std::array<QVector3D, POINT_COUNT> &points() const { return m_points;}
static LineElement *create(int color, const QVector3D *points);
template <typename T> static std::unique_ptr<T> create(const QStringList &list)
{
std::array<QVector3D, T::PointCount> v;
protected:
LineElement(int color, const QVector3D *points);
if (list.size() != (1 + 3 * T::PointCount))
return nullptr;
for (int i = 0; i < T::PointCount; ++i)
v[i] = QVector3D(list[3*i + 1].toFloat(), list[3*i + 2].toFloat(), list[3*i + 3].toFloat());
return std::make_unique<T>(Private { }, list[0].toInt(), v);
}
PointElement(Private, int color, const std::array<QVector3D, POINT_COUNT> &points)
: Element(ELEMENT_TYPE)
, m_points(points)
, m_color(color)
{ }
~PointElement() override = default;
private:
Q_DISABLE_COPY(LineElement)
QVector3D m_points[2];
int m_color;
};
class CondLineElement : public Element
{
public:
int color() const { return m_color; }
const QVector3D *points() const { return m_points;}
uint size() const override { return sizeof(*this); }
static CondLineElement *create(int color, const QVector3D *points);
protected:
CondLineElement(int color, const QVector3D *points);
private:
Q_DISABLE_COPY(CondLineElement)
QVector3D m_points[4];
int m_color;
};
class TriangleElement : public Element
{
public:
int color() const { return m_color; }
const QVector3D *points() const { return m_points;}
uint size() const override { return sizeof(*this); }
static TriangleElement *create(int color, const QVector3D *points);
protected:
TriangleElement(int color, const QVector3D *points);
private:
Q_DISABLE_COPY(TriangleElement)
QVector3D m_points[3];
int m_color;
};
class QuadElement : public Element
{
public:
int color() const { return m_color; }
const QVector3D *points() const { return m_points;}
uint size() const override { return sizeof(*this); }
static QuadElement *create(int color, const QVector3D *points);
protected:
QuadElement(int color, const QVector3D *points);
private:
QVector3D m_points[4];
int m_color;
Q_DISABLE_COPY(QuadElement)
Q_DISABLE_COPY_MOVE(PointElement)
std::array<QVector3D, POINT_COUNT> m_points { };
int m_color = 0;
};
using LineElement = PointElement<Element::Type::Line, 2>;
using CondLineElement = PointElement<Element::Type::CondLine, 4>;
using TriangleElement = PointElement<Element::Type::Triangle, 3>;
using QuadElement = PointElement<Element::Type::Quad, 4>;
class PartElement : public Element
{
struct Private { };
public:
int color() const { return m_color; }
const QMatrix4x4 &matrix() const { return m_matrix; }
Part *part() const { return m_part; }
uint size() const override { return sizeof(*this); }
static PartElement *create(int color, const QMatrix4x4 &m, const QString &filename,
const QString &parentdir);
static std::unique_ptr<PartElement> create(const QStringList &list, const QString &parentdir);
PartElement(Private, int color, const QMatrix4x4 &m, Part *part);
~PartElement() override;
protected:
PartElement(int color, const QMatrix4x4 &m, Part *part);
private:
Q_DISABLE_COPY(PartElement)
Q_DISABLE_COPY_MOVE(PartElement)
QMatrix4x4 m_matrix;
Part * m_part;
int m_color;
Part * m_part = nullptr;
int m_color = 0;
};
} // namespace LDraw
+16 -15
View File
@@ -32,7 +32,7 @@ RenderController::RenderController(QObject *parent)
, m_lines(new QmlRenderLineInstancing())
, m_clearColor(Qt::white)
{
static const float lineGeo[] = {
static const std::array<float, 6 * 3> lineGeo = {
0, -0.5, 0,
0, -0.5, 1,
0, 0.5, 1,
@@ -44,7 +44,8 @@ RenderController::RenderController(QObject *parent)
m_lineGeo->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
m_lineGeo->setStride(3 * sizeof(float));
m_lineGeo->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
m_lineGeo->setVertexData(QByteArray::fromRawData(reinterpret_cast<const char *>(lineGeo), sizeof(lineGeo)));
m_lineGeo->setVertexData(QByteArray::fromRawData(reinterpret_cast<const char *>(lineGeo.data()),
qsizetype(lineGeo.size() * sizeof(decltype(lineGeo)::value_type))));
}
RenderController::~RenderController()
@@ -205,9 +206,9 @@ RenderController::RenderData RenderController::calculateRenderData(Part *part, c
const BrickLink::Color *surfaceColor = it.key();
const bool isTextured = surfaceColor->hasParticles() || (surfaceColor->id() == 0);
const int stride = (3 + 3 + (isTextured ? 2 : 0)) * sizeof(float);
const int stride = (3 + 3 + (isTextured ? 2 : 0)) * int(sizeof(float));
auto geo = new QmlRenderGeometry(surfaceColor);
auto geo = std::make_unique<QmlRenderGeometry>(surfaceColor);
// calculate bounding box
static constexpr auto fmin = std::numeric_limits<float>::min();
@@ -239,17 +240,17 @@ RenderController::RenderData RenderController::calculateRenderData(Part *part, c
if (isTextured) {
geo->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic, 6 * sizeof(float), QQuick3DGeometry::Attribute::F32Type);
QQuick3DTextureData *texData = generateMaterialTextureData(surfaceColor);
texData->setParentItem(geo); // 3D scene parent
texData->setParent(geo); // owning parent
geo->setTextureData(texData);
auto texData = generateMaterialTextureData(surfaceColor);
texData->setParentItem(geo.get()); // 3D scene parent
texData->setParent(geo.get()); // owning parent
geo->setTextureData(texData.release());
}
geo->setBounds(vmin, vmax);
geo->setCenter(surfaceCenter);
geo->setRadius(surfaceRadius);
geo->setVertexData(data);
geos.append(geo);
geos.append(geo.release());
}
for (auto *geo : std::as_const(geos)) {
@@ -378,7 +379,7 @@ void RenderController::fillVertexBuffers(Part *part, const BrickLink::Color *mod
return Qt::black;
};
const auto &elements = part->elements();
const auto elements = part->elements();
for (const Element *e : elements) {
bool isBFCCommand = false;
bool isBFCInvertNext = false;
@@ -428,9 +429,9 @@ void RenderController::fillVertexBuffers(Part *part, const BrickLink::Color *mod
const auto color = mapColor(qe->color());
const auto p = qe->points();
const auto p0m = matrix.map(p[0]);
const auto p1m = matrix.map(p[ccw ? 3 : 1]);
const auto p1m = matrix.map(ccw ? p[3] : p[1]);
const auto p2m = matrix.map(p[2]);
const auto p3m = matrix.map(p[ccw ? 1 : 3]);
const auto p3m = matrix.map(ccw ? p[1] : p[3]);
const auto n = QVector3D::normal(p0m, p1m, p2m);
if (color->hasParticles() || (color->id() == 0)) {
@@ -491,7 +492,7 @@ void RenderController::fillVertexBuffers(Part *part, const BrickLink::Color *mod
}
}
QQuick3DTextureData *RenderController::generateMaterialTextureData(const BrickLink::Color *color)
std::unique_ptr<QQuick3DTextureData> RenderController::generateMaterialTextureData(const BrickLink::Color *color)
{
static constexpr int GeneratorVersion = 1;
@@ -611,7 +612,7 @@ QQuick3DTextureData *RenderController::generateMaterialTextureData(const BrickLi
s_materialTextureDatas.insert(color, texImage);
}
auto texData = new QQuick3DTextureData();
auto texData = std::make_unique<QQuick3DTextureData>();
texData->setFormat(QQuick3DTextureData::RGBA8);
texData->setSize(texImage.size());
texData->setHasTransparency(color->ldrawColor().alpha() < 255);
@@ -619,7 +620,7 @@ QQuick3DTextureData *RenderController::generateMaterialTextureData(const BrickLi
texImage.sizeInBytes() });
return texData;
}
return nullptr;
return { };
}
void RenderController::resetCamera()
+1 -1
View File
@@ -98,7 +98,7 @@ private:
const BrickLink::Color *baseColor, const QMatrix4x4 &matrix,
bool inverted, QHash<const BrickLink::Color *, QByteArray> &surfaceBuffers,
QByteArray &lineBuffer);
static QQuick3DTextureData *generateMaterialTextureData(const BrickLink::Color *color);
static std::unique_ptr<QQuick3DTextureData> generateMaterialTextureData(const BrickLink::Color *color);
static std::vector<std::pair<float, float> > uvMapToNearestPlane(const QVector3D &normal,
std::initializer_list<const QVector3D> vectors);