VisualizationGrid.cpp
Go to the documentation of this file.
1#include "VisualizationGrid.h"
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6#include <limits>
7#include <vector>
8
10
12
13#include <Inventor/SbVec2s.h>
14#include <Inventor/SbVec3f.h>
15#include <Inventor/nodes/SoCoordinate3.h>
16#include <Inventor/nodes/SoFaceSet.h>
17#include <Inventor/nodes/SoSeparator.h>
18#include <Inventor/nodes/SoTexture2.h>
19#include <Inventor/nodes/SoTextureCoordinate2.h>
20#include <Inventor/nodes/SoTextureCoordinateBinding.h>
21
22namespace armarx::viz::coin
23{
24
26 {
27 // Note: the element's pose (origin) and the millimeter units are already
28 // applied by the base ElementVisualization (separator -> units -> transform).
29 // We must not apply the pose again here, otherwise it is doubled.
30 SoSeparator* pGrid = new SoSeparator;
31
32 pImagePlaneSoCoordinate3 = new SoCoordinate3;
33
34 SoFaceSet* pSoFaceSet = new SoFaceSet;
35 pSoFaceSet->numVertices.set1Value(0, 4);
36
37 // Texture coordinates mapping the grid texture onto the quad. The grid's
38 // X/Y axes are swapped relative to the texture (see extents below), so the
39 // mapping is: s along the quad's Y extent (texture width = sizeX), t along
40 // the quad's X extent (texture height = sizeY). Cell (0,0) is at UV (0,0),
41 // which is the (0,0) corner of the quad (vertex 2). No vertical flip is
42 // needed since SoSFImage stores data lower-left first.
43 SoTextureCoordinate2* pSoTextureCoordinate2 = new SoTextureCoordinate2;
44 pSoTextureCoordinate2->point.set1Value(0, SbVec2f(1.0f, 1.0f)); // vertex (X, Y)
45 pSoTextureCoordinate2->point.set1Value(1, SbVec2f(1.0f, 0.0f)); // vertex (0, Y)
46 pSoTextureCoordinate2->point.set1Value(2, SbVec2f(0.0f, 0.0f)); // vertex (0, 0)
47 pSoTextureCoordinate2->point.set1Value(3, SbVec2f(0.0f, 1.0f)); // vertex (X, 0)
48
49 SoTextureCoordinateBinding* pSoTextureCoordinateBinding = new SoTextureCoordinateBinding;
50 pSoTextureCoordinateBinding->value.setValue(SoTextureCoordinateBinding::PER_VERTEX);
51
52 pSoTexture2 = new SoTexture2;
53
54 pSoTexture2->wrapS = pSoTexture2->wrapT = SoTexture2::REPEAT;
55 pGrid->addChild(pSoTextureCoordinate2);
56 pGrid->addChild(pSoTextureCoordinateBinding);
57 pGrid->addChild(pSoTexture2);
58 pGrid->addChild(pImagePlaneSoCoordinate3);
59 pGrid->addChild(pSoFaceSet);
60 // pGrid->unrefNoDelete();
61
62 node->addChild(pGrid);
63 }
64
65 bool
67 {
68 // The element's pose (origin) is applied by the base ElementVisualization,
69 // so it must not be set here. The X/Y axis swap between the grid and the
70 // texture is baked into the quad extents and texture coordinates below.
71
72 // Validate the element before touching any scene graph data. An invalid
73 // grid (e.g. coming over the wire from a remote component) must never
74 // crash the visualizer, so we bail out gracefully instead.
75
76 // The grid dimensions must be strictly positive.
77 if (element.sizeX <= 0 || element.sizeY <= 0)
78 {
79 ARMARX_WARNING << deactivateSpam(1) << "Ignoring grid element with non-positive size ("
80 << element.sizeX << " x " << element.sizeY << ").";
81 return false;
82 }
83
84 // The dimensions are written into an SbVec2s (signed 16-bit) below, so
85 // they must fit into that range to avoid silent overflow / corruption.
86 constexpr int maxDimension = std::numeric_limits<std::int16_t>::max();
87 if (element.sizeX > maxDimension || element.sizeY > maxDimension)
88 {
89 ARMARX_WARNING << deactivateSpam(1) << "Ignoring grid element that is too large ("
90 << element.sizeX << " x " << element.sizeY << ", max is " << maxDimension
91 << " per dimension).";
92 return false;
93 }
94
95 // Compute the expected number of cells in 64-bit to avoid int overflow,
96 // then make sure the provided color data actually matches it.
97 const std::size_t sizeX = static_cast<std::size_t>(element.sizeX);
98 const std::size_t sizeY = static_cast<std::size_t>(element.sizeY);
99 const std::size_t expectedCellCount = sizeX * sizeY;
100
101 if (element.colors.size() != expectedCellCount)
102 {
104 << "Ignoring grid element with mismatched color data: "
105 << "expected " << expectedCellCount << " colors (" << element.sizeX
106 << " x " << element.sizeY << ") but got " << element.colors.size()
107 << ".";
108 return false;
109 }
110
111 // update grid extents
112 {
113 // intentionally swapped X and Y
114 const float X = element.sizeY * element.resolution;
115 const float Y = element.sizeX * element.resolution;
116
117 pImagePlaneSoCoordinate3->point.set1Value(0, SbVec3f(X, Y, 0.0f));
118 pImagePlaneSoCoordinate3->point.set1Value(1, SbVec3f(0, Y, 0.0f));
119 pImagePlaneSoCoordinate3->point.set1Value(2, SbVec3f(0, 0, 0.0f));
120 pImagePlaneSoCoordinate3->point.set1Value(3, SbVec3f(X, 0, 0.0f));
121 }
122
123
124 // update texture (grid values)
125 {
126 using Color = std::array<unsigned char, 4>; // RGBA
127
128 std::vector<Color> pixels(expectedCellCount);
129
130 for (std::size_t x = 0; x < sizeX; x++)
131 {
132 for (std::size_t y = 0; y < sizeY; y++)
133 {
134 const std::size_t idx = (x * sizeY) + y;
135
136 const auto& color = element.colors[idx];
137 pixels[idx] = Color{color.r, color.g, color.b, color.a};
138 }
139 }
140
141
142 pSoTexture2->image.setValue(SbVec2s{static_cast<std::int16_t>(element.sizeX),
143 static_cast<std::int16_t>(element.sizeY)},
144 4,
145 reinterpret_cast<const unsigned char*>(pixels.data()));
146 }
147
148
149 return true;
150 }
151} // namespace armarx::viz::coin
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.
bool update(ElementType const &element)