2525#include < ccScalarField.h>
2626#include < climits>
2727#include < cstdint>
28+ #include < pybind11/detail/common.h>
2829#include < pybind11/pytypes.h>
30+ #include < stdexcept>
2931#include < type_traits>
3032#include < vector>
3133
@@ -43,6 +45,60 @@ static_assert(std::is_same<PointCoordinateType, float>::value,
4345static_assert (sizeof (CCVector3) == sizeof (PointCoordinateType) * 3 ,
4446 " Unexpected layout for CCVector3" );
4547
48+ void ccPointCloud_setColors (ccPointCloud &self,
49+ const py::array_t <ColorCompType> &r,
50+ const py::array_t <ColorCompType> &g,
51+ const py::array_t <ColorCompType> &b,
52+ const py::array_t <ColorCompType> *a)
53+ {
54+ if (r.ndim () != 1 || g.ndim () != 1 || b.ndim () != 1 || (a && a->ndim () != 1 ))
55+ {
56+ throw py::value_error (" arrays must be one dimensional" );
57+ }
58+
59+ if (r.size () != g.size () || r.size () != b.size () || a && r.size () != a->size ())
60+ {
61+ throw py::value_error (" r, g, b must have the same size" );
62+ }
63+
64+ if (r.size () != self.size ())
65+ {
66+ throw py::value_error (" The number of colors must match the number of points" );
67+ }
68+
69+ if (!self.resizeTheRGBTable ())
70+ {
71+ throw std::runtime_error (" failed to allocate rgb table" );
72+ }
73+
74+ py::iterator a_begin;
75+ if (a)
76+ {
77+ a_begin = a->begin ();
78+ }
79+
80+ auto r_it = r.begin ();
81+ auto g_it = g.begin ();
82+ auto b_it = b.begin ();
83+ auto *a_it = a ? &a_begin : nullptr ;
84+
85+ for (size_t i = 0 ; i < self.size (); ++i)
86+ {
87+ const ccColor::Rgba value (r_it->cast <ColorCompType>(),
88+ g_it->cast <ColorCompType>(),
89+ b_it->cast <ColorCompType>(),
90+ a_it ? a_it->cast <ColorCompType>() : ccColor::MAX);
91+ self.setPointColor (i, value);
92+ ++r_it;
93+ ++g_it;
94+ ++b_it;
95+ if (a_it)
96+ {
97+ ++(*a_it);
98+ }
99+ }
100+ }
101+
46102void define_ccPointCloud (py::module &m)
47103{
48104 DEFINE_POINTCLOUDTPL (ccGenericPointCloud, QString, m, " __ccGenericPointCloudTpl" );
@@ -123,8 +179,8 @@ void define_ccPointCloud(py::module &m)
123179 " reference" _a)
124180 // features allocation/resize
125181 .def (" reserveThePointsTable" , &ccPointCloud::reserveThePointsTable, " _numberOfPoints" _a)
126- .def (" reserveTheRGBTable" , &ccPointCloud::reserveThePointsTable )
127- .def (" reserveTheRGBTable " , &ccPointCloud::reserveThePointsTable , " fillWithWhite" _a = false )
182+ .def (" reserveTheRGBTable" , &ccPointCloud::reserveTheRGBTable )
183+ .def (" resizeTheRGBTable " , &ccPointCloud::resizeTheRGBTable , " fillWithWhite" _a = false )
128184 .def (" reserveTheNormsTable" , &ccPointCloud::reserveTheNormsTable)
129185 .def (" resizeTheNormsTable" , &ccPointCloud::resizeTheNormsTable)
130186 .def (" shrinkToFit" , &ccPointCloud::shrinkToFit)
@@ -161,14 +217,180 @@ void define_ccPointCloud(py::module &m)
161217 &ccPointCloud::orientNormalsWithFM,
162218 " level" _a,
163219 " pDlg" _a = nullptr )
164- // Special functions added by pycc
165220 .def (" addPoints" , &PyCC::addPointsFromArrays<ccPointCloud>)
166- .def (" setColor" ,
167- [](ccPointCloud &self,
168- ColorCompType r,
169- ColorCompType g,
170- ColorCompType b,
171- ColorCompType a) { self.setColor (r, g, b, a); })
221+ .def (
222+ " setColor" ,
223+ [](ccPointCloud &self,
224+ ColorCompType r,
225+ ColorCompType g,
226+ ColorCompType b,
227+ ColorCompType a) { self.setColor (r, g, b, a); },
228+ " r" _a,
229+ " g" _a,
230+ " b" _a,
231+ " a" _a = ccColor::MAX,
232+ R"doc(
233+ Sets the color for the whole point cloud
234+ )doc" )
235+ .def (
236+ " setPointColor" ,
237+ [](ccPointCloud &self, const unsigned index, const ccColor::Rgb &color)
238+ {
239+ if (!self.hasColors ())
240+ {
241+ throw std::runtime_error (" colors needs to be enabled first" );
242+ }
243+ if (index >= self.size ())
244+ {
245+ throw py::index_error (" invalid index" );
246+ }
247+ self.setPointColor (index, color);
248+ },
249+ " index" _a,
250+ " color" _a,
251+ R"doc(
252+ Sets the color of the point at the given index with the new value
253+
254+ Colors must have been enabled before
255+ )doc" )
256+ .def (
257+ " setPointColor" ,
258+ [](ccPointCloud &self, const unsigned index, const ccColor::Rgba &color)
259+ {
260+ if (!self.hasColors ())
261+ {
262+ throw std::runtime_error (" colors needs to be enabled first" );
263+ }
264+
265+ if (index >= self.size ())
266+ {
267+ throw py::index_error (" invalid index" );
268+ }
269+ self.setPointColor (index, color);
270+ },
271+ " index" _a,
272+ " color" _a,
273+ R"doc(
274+ Sets the color of the point at the given index with the new value
275+
276+ Colors must have been enabled before
277+ )doc" )
278+ .def (
279+ " getPointColor" ,
280+ [](ccPointCloud &self, const unsigned index)
281+ {
282+ if (!self.hasColors ())
283+ {
284+ throw std::runtime_error (" colors needs to be enabled first" );
285+ }
286+
287+ if (index >= self.size ())
288+ {
289+ throw py::index_error (" invalid index" );
290+ }
291+ return self.getPointColor (index);
292+ },
293+ " index" _a,
294+ R"doc(
295+ Returns the color of the point at the given index
296+
297+ Colors must have been enabled before
298+ )doc" )
299+ .def (" setColors" ,
300+ &ccPointCloud_setColors,
301+ " r" _a,
302+ " g" _a,
303+ " b" _a,
304+ " a" _a = nullptr ,
305+ R"doc(
306+ Sets the colors in the point cloud with the given r, g, b arrays
307+ (and the optinal alpha array).
308+
309+ Enables colors if not done already
310+ )doc" )
311+ .def (
312+ " setColors" ,
313+ [](ccPointCloud &self, const py::array_t <ColorCompType> &colors)
314+ {
315+ if (colors.ndim () != 2 )
316+ {
317+ throw py::value_error (" color array must be 2 dimensional" );
318+ }
319+
320+ if (colors.shape (0 ) != self.size ())
321+ {
322+ throw py::value_error (" The number of colors must match the number of points" );
323+ }
324+
325+ if (colors.shape (1 ) != 3 && colors.shape (1 ) != 4 )
326+ {
327+ throw py::value_error (" Colors must be RGB or RGBA" );
328+ }
329+
330+ if (!self.resizeTheRGBTable ())
331+ {
332+ throw std::runtime_error (" failed to allocate rgb table" );
333+ }
334+
335+ if (colors.shape (1 ) == 3 )
336+ {
337+ auto rgb = colors.unchecked <2 >();
338+ for (size_t i = 0 ; i < self.size (); ++i)
339+ {
340+ self.setPointColor (i, ccColor::Rgb (rgb (i, 0 ), rgb (i, 1 ), rgb (i, 2 )));
341+ }
342+ }
343+ else if (colors.shape (1 ) == 4 )
344+ {
345+ auto rgba = colors.unchecked <2 >();
346+ for (size_t i = 0 ; i < self.size (); ++i)
347+ {
348+ self.setPointColor (
349+ i, ccColor::Rgba (rgba (i, 0 ), rgba (i, 1 ), rgba (i, 2 ), rgba (i, 3 )));
350+ }
351+ }
352+ },
353+ " colors" _a,
354+ R"doc(
355+ Sets the colors in the point cloud with the given r, g, b arrays
356+ (and the optinal alpha array).
357+
358+ Enables colors if not done already
359+ )doc" )
360+ .def (
361+ " colors" ,
362+ [](ccPointCloud &self) -> py::object
363+ {
364+ RGBAColorsTableType *colorsTable = self.rgbaColors ();
365+ if (colorsTable == nullptr )
366+ {
367+ return py::none ();
368+ }
369+
370+ ccColor::Rgba *colors = colorsTable->data ();
371+ if (colors == nullptr )
372+ {
373+ return py::none ();
374+ }
375+
376+ static_assert (CHAR_BIT == 8 , " A char must have 8 bits" );
377+ static_assert (sizeof (ColorCompType) == 1 , " ColorCompType must have 8 bit" );
378+ static_assert (sizeof (ccColor::Rgba) == 4 * sizeof (ColorCompType), " " );
379+ static_assert (alignof (uint8_t ) == alignof (ccColor::Rgba), " Not same alignment" );
380+
381+ auto *ptr = reinterpret_cast <uint8_t *>(colors);
382+
383+ auto capsule = py::capsule (ptr, [](void *) {});
384+ py::array a (py::dtype (" 4u1" ), colorsTable->size (), ptr, capsule);
385+
386+ return a;
387+ },
388+ R"doc(
389+ "Returns a the colors as a "view" in a numpy array
390+ if the point cloud does not have colors, None is returned
391+
392+ As this is a "view", modifications made to this will reflect on the point cloud
393+ )doc" )
172394 .def (" setPointNormal" , &ccPointCloud::setPointNormal, " index" _a, " normal" _a)
173395 .def (" addNorm" , &ccPointCloud::addNorm, " normal" _a)
174396 .def (" addNormals" ,
@@ -207,33 +429,6 @@ void define_ccPointCloud(py::module &m)
207429 .def (" showNormalsAsLines" , &ccPointCloud::showNormalsAsLines, " state" _a)
208430 .def (" colorize" , &ccPointCloud::colorize)
209431 .def (" crop2D" , &ccPointCloud::crop2D, " poly" _a, " orthodDim" _a, " inside" _a = true )
210- .def (" colors" ,
211- [](ccPointCloud &self) -> py::object
212- {
213- RGBAColorsTableType *colorsTable = self.rgbaColors ();
214- if (colorsTable == nullptr )
215- {
216- return py::none ();
217- }
218-
219- ccColor::Rgba *colors = colorsTable->data ();
220- if (colors == nullptr )
221- {
222- return py::none ();
223- }
224-
225- static_assert (CHAR_BIT == 8 , " A char must have 8 bits" );
226- static_assert (sizeof (ColorCompType) == 1 , " ColorCompType must have 8 bit" );
227- static_assert (sizeof (ccColor::Rgba) == 4 * sizeof (ColorCompType), " " );
228- static_assert (alignof (uint8_t ) == alignof (ccColor::Rgba), " Not same alignment" );
229-
230- auto *ptr = reinterpret_cast <uint8_t *>(colors);
231-
232- auto capsule = py::capsule (ptr, [](void *) {});
233- py::array a (py::dtype (" 4u1" ), colorsTable->size (), ptr, capsule);
234-
235- return a;
236- })
237432 .def (" points" ,
238433 [](ccPointCloud &self)
239434 {
0 commit comments