-
Notifications
You must be signed in to change notification settings - Fork 64
pilot: Extract shared paint_world_2d render routine (issue #754 PR1) #899
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,13 +28,14 @@ | |
|
|
||
| #include <modmesh/pilot/R2DWidget.hpp> // Must be the first include. | ||
|
|
||
| #include <modmesh/pilot/RPainter2d.hpp> | ||
|
|
||
| #include <cmath> | ||
|
|
||
| #include <QColor> | ||
| #include <QMouseEvent> | ||
| #include <QPaintEvent> | ||
| #include <QPainter> | ||
| #include <QPainterPath> | ||
| #include <QPen> | ||
| #include <QResizeEvent> | ||
| #include <QWheelEvent> | ||
|
|
@@ -59,11 +60,6 @@ QColor const BACKGROUND(32, 32, 36); | |
| QColor const MINOR_GRID(64, 64, 70); | ||
| QColor const AXIS(200, 200, 80); | ||
| QColor const ORIGIN(220, 80, 80); | ||
| QColor const GEOMETRY(120, 180, 240); | ||
|
|
||
| // Cosmetic (zoom-independent) screen widths for world geometry. | ||
| constexpr double GEOMETRY_LINE_WIDTH_PX = 1.5; | ||
| constexpr int GEOMETRY_POINT_WIDTH_PX = 5; | ||
|
|
||
| double clamp_zoom(double zoom) | ||
| { | ||
|
|
@@ -131,67 +127,6 @@ void R2DWidget::centerViewOnOrigin() | |
| m_view.set_pan_y(static_cast<double>(height()) * 0.5); | ||
| } | ||
|
|
||
| void R2DWidget::paintWorld(QPainter & painter) const | ||
| { | ||
| if (!m_world) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| // Map math-convention world (x, y) to Qt screen pixels; z is dropped. | ||
| auto map = [this](double world_x, double world_y) | ||
| { | ||
| double screen_x = 0.0; | ||
| double screen_y = 0.0; | ||
| m_view.screen_from_world(world_x, world_y, screen_x, screen_y); | ||
| return QPointF(screen_x, screen_y); | ||
| }; | ||
|
|
||
| // Segments and flattened curves share one cosmetic stroke pen. | ||
| QPen geom_pen(GEOMETRY); | ||
| geom_pen.setCosmetic(true); | ||
| geom_pen.setWidthF(GEOMETRY_LINE_WIDTH_PX); | ||
| painter.setPen(geom_pen); | ||
|
|
||
| // 1D straight segments | ||
| std::shared_ptr<SegmentPadFp64> segments = m_world->collect_live_segments(); | ||
| for (size_t i = 0; i < segments->size(); ++i) | ||
| { | ||
| painter.drawLine(map(segments->x0(i), segments->y0(i)), | ||
| map(segments->x1(i), segments->y1(i))); | ||
| } | ||
|
|
||
| // Cubic Beziers; QPainterPath flattens them adaptively, so no sampling. | ||
| std::shared_ptr<CurvePadFp64> curves = m_world->collect_live_curves(); | ||
| if (curves->size() > 0) | ||
| { | ||
| QPainterPath path; | ||
| for (size_t i = 0; i < curves->size(); ++i) | ||
| { | ||
| Bezier3dFp64 const c = curves->get(i); | ||
| path.moveTo(map(c.x0(), c.y0())); | ||
| path.cubicTo(map(c.x1(), c.y1()), map(c.x2(), c.y2()), map(c.x3(), c.y3())); | ||
| } | ||
| painter.setBrush(Qt::NoBrush); // stroke the outline only, never fill | ||
| painter.drawPath(path); | ||
| } | ||
|
|
||
| // 0D standalone points as dots with a fixed pixel size at any zoom. | ||
| std::shared_ptr<PointPadFp64> const & points = m_world->points(); | ||
| if (points->size() > 0) | ||
| { | ||
| QPen point_pen(GEOMETRY); | ||
| point_pen.setCosmetic(true); | ||
| point_pen.setWidth(GEOMETRY_POINT_WIDTH_PX); | ||
| point_pen.setCapStyle(Qt::RoundCap); | ||
| painter.setPen(point_pen); | ||
| for (size_t i = 0; i < points->size(); ++i) | ||
| { | ||
| painter.drawPoint(map(points->x(i), points->y(i))); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void R2DWidget::paintEvent(QPaintEvent * /*event*/) | ||
| { | ||
| QPainter painter(this); | ||
|
|
@@ -259,7 +194,10 @@ void R2DWidget::paintEvent(QPaintEvent * /*event*/) | |
| } | ||
|
|
||
| // World geometry on top of the grid, under the origin marker. | ||
| paintWorld(painter); | ||
| if (m_world) | ||
| { | ||
| paint_world_2d(painter, *m_world, m_view); | ||
| } | ||
|
Comment on lines
+198
to
+200
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| // Origin dot (cosmetic, fixed pixel size regardless of zoom). | ||
| QPen origin_pen(ORIGIN); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /* | ||
| * Copyright (c) 2026, An-Chi Liu <phy.tiger@gmail.com> | ||
| * | ||
| * Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are met: | ||
| * | ||
| * - Redistributions of source code must retain the above copyright notice, | ||
| * this list of conditions and the following disclaimer. | ||
| * - Redistributions in binary form must reproduce the above copyright notice, | ||
| * this list of conditions and the following disclaimer in the documentation | ||
| * and/or other materials provided with the distribution. | ||
| * - Neither the name of the copyright holder nor the names of its contributors | ||
| * may be used to endorse or promote products derived from this software | ||
| * without specific prior written permission. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
|
|
||
| #include <modmesh/pilot/RPainter2d.hpp> // Must be the first include. | ||
|
|
||
| #include <QColor> | ||
| #include <QPainter> | ||
| #include <QPainterPath> | ||
| #include <QPen> | ||
|
|
||
| namespace modmesh | ||
| { | ||
|
|
||
| namespace | ||
| { | ||
|
|
||
| QColor const GEOMETRY(120, 180, 240); | ||
|
|
||
| // Cosmetic (zoom-independent) screen widths for world geometry. | ||
| constexpr double GEOMETRY_LINE_WIDTH_PX = 1.5; | ||
| constexpr int GEOMETRY_POINT_WIDTH_PX = 5; | ||
|
|
||
| } // unnamed namespace | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's called anonymous namespace. "Unnamed" is unconventional.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. |
||
|
|
||
| void paint_world_2d(QPainter & painter, WorldFp64 const & world, ViewTransform2dFp64 const & view) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor the function to here. |
||
| { | ||
| // Map math-convention world (x, y) to Qt screen pixels; z is dropped. | ||
| auto map = [&view](double world_x, double world_y) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When a lambda/closure is used but not for a functional purpose, it is a sign that the enclosing code should be made as a class and the lambda should be a member function. But a function like this size ( A more extensible design is to make it a class now. By holding
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, let's make it a class directly. |
||
| { | ||
| double screen_x = 0.0; | ||
| double screen_y = 0.0; | ||
| view.screen_from_world(world_x, world_y, screen_x, screen_y); | ||
| return QPointF(screen_x, screen_y); | ||
| }; | ||
|
|
||
| // Segments and flattened curves share one cosmetic stroke pen. | ||
| QPen geom_pen(GEOMETRY); | ||
| geom_pen.setCosmetic(true); | ||
| geom_pen.setWidthF(GEOMETRY_LINE_WIDTH_PX); | ||
| painter.setPen(geom_pen); | ||
|
|
||
| // 1D straight segments | ||
| std::shared_ptr<SegmentPadFp64> segments = world.collect_live_segments(); | ||
| for (size_t i = 0; i < segments->size(); ++i) | ||
| { | ||
| painter.drawLine(map(segments->x0(i), segments->y0(i)), | ||
| map(segments->x1(i), segments->y1(i))); | ||
| } | ||
|
|
||
| // Cubic Beziers; QPainterPath flattens them adaptively, so no sampling. | ||
| std::shared_ptr<CurvePadFp64> curves = world.collect_live_curves(); | ||
| if (curves->size() > 0) | ||
| { | ||
| QPainterPath path; | ||
| for (size_t i = 0; i < curves->size(); ++i) | ||
| { | ||
| Bezier3dFp64 const c = curves->get(i); | ||
| path.moveTo(map(c.x0(), c.y0())); | ||
| path.cubicTo(map(c.x1(), c.y1()), map(c.x2(), c.y2()), map(c.x3(), c.y3())); | ||
| } | ||
| painter.setBrush(Qt::NoBrush); // stroke the outline only, never fill | ||
| painter.drawPath(path); | ||
| } | ||
|
|
||
| // 0D standalone points as dots with a fixed pixel size at any zoom. | ||
| std::shared_ptr<PointPadFp64> const & points = world.points(); | ||
| if (points->size() > 0) | ||
| { | ||
| QPen point_pen(GEOMETRY); | ||
| point_pen.setCosmetic(true); | ||
| point_pen.setWidth(GEOMETRY_POINT_WIDTH_PX); | ||
| point_pen.setCapStyle(Qt::RoundCap); | ||
| painter.setPen(point_pen); | ||
| for (size_t i = 0; i < points->size(); ++i) | ||
| { | ||
| painter.drawPoint(map(points->x(i), points->y(i))); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } /* end namespace modmesh */ | ||
|
|
||
| // vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #pragma once | ||
|
|
||
| /* | ||
| * Copyright (c) 2026, An-Chi Liu <phy.tiger@gmail.com> | ||
| * | ||
| * Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are met: | ||
| * | ||
| * - Redistributions of source code must retain the above copyright notice, | ||
| * this list of conditions and the following disclaimer. | ||
| * - Redistributions in binary form must reproduce the above copyright notice, | ||
| * this list of conditions and the following disclaimer in the documentation | ||
| * and/or other materials provided with the distribution. | ||
| * - Neither the name of the copyright holder nor the names of its contributors | ||
| * may be used to endorse or promote products derived from this software | ||
| * without specific prior written permission. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
|
|
||
| #include <modmesh/pilot/common_detail.hpp> // Must be the first include. | ||
|
|
||
| #include <modmesh/universe/ViewTransform2d.hpp> | ||
| #include <modmesh/universe/World.hpp> | ||
|
|
||
| class QPainter; | ||
|
|
||
| namespace modmesh | ||
| { | ||
|
|
||
| /** | ||
| * Paint a world's live points, segments, and curves into a QPainter in | ||
| * screen space, mapping math-convention world coordinates through the given | ||
| * 2D view transform. This is the shared routine used by both the on-screen | ||
| * R2DWidget and the offscreen image renderer, so neither path can drift from | ||
| * the other. It paints only the geometry; the caller owns the background, | ||
| * grid, axes, and any origin marker. | ||
| */ | ||
| void paint_world_2d(QPainter & painter, WorldFp64 const & world, ViewTransform2dFp64 const & view); | ||
|
|
||
| } /* end namespace modmesh */ | ||
|
|
||
| // vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the namd
RPainter2d. The names ofR3DWidgetandR2DWidgetshould follow. But "widget" is not informative. Do not rename hastily. Let's give it more thoughts.The only complain I have with "painter" is that it sounds like a pixel facility, like the Windows legacy suggested. But it's not a big deal. Just to share an instinctive feeling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed to
RWorldRenderer2d.