src/model.h

Fri, 01 Jul 2022 16:46:43 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Fri, 01 Jul 2022 16:46:43 +0300
changeset 312
2637134bc37c
parent 309
d862721d19a3
child 328
3ea38fd469ca
permissions
-rw-r--r--

Fix right click to delete not really working properly
Instead of removing the point that had been added, it would remove
the point that is being drawn, which would cause it to overwrite the
previous point using the new point, causing a bit of a delay

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 - 2020 Teemu Piippo
 *
 *  This program 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once
#include <QAbstractListModel>
#include <memory>
#include "src/basics.h"
#include "src/colors.h"

struct SubfileReference
{
	QString name;
	glm::mat4 transformation;
	bool inverted = false;
};

template<typename T>
struct Colored : T
{
	ldraw::Color color;
};

struct Comment
{
	QString text;
};

struct ParseError
{
	QString code;
};

struct Empty {};

struct CircularFraction
{
	unsigned int segments;
	unsigned int divisions;
};

constexpr bool operator<(const CircularFraction& p, const CircularFraction& q)
{
	// a/b < c/d
	// a < c * b / d
	// a * d < c * b
	return p.segments * q.divisions < q.segments / p.divisions;
}

struct CircularPrimitive
{
	enum Type
	{
		Circle,
		Disc,
		Cylinder,
		CylinderOpen,
		CylinderClosed,
		DiscNegative,
		Chord,
	} type;
	static constexpr int NUM_TYPES = Chord + 1;
	CircularFraction fraction;
	glm::mat4 transformation;
	bool inverted = false;
};

constexpr char circularPrimitiveStems[CircularPrimitive::NUM_TYPES][5] = {
	"edge",
	"disc",
	"cyli",
	"cylo",
	"cylc",
	"ndis",
	"chrd",
};

Q_DECLARE_METATYPE(CircularPrimitive::Type)

struct CircleToolOptions
{
	CircularFraction fraction;
	CircularPrimitive::Type type;
};

using ModelElement = std::variant<
	Colored<SubfileReference>,
	Colored<LineSegment>,
	Colored<Triangle>,
	Colored<Quadrilateral>,
	Colored<ConditionalEdge>,
	Colored<CircularPrimitive>,
	Comment,
	Empty,
	ParseError>;

using PlainPolygonElement = std::variant<
	LineSegment,
	Triangle,
	Quadrilateral,
	ConditionalEdge>;

using PolygonElement = Colored<PlainPolygonElement>;

template<typename T>
struct remove_color {};

template<typename T>
struct remove_color<Colored<T>> { using type = T; };

template<typename T>
struct remove_color<Colored<T>&> { using type = T&; };

template<typename T>
struct remove_color<const Colored<T>&> { using type = const T&; };

template<typename T>
struct remove_color<Colored<T>&&> { using type = T&&; };

template<typename T>
using remove_color_t = typename remove_color<T>::type;

static_assert(std::is_same_v<remove_color_t<Colored<Triangle>>, Triangle>);
static_assert(std::is_same_v<remove_color_t<Colored<Triangle>&>, Triangle&>);
static_assert(std::is_same_v<remove_color_t<const Colored<Triangle>&>, const Triangle&>);

template<typename T>
constexpr remove_color_t<T&&> extract_colored(T&& x)
{
	return static_cast<remove_color_t<T&&>>(x);
}

template<typename Ret, typename Fn1, typename Fn2, typename Fn3, typename Fn4, typename T>
constexpr auto visitPolygon(Fn1&& f1, Fn2&& f2, Fn3&& f3, Fn4&& f4, T&& element)
{
	if (std::holds_alternative<LineSegment>(element)) {
		return f1(std::get<LineSegment>(element));
	}
	else if (std::holds_alternative<Triangle>(element)) {
		return f2(std::get<Triangle>(element));
	}
	else if (std::holds_alternative<Quadrilateral>(element)) {
		return f3(std::get<Quadrilateral>(element));
	}
	else {
		return f4(std::get<ConditionalEdge>(element));
	}
}

template<typename T, typename Fn>
constexpr void visitPoints(Fn&& func, T&& element)
{
	visitPolygon<void>(
		[&func](transfer_cvref_t<T&&, LineSegment> edge)
		{
			func(edge.p1);
			func(edge.p2);
		},
		[&func](transfer_cvref_t<T&&, Triangle>& tri)
		{
			func(tri.p1);
			func(tri.p2);
			func(tri.p3);
		},
		[&func](transfer_cvref_t<T&&, Quadrilateral>& quad)
		{
			func(quad.p1);
			func(quad.p2);
			func(quad.p3);
			func(quad.p4);
		},
		[&func](transfer_cvref_t<T&&, ConditionalEdge>& cedge)
		{
			func(cedge.p1);
			func(cedge.p2);
			func(cedge.c1);
			func(cedge.c2);
		},
		element);
}

QString modelElementToString(const ModelElement& element);
struct ElementId
{
	std::int32_t value;
	constexpr auto operator<=>(const ElementId& other) const = default;
};

constexpr auto qHash(ElementId id)
{
	return qHash(id.value);
}

class Model : public QAbstractListModel
{
	Q_OBJECT
	struct Entry {
		ModelElement data;
		ElementId id;
	};
	std::vector<Entry> body;
	std::map<ElementId, std::size_t> positions;
	ElementId runningId = {1};
public:
	explicit Model(QObject* parent);
	virtual ~Model();
	ElementId append(const ModelElement& value);
	const ModelElement& at(std::size_t position) const;
	ElementId idAt(std::size_t position) const;
	void assignAt(std::size_t position, const ModelElement& element);
	std::optional<std::size_t> find(ElementId id) const;
	void remove(std::size_t index);
	int rowCount(const QModelIndex&) const override;
	QVariant data(const QModelIndex& index, int role) const override;
	const ModelElement& operator[](std::size_t index) const;
	std::size_t size() const;
	auto operator[](const std::size_t index) {
		struct {
			Model& model;
			const std::size_t index;
			operator const ModelElement&() {
				return model.at(index);
			}
			auto& operator=(const ModelElement& newData) {
				model.assignAt(index, newData);
				return *this;
			}
			const auto* operator&() {
				return &(this->operator const ModelElement&());
			}
		} result{*this, index};
		return result;
	}
};

void save(const Model& model, QIODevice *device);
void updateHeaderNameField(Model& model, const QString &name);

template<typename T>
void iterate(const Model& model, std::function<void(const T&)> fn)
{
	for (std::size_t i = 0; i < model.size(); ++i) {
		if (std::holds_alternative<T>(model[i])) {
			fn(std::get<T>(model[i]));
		}
	}
}

constexpr Colored<LineSegment> edge(const glm::vec3& p1, const glm::vec3& p2)
{
	return Colored<LineSegment>{{.p1 = p1, .p2 = p2}, EDGE_COLOR};
}

constexpr Colored<Triangle> triangle(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3)
{
	return Colored<Triangle>{{.p1 = p1, .p2 = p2, .p3 = p3}, MAIN_COLOR};
}

constexpr Colored<Quadrilateral> quadrilateral(
	const glm::vec3& p1,
	const glm::vec3& p2,
	const glm::vec3& p3,
	const glm::vec3& p4)
{
	return Colored<Quadrilateral>{{.p1 = p1, .p2 = p2, .p3 = p3, .p4 = p4}, MAIN_COLOR};
}

struct AppendToModel
{
	ModelElement newElement;
};

struct DeleteFromModel
{
	std::size_t position;
};

struct ModifyModel
{
	std::size_t position;
	ModelElement newElement;
};

using ModelAction = std::variant<
	AppendToModel,
	DeleteFromModel,
	ModifyModel>;

mercurial