[education/kstars] /: New feature: Allow the user to mirror the sky map

Akarsh Simha null at kde.org
Sun Feb 18 13:48:37 GMT 2024


Git commit af76a2a8b548d87c8e8b383c8c2a6cec1fe5267f by Akarsh Simha.
Committed on 18/02/2024 at 13:48.
Pushed by asimha into branch 'master'.

New feature: Allow the user to mirror the sky map

With a new option in the "View" menu, the user will be able to mirror the sky map so as to be able to match the view through an erecting prism used for example on a Schmidt-Cassegrain or Refracting type telescope.

The rotation feature overlay now also marks East in addition to north and zenith, so as to know easily whether the display is mirrored or not.

M  +5    -2    doc/config.docbook
M  +1    -0    kstars/data/kstarsui.rc
M  +1    -1    kstars/kstars.cpp
M  +5    -0    kstars/kstars.kcfg
M  +5    -0    kstars/kstarsactions.cpp
M  +5    -3    kstars/kstarsinit.cpp
M  +3    -0    kstars/projections/projector.cpp
M  +7    -4    kstars/projections/projector.h
M  +1    -4    kstars/skymap.cpp
M  +53   -48   kstars/skymapdrawabstract.cpp

https://invent.kde.org/education/kstars/-/commit/af76a2a8b548d87c8e8b383c8c2a6cec1fe5267f

diff --git a/doc/config.docbook b/doc/config.docbook
index f3c2f225dc..a65cf581f1 100644
--- a/doc/config.docbook
+++ b/doc/config.docbook
@@ -1621,13 +1621,15 @@ from the <menuchoice><guimenu>Settings</guimenu><guisubmenu>FOV Symbols</guisubm
 <sect1 id="skymap_orientation">
   <title>Adjusting orientation of the sky map</title>
   <para>
-    You can tweak various settings to make the orientation of the sky map match the view through your optical instrument, provided (as of this version) the instrument does not mirror the field-of-view (as is done by prisms used with SCTs and refractors).
+    You can tweak various settings to make the orientation of the sky map match the view through your optical instrument.
   </para>
   <para>
     First, pick the coordinate system that matches your mount. For an equatorially mounted instrument, switch to the Equatorial Coordinate mode in the <guimenu>View</guimenu> menu. The option to toggle the coordinate system should read <guilabel>Switch to Horizontal View (Horizontal Coordinates)</guilabel> when the current mode is Equatorial Coordinates. For an altazimuth-mounted instrument or naked-eye viewing, switch to Horizontal Coordinates, so that the option in the <guimenu>View</guimenu> menu reads <guilabel>Switch to Star Globe View (Equatorial Coordinates)</guilabel>. This sets the base coordinate system used to render the sky map, and also sets the reference for the orientation of the skymap: zenith or north.
   </para>
   <para>
-    To rotate the sky map freely, you can hold down the &Shift; key and drag the mouse on the sky map. A temporary overlay will appear showing the direction of north and zenith at the point, and displaying the angle they make with the vertical in a counterclockwise sense. The orientations of zenith and north will update as you rotate the sky map. Letting go of &Shift; or the mouse button will stop the rotation operation. As you pan the sky map or focus it on different objects, the rotation you set is retained as an offset from the reference direction. The reference direction is north when using Equatorial Coordinates and zenith when using Horizontal Coordinates. As a reminder, the reference direction is solid and brighter in the temporary overlay. For the two common orientations of erect and inverted, the rotation can be set / reset using the <menuchoice><guimenu>View</guimenu><guisubmenu>Skymap Orientation</guisubmenu></menuchoice> submenu. Select "North Down" or "Zenith Down" as is applicable to set an orientation of 180 degrees.
+    If your instrument is using an erecting prism, typically used on Schmidt-Cassegrain and refracting type telescopes, the view through the eyepiece will be mirrored horizontally. You can have the sky map match this by checking the <guilabel>Mirrored View</guilabel> option under the <guimenu>View</guimenu> menu.
+  <para>
+    Next, to rotate the sky map freely, you can hold down the &Shift; key and drag the mouse on the sky map. A temporary overlay will appear showing the direction of north and zenith at the point, and displaying the angle they make with the vertical in a counterclockwise sense. The orientations of zenith and north will update as you rotate the sky map. Letting go of &Shift; or the mouse button will stop the rotation operation. As you pan the sky map or focus it on different objects, the rotation you set is retained as an offset from the reference direction. The reference direction is north when using Equatorial Coordinates and zenith when using Horizontal Coordinates. As a reminder, the reference direction is solid and brighter in the temporary overlay. For the two common orientations of erect and inverted, the rotation can be set / reset using the <menuchoice><guimenu>View</guimenu><guisubmenu>Skymap Orientation</guisubmenu></menuchoice> submenu. Select "North Down" or "Zenith Down" as is applicable to set an orientation of 180 degrees.
   </para>
   <para>
     If you are visually observing through an eyepiece of an instrument, you may need to do some more correction. For the common case of a large Dobsonian telescope (or more generally a Newtonian design mounted on an altazimuth mount), a systematic additional correction is of help. This correction applies because we stand erect while using the telescope irrespective of the angle the telescope tube is making with the ground. So as we move the telescope in altitude, an additional correction depending on the altitude of the object needs to be applied to make the sky map match the view through the eyepiece. This correction is enabled by checking the <guilabel>Erect observer correction</guilabel> checkbox in the <menuchoice><guimenu>View</guimenu><guisubmenu>Skymap Orientation</guisubmenu></menuchoice> submenu. This correction only makes sense in Horizontal Coordinate mode and is disabled when using equatorial coordinates.
@@ -1638,6 +1640,7 @@ from the <menuchoice><guimenu>Settings</guimenu><guisubmenu>FOV Symbols</guisubm
       <listitem><para>Naked-eye observing: Choose Horizontal Coordinates and a <guilabel>Zenith Up</guilabel> orientation under <menuchoice><guimenu>View</guimenu><guisubmenu>Skymap Orientation</guisubmenu></menuchoice>.</para></listitem>
       <listitem><para>Camera on an equatorially mounted telescope: Choose Equatorial Coordinates and adjust the orientation of the sky map so that it matches your camera. As your mount points to different regions of the sky, the orientation should be rendered correctly.</para></listitem>
       <listitem><para>Using binoculars: Same settings as Naked-eye observing</para></listitem>
+      <listitem><para>Eyepiece of an altazimuth Schmidt-Cassegrin telescope with an erecting prism: Under the <guimenu>View</guimenu> menu, choose <guilabel>Mirrored View</guilabel>, and under the <guisubmenu>Skymap Orientation</guisubmenu> sub-menu, choose <guilabel>Zenith Up</guilabel>. Finally, tweak the rotation manually to match the eyepiece view according to the angle you are using for your erecting prism.</para></listitem>
       <listitem><para>Using a RACI finder scope on an altazimuth mounted telescope: Same settings as Naked-eye observing, except you may need to tweak the orientation manually once if you have it mounted at an angle</para></listitem>
       <listitem><para>Using a straight-through (inverted view) finder scope on an altazimuth mounted telescope: Choose Horizontal Coordinates and a sky-map orientation of <guilabel>Zenith Down</guilabel> in <menuchoice><guimenu>View</guimenu><guisubmenu>Skymap Orientation</guisubmenu></menuchoice> submenu</para></listitem>
       <listitem><para>Eyepiece of a Dobsonian telescope: Choose Horizontal Coordinates, and in the <menuchoice><guimenu>View</guimenu><guisubmenu>Skymap Orientation</guisubmenu></menuchoice> submenu, select <guilabel>Zenith Down</guilabel> and check the <guilabel>Erect observer correction</guilabel> option. Then adjust the orientation manually once to match your telescope eyepiece view, and it should henceforth track it correctly.</para></listitem>
diff --git a/kstars/data/kstarsui.rc b/kstars/data/kstarsui.rc
index da69591619..8a59a30064 100644
--- a/kstars/data/kstarsui.rc
+++ b/kstars/data/kstarsui.rc
@@ -48,6 +48,7 @@
                 <Action name="fullscreen" />
                 <Separator />
                 <Action name="coordsys" />
+                <Action name="mirror_skymap" />
                 <Action name="skymap_orientation" />
                 <Action name="toggle_terrain" />
                 <Action name="toggle_image_overlays" />
diff --git a/kstars/kstars.cpp b/kstars/kstars.cpp
index 9d0efaa490..6cd2f27545 100644
--- a/kstars/kstars.cpp
+++ b/kstars/kstars.cpp
@@ -168,7 +168,6 @@ KStars::KStars(bool doSplash, bool clockrun, const QString &startdate)
     domeGroup->setExclusive(false);
     skymapOrientationGroup = new QActionGroup(this);
 
-
     m_KStarsData = KStarsData::Create();
     Q_ASSERT(m_KStarsData);
     //Set Geographic Location from Options
@@ -345,6 +344,7 @@ void KStars::applyConfig(bool doApplyFocus)
     actionCollection()->action("show_satellites")->setChecked(Options::showSatellites());
     actionCollection()->action("erect_observer_correction")->setChecked(Options::erectObserverCorrection());
     actionCollection()->action("erect_observer_correction")->setEnabled(Options::useAltAz());
+    actionCollection()->action("mirror_skymap")->setChecked(Options::mirrorSkyMap());
     statusBar()->setVisible(Options::showStatusBar());
 
     //color scheme
diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg
index ceae1c310a..8e8d91625a 100644
--- a/kstars/kstars.kcfg
+++ b/kstars/kstars.kcfg
@@ -806,6 +806,11 @@
          <min>0.</min>
          <max>359.9999</max>
       </entry>
+      <entry name="MirrorSkyMap" type="Bool">
+         <label>Mirrors the sky map</label>
+         <whatsthis>Enable this if you want the sky map to be mirrored left-right, e.g. to match the view through an erecting prism.</whatsthis>
+         <default>false</default>
+      </entry>
       <entry name="ErectObserverCorrection" type="Bool">
          <label>Orients the sky-map to account for an erect observer at the eyepiece</label>
          <whatsthis>Enable this if you are using your eye at the eyepiece in an altazimuth mounted Newtonian telescope. This accounts for the fact that the observer stands erect as the telescope moves up and down, so that the orientation of the sky map will track what is seen in your eyepiece once it is set up correctly.</whatsthis>
diff --git a/kstars/kstarsactions.cpp b/kstars/kstarsactions.cpp
index 26b2aede1d..c0f7c291fd 100644
--- a/kstars/kstarsactions.cpp
+++ b/kstars/kstarsactions.cpp
@@ -1743,12 +1743,17 @@ void KStars::slotSkyMapOrientation()
     {
         Options::setSkyRotation(180.0);
     }
+    else if (sender() == actionCollection()->action("mirror_skymap"))
+    {
+        ;
+    }
     else
     {
         Q_ASSERT(false && "Unhandled orientation action");
         qCWarning(KSTARS) << "Unhandled orientation action";
     }
 
+    Options::setMirrorSkyMap(actionCollection()->action("mirror_skymap")->isChecked());
     Options::setErectObserverCorrection(actionCollection()->action("erect_observer_correction")->isChecked());
     map()->forceUpdate();
 }
diff --git a/kstars/kstarsinit.cpp b/kstars/kstarsinit.cpp
index 823f51cee8..8774628ac8 100644
--- a/kstars/kstarsinit.cpp
+++ b/kstars/kstarsinit.cpp
@@ -132,7 +132,6 @@ bool KStars::setResourceFile(QString const rc)
     else return false;
 }
 
-
 void KStars::initActions()
 {
     // Check if we have this specific Breeze icon. If not, try to set the theme search path and if appropriate, the icon theme rcc file
@@ -269,6 +268,11 @@ void KStars::initActions()
                 i18n("Switch to Horizontal View (Horizontal &Coordinates)"))
             << QKeySequence("Space");
 
+    newToggleAction(
+        actionCollection(), "mirror_skymap",
+        i18nc("Mirror the view of the sky map", "Mirrored View"),
+        this, SLOT(slotSkyMapOrientation())) << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M);;
+
     actionCollection()->addAction("toggle_terrain", this, SLOT(slotTerrain()))
             << (Options::showTerrain() ? i18n("Hide Terrain") :
                 i18n("Show Terrain"))
@@ -339,7 +343,6 @@ void KStars::initActions()
     newToggleAction(actionCollection(), "show_sbJ2000RADec", i18n("Show J2000.0 RA/Dec Field"), this,
                     SLOT(slotShowGUIItem(bool)));
 
-
     populateThemes();
 
     //Color scheme actions.  These are added to the "colorschemes" KActionMenu.
@@ -742,7 +745,6 @@ void KStars::repopulateOrientation()
         << ToolTip(i18nc("Orientation of the sky map",
                          "This mode is selected automatically if you manually rotated the sky map using Shift + Drag mouse action, to inform you that the orientation is arbitrary")));
 
-
     orientationActionMenu->addSeparator();
     QAction *erectObserverAction = newToggleAction(
                                        actionCollection(), "erect_observer_correction",
diff --git a/kstars/projections/projector.cpp b/kstars/projections/projector.cpp
index d8ee191e6a..cb469efe21 100644
--- a/kstars/projections/projector.cpp
+++ b/kstars/projections/projector.cpp
@@ -371,6 +371,9 @@ QVector<Eigen::Vector2f> Projector::groundPoly(SkyPoint *labelpoint, bool *drawL
         return ground;
     }
 
+    if (m_vp.mirror)
+        std::reverse(ground.begin(), ground.end());
+
     //In Gnomonic projection, or if sufficiently zoomed in, we can complete
     //the ground polygon by simply adding offscreen points
     //FIXME: not just gnomonic
diff --git a/kstars/projections/projector.h b/kstars/projections/projector.h
index 4d2b437a89..e08d4df980 100644
--- a/kstars/projections/projector.h
+++ b/kstars/projections/projector.h
@@ -42,10 +42,11 @@ class ViewParams
         bool useRefraction;
         bool useAltAz;
         bool fillGround; ///<If the ground is filled, then points below horizon are invisible
+        bool mirror;     // Mirrors along the X-axis
         SkyPoint *focus;
         ViewParams() : width(0), height(0), zoomFactor(0), rotationAngle(0),
             useRefraction(false), useAltAz(false), fillGround(false),
-            focus(nullptr) {}
+            mirror(false), focus(nullptr) {}
 };
 
 /**
@@ -309,10 +310,11 @@ class Projector
          */
         inline Eigen::Vector2f rst(double x, double y) const
         {
+            const double sgn = m_vp.mirror ? -1. : 1.;
             return
             {
-                m_vp.width / 2 - m_vp.zoomFactor * (x * m_vp.rotationAngle.cos() - y * m_vp.rotationAngle.sin()),
-                m_vp.height / 2 - m_vp.zoomFactor * (x * m_vp.rotationAngle.sin() + y * m_vp.rotationAngle.cos())
+                m_vp.width / 2 - m_vp.zoomFactor * (x * sgn * m_vp.rotationAngle.cos() - y * m_vp.rotationAngle.sin()),
+                    m_vp.height / 2 - m_vp.zoomFactor * (x * sgn * m_vp.rotationAngle.sin() + y * m_vp.rotationAngle.cos())
             };
         }
 
@@ -330,11 +332,12 @@ class Projector
          */
         inline Eigen::Vector2f derst(double x, double y) const
         {
+            const double sgn = m_vp.mirror ? -1. : 1;
             const double X = (m_vp.width / 2 - x) / m_vp.zoomFactor;
             const double Y = (m_vp.height / 2 - y) / m_vp.zoomFactor;
             return
             {
-                m_vp.rotationAngle.cos() * X + m_vp.rotationAngle.sin() * Y,
+                sgn * (m_vp.rotationAngle.cos() * X + m_vp.rotationAngle.sin() * Y),
                 -m_vp.rotationAngle.sin() * X + m_vp.rotationAngle.cos() * Y
             };
         }
diff --git a/kstars/skymap.cpp b/kstars/skymap.cpp
index 8173b8ffae..b0a48ecb99 100644
--- a/kstars/skymap.cpp
+++ b/kstars/skymap.cpp
@@ -540,7 +540,6 @@ void SkyMap::slotCopyCoordinates()
                                        Alt.toDMSString()));
 }
 
-
 void SkyMap::slotCopyTLE()
 {
 
@@ -555,7 +554,6 @@ void SkyMap::slotCopyTLE()
         tle = "NO TLE FOR OBJECT";
     }
 
-
     QApplication::clipboard()->setText(tle);
 }
 
@@ -1246,6 +1244,7 @@ void SkyMap::setupProjector()
     p.useRefraction = Options::useRefraction();
     p.zoomFactor    = Options::zoomFactor();
     p.rotationAngle = determineSkyRotation();
+    p.mirror        = Options::mirrorSkyMap();
     p.fillGround    = Options::showGround();
     //Check if we need a new projector
     if (m_proj && Options::projection() == m_proj->type())
@@ -1364,5 +1363,3 @@ void SkyMap::slotStartXplanetViewer()
     else
         new XPlanetImageViewer(i18n("Saturn"), this);
 }
-
-
diff --git a/kstars/skymapdrawabstract.cpp b/kstars/skymapdrawabstract.cpp
index b53687f5ed..9e7395354c 100644
--- a/kstars/skymapdrawabstract.cpp
+++ b/kstars/skymapdrawabstract.cpp
@@ -86,7 +86,10 @@ void SkyMapDrawAbstract::drawOverlays(QPainter &p, bool drawFov)
 
     drawZoomBox(p);
 
-    drawOrientationArrows(p);
+    if (m_SkyMap->rotationStart.x() > 0 && m_SkyMap->rotationStart.y() > 0)
+    {
+        drawOrientationArrows(p);
+    }
 
     // FIXME: Maybe we should take care of this differently. Maybe
     // drawOverlays should remain in SkyMap, since it just calls
@@ -113,51 +116,51 @@ void SkyMapDrawAbstract::drawAngleRuler(QPainter &p)
 
 void SkyMapDrawAbstract::drawOrientationArrows(QPainter &p)
 {
-    if (m_SkyMap->rotationStart.x() > 0 && m_SkyMap->rotationStart.y() > 0)
+    auto* data = m_KStarsData;
+    const SkyPoint centerSkyPoint = m_SkyMap->m_proj->fromScreen(
+                                                                 p.viewport().center(),
+                                                                 data->lst(), data->geo()->lat());
+
+    QPointF centerScreenPoint = p.viewport().center();
+    double northRotation = m_SkyMap->m_proj->findNorthPA(
+                                                         &centerSkyPoint, centerScreenPoint.x(), centerScreenPoint.y());
+    double zenithRotation = m_SkyMap->m_proj->findZenithPA(
+                                                           &centerSkyPoint, centerScreenPoint.x(), centerScreenPoint.y());
+
+    QColor overlayColor(data->colorScheme()->colorNamed("CompassColor"));
+    p.setPen(Qt::NoPen);
+    auto drawArrow = [&](double angle, const QString & marker, const float labelRadius, const bool primary)
     {
-        auto* data = m_KStarsData;
-        const SkyPoint centerSkyPoint = m_SkyMap->m_proj->fromScreen(
-                                            p.viewport().center(),
-                                            data->lst(), data->geo()->lat());
-
-        QPointF centerScreenPoint = p.viewport().center();
-        double northRotation = m_SkyMap->m_proj->findNorthPA(
-                                   &centerSkyPoint, centerScreenPoint.x(), centerScreenPoint.y());
-        double zenithRotation = m_SkyMap->m_proj->findZenithPA(
-                                    &centerSkyPoint, centerScreenPoint.x(), centerScreenPoint.y());
-
-        QColor overlayColor(data->colorScheme()->colorNamed("CompassColor"));
-        p.setPen(Qt::NoPen);
-        auto drawArrow = [&](double angle, const QString & marker, const float labelRadius, const bool primary)
+        constexpr float radius = 150.0f; // In pixels
+        const auto fontMetrics = QFontMetricsF(QFont());
+        QTransform transform;
+        QColor color = overlayColor;
+        color.setAlphaF(primary ? 1.0 : 0.75);
+        QPen pen(color, 1.0, primary ? Qt::SolidLine : Qt::DotLine);
+        QBrush brush(color);
+
+        QPainterPath arrowstem;
+        arrowstem.moveTo(0.f, 0.f);
+        arrowstem.lineTo(0.f, -radius + radius / 7.5f);
+        transform.reset();
+        transform.translate(centerScreenPoint.x(), centerScreenPoint.y());
+        transform.rotate(angle);
+        arrowstem = transform.map(arrowstem);
+        p.strokePath(arrowstem, pen);
+
+        QPainterPath arrowhead;
+        arrowhead.moveTo(0.f, 0.f);
+        arrowhead.lineTo(-radius / 30.f, radius / 7.5f);
+        arrowhead.lineTo(radius / 30.f, radius / 7.5f);
+        arrowhead.lineTo(0.f, 0.f);
+        arrowhead.addText(QPointF(-1.1 * fontMetrics.width(marker), radius / 7.5f + 1.2f * fontMetrics.ascent()),
+                          QFont(), marker);
+        transform.translate(0, -radius);
+        arrowhead = transform.map(arrowhead);
+        p.fillPath(arrowhead, brush);
+
+        if (labelRadius > 0.f)
         {
-            constexpr float radius = 150.0f; // In pixels
-            const auto fontMetrics = QFontMetricsF(QFont());
-            QTransform transform;
-            QColor color = overlayColor;
-            color.setAlphaF(primary ? 1.0 : 0.75);
-            QPen pen(color, 1.0, primary ? Qt::SolidLine : Qt::DotLine);
-            QBrush brush(color);
-
-            QPainterPath arrowstem;
-            arrowstem.moveTo(0.f, 0.f);
-            arrowstem.lineTo(0.f, -radius + radius / 7.5f);
-            transform.reset();
-            transform.translate(centerScreenPoint.x(), centerScreenPoint.y());
-            transform.rotate(angle);
-            arrowstem = transform.map(arrowstem);
-            p.strokePath(arrowstem, pen);
-
-            QPainterPath arrowhead;
-            arrowhead.moveTo(0.f, 0.f);
-            arrowhead.lineTo(-radius / 30.f, radius / 7.5f);
-            arrowhead.lineTo(radius / 30.f, radius / 7.5f);
-            arrowhead.lineTo(0.f, 0.f);
-            arrowhead.addText(QPointF(-1.1 * fontMetrics.width(marker), radius / 7.5f + 1.2f * fontMetrics.ascent()),
-                              QFont(), marker);
-            transform.translate(0, -radius);
-            arrowhead = transform.map(arrowhead);
-            p.fillPath(arrowhead, brush);
-
             QRectF angleMarkerRect(centerScreenPoint.x() - labelRadius, centerScreenPoint.y() - labelRadius,
                                    2.f * labelRadius, 2.f * labelRadius);
             p.setPen(pen);
@@ -179,11 +182,13 @@ void SkyMapDrawAbstract::drawOrientationArrows(QPainter &p)
             transform.rotate(90);
             angleLabel = transform.map(angleLabel);
             p.fillPath(angleLabel, brush);
+        }
 
-        };
-        drawArrow(northRotation, i18nc("North", "N"), 80.f, !Options::useAltAz());
-        drawArrow(zenithRotation, i18nc("Zenith", "Z"), 40.f, Options::useAltAz());
-    }
+    };
+    auto eastRotation = northRotation + (m_SkyMap->m_proj->viewParams().mirror ? 90 : -90);
+    drawArrow(northRotation, i18nc("North", "N"), 80.f, !Options::useAltAz());
+    drawArrow(eastRotation, i18nc("East", "E"), -1.f, !Options::useAltAz());
+    drawArrow(zenithRotation, i18nc("Zenith", "Z"), 40.f, Options::useAltAz());
 }
 
 void SkyMapDrawAbstract::drawZoomBox(QPainter &p)


More information about the kde-doc-english mailing list