1 /*
2  *  star_profile.cpp
3  *  PHD Guiding
4  *
5  *  Created by Sylvain Girard
6  *  Copyright (c) 2013 Sylvain Girard
7  *  All rights reserved.
8  *
9  *  This source code is distributed under the following "BSD" license
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions are met:
12  *    Redistributions of source code must retain the above copyright notice,
13  *     this list of conditions and the following disclaimer.
14  *    Redistributions in binary form must reproduce the above copyright notice,
15  *     this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *    Neither the name of Bret McKee, Dad Dog Development Ltd, nor the names of its
18  *     contributors may be used to endorse or promote products derived from
19  *     this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  *  POSSIBILITY OF SUCH DAMAGE.
32  *
33  *
34  */
35 
36 #include "phd.h"
37 
38 BEGIN_EVENT_TABLE(ProfileWindow, wxWindow)
39     EVT_PAINT(ProfileWindow::OnPaint)
40     EVT_LEFT_DOWN(ProfileWindow::OnLClick)
41 END_EVENT_TABLE()
42 
43 enum
44 {
45     HALFW = 10,
46     FULLW = 2 * HALFW + 1,
47 };
48 
ProfileWindow(wxWindow * parent)49 ProfileWindow::ProfileWindow(wxWindow *parent) :
50     wxWindow(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize, wxFULL_REPAINT_ON_RESIZE,_("Profile"))
51 {
52     SetBackgroundStyle(wxBG_STYLE_PAINT);
53 
54     this->visible = false;
55     this->mode = 0; // 2D profile
56     this->SetBackgroundStyle(wxBG_STYLE_CUSTOM);
57     this->data = new unsigned short[FULLW * FULLW];  // 21x21 subframe
58 }
59 
~ProfileWindow()60 ProfileWindow::~ProfileWindow()
61 {
62     delete[] data;
63 }
64 
OnLClick(wxMouseEvent & WXUNUSED (mevent))65 void ProfileWindow::OnLClick(wxMouseEvent& WXUNUSED(mevent))
66 {
67     this->mode = this->mode + 1;
68     if (this->mode > 2) this->mode = 0;
69     Refresh();
70 }
71 
SetState(bool is_active)72 void ProfileWindow::SetState(bool is_active)
73 {
74     this->visible = is_active;
75     if (is_active)
76         Refresh();
77 }
78 
UpdateData(const usImage * img,float xpos,float ypos)79 void ProfileWindow::UpdateData(const usImage *img, float xpos, float ypos)
80 {
81     if (this->data == NULL) return;
82     int xstart = ROUNDF(xpos) - HALFW;
83     int ystart = ROUNDF(ypos) - HALFW;
84     if (xstart < 0) xstart = 0;
85     else if (xstart > (img->Size.GetWidth() - (FULLW + 1)))
86         xstart = img->Size.GetWidth() - (FULLW + 1);
87     if (ystart < 0) ystart = 0;
88     else if (ystart > (img->Size.GetHeight() - (FULLW + 1)))
89         ystart = img->Size.GetHeight() - (FULLW + 1);
90 
91     int x,y;
92     unsigned short *uptr = this->data;
93     const int xrowsize = img->Size.GetWidth();
94     for (x = 0; x < FULLW; x++)
95         horiz_profile[x] = vert_profile[x] = midrow_profile[x] = 0;
96     for (y = 0; y < FULLW; y++) {
97         for (x = 0; x < FULLW; x++, uptr++) {
98             *uptr = *(img->ImageData + xstart + x + (ystart + y) * xrowsize);
99             horiz_profile[x] += (int) *uptr;
100             vert_profile[y] += (int) *uptr;
101         }
102     }
103     uptr = this->data + (FULLW * HALFW);
104     for (x = 0; x < FULLW; x++, uptr++)
105         midrow_profile[x] = (int) *uptr;
106     if (this->visible)
107         Refresh();
108 }
109 
OnPaint(wxPaintEvent & WXUNUSED (evt))110 void ProfileWindow::OnPaint(wxPaintEvent& WXUNUSED(evt))
111 {
112     wxAutoBufferedPaintDC dc(this);
113 
114     dc.SetBackground(wxColour(10, 30, 30));
115     dc.Clear();
116 
117     if (!pFrame || !pFrame->pGuider || pFrame->pGuider->GetState() == STATE_UNINITIALIZED)
118         return;
119 
120     const int xsize = this->GetSize().GetX();
121     const int ysize = this->GetSize().GetY();
122 
123 #if defined (__APPLE__)
124     const wxFont& smallFont = *wxSMALL_FONT;
125 #else
126     const wxFont& smallFont = *wxSWISS_FONT;
127 #endif
128     dc.SetFont(smallFont);
129     int smallFontHeight = dc.GetTextExtent("0").GetHeight();
130 
131     bool inFocusingMode = (ysize > xsize/2 + 20);
132 
133     wxFont largeFont;
134     int largeFontHeight;
135     int labelTextHeight;
136 
137     if (inFocusingMode) {
138         //todo: Tuning the scaling factor
139         int scale = ysize / 50;
140         largeFont = smallFont.Scaled(scale);
141 
142         dc.SetFont(largeFont);
143         largeFontHeight = dc.GetTextExtent("0").GetHeight();
144         dc.SetFont(smallFont);
145         labelTextHeight = 5 + smallFontHeight + largeFontHeight + 5;
146     }
147     else {
148         labelTextHeight = 5 + smallFontHeight + 5;
149     }
150 
151     wxPen RedPen(wxColour(255,0,0));
152     //  GreyDashPen = wxPen(wxColour(200,200,200),1, wxDOT);
153     //  BluePen = wxPen(wxColour(100,100,255));
154 
155     int i;
156     int *profptr;
157     wxString profileLabel;
158     switch (this->mode) {  // Figure which profile to use
159     case 0: // mid-row
160     default:
161         profptr = midrow_profile;
162         profileLabel = _("Mid row");
163         break;
164     case 1: // avg row
165         profptr = horiz_profile;
166         profileLabel = _("Avg row");
167         break;
168     case 2:
169         profptr = vert_profile;
170         profileLabel = _("Avg col");
171         break;
172     }
173 
174     float fwhm = 0.f;
175 
176     // Figure max and min
177     int Prof_Min, Prof_Max, Prof_Mid;
178     Prof_Min = Prof_Max = *profptr;
179 
180     for (i = 1; i < FULLW; i++) {
181         if (*(profptr + i) < Prof_Min)
182             Prof_Min = *(profptr + i);
183         else if (*(profptr + i) > Prof_Max)
184             Prof_Max = *(profptr + i);
185     }
186 
187     wxPoint Prof[FULLW];
188 
189     if (Prof_Min < Prof_Max)
190     {
191         Prof_Mid = (Prof_Max - Prof_Min) / 2 + Prof_Min;
192         // Figure the actual points in the window
193         float Prof_Range = (float)(Prof_Max - Prof_Min) / (float)(ysize - labelTextHeight - 5);
194         if (!Prof_Range) Prof_Range = 1;
195         int wprof = (xsize - 15) / 2 - 5;
196         wprof /= 20;
197         for (i = 0; i < FULLW; i++)
198             Prof[i] = wxPoint(5 + i * wprof, ysize - labelTextHeight - ((float)(*(profptr + i) - Prof_Min) / Prof_Range));
199 
200         // fwhm
201         int x1 = 0;
202         int x2 = 0;
203         int profval;
204         int profvalprec;
205         for (i = 1; i < FULLW; i++)
206         {
207             profval = *(profptr + i);
208             profvalprec = *(profptr + i - 1);
209             if (profvalprec <= Prof_Mid && profval >= Prof_Mid)
210                 x1 = i;
211             else if (profvalprec >= Prof_Mid && profval <= Prof_Mid)
212                 x2 = i;
213         }
214         profval = *(profptr + x1);
215         profvalprec = *(profptr + x1 - 1);
216         float f1 = (float)x1 - (float)(profval - Prof_Mid) / (float)(profval - profvalprec);
217         profval = *(profptr + x2);
218         profvalprec = *(profptr + x2 - 1);
219         float f2 = (float)x2 - (float)(profvalprec - Prof_Mid) / (float)(profvalprec - profval);
220         fwhm = f2 - f1;
221 
222         // Draw it
223         dc.SetPen(RedPen);
224         dc.DrawLines(FULLW, Prof);
225     }
226 
227     //dc.SetTextForeground(wxColour(100,100,255));
228     dc.SetTextForeground(wxColour(255,0,0));
229 
230     const Star& star = pFrame->pGuider->PrimaryStar();
231     if (star.IsValid()) {
232         dc.DrawText(_("Peak"), 3, 3);
233         dc.DrawText(wxString::Format("%u", star.PeakVal), 3, 3 + smallFontHeight);
234     }
235 
236     float hfd = star.HFD;
237     if (hfd != 0.f) {
238         float hfdArcSec = hfd * pFrame->GetCameraPixelScale();
239         if (inFocusingMode) {
240             dc.DrawText(wxString::Format(_("%s FWHM: %.2f"), profileLabel, fwhm), 5, ysize - labelTextHeight + 5);
241             int x = 5;
242             wxString s(_("HFD: "));
243             dc.DrawText(s, x, ysize - largeFontHeight / 2 - smallFontHeight / 2);
244             x += dc.GetTextExtent(s).GetWidth();
245 
246             dc.SetFont(largeFont);
247             s = wxString::Format(_T("%.2f"), hfd);
248             dc.DrawText(s, x, ysize - largeFontHeight);
249             x += dc.GetTextExtent(s).GetWidth();
250 
251             dc.SetFont(smallFont);
252             s = wxString::Format(_T("  %.2f\""), hfdArcSec);
253             dc.DrawText(s, x, ysize - largeFontHeight / 2 - smallFontHeight / 2);
254         }
255         else {
256             dc.DrawText(wxString::Format(_("%s FWHM: %.2f, HFD: %.2f (%.2f\")"), profileLabel, fwhm, hfd, hfdArcSec), 5, ysize - smallFontHeight - 5);
257         }
258     }
259     else {
260         dc.DrawText(wxString::Format(_("%s FWHM: %.2f"), profileLabel, fwhm), 5, ysize - smallFontHeight - 5);
261     }
262 
263     // JBW: draw zoomed guidestar subframe (todo: make constants symbolic)
264     wxImage* img = pFrame->pGuider->DisplayedImage();
265     double scaleFactor = pFrame->pGuider->ScaleFactor();
266     if (img) {
267         int xoffset = (xsize - 15) / 2;
268         int width = xsize - xoffset - 5;
269         if (width > ysize + 5) width = ysize - 5;
270         int midwidth = width / 2;
271         // grab width(30) px box around lock pos, scale by 2 & display next to profile
272         double LockX = pFrame->pGuider->LockPosition().X * scaleFactor;
273         double LockY = pFrame->pGuider->LockPosition().Y * scaleFactor;
274         double dStarX = LockX - pFrame->pGuider->CurrentPosition().X * scaleFactor;
275         double dStarY = LockY - pFrame->pGuider->CurrentPosition().Y * scaleFactor;
276         // grab the subframe
277         wxBitmap dBmp(*img);
278         int lkx = ROUND(LockX);
279         int l = std::max(0, lkx - 15);
280         int r = std::min(dBmp.GetWidth() - 1, lkx + 15);
281         int w = std::min(lkx - l, r - lkx);
282         int lky = ROUND(LockY);
283         int t = std::max(0, lky - 15);
284         int b = std::min(dBmp.GetHeight() - 1, lky + 15);
285         int h = std::min(lky - t, b - lky);
286         int sz = std::min(w, h);
287 
288         wxBitmap subDBmp = dBmp.GetSubBitmap(wxRect(lkx - sz, lky - sz, sz * 2, sz * 2));
289         wxImage subDImg = subDBmp.ConvertToImage();
290         // scale by 2
291         wxBitmap zoomedDBmp(subDImg.Rescale(width, width, wxIMAGE_QUALITY_HIGH));
292         wxMemoryDC tmpMdc;
293         tmpMdc.SelectObject(zoomedDBmp);
294         // blit into profile DC
295         dc.Blit(xoffset, 0, width, width, &tmpMdc, 0, 0, wxCOPY, false);
296         // lines for the lock pos + red dot at star centroid
297         dc.SetPen(wxPen(wxColor(0, 200, 0), 1, wxPENSTYLE_DOT));
298         dc.DrawLine(xoffset, midwidth, xoffset + width, midwidth);
299         dc.DrawLine(xoffset + midwidth, 0, xoffset + midwidth, width);
300         if (sz > 0)
301         {
302             // and a small cross at the centroid
303             double starX = xoffset + midwidth - dStarX * (width / (sz * 2)) + 1, starY = midwidth - dStarY * (width / (sz * 2)) + 1;
304             if (starX >= xoffset) {
305                 dc.SetPen(RedPen);
306                 dc.DrawLine(starX - 3, starY, starX + 3, starY);
307                 dc.DrawLine(starX, starY - 3, starX, starY + 3);
308             }
309         }
310     }
311 }
312 
313