winstarbrowser.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:18k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // winstarbrowser.cpp
  2. // 
  3. // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
  4. //
  5. // Star browser tool for Windows.
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. #include <string>
  12. #include <algorithm>
  13. #include <set>
  14. #include <windows.h>
  15. #include <commctrl.h>
  16. #include <cstring>
  17. #include "winstarbrowser.h"
  18. #include "celutil/winutil.h"
  19. #include "res/resource.h"
  20. extern void SetMouseCursor(LPCTSTR lpCursor);
  21. using namespace std;
  22. static const int MinListStars = 10;
  23. static const int MaxListStars = 500;
  24. static const int DefaultListStars = 100;
  25. // TODO: More of the functions in this module should be converted to
  26. // methods of the StarBrowser class.
  27. enum {
  28.     BrightestStars = 0,
  29.     NearestStars = 1,
  30.     StarsWithPlanets = 2,
  31. };
  32. static Point3f toMicroLY(const Point3f& p)
  33. {
  34.     return Point3f(p.x * 1e6f, p.y * 1e6f, p.z * 1e6f);
  35. }
  36. static Point3f fromMicroLY(const Point3f& p)
  37. {
  38.     return Point3f(p.x * 1e-6f, p.y * 1e-6f, p.z * 1e-6f);
  39. }
  40. bool InitStarBrowserColumns(HWND listView)
  41. {
  42.     LVCOLUMN lvc;
  43.     LVCOLUMN columns[5];
  44.     lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  45.     lvc.fmt = LVCFMT_LEFT;
  46.     lvc.cx = 60;
  47.     lvc.pszText = "";
  48.     int nColumns = sizeof(columns) / sizeof(columns[0]);
  49.     int i;
  50.     for (i = 0; i < nColumns; i++)
  51.         columns[i] = lvc;
  52.     bind_textdomain_codeset("celestia", CurrentCP());
  53.     columns[0].pszText = _("Name");
  54.     columns[0].cx = 100;
  55.     columns[1].pszText = _("Distance (ly)");
  56.     columns[1].fmt = LVCFMT_RIGHT;
  57.     columns[1].cx = 75;
  58.     columns[2].pszText = _("App. mag");
  59.     columns[2].fmt = LVCFMT_RIGHT;
  60.     columns[3].pszText = _("Abs. mag");
  61.     columns[3].fmt = LVCFMT_RIGHT;
  62.     columns[4].pszText = _("Type");
  63.     bind_textdomain_codeset("celestia", "UTF8");
  64.     for (i = 0; i < nColumns; i++)
  65.     {
  66.         columns[i].iSubItem = i;
  67.         if (ListView_InsertColumn(listView, i, &columns[i]) == -1)
  68.             return false;
  69.     }
  70.     return true;
  71. }
  72. struct CloserStarPredicate
  73. {
  74.     Point3f pos;
  75.     bool operator()(const Star* star0, const Star* star1) const
  76.     {
  77.         return ((pos - star0->getPosition()).lengthSquared() <
  78.                 (pos - star1->getPosition()).lengthSquared());
  79.                                
  80.     }
  81. };
  82. struct BrighterStarPredicate
  83. {
  84.     Point3f pos;
  85.     UniversalCoord ucPos;
  86.     bool operator()(const Star* star0, const Star* star1) const
  87.     {
  88.         float d0 = pos.distanceTo(star0->getPosition());
  89.         float d1 = pos.distanceTo(star1->getPosition());
  90.         // If the stars are closer than one light year, use
  91.         // a more precise distance estimate.
  92.         if (d0 < 1.0f)
  93.             d0 = (toMicroLY(star0->getPosition()) - ucPos).length() * 1e-6f;
  94.         if (d1 < 1.0f)
  95.             d1 = (toMicroLY(star1->getPosition()) - ucPos).length() * 1e-6f;
  96.         return (star0->getApparentMagnitude(d0) <
  97.                 star1->getApparentMagnitude(d1));
  98.     }
  99. };
  100. struct SolarSystemPredicate
  101. {
  102.     Point3f pos;
  103.     SolarSystemCatalog* solarSystems;
  104.     bool operator()(const Star* star0, const Star* star1) const
  105.     {
  106.         SolarSystemCatalog::iterator iter;
  107.         iter = solarSystems->find(star0->getCatalogNumber());
  108.         bool hasPlanets0 = (iter != solarSystems->end());
  109.         iter = solarSystems->find(star1->getCatalogNumber());
  110.         bool hasPlanets1 = (iter != solarSystems->end());
  111.         if (hasPlanets1 == hasPlanets0)
  112.         {
  113.             return ((pos - star0->getPosition()).lengthSquared() <
  114.                     (pos - star1->getPosition()).lengthSquared());
  115.         }
  116.         else
  117.         {
  118.             return hasPlanets0;
  119.         }
  120.     }
  121. };
  122. // Find the nearest/brightest/X-est N stars in a database.  The
  123. // supplied predicate determines which of two stars is a better match.
  124. template<class Pred> vector<const Star*>*
  125. FindStars(const StarDatabase& stardb, Pred pred, int nStars)
  126. {
  127.     vector<const Star*>* finalStars = new vector<const Star*>();
  128.     if (nStars == 0)
  129.         return finalStars;
  130.     typedef multiset<const Star*, Pred> StarSet;
  131.     StarSet firstStars(pred);
  132.     int totalStars = stardb.size();
  133.     if (totalStars < nStars)
  134.         nStars = totalStars;
  135.     // We'll need at least nStars in the set, so first fill
  136.     // up the list indiscriminately.
  137.     int i = 0;
  138.     for (i = 0; i < nStars; i++)
  139.     {
  140.         Star* star = stardb.getStar(i);
  141.         if (star->getVisibility())
  142.             firstStars.insert(star);
  143.     }
  144.     // From here on, only add a star to the set if it's
  145.     // a better match than the worst matching star already
  146.     // in the set.
  147.     const Star* lastStar = *--firstStars.end();
  148.     for (; i < totalStars; i++)
  149.     {
  150.         Star* star = stardb.getStar(i);
  151.         if (star->getVisibility() && pred(star, lastStar))
  152.         {
  153.             firstStars.insert(star);
  154.             firstStars.erase(--firstStars.end());
  155.             lastStar = *--firstStars.end();
  156.         }
  157.     }
  158.     // Move the best matching stars into the vector
  159.     finalStars->reserve(nStars);
  160.     for (StarSet::const_iterator iter = firstStars.begin();
  161.          iter != firstStars.end(); iter++)
  162.     {
  163.         finalStars->insert(finalStars->end(), *iter);
  164.     }
  165.     return finalStars;
  166. }
  167. bool InitStarBrowserLVItems(HWND listView, vector<const Star*>& stars)
  168. {
  169.     LVITEM lvi;
  170.     lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
  171.     lvi.state = 0;
  172.     lvi.stateMask = 0;
  173.     lvi.pszText = LPSTR_TEXTCALLBACK;
  174.     for (unsigned int i = 0; i < stars.size(); i++)
  175.     {
  176.         lvi.iItem = i;
  177.         lvi.iSubItem = 0;
  178.         lvi.lParam = (LPARAM) stars[i];
  179.         ListView_InsertItem(listView, &lvi);
  180.     }
  181.     return true;
  182. }
  183. bool InitStarBrowserItems(HWND listView, StarBrowser* browser)
  184. {
  185.     Universe* univ = browser->appCore->getSimulation()->getUniverse();
  186.     StarDatabase* stardb = univ->getStarCatalog();
  187.     SolarSystemCatalog* solarSystems = univ->getSolarSystemCatalog();
  188.     vector<const Star*>* stars = NULL;
  189.     switch (browser->predicate)
  190.     {
  191.     case BrightestStars:
  192.         {
  193.             BrighterStarPredicate brighterPred;
  194.             brighterPred.pos = browser->pos;
  195.             brighterPred.ucPos = browser->ucPos;
  196.             stars = FindStars(*stardb, brighterPred, browser->nStars);
  197.         }
  198.         break;
  199.     case NearestStars:
  200.         {
  201.             CloserStarPredicate closerPred;
  202.             closerPred.pos = browser->pos;
  203.             stars = FindStars(*stardb, closerPred, browser->nStars);
  204.         }
  205.         break;
  206.     case StarsWithPlanets:
  207.         {
  208.             if (solarSystems == NULL)
  209.                 return false;
  210.             SolarSystemPredicate solarSysPred;
  211.             solarSysPred.pos = browser->pos;
  212.             solarSysPred.solarSystems = solarSystems;
  213.             stars = FindStars(*stardb, solarSysPred,
  214.                               min((unsigned int) browser->nStars, solarSystems->size()));
  215.         }
  216.         break;
  217.     default:
  218.         return false;
  219.     }
  220.             
  221.     bool succeeded = InitStarBrowserLVItems(listView, *stars);
  222.     delete stars;
  223.     return succeeded;
  224. }
  225. // Crud used for the list item display callbacks
  226. static string starNameString("");
  227. static char callbackScratch[256];
  228. struct StarBrowserSortInfo
  229. {
  230.     int subItem;
  231.     Point3f pos;
  232.     UniversalCoord ucPos;
  233. };
  234. int CALLBACK StarBrowserCompareFunc(LPARAM lParam0, LPARAM lParam1,
  235.                                     LPARAM lParamSort)
  236. {
  237.     StarBrowserSortInfo* sortInfo = reinterpret_cast<StarBrowserSortInfo*>(lParamSort);
  238.     Star* star0 = reinterpret_cast<Star*>(lParam0);
  239.     Star* star1 = reinterpret_cast<Star*>(lParam1);
  240.     switch (sortInfo->subItem)
  241.     {
  242.     case 0:
  243.         return 0;
  244.     case 1:
  245.         {
  246.             float d0 = sortInfo->pos.distanceTo(star0->getPosition());
  247.             float d1 = sortInfo->pos.distanceTo(star1->getPosition());
  248.             return (int) sign(d0 - d1);
  249.         }
  250.     case 2:
  251.         {
  252.             float d0 = sortInfo->pos.distanceTo(star0->getPosition());
  253.             float d1 = sortInfo->pos.distanceTo(star1->getPosition());
  254.             if (d0 < 1.0f)
  255.                 d0 = (toMicroLY(star0->getPosition()) - sortInfo->ucPos).length() * 1e-6f;
  256.             if (d1 < 1.0f)
  257.                 d1 = (toMicroLY(star1->getPosition()) - sortInfo->ucPos).length() * 1e-6f;
  258.             return (int) sign(astro::absToAppMag(star0->getAbsoluteMagnitude(), d0) -
  259.                               astro::absToAppMag(star1->getAbsoluteMagnitude(), d1));
  260.         }
  261.     case 3:
  262.         return (int) sign(star0->getAbsoluteMagnitude() - star1->getAbsoluteMagnitude());
  263.     case 4:
  264.         return strcmp(star0->getSpectralType(), star1->getSpectralType());
  265.     default:
  266.         return 0;
  267.     }
  268. }
  269. void StarBrowserDisplayItem(LPNMLVDISPINFOA nm, StarBrowser* browser)
  270. {
  271.     double tdb = browser->appCore->getSimulation()->getTime();
  272.     Star* star = reinterpret_cast<Star*>(nm->item.lParam);
  273.     if (star == NULL)
  274.     {
  275.         nm->item.pszText = "";
  276.         return;
  277.     }
  278.     switch (nm->item.iSubItem)
  279.     {
  280.     case 0:
  281.         {
  282.             Universe* u = browser->appCore->getSimulation()->getUniverse();
  283.             starNameString = UTF8ToCurrentCP(u->getStarCatalog()->getStarName(*star));
  284.             nm->item.pszText = const_cast<char*>(starNameString.c_str());
  285.         }
  286.         break;
  287.             
  288.     case 1:
  289.         {
  290.             Vec3d r = star->getPosition(tdb) - browser->ucPos;
  291.             sprintf(callbackScratch, "%.4g", r.length() * 1.0e-6);
  292.             nm->item.pszText = callbackScratch;
  293.         }
  294.         break;
  295.     case 2:
  296.         {
  297.             Vec3d r = star->getPosition(tdb) - browser->ucPos;
  298.             double appMag = astro::absToAppMag((double) star->getAbsoluteMagnitude(),
  299.                                                (r.length() * 1e-6));
  300.             sprintf(callbackScratch, "%.2f", appMag);
  301.             nm->item.pszText = callbackScratch;
  302.         }
  303.         break;
  304.             
  305.     case 3:
  306.         sprintf(callbackScratch, "%.2f", star->getAbsoluteMagnitude());
  307.         nm->item.pszText = callbackScratch;
  308.         break;
  309.     case 4:
  310.         strncpy(callbackScratch, star->getSpectralType(),
  311.                 sizeof(callbackScratch));
  312.         callbackScratch[sizeof(callbackScratch) - 1] = '';
  313.         nm->item.pszText = callbackScratch;
  314.         break;
  315.     }
  316. }
  317. void RefreshItems(HWND hDlg, StarBrowser* browser)
  318. {
  319.     SetMouseCursor(IDC_WAIT);
  320.     Simulation* sim = browser->appCore->getSimulation();
  321.     browser->ucPos = sim->getObserver().getPosition();
  322.     browser->pos = fromMicroLY((Point3f) browser->ucPos);
  323.     HWND hwnd = GetDlgItem(hDlg, IDC_STARBROWSER_LIST);
  324.     if (hwnd != 0)
  325.     {
  326.         ListView_DeleteAllItems(hwnd);
  327.         InitStarBrowserItems(hwnd, browser);
  328.     }
  329.     SetMouseCursor(IDC_ARROW);
  330. }
  331. BOOL APIENTRY StarBrowserProc(HWND hDlg,
  332.                               UINT message,
  333.                               UINT wParam,
  334.                               LONG lParam)
  335. {
  336.     StarBrowser* browser = reinterpret_cast<StarBrowser*>(GetWindowLong(hDlg, DWL_USER));
  337.     switch (message)
  338.     {
  339.     case WM_INITDIALOG:
  340.         {
  341.             StarBrowser* browser = reinterpret_cast<StarBrowser*>(lParam);
  342.             if (browser == NULL)
  343.                 return EndDialog(hDlg, 0);
  344.             SetWindowLong(hDlg, DWL_USER, lParam);
  345.             HWND hwnd = GetDlgItem(hDlg, IDC_STARBROWSER_LIST);
  346.             InitStarBrowserColumns(hwnd);
  347.             InitStarBrowserItems(hwnd, browser);
  348.             CheckRadioButton(hDlg, IDC_RADIO_NEAREST, IDC_RADIO_WITHPLANETS, IDC_RADIO_NEAREST);
  349.             //Initialize Max Stars edit box
  350.             char val[16];
  351.             hwnd = GetDlgItem(hDlg, IDC_MAXSTARS_EDIT);
  352.             sprintf(val, "%d", DefaultListStars);
  353.             SetWindowText(hwnd, val);
  354.             SendMessage(hwnd, EM_LIMITTEXT, 3, 0);
  355.             //Initialize Max Stars Slider control
  356.             SendDlgItemMessage(hDlg, IDC_MAXSTARS_SLIDER, TBM_SETRANGE,
  357.                 (WPARAM)TRUE, (LPARAM)MAKELONG(MinListStars, MaxListStars));
  358.             SendDlgItemMessage(hDlg, IDC_MAXSTARS_SLIDER, TBM_SETPOS,
  359.                 (WPARAM)TRUE, (LPARAM)DefaultListStars);
  360.             
  361.             return(TRUE);
  362.         }
  363.     case WM_DESTROY:
  364.         if (browser != NULL && browser->parent != NULL)
  365.         {
  366.             SendMessage(browser->parent, WM_COMMAND, IDCLOSE,
  367.                         reinterpret_cast<LPARAM>(browser));
  368.         }
  369.         break;
  370.     case WM_COMMAND:
  371.         switch (LOWORD(wParam))
  372.         {
  373.         case IDOK:
  374.         case IDCANCEL:
  375.             if (browser != NULL && browser->parent != NULL)
  376.             {
  377.                 SendMessage(browser->parent, WM_COMMAND, IDCLOSE,
  378.                             reinterpret_cast<LPARAM>(browser));
  379.             }
  380.             EndDialog(hDlg, 0);
  381.             return TRUE;
  382.         case IDC_BUTTON_CENTER:
  383.             browser->appCore->charEntered('c');
  384.             break;
  385.         case IDC_BUTTON_GOTO:
  386.             browser->appCore->charEntered('G');
  387.             break;
  388.         case IDC_RADIO_BRIGHTEST:
  389.             browser->predicate = BrightestStars;
  390.             RefreshItems(hDlg, browser);
  391.             break;
  392.         case IDC_RADIO_NEAREST:
  393.             browser->predicate = NearestStars;
  394.             RefreshItems(hDlg, browser);
  395.             break;
  396.         case IDC_RADIO_WITHPLANETS:
  397.             browser->predicate = StarsWithPlanets;
  398.             RefreshItems(hDlg, browser);
  399.             break;
  400.         case IDC_BUTTON_REFRESH:
  401.             RefreshItems(hDlg, browser);
  402.             break;
  403.         case IDC_MAXSTARS_EDIT:
  404.             // TODO: browser != NULL check should be in a lot more places
  405.             if (HIWORD(wParam) == EN_KILLFOCUS && browser != NULL)
  406.             {
  407.                 char val[16];
  408.                 DWORD nNewStars;
  409.                 DWORD minRange, maxRange;
  410.                 GetWindowText((HWND) lParam, val, sizeof(val));
  411.                 nNewStars = atoi(val);
  412.                 // Check if new value is different from old. Don't want to
  413.                 // cause a refresh to occur if not necessary.
  414.                 if (nNewStars != browser->nStars)
  415.                 {
  416.                     minRange = SendDlgItemMessage(hDlg, IDC_MAXSTARS_SLIDER, TBM_GETRANGEMIN, 0, 0);
  417.                     maxRange = SendDlgItemMessage(hDlg, IDC_MAXSTARS_SLIDER, TBM_GETRANGEMAX, 0, 0);
  418.                     if (nNewStars < minRange)
  419.                         nNewStars = minRange;
  420.                     else if (nNewStars > maxRange)
  421.                         nNewStars = maxRange;
  422.                     // If new value has been adjusted from what was entered,
  423.                     // reflect new value back in edit control.
  424.                     if (atoi(val) != nNewStars)
  425.                     {
  426.                         sprintf(val, "%d", nNewStars);
  427.                         SetWindowText((HWND)lParam, val);
  428.                     }
  429.                     // Recheck value if different from original.
  430.                     if (nNewStars != browser->nStars)
  431.                     {
  432.                         browser->nStars = nNewStars;
  433.                         SendDlgItemMessage(hDlg,
  434.                                            IDC_MAXSTARS_SLIDER,
  435.                                            TBM_SETPOS,
  436.                                            (WPARAM) TRUE,
  437.                                            (LPARAM) browser->nStars);
  438.                         RefreshItems(hDlg, browser);
  439.                     }
  440.                 }
  441.             }
  442.             break;
  443.         }
  444.         break;
  445.     case WM_NOTIFY:
  446.         {
  447.             LPNMHDR hdr = (LPNMHDR) lParam;
  448.             if (hdr->idFrom == IDC_STARBROWSER_LIST && browser != NULL)
  449.             {
  450.                 switch(hdr->code)
  451.                 {
  452.                 case LVN_GETDISPINFO:
  453.                     StarBrowserDisplayItem((LPNMLVDISPINFOA) lParam, browser);
  454.                     break;
  455.                 case LVN_ITEMCHANGED:
  456.                     {
  457.                         LPNMLISTVIEW nm = (LPNMLISTVIEW) lParam;
  458.                         if ((nm->uNewState & LVIS_SELECTED) != 0)
  459.                         {
  460.                             Simulation* sim = browser->appCore->getSimulation();
  461.                             Star* star = reinterpret_cast<Star*>(nm->lParam);
  462.                             if (star != NULL)
  463.                                 sim->setSelection(Selection(star));
  464.                         }
  465.                         break;
  466.                     }
  467.                 case LVN_COLUMNCLICK:
  468.                     {
  469.                         HWND hwnd = GetDlgItem(hDlg, IDC_STARBROWSER_LIST);
  470.                         if (hwnd != 0)
  471.                         {
  472.                             LPNMLISTVIEW nm = (LPNMLISTVIEW) lParam;
  473.                             StarBrowserSortInfo sortInfo;
  474.                             sortInfo.subItem = nm->iSubItem;
  475.                             sortInfo.ucPos = browser->ucPos;
  476.                             sortInfo.pos = browser->pos;
  477.                             ListView_SortItems(hwnd, StarBrowserCompareFunc,
  478.                                                reinterpret_cast<LPARAM>(&sortInfo));
  479.                         }
  480.                     }
  481.                 }
  482.             }
  483.         }
  484.         break;
  485.     case WM_HSCROLL:
  486.         {
  487.             WORD sbValue = LOWORD(wParam);
  488.             switch(sbValue)
  489.             {
  490.                 case SB_THUMBTRACK:
  491.                 {
  492.                     char val[16];
  493.                     HWND hwnd = GetDlgItem(hDlg, IDC_MAXSTARS_EDIT);
  494.                     sprintf(val, "%d", HIWORD(wParam));
  495.                     SetWindowText(hwnd, val);
  496.                     break;
  497.                 }
  498.                 case SB_THUMBPOSITION:
  499.                 {
  500.                     browser->nStars = (int)HIWORD(wParam);
  501.                     RefreshItems(hDlg, browser);
  502.                     break;
  503.                 }
  504.             }
  505.         }
  506.         break;
  507.     }
  508.     return FALSE;
  509. }
  510. StarBrowser::StarBrowser(HINSTANCE appInstance,
  511.                          HWND _parent,
  512.                          CelestiaCore* _appCore) :
  513.     appCore(_appCore),
  514.     parent(_parent)
  515. {
  516.     ucPos = appCore->getSimulation()->getObserver().getPosition();
  517.     pos = fromMicroLY((Point3f) ucPos);
  518.     predicate = NearestStars;
  519.     nStars = DefaultListStars;
  520.     hwnd = CreateDialogParam(appInstance,
  521.                              MAKEINTRESOURCE(IDD_STARBROWSER),
  522.                              parent,
  523.                              StarBrowserProc,
  524.                              reinterpret_cast<LONG>(this));
  525. }
  526. StarBrowser::~StarBrowser()
  527. {
  528.     SetWindowLong(hwnd, DWL_USER, 0);
  529. }