Skip to content

Commit 4f44fca

Browse files
committed
[core] example 010 shows how to define a new user optional component
1 parent 434b1cc commit 4f44fca

File tree

6 files changed

+180
-38
lines changed

6 files changed

+180
-38
lines changed

examples/core/010-mesh-new-user-component/bar_component.h

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#ifndef BAR_COMPONENT_H
2424
#define BAR_COMPONENT_H
2525

26+
#include <vclib/concepts/const_correctness.h>
27+
#include <vclib/concepts/mesh/components/component.h>
2628
#include <vclib/mesh/components/bases/component.h>
2729
#include <vclib/types.h>
2830

@@ -32,28 +34,27 @@
3234
* Due to the complexity of this class, VCLib provides a vcl::Component class
3335
* that can be used as a base class.
3436
*
35-
* In this example, the component stores two values: an integer that can be
36-
* accessed trough the member function `foo()`, and a double that can be
37-
* accessed trough the member function `bar()`.
37+
* In this example, the component stores two values: a double that can be
38+
* accessed trough the member function `bar()`, and a vector of uint that can be
39+
* accessed trough the member function `barVector()`.
3840
*/
3941

4042
// a component needs a concept, that allows to test if other elements/meshes
4143
// have the same component.
4244
// the concept should just check if the element/mesh has the member functions
4345
// that are part of the component class.
4446
template<typename T>
45-
concept HasBarComponent = requires (T t, const T& ct) {
46-
// accessor to the int value of the bar component, returns int&
47-
{ t.foo() } -> std::same_as<int&>;
48-
49-
// const accessor to the int value of the bar component
50-
{ ct.foo() } -> std::same_as<int>;
51-
52-
// accessor to the double value of the bar component, returns int&
53-
{ t.bar() } -> std::same_as<double&>;
54-
55-
// const accessor to the double value of the bar component
56-
{ ct.bar() } -> std::same_as<double>;
47+
concept HasBarComponent = requires (T&& obj) {
48+
// accessors to the bar component, for const and non-const objects
49+
{ obj.bar() } -> std::convertible_to<double>;
50+
{ obj.barVector() } -> std::convertible_to<std::vector<uint>>;
51+
52+
// non const requirements
53+
requires vcl::IsConst<T> || requires {
54+
// non-const accessors, return references
55+
{ obj.bar() } -> std::same_as<double&>;
56+
{ obj.barVector() } -> std::same_as<std::vector<uint>&>;
57+
};
5758
};
5859

5960
// if you want, you can also define a concept that checks if an element has
@@ -65,17 +66,17 @@ concept HasOptionalColor =
6566

6667
// define a constant uint that identifies the component (same idea used for
6768
// COMPONENT_ID in the FooComponent)
68-
inline static const uint BAR_COMPONENT = vcl::COMPONENTS_NUMBER + 0;
69+
inline static const uint BAR_COMPONENT = vcl::CompId::COMPONENTS_NUMBER + 1;
6970

7071
namespace detail {
7172

7273
// we wrap all the data stored by the component in a struct.
73-
// in this case we could use also a std::pair, but it doesn't matter
74+
// in this case we could use also a std::pair, but it doesn't matter.
7475
// the only important thing is to have all the data wrapped in a single type
7576
struct BarData
7677
{
77-
int foo;
7878
double bar;
79+
std::vector<uint> barVector;
7980
};
8081

8182
} // namespace detail
@@ -93,20 +94,26 @@ bool isBarComponentAvailableOn(const vcl::ElementOrMeshConcept auto& element);
9394
// we called it BarComponentT because BarComponent will be aliased without
9495
// template arguments later (and will be the horizontal component).
9596
//
96-
// template arguments are two:
97-
// - ElementType: void if the component will be horizontal, or the type of the
98-
// Element that will have the component if vertical;
99-
// - OPTIONAL: true if the component will be optional. Will be valid only if
97+
// template arguments are three:
98+
// - ElementType: the type of the Element that will have the component (could
99+
// be void if the component will always be horizontal and does not need to
100+
// access the derived Element type)
101+
// - VERTICAL: true if the component will be vertical. Will be valid only if
100102
// ElementType != void
103+
// - OPTIONAL: true if the component will be optional. Will be valid only if
104+
// VERTICAL == true
101105
//
102106
// The class inherits from the vcl::comp::Component class, that has a list of
103107
// template arguments.
104-
template<typename ElementType = void, bool OPTIONAL = false>
108+
template<
109+
typename ElementType = void,
110+
bool VERTICAL = false,
111+
bool OPTIONAL = false>
105112
class BarComponentT :
106113
public vcl::comp::Component<
107114

108115
// CRTP: the first argument is always the component class itself
109-
BarComponentT<ElementType, OPTIONAL>,
116+
BarComponentT<ElementType, VERTICAL, OPTIONAL>,
110117

111118
// ID of the component, same used in the FooComponent, but in this
112119
// case it is a template argument of the vcl::comp::Component class
@@ -119,16 +126,20 @@ class BarComponentT :
119126
// Same ElementType argument of the BarComponentT class
120127
ElementType,
121128

129+
// Same VERTICAL argument of the BarComponentT class
130+
VERTICAL,
131+
122132
// Same OPTIONAL argument of the BarComponentT class
123133
OPTIONAL>
124134
{
125135
// alias of the base vcl::comp::Component class - just for detail use
126136
// to access the member functions of the base class
127137
using Base = vcl::comp::Component<
128-
BarComponentT<ElementType, OPTIONAL>,
138+
BarComponentT<ElementType, VERTICAL, OPTIONAL>,
129139
BAR_COMPONENT,
130140
detail::BarData,
131141
ElementType,
142+
VERTICAL,
132143
OPTIONAL>;
133144

134145
public:
@@ -137,25 +148,28 @@ class BarComponentT :
137148
// we access the data stored by calling Base::data(). The returned type
138149
// is the same of the third template argument of the vcl::comp::Component
139150
// class -- in this case, detail::BarData
140-
int& foo() { return Base::data().foo; }
141-
142-
int foo() const { return Base::data().foo; }
143-
144151
double& bar() { return Base::data().bar; }
145152

146153
double bar() const { return Base::data().bar; }
147154

155+
std::vector<uint>& barVector() { return Base::data().barVector; }
156+
157+
const std::vector<uint>& barVector() const
158+
{
159+
return Base::data().barVector;
160+
}
161+
148162
protected:
149-
// first requirement: a protected `importFrom` member function
163+
// the protected `importFrom` member function
150164
template<typename Element> // another element, maybe from another mesh type
151165
void importFrom(const Element& e, bool = true)
152166
{
153167
// will import the bar component from the element e only if it also has
154168
// the bar component
155169
if constexpr (HasBarComponent<Element>) { // compile time check
156170
if (isBarComponentAvailableOn(e)) { // runtime check
157-
foo() = e.foo();
158171
bar() = e.bar();
172+
barVector() = e.barVector();
159173
}
160174
}
161175
}
@@ -173,10 +187,14 @@ bool isBarComponentAvailableOn(const vcl::ElementOrMeshConcept auto& element)
173187
using BarComponent = BarComponentT<>; // horizontal component
174188

175189
template<typename ElementType>
176-
using VerticalBarComponent = BarComponentT<ElementType>; // vertical component
190+
using VerticalBarComponent = BarComponentT<ElementType, true>; // vertical
177191

178192
template<typename ElementType>
179-
using OptionalBarComponent = BarComponentT<ElementType, true>; // optional
193+
using OptionalBarComponent = BarComponentT<ElementType, true, true>; // optional
194+
195+
static_assert(
196+
vcl::comp::ComponentConcept<BarComponent>,
197+
"Make sure that the BarComponent satisfies the ComponentConcept.");
180198

181199
static_assert(
182200
HasBarComponent<BarComponent>,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*****************************************************************************
2+
* VCLib *
3+
* Visual Computing Library *
4+
* *
5+
* Copyright(C) 2021-2025 *
6+
* Visual Computing Lab *
7+
* ISTI - Italian National Research Council *
8+
* *
9+
* All rights reserved. *
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the Mozilla Public License Version 2.0 as published *
13+
* by the Mozilla Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
* This program is distributed in the hope that it will be useful, *
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19+
* Mozilla Public License Version 2.0 *
20+
* (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21+
****************************************************************************/
22+
23+
#ifndef BAR_MESH_H
24+
#define BAR_MESH_H
25+
26+
#include <vclib/mesh/mesh.h>
27+
#include <vclib/mesh/requirements.h>
28+
29+
#include "bar_component.h"
30+
31+
/*
32+
* This file defines a BarMesh class that uses the optional BarComponent in the
33+
* Vertex Element.
34+
*/
35+
36+
class BarMesh;
37+
38+
namespace barmesh {
39+
40+
class Vertex;
41+
42+
class Face;
43+
44+
class Vertex :
45+
public vcl::Vertex<
46+
BarMesh,
47+
vcl::vert::BitFlags,
48+
vcl::vert::Coordinate3d,
49+
vcl::vert::Normal3d,
50+
vcl::vert::Color,
51+
OptionalBarComponent<Vertex>> // the optional BarComponent
52+
{
53+
};
54+
55+
class Face :
56+
public vcl::Face<
57+
BarMesh,
58+
vcl::face::TriangleBitFlags,
59+
vcl::face::TriangleVertexPtrs<Vertex, Face>,
60+
vcl::face::Normal3d>
61+
{
62+
};
63+
64+
} // namespace barmesh
65+
66+
class BarMesh :
67+
public vcl::Mesh<
68+
vcl::mesh::VertexContainer<barmesh::Vertex>,
69+
vcl::mesh::FaceContainer<barmesh::Face>,
70+
vcl::mesh::BoundingBox3d>
71+
{
72+
public:
73+
using ScalarType = double;
74+
};
75+
76+
#endif // BAR_MESH_H

examples/core/010-mesh-new-user-component/foo_component.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#ifndef FOO_COMPONENT_H
2424
#define FOO_COMPONENT_H
2525

26+
#include <vclib/concepts/const_correctness.h>
27+
#include <vclib/concepts/mesh/components/component.h>
2628
#include <vclib/types.h>
2729

2830
/*
@@ -36,12 +38,15 @@
3638
// the concept should just check if the element/mesh has the member functions
3739
// that are part of the component class.
3840
template<typename T>
39-
concept HasFooComponent = requires (T t, const T& ct) {
40-
// accessor to the foo component, returns int&
41-
{ t.foo() } -> std::same_as<int&>;
41+
concept HasFooComponent = requires (T&& obj) {
42+
// accessor to the foo component, for const and non-const objects
43+
{ obj.foo() } -> std::convertible_to<int>;
4244

43-
// const accessor to the foo component
44-
{ ct.foo() } -> std::same_as<int>;
45+
// non const requirements
46+
requires vcl::IsConst<T> || requires {
47+
// non-const accessor, returns int&
48+
{ obj.foo() } -> std::same_as<int&>;
49+
};
4550
};
4651

4752
// class of the Foo component
@@ -78,6 +83,21 @@ class FooComponent
7883
int data;
7984
};
8085

86+
namespace vcl {
87+
88+
// specialize the ComponentString for the FooComponent
89+
template<>
90+
struct ComponentString<FooComponent::COMPONENT_ID>
91+
{
92+
const char* str = "FooComponent";
93+
};
94+
95+
} // namespace vcl
96+
97+
static_assert(
98+
vcl::comp::ComponentConcept<FooComponent>,
99+
"Make sure that the FooComponent satisfies the ComponentConcept.");
100+
81101
static_assert(
82102
HasFooComponent<FooComponent>,
83103
"Make sure that the concept is satisfied with its component.");

examples/core/010-mesh-new-user-component/foo_mesh.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <vclib/mesh/requirements.h>
2828

2929
#include "foo_component.h"
30+
#include "bar_component.h"
3031

3132
/*
3233
* This file defines a FooMesh class that uses the FooComponent in the Vertex

examples/core/010-mesh-new-user-component/main.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
****************************************************************************/
2222

2323
#include "foo_mesh.h"
24+
#include "bar_mesh.h"
2425

2526
#include <vclib/meshes/tri_mesh.h>
2627

2728
int main(int argc, char** argv)
2829
{
30+
// A mesh with a Vertex that has a FooComponent
2931
FooMesh m;
3032

3133
m.addVertices(10);
@@ -42,4 +44,27 @@ int main(int argc, char** argv)
4244

4345
// reverse importFrom, to assert that everything builds correctly
4446
m.importFrom(tm);
47+
48+
// A mesh with a Vertex that has a optional BarComponent
49+
BarMesh bm;
50+
51+
bm.addVertices(10);
52+
53+
// enable the bar component in the vertices
54+
bm.enablePerElementComponent<vcl::ElemId::VERTEX, BAR_COMPONENT>();
55+
56+
for (auto& v : bm.vertices()) {
57+
v.bar() = bm.index(v) + 42;
58+
v.barVector() = {1, 2, 3};
59+
}
60+
61+
for (auto& v : bm.vertices()) {
62+
std::cout << "Vertex: " << v.index() << std::endl;
63+
std::cout << "Bar: " << v.bar() << std::endl;
64+
std::cout << "Bar vector: ";
65+
for (auto i : v.barVector()) {
66+
std::cout << i << ' ';
67+
}
68+
std::cout << std::endl;
69+
}
4570
}

vclib/core/todo.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,5 @@
6262
- [ ] look for a json header only library that is compatible with c++20 modules
6363
- Documentation:
6464
- [ ] How to implement a user component
65+
- Examples:
66+
- [ ] Create an example for new custom element (similar to 010-mesh-new-user-component)

0 commit comments

Comments
 (0)