Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement facelist property #237

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d2ef593
implement FaceProperty
Mar 29, 2022
955b285
Add FaceList type
Mar 11, 2022
062d558
Implement FaceListProperty
Mar 11, 2022
e94742b
add FaceListPropertyWidget skeleton
Mar 11, 2022
54e1586
StyleTag has face list property
Mar 11, 2022
8c48b95
minor style fixes
Mar 11, 2022
8a69d7c
Throw instead of assert-fail in a rare condition
Mar 11, 2022
d54edb1
More consistent property widgets code style
Mar 11, 2022
930b414
Move joint-point aware PathPoint-equality to PathPoint class
Mar 12, 2022
bf795be
Delay conversion of faces to QPainterPath
Mar 12, 2022
e2ea135
simplify FaceListWidget
Mar 13, 2022
0621b50
Face selection mode
Mar 13, 2022
8dbe5d0
remove no longer required code
Mar 13, 2022
319bd04
hover faces
Mar 14, 2022
c53cce5
Merge branch 'implement-faces-property' into implement-facelist-property
Apr 1, 2022
30da911
fix Face list type label confusion
Apr 2, 2022
1ae4ef6
don't overwrite old scene file if serialization fails (only JSON)
Apr 2, 2022
54cd3aa
guard ::contains function with requires-expression
Apr 3, 2022
a501209
fix PathPoint::is_dangling implementation
Apr 3, 2022
6e7a664
add compile option DRAW_POINT_IDS
Apr 7, 2022
edcf5cf
rename PathVector::outline to to_painter_path
Apr 7, 2022
133e298
add Path::to_painter_path method
Apr 7, 2022
9cc12cb
add PathVector::draw_point_ids(QPainter&)
Apr 7, 2022
4a40b28
simplify testutil::Application
Apr 7, 2022
fb15a8c
Graph::faces returns a set instead of a vector
Apr 7, 2022
d5b4234
fix missing const
Apr 7, 2022
96bbb27
provide another (failing) face detection test case
Apr 7, 2022
41487f7
fix Face::contains
Apr 7, 2022
1b33f3b
improve face selection
Apr 7, 2022
789fe54
remove ambiguous Face operator== and operator<
Apr 8, 2022
3661fb6
disable FaceDetection tests because they are known to be broken
Apr 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ option(USE_QT_5_12 "Allow to use Qt 5.12. Set this option to true for static ana
Builds with this configuration are not supposed to be run."
OFF
)
option(DRAW_POINT_IDS "Draw the id of path points next to the point." OFF)
option(WERROR "Error on compiler warnings. Not available for MSVC." ON)

if (USE_QT_5_12)
Expand Down
488 changes: 487 additions & 1 deletion icons/icons.omm

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions keybindings/default_keybindings.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export ...: Ctrl+E
remove selected points: Ctrl+Del
remove selected items: Del
scene_mode.vertex:
scene_mode.face:
scene_mode.object:
scene_mode.cycle: Ctrl+Tab
convert objects: C
Expand Down Expand Up @@ -78,6 +79,7 @@ StyleTag:
NodesTag:

# Tools:
SelectFacesTool: M, F
SelectObjectsTool: O, O
SelectPointsTool: P, P
BrushSelectTool: P, B
Expand Down
1 change: 1 addition & 0 deletions lists/properties.lst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"items": [
"BoolProperty",
"ColorProperty",
"FaceListProperty",
"FloatProperty",
"IntegerProperty",
"OptionProperty",
Expand Down
1 change: 1 addition & 0 deletions lists/tools.lst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"BrushSelectTool",
"KnifeTool",
"PathTool",
"SelectFacesTool",
"SelectObjectsTool",
"SelectPointsTool",
"SelectSimilarTool",
Expand Down
27 changes: 27 additions & 0 deletions sample-scenes/basic.omm
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@
"animated": false,
"key": "edit-style",
"type": "TriggerProperty"
},
{
"animatable": true,
"animated": false,
"key": "facelist",
"type": "FaceListProperty",
"value": {
"path": 0
}
}
],
"type": "StyleTag"
Expand Down Expand Up @@ -300,6 +309,15 @@
"animated": false,
"key": "edit-style",
"type": "TriggerProperty"
},
{
"animatable": true,
"animated": false,
"key": "facelist",
"type": "FaceListProperty",
"value": {
"path": 0
}
}
],
"type": "StyleTag"
Expand Down Expand Up @@ -443,6 +461,15 @@
"animated": false,
"key": "edit-style",
"type": "TriggerProperty"
},
{
"animatable": true,
"animated": false,
"key": "facelist",
"type": "FaceListProperty",
"value": {
"path": 0
}
}
],
"type": "StyleTag"
Expand Down
18 changes: 18 additions & 0 deletions sample-scenes/glshader.omm
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@
"animated": false,
"key": "edit-style",
"type": "TriggerProperty"
},
{
"animatable": true,
"animated": false,
"key": "facelist",
"type": "FaceListProperty",
"value": {
"path": 0
}
}
],
"type": "StyleTag"
Expand Down Expand Up @@ -296,6 +305,15 @@
"animated": false,
"key": "edit-style",
"type": "TriggerProperty"
},
{
"animatable": true,
"animated": false,
"key": "facelist",
"type": "FaceListProperty",
"value": {
"path": 0
}
}
],
"type": "StyleTag"
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ target_sources(libommpfritt PRIVATE
dnf.h
enumnames.cpp
enumnames.h
facelist.cpp
facelist.h
logging.cpp
logging.h
maybeowner.h
Expand Down
13 changes: 8 additions & 5 deletions src/aspects/abstractpropertyowner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ AbstractPropertyOwner::AbstractPropertyOwner(Kind kind, Scene* scene) : kind(kin

AbstractPropertyOwner::AbstractPropertyOwner(const AbstractPropertyOwner& other)
: QObject() // NOLINT(readability-redundant-member-init)
,
kind(other.kind), m_scene(other.m_scene)
, kind(other.kind)
, m_scene(other.m_scene)
{
for (auto&& key : other.m_properties.keys()) {
AbstractPropertyOwner::add_property(key, other.m_properties.at(key)->clone());
Expand Down Expand Up @@ -80,6 +80,7 @@ void AbstractPropertyOwner::serialize(serialization::SerializerWorker& worker) c

void AbstractPropertyOwner::deserialize(serialization::DeserializerWorker& worker)
{
using DeserializeError = serialization::AbstractDeserializer::DeserializeError;
m_id = worker.sub(ID_POINTER)->get_size_t();
worker.deserializer().register_reference(m_id, *this);

Expand All @@ -88,17 +89,19 @@ void AbstractPropertyOwner::deserialize(serialization::DeserializerWorker& worke
const auto property_type = worker_i.sub(PROPERTY_TYPE_POINTER)->get_string();

if (properties().contains(property_key)) {
assert(property_type == property(property_key)->type());
if (property_type != property(property_key)->type()) {
throw DeserializeError("Built-in property does not have expected type.");
}
property(property_key)->deserialize(worker_i);
} else {
std::unique_ptr<Property> property;
try {
property = Property::make(property_type);
} catch (const std::out_of_range&) {
const auto msg = "Failed to retrieve property type '" + property_type + "'.";
throw serialization::AbstractDeserializer::DeserializeError(msg.toStdString());
throw DeserializeError(msg.toStdString());
} catch (const Property::InvalidKeyError& e) {
throw serialization::AbstractDeserializer::DeserializeError(e.what());
throw DeserializeError(e.what());
}
property->deserialize(worker_i);
[[maybe_unused]] Property& ref = add_property(property_key, std::move(property));
Expand Down
12 changes: 4 additions & 8 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ enum class Flag {

enum class InterpolationMode { Linear, Smooth, Bezier };
enum class HandleStatus { Hovered, Active, Inactive };
enum class SceneMode { Object, Vertex };
enum class SceneMode { Object, Vertex, Face };

} // namespace omm

Expand Down Expand Up @@ -100,14 +100,10 @@ SetA merge(SetA&& a, SetB&& b, Sets&&... sets)
}

template<typename Container, typename S>
bool contains(const Container& set, S&& key)
bool contains(const Container& set, const S& key)
requires requires { { *begin(set) == key } -> std::same_as<bool>; }
{
if constexpr (std::is_pointer_v<S> || std::is_reference_v<S>) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
return std::find(set.begin(), set.end(), const_cast<std::remove_const_t<S>>(key)) != set.end();
} else {
return std::find(set.begin(), set.end(), key) != set.end();
}
return std::find_if(begin(set), end(set), [&key](const auto& v) { return v == key; }) != end(set);
}

template<typename S, typename... Ts> bool contains(const std::map<Ts...>& map, S&& key)
Expand Down
2 changes: 2 additions & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ static constexpr auto ommpfritt_version_patch = "@CMAKE_PROJECT_VERSION_PATCH@";
static constexpr auto source_directory = "@CMAKE_SOURCE_DIR@";
static constexpr auto qt_qm_path = "@qt_qm_path@";
std::string_view git_describe();

#cmakedefine DRAW_POINT_IDS
178 changes: 178 additions & 0 deletions src/facelist.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#include "facelist.h"
#include "path/face.h"
#include "path/edge.h"
#include "path/pathpoint.h"
#include "path/pathvector.h"
#include "serializers/deserializerworker.h"
#include "serializers/serializerworker.h"
#include "serializers/abstractdeserializer.h"
#include "transform.h"
#include "objects/pathobject.h"

namespace
{

static constexpr auto FACES_POINTER = "faces";
static constexpr auto PATH_ID_POINTER = "path";

} // namespace

namespace omm
{

class FaceList::ReferencePolisher : public omm::serialization::ReferencePolisher
{
public:
explicit ReferencePolisher(const std::size_t path_id)
: m_path_id(path_id)
{
}

void update_references(const std::map<std::size_t, AbstractPropertyOwner*>& map) override
{
const auto& path_object = dynamic_cast<const omm::PathObject&>(*map.at(m_path_id));
const auto& path_vector = path_object.path_vector();
for (std::size_t i = 0; i < m_faces.size(); ++i) {
for (const auto& edge_repr : m_face_reprs.at(i)) {
auto& p1 = path_vector.point_at_index(edge_repr.first);
auto& p2 = path_vector.point_at_index(edge_repr.second);
m_faces.at(i)->add_edge(Edge{p1, p2});
}
}
}

using EdgeRepr = std::pair<std::size_t, std::size_t>;
using FaceRepr = std::deque<EdgeRepr>;

FaceRepr& start_face(Face& face)
{
m_faces.emplace_back(&face);
return m_face_reprs.emplace_back();
}

private:
const std::size_t m_path_id;
std::deque<FaceRepr> m_face_reprs;
std::deque<Face*> m_faces;
};

FaceList::FaceList() = default;
FaceList::~FaceList() = default;

FaceList::FaceList(const FaceList& other)
: m_path_object(other.m_path_object)
, m_faces(::copy(other.m_faces))
{
}

FaceList::FaceList(FaceList&& other) noexcept
{
swap(*this, other);
}

FaceList& FaceList::operator=(FaceList other)
{
swap(*this, other);
return *this;
}

FaceList& FaceList::operator=(FaceList&& other) noexcept
{
swap(*this, other);
return *this;
}

void swap(FaceList& a, FaceList& b) noexcept
{
std::swap(a.m_path_object, b.m_path_object);
std::swap(a.m_faces, b.m_faces);
}

void FaceList::serialize(serialization::SerializerWorker& worker) const
{
auto path_id_worker = worker.sub(PATH_ID_POINTER);
if (m_path_object == nullptr) {
path_id_worker->set_value(static_cast<std::size_t>(0));
} else {
path_id_worker->set_value(m_path_object->id());
worker.sub(FACES_POINTER)->set_value(m_faces, [](const auto& f, auto& face_worker) {
face_worker.set_value(f->edges(), [](const Edge& edge, auto& edge_worker) {
edge_worker.set_value(std::vector{edge.a->index(), edge.b->index()});
});
});
}
}

void FaceList::deserialize(serialization::DeserializerWorker& worker)
{
m_faces.clear();
m_path_object = nullptr;
const auto path_id = worker.sub(PATH_ID_POINTER)->get<std::size_t>();
if (path_id == 0) {
return;
}

auto reference_polisher = std::make_unique<ReferencePolisher>(path_id);
worker.sub(FACES_POINTER)->get_items([this, &reference_polisher](auto& face_worker) {
auto& face_repr = reference_polisher->start_face(*m_faces.emplace_back(std::make_unique<Face>()));
face_worker.get_items([&face_repr](auto& edge_worker) {
const auto ids = edge_worker.template get<std::vector<std::size_t>>();
if (ids.size() != 2) {
throw serialization::AbstractDeserializer::DeserializeError("Expected two points per edge.");
}
face_repr.emplace_back(ids[0], ids[1]);
});
});

worker.deserializer().register_reference_polisher(std::move(reference_polisher));
}

bool FaceList::operator==(const FaceList& other) const
{
if (m_path_object != other.m_path_object || m_faces.size() != other.m_faces.size()) {
return false;
}

for (std::size_t i = 0; i < m_faces.size(); ++i) {
if (*m_faces[i] != *other.m_faces[i]) {
return false;
}
}
return true;
}

bool FaceList::operator!=(const FaceList& other) const
{
return !(*this == other);
}

bool FaceList::operator<(const FaceList& other) const
{
if (m_path_object == nullptr || other.m_path_object == nullptr) {
return m_path_object < other.m_path_object;
}

if (m_path_object != other.m_path_object) {
return m_path_object->id() < other.m_path_object->id();
}

if (m_faces.size() != other.m_faces.size()) {
return m_faces.size() < other.m_faces.size();
}

for (std::size_t i = 0; i < m_faces.size(); ++i) {
auto& face_i = *m_faces.at(i);
auto& other_face_i = *other.m_faces.at(i);
if (face_i != other_face_i) {
return face_i < other_face_i;
}
}
return false; // face lists are equal
}

std::deque<Face> FaceList::faces() const
{
return util::transform(m_faces, [](const auto& face) { return *face; });
}

} // namespace omm
Loading