Sketcher: Add support for more than 3 points
While this adds a new way, it keeps the old to make it easier to merge. This will allow other work to be merge before without rebase issues
This commit is contained in:
committed by
Kacper Donat
parent
6eff3ed949
commit
ac07a56b75
@@ -24,9 +24,16 @@
|
||||
#ifndef _PreComp_
|
||||
#include <QDateTime>
|
||||
#include <boost/random.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <ranges>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Writer.h>
|
||||
@@ -75,19 +82,24 @@ Constraint* Constraint::copy() const
|
||||
temp->Type = this->Type;
|
||||
temp->AlignmentType = this->AlignmentType;
|
||||
temp->Name = this->Name;
|
||||
temp->First = this->First;
|
||||
temp->FirstPos = this->FirstPos;
|
||||
temp->Second = this->Second;
|
||||
temp->SecondPos = this->SecondPos;
|
||||
temp->Third = this->Third;
|
||||
temp->ThirdPos = this->ThirdPos;
|
||||
temp->LabelDistance = this->LabelDistance;
|
||||
temp->LabelPosition = this->LabelPosition;
|
||||
temp->isDriving = this->isDriving;
|
||||
temp->InternalAlignmentIndex = this->InternalAlignmentIndex;
|
||||
temp->isInVirtualSpace = this->isInVirtualSpace;
|
||||
temp->isActive = this->isActive;
|
||||
temp->elements = this->elements;
|
||||
// Do not copy tag, otherwise it is considered a clone, and a "rename" by the expression engine.
|
||||
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
temp->First = this->First;
|
||||
temp->FirstPos = this->FirstPos;
|
||||
temp->Second = this->Second;
|
||||
temp->SecondPos = this->SecondPos;
|
||||
temp->Third = this->Third;
|
||||
temp->ThirdPos = this->ThirdPos;
|
||||
#endif
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
@@ -145,19 +157,42 @@ void Constraint::Save(Writer& writer) const
|
||||
<< "InternalAlignmentIndex=\"" << InternalAlignmentIndex << "\" ";
|
||||
}
|
||||
writer.Stream() << "Value=\"" << Value << "\" "
|
||||
<< "First=\"" << First << "\" "
|
||||
<< "FirstPos=\"" << (int)FirstPos << "\" "
|
||||
<< "Second=\"" << Second << "\" "
|
||||
<< "SecondPos=\"" << (int)SecondPos << "\" "
|
||||
<< "Third=\"" << Third << "\" "
|
||||
<< "ThirdPos=\"" << (int)ThirdPos << "\" "
|
||||
<< "LabelDistance=\"" << LabelDistance << "\" "
|
||||
<< "LabelPosition=\"" << LabelPosition << "\" "
|
||||
<< "IsDriving=\"" << (int)isDriving << "\" "
|
||||
<< "IsInVirtualSpace=\"" << (int)isInVirtualSpace << "\" "
|
||||
<< "IsActive=\"" << (int)isActive << "\" />"
|
||||
<< "IsActive=\"" << (int)isActive << "\" ";
|
||||
|
||||
<< std::endl;
|
||||
// Save elements
|
||||
{
|
||||
// Ensure backwards compatibility with old versions
|
||||
writer.Stream() << "First=\"" << getElement(0).GeoId << "\" "
|
||||
<< "FirstPos=\"" << getElement(0).posIdAsInt() << "\" "
|
||||
<< "Second=\"" << getElement(1).GeoId << "\" "
|
||||
<< "SecondPos=\"" << getElement(1).posIdAsInt() << "\" "
|
||||
<< "Third=\"" << getElement(2).GeoId << "\" "
|
||||
<< "ThirdPos=\"" << getElement(2).posIdAsInt() << "\" ";
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
auto elements = std::views::iota(size_t {0}, this->elements.size())
|
||||
| std::views::transform([&](size_t i) {
|
||||
return getElement(i);
|
||||
});
|
||||
#endif
|
||||
auto geoIds = elements | std::views::transform([](const GeoElementId& e) {
|
||||
return e.GeoId;
|
||||
});
|
||||
auto posIds = elements | std::views::transform([](const GeoElementId& e) {
|
||||
return e.posIdAsInt();
|
||||
});
|
||||
|
||||
const std::string ids = fmt::format("{}", fmt::join(geoIds, " "));
|
||||
const std::string positions = fmt::format("{}", fmt::join(posIds, " "));
|
||||
|
||||
writer.Stream() << "ElementIds=\"" << ids << "\" "
|
||||
<< "ElementPositions=\"" << positions << "\" ";
|
||||
}
|
||||
|
||||
writer.Stream() << "/>\n";
|
||||
}
|
||||
|
||||
void Constraint::Restore(XMLReader& reader)
|
||||
@@ -166,10 +201,6 @@ void Constraint::Restore(XMLReader& reader)
|
||||
Name = reader.getAttribute<const char*>("Name");
|
||||
Type = reader.getAttribute<ConstraintType>("Type");
|
||||
Value = reader.getAttribute<double>("Value");
|
||||
First = reader.getAttribute<long>("First");
|
||||
FirstPos = reader.getAttribute<PointPos>("FirstPos");
|
||||
Second = reader.getAttribute<long>("Second");
|
||||
SecondPos = reader.getAttribute<PointPos>("SecondPos");
|
||||
|
||||
if (this->Type == InternalAlignment) {
|
||||
AlignmentType = reader.getAttribute<InternalAlignmentType>("InternalAlignmentType");
|
||||
@@ -182,12 +213,6 @@ void Constraint::Restore(XMLReader& reader)
|
||||
AlignmentType = Undef;
|
||||
}
|
||||
|
||||
// read the third geo group if present
|
||||
if (reader.hasAttribute("Third")) {
|
||||
Third = reader.getAttribute<long>("Third");
|
||||
ThirdPos = reader.getAttribute<PointPos>("ThirdPos");
|
||||
}
|
||||
|
||||
// Read the distance a constraint label has been moved
|
||||
if (reader.hasAttribute("LabelDistance")) {
|
||||
LabelDistance = (float)reader.getAttribute<double>("LabelDistance");
|
||||
@@ -208,19 +233,90 @@ void Constraint::Restore(XMLReader& reader)
|
||||
if (reader.hasAttribute("IsActive")) {
|
||||
isActive = reader.getAttribute<bool>("IsActive");
|
||||
}
|
||||
|
||||
if (reader.hasAttribute("ElementIds") && reader.hasAttribute("ElementPositions")) {
|
||||
auto splitAndClean = [](std::string_view input) {
|
||||
const char delimiter = ' ';
|
||||
|
||||
auto tokens = input | std::views::split(delimiter)
|
||||
| std::views::transform([](auto&& subrange) {
|
||||
// workaround due to lack of std::ranges::to in c++20
|
||||
std::string token;
|
||||
auto size = std::ranges::distance(subrange);
|
||||
token.reserve(size);
|
||||
for (char c : subrange) {
|
||||
token.push_back(c);
|
||||
}
|
||||
return token;
|
||||
})
|
||||
| std::views::filter([](const std::string& s) {
|
||||
return !s.empty();
|
||||
});
|
||||
|
||||
return std::vector<std::string>(tokens.begin(), tokens.end());
|
||||
};
|
||||
|
||||
const std::string elementIds = reader.getAttribute<const char*>("ElementIds");
|
||||
const std::string elementPositions = reader.getAttribute<const char*>("ElementPositions");
|
||||
|
||||
const auto ids = splitAndClean(elementIds);
|
||||
const auto positions = splitAndClean(elementPositions);
|
||||
|
||||
if (ids.size() != positions.size()) {
|
||||
throw Base::ParserError(fmt::format("ElementIds and ElementPositions do not match in "
|
||||
"size. Got {} ids and {} positions.",
|
||||
ids.size(),
|
||||
positions.size()));
|
||||
}
|
||||
|
||||
elements.clear();
|
||||
for (size_t i = 0; i < std::min(ids.size(), positions.size()); ++i) {
|
||||
const int geoId {std::stoi(ids[i])};
|
||||
const PointPos pos {static_cast<PointPos>(std::stoi(positions[i]))};
|
||||
addElement(GeoElementId(geoId, pos));
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have at least 3 elements
|
||||
while (getElementsSize() < 3) {
|
||||
addElement(GeoElementId(GeoEnum::GeoUndef, PointPos::none));
|
||||
}
|
||||
|
||||
// Load deprecated First, Second, Third elements
|
||||
// These take precedence over the new elements
|
||||
// Even though these are deprecated, we still need to read them
|
||||
// for compatibility with old files.
|
||||
{
|
||||
constexpr std::array<const char*, 3> names = {"First", "Second", "Third"};
|
||||
constexpr std::array<const char*, 3> posNames = {"FirstPos", "SecondPos", "ThirdPos"};
|
||||
static_assert(names.size() == posNames.size());
|
||||
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
if (reader.hasAttribute(names[i])) {
|
||||
const int geoId {reader.getAttribute<int>(names[i])};
|
||||
const PointPos pos {reader.getAttribute<PointPos>(posNames[i])};
|
||||
setElement(i, GeoElementId(geoId, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Constraint::substituteIndex(int fromGeoId, int toGeoId)
|
||||
{
|
||||
if (this->First == fromGeoId) {
|
||||
this->First = toGeoId;
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
for (size_t i = 0; i < elements.size(); ++i) {
|
||||
const GeoElementId element = getElement(i);
|
||||
if (element.GeoId == fromGeoId) {
|
||||
setElement(i, GeoElementId(toGeoId, element.Pos));
|
||||
}
|
||||
}
|
||||
if (this->Second == fromGeoId) {
|
||||
this->Second = toGeoId;
|
||||
}
|
||||
if (this->Third == fromGeoId) {
|
||||
this->Third = toGeoId;
|
||||
#else
|
||||
for (auto& element : elements) {
|
||||
if (element.GeoId == fromGeoId) {
|
||||
element = GeoElementId(toGeoId, element.Pos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Constraint::substituteIndexAndPos(int fromGeoId,
|
||||
@@ -228,18 +324,22 @@ void Constraint::substituteIndexAndPos(int fromGeoId,
|
||||
int toGeoId,
|
||||
PointPos toPosId)
|
||||
{
|
||||
if (this->First == fromGeoId && this->FirstPos == fromPosId) {
|
||||
this->First = toGeoId;
|
||||
this->FirstPos = toPosId;
|
||||
const GeoElementId from {fromGeoId, fromPosId};
|
||||
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
for (size_t i = 0; i < elements.size(); ++i) {
|
||||
const GeoElementId element = getElement(i);
|
||||
if (element == from) {
|
||||
setElement(i, GeoElementId(toGeoId, toPosId));
|
||||
}
|
||||
}
|
||||
if (this->Second == fromGeoId && this->SecondPos == fromPosId) {
|
||||
this->Second = toGeoId;
|
||||
this->SecondPos = toPosId;
|
||||
}
|
||||
if (this->Third == fromGeoId && this->ThirdPos == fromPosId) {
|
||||
this->Third = toGeoId;
|
||||
this->ThirdPos = toPosId;
|
||||
#else
|
||||
for (auto& element : elements) {
|
||||
if (element == from) {
|
||||
element = GeoElementId(toGeoId, toPosId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Constraint::typeToString(ConstraintType type)
|
||||
@@ -251,3 +351,91 @@ std::string Constraint::internalAlignmentTypeToString(InternalAlignmentType alig
|
||||
{
|
||||
return internalAlignmentType2str[alignment];
|
||||
}
|
||||
|
||||
bool Constraint::involvesGeoId(int geoId) const
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
auto elements =
|
||||
std::views::iota(size_t {0}, this->elements.size()) | std::views::transform([&](size_t i) {
|
||||
return getElement(i);
|
||||
});
|
||||
#endif
|
||||
return std::ranges::any_of(elements, [geoId](const auto& element) {
|
||||
return element.GeoId == geoId;
|
||||
});
|
||||
}
|
||||
/// utility function to check if (`geoId`, `posId`) is one of the points/curves
|
||||
bool Constraint::involvesGeoIdAndPosId(int geoId, PointPos posId) const
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
auto elements =
|
||||
std::views::iota(size_t {0}, this->elements.size()) | std::views::transform([&](size_t i) {
|
||||
return getElement(i);
|
||||
});
|
||||
#endif
|
||||
return std::ranges::find(elements, GeoElementId(geoId, posId)) != elements.end();
|
||||
}
|
||||
|
||||
GeoElementId Constraint::getElement(size_t index) const
|
||||
{
|
||||
if (index >= elements.size()) {
|
||||
throw Base::IndexError("Constraint::getElement index out of range");
|
||||
}
|
||||
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return GeoElementId(First, FirstPos);
|
||||
case 1:
|
||||
return GeoElementId(Second, SecondPos);
|
||||
case 2:
|
||||
return GeoElementId(Third, ThirdPos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return elements[index];
|
||||
}
|
||||
void Constraint::setElement(size_t index, GeoElementId element)
|
||||
{
|
||||
if (index >= elements.size()) {
|
||||
throw Base::IndexError("Constraint::getElement index out of range");
|
||||
}
|
||||
|
||||
elements[index] = element;
|
||||
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
First = element.GeoId;
|
||||
FirstPos = element.Pos;
|
||||
break;
|
||||
case 1:
|
||||
Second = element.GeoId;
|
||||
SecondPos = element.Pos;
|
||||
break;
|
||||
case 2:
|
||||
Third = element.GeoId;
|
||||
ThirdPos = element.Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t Constraint::getElementsSize() const
|
||||
{
|
||||
return elements.size();
|
||||
}
|
||||
|
||||
void Constraint::addElement(GeoElementId element)
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
int i = elements.size();
|
||||
elements.resize(i + 1);
|
||||
setElement(i, element);
|
||||
#else
|
||||
elements.push_back(element);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
#include "GeoEnum.h"
|
||||
|
||||
|
||||
// Flipping this to 0 removes old legazy members First, FirstPos, Second...
|
||||
// Will be used when everything has been migrated to new api.
|
||||
#define SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS 1
|
||||
|
||||
|
||||
namespace Sketcher
|
||||
{
|
||||
/*!
|
||||
@@ -129,24 +134,18 @@ public:
|
||||
|| Type == Diameter || Type == Angle || Type == SnellsLaw || Type == Weight;
|
||||
}
|
||||
|
||||
/// utility function to swap the index in First/Second/Third of the provided constraint from the
|
||||
/// utility function to swap the index in elements of the provided constraint from the
|
||||
/// fromGeoId GeoId to toGeoId
|
||||
void substituteIndex(int fromGeoId, int toGeoId);
|
||||
/// utility function to swap the index and position in First/Second/Third of the provided
|
||||
/// utility function to swap the index and position in elements of the provided
|
||||
/// constraint from {fromGeoId, fromPosId} to {toGeoId, toPosId}.
|
||||
void substituteIndexAndPos(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId);
|
||||
|
||||
/// utility function to check if `geoId` is one of the geometries
|
||||
bool involvesGeoId(int geoId) const
|
||||
{
|
||||
return First == geoId || Second == geoId || Third == geoId;
|
||||
}
|
||||
bool involvesGeoId(int geoId) const;
|
||||
|
||||
/// utility function to check if (`geoId`, `posId`) is one of the points/curves
|
||||
bool involvesGeoIdAndPosId(int geoId, PointPos posId) const
|
||||
{
|
||||
return (First == geoId && FirstPos == posId) || (Second == geoId && SecondPos == posId)
|
||||
|| (Third == geoId && ThirdPos == posId);
|
||||
}
|
||||
bool involvesGeoIdAndPosId(int geoId, PointPos posId) const;
|
||||
|
||||
std::string typeToString() const
|
||||
{
|
||||
@@ -210,12 +209,6 @@ public:
|
||||
ConstraintType Type {None};
|
||||
InternalAlignmentType AlignmentType {Undef};
|
||||
std::string Name;
|
||||
int First {GeoEnum::GeoUndef};
|
||||
PointPos FirstPos {PointPos::none};
|
||||
int Second {GeoEnum::GeoUndef};
|
||||
PointPos SecondPos {PointPos::none};
|
||||
int Third {GeoEnum::GeoUndef};
|
||||
PointPos ThirdPos {PointPos::none};
|
||||
float LabelDistance {10.F};
|
||||
float LabelPosition {0.F};
|
||||
bool isDriving {true};
|
||||
@@ -226,6 +219,25 @@ public:
|
||||
|
||||
bool isActive {true};
|
||||
|
||||
GeoElementId getElement(size_t index) const;
|
||||
void setElement(size_t index, GeoElementId element);
|
||||
size_t getElementsSize() const;
|
||||
void addElement(GeoElementId element);
|
||||
|
||||
#ifdef SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
// Deprecated, use getElement/setElement instead
|
||||
int First {GeoEnum::GeoUndef};
|
||||
int Second {GeoEnum::GeoUndef};
|
||||
int Third {GeoEnum::GeoUndef};
|
||||
PointPos FirstPos {PointPos::none};
|
||||
PointPos SecondPos {PointPos::none};
|
||||
PointPos ThirdPos {PointPos::none};
|
||||
#endif
|
||||
|
||||
private:
|
||||
// New way to access point ids and positions.
|
||||
// While the old way is still supported, it is recommended to the getters and setters instead.
|
||||
std::vector<GeoElementId> elements {GeoElementId(), GeoElementId(), GeoElementId()};
|
||||
|
||||
protected:
|
||||
boost::uuids::uuid tag;
|
||||
|
||||
@@ -32,12 +32,15 @@
|
||||
#ifdef _PreComp_
|
||||
|
||||
// standard
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Qt
|
||||
|
||||
Reference in New Issue
Block a user