12 const QString imagePrefix =
"test_geometry_";
13 static bool generatedOnce =
false;
19 qWarning() <<
"Generating Test Geometry Images";
21 const QList<ActuatorGeometry> geometries[] = {
71 const QSize sizes[] = {
78 for (
unsigned sizeIdx = 0; sizeIdx <
sizeof(sizes) /
sizeof(sizes[0]); ++sizeIdx) {
79 const QSize& size = sizes[sizeIdx];
80 for (
unsigned geometryIdx = 0; geometryIdx <
sizeof(geometries) /
sizeof(geometries[0]); ++geometryIdx) {
81 provider.actuators() = geometries[geometryIdx];
82 QPixmap pixmap = provider.requestPixmap(
"",
nullptr, size);
84 QString imageFileName = QDir(QDir::currentPath()).filePath(imagePrefix + QString::number(sizeIdx)+
"_"
85 +QString::number(geometryIdx)+
".png");
86 qWarning() <<
"Generating image" << imageFileName;
87 QFile file(imageFileName);
88 file.open(QIODevice::WriteOnly);
89 pixmap.save(&file,
"PNG");
104 const float lineLength = fontSize * 2.f;
105 const float arrowWidth = 6.f;
106 const float arrowHeight = 8.f;
108 p.setPen(QPen{color, 1.5f});
111 QFont font = p.font();
112 font.setPixelSize(fontSize);
115 auto drawArrow = [&](
const QPointF& start,
const QPointF& end) {
116 float arrowLineLength = QLineF{start, end}.length();
119 float angle = atan2f(end.y()-start.y(), end.x()-start.x());
120 p.rotate(angle * (180.f / M_PI) + 90.f);
121 p.drawLine(QPointF{0, arrowHeight/2}, QPointF{0, arrowLineLength});
123 QPointF{0.f - arrowWidth/2.f, arrowHeight},
125 QPointF{0.f + arrowWidth/2.f, arrowHeight},
127 p.drawConvexPolygon(arrow,
sizeof(arrow) /
sizeof(arrow[0]));
133 QPointF endPos{origin.x(), origin.y() - lineLength};
134 drawArrow(origin, endPos);
138 QRectF textRect{-lineLength, -lineLength, lineLength * 2.f, lineLength};
139 p.drawText(textRect, Qt::AlignHCenter | Qt::AlignBottom,
"x");
145 QPointF endPos{origin.x() + lineLength, origin.y()};
146 drawArrow(origin, endPos);
150 QRectF textRect{0, -lineLength / 2, lineLength, lineLength};
151 p.drawText(textRect, Qt::AlignLeft | Qt::AlignBaseline,
" y");
161 _imageSize = requestedSize;
162 int width = requestedSize.width();
163 int height = requestedSize.height();
165 *size = QSize(width, height);
167 QPixmap pixmap(width, height);
168 pixmap.fill(Qt::transparent);
170 _actuatorImagePositions.clear();
173 QVector3D min{1e10f, 1e10f, 1e10f};
174 QVector3D max{-1e10f, -1e10f, -1e10f};
175 for (
const auto& actuator : _actuators) {
176 for (
int i = 0; i < 3; ++i) {
177 if (actuator.position[i] < min[i]) {
178 min[i] = actuator.position[i];
180 if (actuator.position[i] > max[i]) {
181 max[i] = actuator.position[i];
186 if (_actuators.size() <= 1 || max.x() - min.x() < 0.0001f || max.y() - min.y() < 0.0001f ) {
192 QList<ActuatorGeometry> coaxActuators;
193 for (
const auto& actuator : _actuators) {
196 for (
const auto& actuatorBefore : _actuators) {
198 if (&actuatorBefore == &actuator) {
201 QVector2D diff = actuatorBefore.position.toVector2D() - actuator.position.toVector2D();
202 if (diff.length() < 0.03f) {
203 coaxActuators.append(actuator);
216 p.setRenderHint(QPainter::Antialiasing);
217 p.setRenderHint(QPainter::TextAntialiasing);
219 const float axisIndicatorSize = 15.f;
220 const float margin = 5.f;
223 float usableWidth = width - 2.f * margin;
224 float usableHeight = height - 2.f * margin;
225 float extraOffsetX = 0.f;
227 if (width < height + axisIndicatorSize * 4.f) {
228 usableWidth -= axisIndicatorSize;
229 usableHeight -= axisIndicatorSize;
230 extraOffsetX = axisIndicatorSize;
233 const float rotorDiameter = std::min(usableWidth, usableHeight) * (_actuators.length() <= 6 ? 0.31f : 0.25f);
234 const float fontSize = rotorDiameter * 0.4f;
235 const float extraYMargin = coaxActuators.length() > 0 ? fontSize * 1.1f : 0.f;
237 const float scaleX = (usableWidth - rotorDiameter) / (max.y() - min.y());
238 const float scaleY = (usableHeight - extraYMargin - rotorDiameter) / (max.x() - min.x());
239 const float scale = std::min(scaleX, scaleY);
240 const float offsetX = margin + extraOffsetX + usableWidth / 2.f - (max.y() + min.y()) / 2.f * scale;
241 const float offsetY = margin + (usableHeight - extraYMargin) / 2.f + (max.x() + min.x()) / 2.f * scale;
244 const QColor clockWiseColor{ 21, 158, 31, 200 };
245 const QColor counterClockWiseColor{ 78, 195, 232, 200 };
246 const QColor frameArrowColor{ 255, 68, 43, 200 };
247 const QColor frameColor{ 150, 150, 150 };
248 const float frameWidth{ 6.f };
249 const float rotorFontSize{ rotorDiameter * 0.4f };
250 const QColor rotorHighlightColor{ frameArrowColor };
251 const QColor fontColor{ _palette.text() };
253 auto iterateMotors = [scale, offsetX, offsetY](
const QList<ActuatorGeometry> &actuatorsList,
255 for (
const auto& actuator : actuatorsList) {
258 offsetX + actuator.position.y()*scale,
259 offsetY - actuator.position.x()*scale
268 p.setPen(QPen{frameColor, frameWidth});
269 p.drawLine(QPointF{offsetX, offsetY}, pos);
273 p.setPen(frameColor);
274 p.setBrush(QBrush{frameColor});
275 float centerSize = rotorDiameter * 0.8f;
276 p.drawRoundedRect(QRectF{offsetX - centerSize / 2.f, offsetY - centerSize / 2.f, centerSize, centerSize}, frameWidth, frameWidth);
277 p.setPen(frameArrowColor);
278 p.setBrush(frameArrowColor);
279 float arrowWidth = rotorDiameter / 4.f;
280 float arrowHeight = rotorDiameter / 2.f;
282 QPointF{offsetX - arrowWidth / 2.f, offsetY + arrowHeight / 2.f},
283 QPointF{offsetX, offsetY - arrowHeight / 2.f},
284 QPointF{offsetX + arrowWidth / 2.f, offsetY + arrowHeight / 2.f},
286 p.drawConvexPolygon(arrow,
sizeof(arrow) /
sizeof(arrow[0]));
288 drawAxisIndicator(p, QPointF{axisIndicatorSize / 2.f, height - axisIndicatorSize / 2.f}, axisIndicatorSize, fontColor);
290 auto drawMotor = [&](
const ActuatorGeometry& actuator, QPointF pos,
float yPosOffset,
bool labelAtBottom) {
294 p.setBrush(QBrush{clockWiseColor});
295 arrowColor = clockWiseColor;
297 p.setBrush(QBrush{counterClockWiseColor});
298 arrowColor = counterClockWiseColor;
300 arrowColor.setAlpha(255);
302 arrowColor = arrowColor.darker(200);
304 arrowColor = arrowColor.lighter(130);
307 pos.setY(pos.y() + yPosOffset);
309 p.drawEllipse(pos, rotorDiameter/2, rotorDiameter/2);
313 textRect = QRectF{pos.x()-rotorDiameter/2.f, pos.y()+rotorDiameter/2.f-yPosOffset, rotorDiameter, yPosOffset};
315 textRect = QRectF{pos.x()-rotorDiameter/2.f, pos.y()-rotorDiameter/2.f, rotorDiameter, rotorDiameter};
317 if (actuator.renderOptions.highlight) {
318 p.setPen(rotorHighlightColor);
319 p.setBrush(rotorHighlightColor);
322 radius = rotorFontSize / 2.f;
324 radius = rotorDiameter / 2.f;
326 p.drawEllipse(textRect.center(), radius, radius);
327 _actuatorImagePositions.append(
ImagePosition{actuator.
type, actuator.index, textRect.center(), radius});
330 QFont font = p.font();
331 font.setPixelSize(rotorFontSize);
333 p.drawText(textRect, Qt::AlignCenter, QString::number(actuator.index + actuator.labelIndexOffset));
337 float spinArrowWidth = frameWidth;
338 float spinArrowHeight = frameWidth * 1.25f;
339 float arrowPosition = rotorDiameter / 2.f;
340 p.setPen(QPen{arrowColor, 2.5f});
341 p.setBrush(arrowColor);
342 std::array<int, 2> angleOffsets;
344 angleOffsets = {30, 180-30};
346 angleOffsets = {0, 180};
348 for (
int angleOffset : angleOffsets) {
353 p.rotate(angle / 2.f + angleOffset);
356 p.rotate(-angle / 2.f + angleOffset);
358 QRectF arrowRect{-arrowPosition, -arrowPosition, arrowPosition * 2.f, arrowPosition * 2.f};
359 p.drawArc(arrowRect, 0, -ySign * 16 * angle);
360 QPointF spinArrow[3] = {
361 QPointF{arrowPosition - spinArrowWidth/2.f, ySign*spinArrowHeight/2.f},
362 QPointF{arrowPosition, -ySign*spinArrowHeight/2.f},
363 QPointF{arrowPosition + spinArrowWidth/2.f, ySign*spinArrowHeight/2.f},
365 p.drawConvexPolygon(spinArrow,
sizeof(spinArrow) /
sizeof(spinArrow[0]));
371 iterateMotors(coaxActuators, [&](
const ActuatorGeometry& actuator, QPointF pos) {
372 drawMotor(actuator, pos, extraYMargin,
true);
377 drawMotor(actuator, pos, 0.f,
false);