12 #include <QXmlStreamReader>
13 #include <QXmlStreamWriter>
43 m_absoluteMinValue(0.0),
44 m_absoluteRange(1.0) {
49 m_uuid = QUuid::createUuid().toString();
53 const QString& uuid )
const {
63 }
catch (
const std::bad_alloc&) {
65 QStringLiteral(
"Not enough memory to copy the color scale"));
77 CVLog::Warning(QString(
"[ccColorScale::insert] Scale '%1' is locked!")
93 CVLog::Warning(QString(
"[ccColorScale::clear] Scale '%1' is locked!")
105 CVLog::Warning(QString(
"[ccColorScale::remove] Scale '%1' is locked!")
128 assert(
m_steps.front().getRelativePos() == 0.0);
129 assert(
m_steps.back().getRelativePos() == 1.0);
130 if (
m_steps.front().getRelativePos() != 0.0 ||
131 m_steps.back().getRelativePos() != 1.0) {
133 "(boundaries are not [0.0-1.0]")
137 for (
unsigned i = 0; i <
MAX_STEPS; ++i) {
138 const double relativePos =
139 static_cast<double>(i) / (
MAX_STEPS - 1);
143 m_steps[j + 1].getRelativePos() < relativePos)
148 m_steps[j].getColor().greenF(),
149 m_steps[j].getColor().blueF());
151 m_steps[j + 1].getColor().greenF(),
152 m_steps[j + 1].getColor().blueF());
156 (relativePos -
m_steps[j].getRelativePos()) /
157 (
m_steps[j + 1].getRelativePos() -
161 colBefore + (colNext - colBefore) * alpha;
173 CVLog::Warning(QString(
"[ccColorScale] Scale '%1' is invalid! (not "
186 if (dataVersion < 27) {
191 QDataStream outStream(&out);
200 if (out.write((
const char*)&
m_relative,
sizeof(
bool)) < 0)
211 if (out.write((
const char*)&
m_locked,
sizeof(
bool)) < 0)
221 for (uint32_t i = 0; i <
stepCount; ++i) {
222 outStream <<
m_steps[i].getRelativePos();
223 outStream <<
m_steps[i].getColor();
228 if (dataVersion >= 40) {
230 uint32_t labelCount =
static_cast<uint32_t
>(
m_customLabels.size());
231 if (out.write((
const char*)&labelCount, 4) < 0)
return WriteError();
236 outStream << it->value;
237 if (dataVersion >= 54) {
238 outStream << it->text;
247 short minVersion = 27;
252 if (!label.text.isEmpty()) {
265 if (dataVersion < 27)
268 QDataStream inStream(&in);
297 for (uint32_t i = 0; i <
stepCount; ++i) {
298 double relativePos = 0.0;
300 inStream >> relativePos;
310 if (dataVersion >= 40) {
312 uint32_t labelCount = 0;
313 if (in.read((
char*)&labelCount, 4) < 0)
return ReadError();
316 for (uint32_t i = 0; i < labelCount; ++i) {
321 if (dataVersion >= 54) {
327 }
catch (
const std::bad_alloc&) {
337 assert(maxVal >= minVal);
356 if (!file.open(QFile::WriteOnly | QFile::Text)) {
358 QString(
"Failed to open file '%1' for writing!").arg(
filename));
363 QXmlStreamWriter stream(&file);
364 stream.setAutoFormatting(
true);
365 stream.writeStartDocument();
372 stream.writeAttribute(
"version",
378 stream.writeTextElement(QStringLiteral(
"name"),
getName());
379 stream.writeTextElement(QStringLiteral(
"uuid"),
getUuid());
380 stream.writeTextElement(QStringLiteral(
"absolute"),
382 : QStringLiteral(
"1"));
384 stream.writeTextElement(
385 QStringLiteral(
"minValue"),
387 stream.writeTextElement(
388 QStringLiteral(
"range"),
392 stream.writeEndElement();
399 for (QList<ccColorScaleElement>::const_iterator it =
402 stream.writeStartElement(QStringLiteral(
"step"));
408 stream.writeAttribute(
410 QString::number(
color.red()));
411 stream.writeAttribute(
413 QString::number(
color.green()));
414 stream.writeAttribute(
416 QString::number(
color.blue()));
417 stream.writeAttribute(
418 QStringLiteral(
"pos"),
419 QString::number(relativePos,
'g', 12));
421 stream.writeEndElement();
427 for (LabelSet::const_iterator it =
431 stream.writeStartElement(
432 QStringLiteral(
"label"));
434 stream.writeAttribute(
435 QStringLiteral(
"val"),
436 QString::number(it->value,
'g',
438 if (!it->text.isEmpty()) {
439 stream.writeAttribute(
440 QStringLiteral(
"text"),
444 stream.writeEndElement();
449 stream.writeEndElement();
451 stream.writeEndElement();
453 stream.writeEndElement();
455 stream.writeEndDocument();
462 if (!file.open(QFile::ReadOnly | QFile::Text)) {
464 QString(
"Failed to open file '%1' for reading!").arg(
filename));
471 QXmlStreamReader stream(&file);
476 if (!stream.readNextStartElement() ||
483 if (!stream.readNextStartElement() ||
489 QXmlStreamAttributes attributes = stream.attributes();
490 if (attributes.size() == 0 ||
491 attributes[0].name() != QStringLiteral(
"version")) {
495 int version = attributes[0].value().toString().toInt(&ok);
505 if (!stream.readNextStartElement() ||
514 int missingItems = 3;
515 while (!stream.atEnd() && missingItems > 0) {
516 if (!stream.readNextStartElement()) {
520 QString itemValue = stream.readElementText();
522 .arg(itemName.toString(), itemValue));
524 if (itemName == QStringLiteral(
"name")) {
525 scale->setName(itemValue);
527 }
else if (itemName == QStringLiteral(
"uuid")) {
528 scale->setUuid(itemValue);
530 }
else if (itemName == QStringLiteral(
"absolute")) {
531 if (itemValue == QStringLiteral(
"1")) {
538 }
else if (itemName == QStringLiteral(
"minValue")) {
539 scale->m_absoluteMinValue = itemValue.toDouble(&ok);
542 }
else if (itemName == QStringLiteral(
"range")) {
543 scale->m_absoluteRange = itemValue.toDouble(&ok);
549 if (missingItems > 0) {
551 QString(
"[ccColorScale::LoadFromXML] Missing properties!"));
554 stream.skipCurrentElement();
557 if (!stream.readNextStartElement() ||
561 .arg(stream.name().toString()));
566 bool dataError =
false;
568 while (!stream.atEnd()) {
569 if (!stream.readNextStartElement())
break;
570 if (stream.name() == QStringLiteral(
"step")) {
571 QXmlStreamAttributes attributes = stream.attributes();
572 int attributeCount = attributes.size();
573 if (attributeCount < 4) {
579 for (
int i = 0; i < attributes.size(); ++i) {
581 attributes[i].name().toString().toUpper();
582 QString value = attributes[i].value().toString();
583 if (
name == QStringLiteral(
"R"))
584 rgb.setRed(value.toInt());
585 else if (
name == QStringLiteral(
"G"))
586 rgb.setGreen(value.toInt());
587 else if (
name == QStringLiteral(
"B"))
588 rgb.setBlue(value.toInt());
589 else if (
name == QStringLiteral(
"POS"))
590 pos = value.toDouble();
595 if (attributeCount < 4) {
597 QString(
"[ccColorScale::LoadFromXML] Missing "
598 "data attributes!"));
602 stream.skipCurrentElement();
605 }
else if (stream.name() == QStringLiteral(
"label")) {
606 QXmlStreamAttributes attributes = stream.attributes();
607 int attributeCount = attributes.size();
608 if (attributeCount < 1) {
613 double value = std::numeric_limits<double>::quiet_NaN();
615 for (
int i = 0; i < attributes.size(); ++i) {
617 attributes[i].name().toString().toUpper();
618 if (
name == QStringLiteral(
"VAL")) {
619 QString valueStr = attributes[i].value().toString();
621 value = valueStr.toDouble(&ok);
625 "[ccColorScale::LoadFromXML] "
628 value = std::numeric_limits<
629 double>::quiet_NaN();
632 }
else if (
name == QStringLiteral(
"TEXT")) {
633 text = attributes[i].value().toString();
637 if (std::isfinite(value)) {
639 scale->m_customLabels.insert({value, text});
642 stream.skipCurrentElement();
645 }
catch (
const std::bad_alloc&) {
647 QString(
"[ccColorScale::LoadFromXML] Not enough memory!"));
659 CVLog::Error(QString(
"An error occurred while reading file '%1'")
QStringView QtCompatStringRef
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Color scale element: one value + one color.
const QColor & getColor() const
Returns color.
double getRelativePos() const
Returns step position (relative to scale boundaries)
static bool IsSmaller(const ccColorScaleElement &e1, const ccColorScaleElement &e2)
Comparison operator between two color scale elements.
bool saveAsXML(QString filename) const
Saves this color scale as an XML file.
ccColorScaleElement & step(int index)
Access to a given step.
QString getUuid() const
Returns unique ID.
void setAbsolute(double minVal, double maxVal)
Sets scale as absolute.
void update()
Updates internal representation.
static const unsigned MIN_STEPS
Minimum number of steps.
QList< ccColorScaleElement > m_steps
Elements.
const QString & getName() const
Returns name.
bool m_relative
Whether scale is relative or not.
bool toFile(QFile &out, short dataVersion) const override
Saves data to binary stream.
bool isRelative() const
Returns whether scale is relative or absoute.
ccColorScale(const QString &name, const QString &uuid=QString())
Default constructor.
bool m_locked
Whether scale is locked or not.
ecvColor::Rgb m_rgbaScale[MAX_STEPS]
Internal representation (RGB)
void remove(int index, bool autoUpdate=true)
Deletes a given step.
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
static Shared LoadFromXML(QString filename)
Loads a color scale from an XML file.
short minimumFileVersion() const override
Returns the minimum file version required to save this instance.
void getAbsoluteBoundaries(double &minVal, double &maxVal) const
Get absolute scale boundaries.
void insert(const ccColorScaleElement &step, bool autoUpdate=true)
Adds a step.
void generateNewUuid()
Generates a new unique ID.
double m_absoluteMinValue
'Absolute' minimum value
double m_absoluteRange
'Absolute' range
int stepCount() const
Returns the current number of steps.
ccColorScale::Shared copy(const QString &uuid=QString()) const
Creates a copy of this color scale (with a specified unique id)
virtual ~ccColorScale()
Destructor.
QSharedPointer< ccColorScale > Shared
Shared pointer type.
void clear()
Clears all steps.
static ccColorScale::Shared Create(const QString &name)
Creates a new color scale (with auto-generated unique id)
void sort()
Sort elements.
LabelSet m_customLabels
List of custom labels.
bool m_updated
Internal representation validity.
static const unsigned MAX_STEPS
Maximum number of steps (internal representation)
QMultiMap< unsigned, unsigned > LoadedIDMap
Map of loaded unique IDs (old ID --> new ID)
static bool ReadError()
Sends a custom error message (read error) and returns 'false'.
static bool WriteError()
Sends a custom error message (write error) and returns 'false'.
static bool MemoryError()
Sends a custom error message (not enough memory) and returns 'false'.
static const QString s_xmlColorScaleData("Data")
static const QString s_xmlColorScaleTitle("ColorScale")
static const QString s_xmlACloudViewer("ACloudViewer")
static const QString s_xmlCloudCompare("CloudCompare")
constexpr int s_xmlColorScaleVer
static const QString s_xmlColorScaleProperties("Properties")
unsigned char ColorCompType
Default color components type (R,G and B)
constexpr Rgb black(0, 0, 0)
constexpr Rgb white(MAX, MAX, MAX)
constexpr ColorCompType MAX
Max value of a single color component (default type)
RgbaTpl< ColorCompType > Rgba
4 components, default type