├── mac ├── qt.conf ├── appIcon.icns └── complete-bundle.sh ├── linux ├── panini.png ├── panini.desktop └── panini.appdata.xml ├── ui ├── paniniWin.rc ├── panini-icon-blue.ico ├── panini-icon-blue.jpg ├── PaniniIcon.qrc ├── ShowText.ui ├── CubeLimit_dialog.ui ├── TurnDialog.ui ├── About.ui ├── picTypeDialog.ui └── mainwindow.ui ├── .gitignore ├── README.md ├── .github └── FUNDING.yml ├── LICENSE ├── MSVS-project ├── panini.sln └── pvQt_1 │ └── pvQt_1.vcproj ├── INSTALL.md ├── src ├── About.h ├── About.cpp ├── main.cpp ├── panocylinder.h ├── CubeLimit_dialog.h ├── panosphere.h ├── pvQtMouseModes.h ├── TurnDialog.h ├── picTypeDialog.h ├── picTypeDialog.cpp ├── GLwindow.h ├── TurnDialog.cpp ├── pvQt_QTVR.h ├── MainWindow.h ├── panosurface.h ├── panocylinder.cpp ├── pictureTypes.cpp ├── pvQtView.h ├── panosurface.cpp ├── MainWindow.cpp ├── pvQtPic.h └── panosphere.cpp ├── BUILD.md ├── panini.pro ├── NEWS └── USAGE.md /mac/qt.conf: -------------------------------------------------------------------------------- 1 | [Paths] 2 | Plugins = plugins 3 | -------------------------------------------------------------------------------- /linux/panini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazarus-pkgs/panini/HEAD/linux/panini.png -------------------------------------------------------------------------------- /mac/appIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazarus-pkgs/panini/HEAD/mac/appIcon.icns -------------------------------------------------------------------------------- /ui/paniniWin.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "panini-icon-blue.ico" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile* 2 | Panini 3 | panini 4 | build/ 5 | release/ 6 | *.pro.user 7 | *.stash 8 | -------------------------------------------------------------------------------- /ui/panini-icon-blue.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazarus-pkgs/panini/HEAD/ui/panini-icon-blue.ico -------------------------------------------------------------------------------- /ui/panini-icon-blue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazarus-pkgs/panini/HEAD/ui/panini-icon-blue.jpg -------------------------------------------------------------------------------- /ui/PaniniIcon.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | panini-icon-blue.jpg 4 | 5 | 6 | -------------------------------------------------------------------------------- /linux/panini.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Panini 3 | GenericName=Panini perspective tool 4 | Comment=Perspective views from panoramic images 5 | Exec=panini %f 6 | Terminal=false 7 | Type=Application 8 | Icon=panini 9 | Categories=Graphics;Viewer; 10 | MimeType=video/quicktime;image/jpeg;image/tiff;image/png; 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # panini 2 | 3 | Panini is a visual tool for creating perspective views from panoramic and wide angle photographs. More than a pano viewer, more than a view camera, with features of both. For Linux/Unix, Win32, and Mac systems with OpenGL 2.0+. 4 | 5 | ## Installation 6 | See [INSTALL](INSTALL.md). 7 | ## Dependencies 8 | 9 | * Qt5 10 | * zlib 11 | 12 | ## Get source code 13 | 14 | ``` 15 | git clone https://github.com/lazarus-pkgs/panini 16 | cd panini 17 | ``` 18 | 19 | ## Compile 20 | 21 | ``` 22 | qmake #qmake-qt5 on some systems 23 | make 24 | ./panini 25 | ``` 26 | 27 | Also see: [BUILD](BUILD.md). 28 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jubalh 4 | patreon: jubalh 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Panini is free software, 2 | copyright (c) 2008-09 Thomas K Sharpless. 3 | copyright (c) 2017 Michael Vetter. 4 | You can redistribute and/or modify it under the terms of the GNU General 5 | Public License as published by the Free Software Foundation; either 6 | version 3 of the License, or (at your option) any later version. 7 | 8 | Panini is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11 | for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with Panini; if not, write to Free Software Foundation, Inc., 15 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | -------------------------------------------------------------------------------- /MSVS-project/panini.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvQt_1", "pvQt_1\pvQt_1.vcproj", "{A70594CC-2C31-4F76-AC81-DB3B0475F57E}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {A70594CC-2C31-4F76-AC81-DB3B0475F57E}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {A70594CC-2C31-4F76-AC81-DB3B0475F57E}.Debug|Win32.Build.0 = Debug|Win32 14 | {A70594CC-2C31-4F76-AC81-DB3B0475F57E}.Release|Win32.ActiveCfg = Release|Win32 15 | {A70594CC-2C31-4F76-AC81-DB3B0475F57E}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | If you want to compile from source please read [BUILD](BUILD.md). 4 | 5 | ## Binary packages 6 | Binary packages of Panini built by [OBS](http://openbuildservice.org/) can be downloaded [here](https://software.opensuse.org/download.html?project=home:jubalh:panini&package=panini). 7 | 8 | Currently we are building packages for: 9 | * CentOS 7 10 | * Fedora 27 11 | * Fedora Rawhide 12 | * SLE 12 SP3 13 | * openSUSE Leap 42.3 14 | * openSUSE Tumblweed 15 | 16 | To install it on openSUSE you can also type: 17 | ``` 18 | zypper addrepo -f obs://home:jubalh:panini panini 19 | zypper refresh 20 | zypper in panini 21 | ``` 22 | 23 | ## AppImage 24 | An easy way to run Panini on any Linux distribution without installation is to use the AppImage. 25 | 26 | * Download it from the [release](https://github.com/lazarus-pkgs/panini/releases) page. 27 | * Make it executable (`chmod u+x panini-0.72-x86_64.AppImage` or right click -> properties -> permissions) 28 | * Click on it 29 | -------------------------------------------------------------------------------- /src/About.h: -------------------------------------------------------------------------------- 1 | /* 2 | (C) copyright 2008, 209 Thomas K Sharpless 3 | 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This file is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this file; if not, write to Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | */ 18 | 19 | #ifndef ABOUT_H 20 | #define ABOUT_H 21 | 22 | #include "ui_About.h" 23 | 24 | class pvQtAbout 25 | : public QDialog, public Ui_AboutDialog 26 | { 27 | Q_OBJECT 28 | public: 29 | pvQtAbout( QWidget * parent = 0 ); 30 | void setInfo( QString info ); 31 | }; 32 | 33 | #endif //ndef ABOUT_H 34 | -------------------------------------------------------------------------------- /ui/ShowText.ui: -------------------------------------------------------------------------------- 1 | 2 | ShowText 3 | 4 | 5 | 6 | 0 7 | 0 8 | 371 9 | 249 10 | 11 | 12 | 13 | 14 | Sans Serif 15 | 10 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 10 25 | 10 26 | 351 27 | 231 28 | 29 | 30 | 31 | 32 | Sans Serif 33 | 10 34 | 35 | 36 | 37 | false 38 | 39 | 40 | true 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/About.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * About.cpp for pvQt 3 | 4 | (C) copyright 2008, 209 Thomas K Sharpless 5 | 6 | * This file is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This file is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this file; if not, write to Free Software Foundation, Inc., 18 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | **/ 20 | 21 | #include "About.h" 22 | 23 | pvQtAbout::pvQtAbout( QWidget * parent ) 24 | : QDialog( parent ) 25 | { 26 | setupUi( this ); 27 | VersionLabel->setText( QString(VERSION) ); 28 | setFixedSize(425, 320); 29 | } 30 | 31 | void pvQtAbout::setInfo( QString info ) 32 | { 33 | lblSystemInfo->setText( info ); 34 | } 35 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* main.cpp for pvQt 08Sep2008 2 | * Copyright (C) 2008 Thomas K Sharpless 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This file is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this file; if not, write to Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | * 18 | */ 19 | 20 | #include 21 | 22 | #include "MainWindow.h" 23 | 24 | int main(int argc, char **argv ) 25 | { 26 | QApplication app(argc, argv); 27 | 28 | MainWindow *window = new MainWindow; 29 | // ok, run the GUI... 30 | window->show(); 31 | // process commandline, abort if fails 32 | if( !window->postArgs(argc, argv) ) return 3; 33 | // event loop 34 | return app.exec(); 35 | } 36 | -------------------------------------------------------------------------------- /src/panocylinder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * panocylinder.h for Panini 29 Jan 2009 TKS 3 | 4 | Arrays of vertices on a unit cylinder and their 5 | 2D texture coordinates for various projections, 6 | plus arrays of linear indices that map the 7 | vertices to line segments and quadrilaterals. 8 | 9 | * 10 | * Copyright (C) 2009 Thomas K Sharpless 11 | * 12 | * This file is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This file is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this file; if not, write to Free Software Foundation, Inc., 24 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 | * 26 | */ 27 | 28 | #ifndef PANOCYLINDER_H 29 | #define PANOCYLINDER_H 30 | #include "panosurface.h" 31 | 32 | class panocylinder : public panosurface { 33 | public: 34 | panocylinder( int divs = 120 ); 35 | }; 36 | #endif //ndef PANOCYLINDER_H 37 | -------------------------------------------------------------------------------- /src/CubeLimit_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | (C) copyright 2008, 209 Thomas K Sharpless 3 | 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This file is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this file; if not, write to Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | */ 18 | 19 | #ifndef CUBELIMIT_DIALOG_H 20 | #define CUBELIMIT_DIALOG_H 21 | 22 | #include "ui_CubeLimit_dialog.h" 23 | 24 | class CubeLimit_dialog 25 | : public QDialog, 26 | public Ui_Dialog { 27 | Q_OBJECT 28 | public: 29 | CubeLimit_dialog( int lim, QWidget * parent = 0 ) 30 | : QDialog( parent ) { 31 | setupUi( this ); 32 | setLimit( lim ); 33 | } 34 | int limit(){ return spinBox->value(); } 35 | void setLimit( int lim ){ spinBox->setValue( lim ); } 36 | }; 37 | 38 | #endif // CUBELIMIT_DIALOG_H 39 | -------------------------------------------------------------------------------- /linux/panini.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | panini.desktop 7 | CC0-1.0 8 | Panini 9 | Create perspective views from panoramic and wide angle images 10 | 11 |

12 | Panini is a tool for creating perspective views from panoramic and wide angle images. 13 |

14 |

15 | Panini can load most common photo and panoramic formats from image files such 16 | as those created with hugin or QuickTimeVR (QTVR .mov) files. Like all pano 17 | viewers, it then shows a linear perspective view that can be panned and zoomed. 18 | But Panini can also display a range of wide angle perspectives via the 19 | stereographic and "Pannini" vedutismo families of projections, and shift, 20 | rotate, and stretch the image like a software view camera. 21 |

22 |

23 | Panini can do those things because it paints the picture on a three dimensional 24 | surface, either a sphere or a cylinder, which you then view in perspective. 25 | Shifting the point of view changes the apparent perspective of the image, and 26 | other controls let you frame the view to your liking. Then you can save the 27 | screen image to a file at higher-than-screen resolution. 28 |

29 |
30 | 31 | https://lazarus-pkgs.github.io/lazarus-pkgs/panini.html 32 | jubalh AT iodoru DOT org 33 |
34 | -------------------------------------------------------------------------------- /src/panosphere.h: -------------------------------------------------------------------------------- 1 | /* 2 | * panosphere.h for Panini 29 Jan 2009 TKS 3 | Arrays of vertices on the unit sphere and their 4 | 2D texture coordinates for various projections, 5 | plus arrays of linear indices that map the 6 | vertices to line segments and quadrilaterals. 7 | 8 | The sphere is subdivided starting from the corners 9 | of the inscribed cube whose faces are centered on 10 | the coordinate axes. The number of subdivisions 11 | along each cube edge, divs, is the c'tor argument. 12 | divs will be rounded up to the next even number if 13 | odd. There are 6 * divs * divs quads in the final 14 | tesselation. 15 | 16 | The vertices are unit 3-vectors, and are also the 17 | normals OGL needs to generate cubic texture 18 | coordinates. 19 | 20 | * 21 | * Copyright (C) 2009 Thomas K Sharpless 22 | * 23 | * This file is free software; you can redistribute it and/or modify 24 | * it under the terms of the GNU General Public License as published by 25 | * the Free Software Foundation; either version 3 of the License, or 26 | * (at your option) any later version. 27 | * 28 | * This file is distributed in the hope that it will be useful, 29 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | * GNU General Public License for more details. 32 | * 33 | * You should have received a copy of the GNU General Public License 34 | * along with this file; if not, write to Free Software Foundation, Inc., 35 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 36 | */ 37 | 38 | #ifndef PANOSPHERE_H 39 | #define PANOSPHERE_H 40 | #include "panosurface.h" 41 | 42 | class panosphere : public panosurface { 43 | public: 44 | panosphere( int divs = 30 ); 45 | }; 46 | #endif //ndef PANOSPHERE_H 47 | -------------------------------------------------------------------------------- /src/pvQtMouseModes.h: -------------------------------------------------------------------------------- 1 | /* 2 | (C) copyright 2008, 209 Thomas K Sharpless 3 | 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This file is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this file; if not, write to Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | */ 18 | #ifndef MouseModes_H 19 | #define MouseModes_H 20 | 21 | #include "ui_ShowText.h" 22 | 23 | class pvQtMouseModes 24 | : public QDialog, public Ui_ShowText 25 | { 26 | Q_OBJECT 27 | public: 28 | pvQtMouseModes( QWidget * parent = 0 ) 29 | : QDialog(parent) { 30 | setupUi( this ); 31 | setWindowTitle(" Panini Mouse Modes"); 32 | plainTextEdit->setPlainText( QString( "\ 33 | Key Buttons Horizontal Vertical\n\ 34 | left Yaw Pitch\n\ 35 | right EyeZ Zoom\n\ 36 | both Roll Pitch\n\ 37 | Shift left FrameX FrameY\n\ 38 | Shift right EyeX EyeY\n\ 39 | Shift both hFov vFov\n\ 40 | \n\ 41 | Hold Control key for horizontal-only\n\ 42 | Hold Alt key for vertical-only\n\ 43 | \n\ 44 | Scroll wheel: Zoom ")); 45 | } 46 | protected: 47 | void resizeEvent( QResizeEvent * ev ){ 48 | int r = ev->size().width(), 49 | b = ev->size().height(); 50 | plainTextEdit->setGeometry(QRect(10, 10, r - 18, b - 18)); 51 | } 52 | 53 | }; 54 | 55 | #endif //ndef MouseModes_H 56 | -------------------------------------------------------------------------------- /src/TurnDialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TurnDialog.h for Panini 0.62 27 Jan 2009 3 | * Copyright (C) 2009 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | * 19 | Modal dialog, sets orientation of image on panosurface. 20 | C'tor sets default all 0 orientation. This can be 21 | changed by calling setTurn(); 22 | emits signal newTurn on user change only 23 | slot setTurn silently clips settings legal 24 | 25 | turn is number of 90 degree CW steps 0:3 26 | roll and pitch are in degrees 27 | 28 | */ 29 | 30 | #ifndef TURNDIALOG_H 31 | #define TURNDIALOG_H 32 | 33 | #include "ui_TurnDialog.h" 34 | 35 | class TurnDialog 36 | : public QDialog, public Ui_TurnDialog 37 | { 38 | Q_OBJECT 39 | public: 40 | TurnDialog( QWidget * parent = 0 ); 41 | void getTurn( int& turn, double& roll, double& pitch, double& yaw ); 42 | void enableYaw( bool enb = true ); 43 | void enablePitch( bool enb = true ); 44 | void enableTurn( bool enb = true ); 45 | signals: 46 | void newTurn( int turn, double roll, double pitch, double yaw ); 47 | public slots: 48 | void setTurn( int turn, double roll, double pitch, double yaw ); 49 | private slots: 50 | void onTurnList_currentIndexChanged(int i); 51 | void onRollBox_valueChanged(double i); 52 | void onPitchBox_valueChanged(double i); 53 | void onYawBox_valueChanged(double i); 54 | private: 55 | bool turnEnb; 56 | bool pitchEnb, yawEnb; 57 | }; 58 | #endif //ndef TURNDIALOG_H 59 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | To build Panini with Qt Creator, open "Panini.pro" (a simple double click should work) and select "build all" or "build project" on the Build menu. 4 | 5 | To build from a command shell (on Windows, use the one supplied with Qt): In the panini directory, type: 6 | `qmake panini.pro` 7 | 8 | On Linux or Windows that command will create Makefiles, on OSX it creates an XCode project, that you can open and build. 9 | To get a Makefile instead, use: 10 | `qmake -spec macx-g++` 11 | 12 | Qt Creator issues this command, too. 13 | 14 | The version number of panini is defined in the `.pro` file. 15 | 16 | To build on Windows or Linux (or from a Mac Makefile) type `make release` or `make debug`. 17 | The resulting executable will be Release/Panini or Debug/Panini on Windows or Linux; on OSX, possibly just Panini.app in the package root. 18 | 19 | To install, just copy Panini.exe or Panini.app to wherever you like. On Windows, you may have to add the Qt4 bin directory to the "all users" search path, though that will probably have been done when Qt was installed. This is important because that directory contains required plugin subdirectories as well as the Qt DLLs (DON'T copy Qt DLL's to Windows\System32. Any system which requires that is too old to run Panini). If you have a preexisting installation of MingGW, it is OK to put the MinGW DLL in System32; but if you installed MinGw with Qt, there will be a copy of the MinGW DLL in Qt4 bin. 20 | 21 | The qmake project file, Panini.pro, is set up to build both debug and release versions (release by default, except XCode always defaults to debug). This has some quirks which you should be aware of. There are two subordinate Makefiles. "make release" runs one, building executable bin/panini and "make debug" runs the other, building bin/panini-d; plain "make" runs the default build. However both builds use the same object file names; so if you run "make release" then "make debug", it may incorrectly create panini by linking the existing release object files, which lack symbol tables and so won't work with the debugger, or vice versa. The safest thing is to "make clean" when swtiching configurations. 22 | 23 | Qt Creator (version 4.5) will show release and debug build configurations and a single "run configuration" just called Panini. That actually runs the exe corresponding to the current build configuration (both the "run" and the "debug" buttons do this) so to debug you have to first select the debug build. 24 | -------------------------------------------------------------------------------- /src/picTypeDialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * picTypeDialog.h for pvQt 06 Oct 2008 3 | * Copyright (C) 2008 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | * 19 | 20 | The user is shown a "source" name (filename or url) 21 | and asked to choose the appropriate picture type and 22 | the angular size of the longer image axis. Type 23 | is just the index of the selected line in a combobox, 24 | which you must load with meaningful strings. 25 | 26 | FOVs are passed and returned as QSizeF objects, so there are 27 | lower and upper limits for both axes. However only the fov 28 | of the longer (or 1st) axis is changed (the other is returned 29 | as zero). You must set valid image dimensions before running 30 | the dialog, and calculate the "short" fov afterward. 31 | */ 32 | 33 | #ifndef PICTYPEDIALOG_H 34 | #define PICTYPEDIALOG_H 35 | 36 | #include "ui_picTypeDialog.h" 37 | 38 | class picTypeDialog 39 | : public QDialog, public Ui_picTypeDialog 40 | { 41 | Q_OBJECT 42 | public: 43 | picTypeDialog( QWidget * parent = 0 ); 44 | void setNameLabel( QString name ); 45 | void setPicTypes( QStringList types ); 46 | void selectPicType( int t, bool lock = false ); 47 | void setDims( QSize wh ); 48 | void setMinFOV( QSizeF fovs ); 49 | void setMaxFOV( QSizeF fovs ); 50 | void setFOV( QSizeF fovs ); 51 | void setFreeFovs( bool val ); 52 | 53 | int chosenType(); 54 | QSizeF getFOV(); 55 | //state of "unlock" checkbox 56 | bool freeFovs(); 57 | 58 | signals: 59 | void picTypeSelected( int t ); // also free toggled 60 | void hFovChanged( double h ); 61 | void vFovChanged( double v ); 62 | 63 | private slots: 64 | void freeToggled( bool ckd ); 65 | private: 66 | QSize dims; 67 | QSizeF minfov, maxfov, thefov; 68 | 69 | }; 70 | 71 | #endif //ndef PICTYPEDIALOG_H 72 | -------------------------------------------------------------------------------- /panini.pro: -------------------------------------------------------------------------------- 1 | ## qmake project for panini ## 2 | TEMPLATE = app 3 | VERSION = 0.73.0 4 | TARGET = panini 5 | CONFIG += debug_and_release 6 | QT = gui core opengl 7 | LIBS += -lz -lGLU 8 | 9 | # We want Qt5 now 10 | lessThan(QT_MAJOR_VERSION, 5): error("requires Qt 5") 11 | 12 | ## Directories ## 13 | OBJECTS_DIR = build 14 | MOC_DIR = build 15 | UI_DIR = build 16 | 17 | ## Platform specific build settings.... ## 18 | 19 | ## TODO: add option to make Mac OS unversal binaries; 20 | ## add scripts to build distribution packages 21 | 22 | win32 { 23 | # this is just for zlib, change if necessary... 24 | INCLUDEPATH += c:/MinGW/GnuWin32/include 25 | # this sets the program icon the Windows way... 26 | RC_FILE = ui/paniniWin.rc 27 | } 28 | 29 | ## Source Files ## 30 | FORMS = ui/mainwindow.ui 31 | HEADERS = src/pvQtPic.h \ 32 | src/CubeLimit_dialog.h 33 | SOURCES = src/main.cpp \ 34 | src/pvQtPic.cpp 35 | HEADERS += src/pvQtView.h \ 36 | src/MainWindow.h \ 37 | src/GLwindow.h 38 | SOURCES += src/pvQtView.cpp \ 39 | src/MainWindow.cpp \ 40 | src/GLwindow.cpp 41 | HEADERS += src/pvQt_QTVR.h 42 | SOURCES += src/pvQt_QTVR.cpp 43 | FORMS += ui/picTypeDialog.ui 44 | HEADERS += src/picTypeDialog.h 45 | SOURCES += src/picTypeDialog.cpp \ 46 | src/pictureTypes.cpp 47 | FORMS += ui/About.ui 48 | HEADERS += src/About.h 49 | HEADERS += src/panosurface.h 50 | SOURCES += src/panosurface.cpp 51 | HEADERS += src/panosphere.h 52 | SOURCES += src/panosphere.cpp 53 | HEADERS += src/panocylinder.h 54 | SOURCES += src/panocylinder.cpp 55 | FORMS += ui/ShowText.ui 56 | HEADERS += src/pvQtMouseModes.h 57 | FORMS += ui/TurnDialog.ui 58 | HEADERS += src/TurnDialog.h 59 | SOURCES += src/TurnDialog.cpp 60 | RESOURCES = ui/PaniniIcon.qrc 61 | FORMS += ui/CubeLimit_dialog.ui 62 | SOURCES += src/About.cpp 63 | 64 | ## Version Number ## 65 | DEFINES += VERSION=\\\"$$VERSION\\\" 66 | 67 | ## Install Files ## 68 | 69 | # Location 70 | isEmpty( PREFIX ) { 71 | PREFIX = /usr 72 | } 73 | 74 | # binary to /usr/bin 75 | target.path = $$PREFIX$$/bin 76 | INSTALLS += target 77 | 78 | linux-g++* { 79 | ## Desktop File ## 80 | desktopfile.path = $$PREFIX$$/share/applications/ 81 | desktopfile.files = linux/*.desktop 82 | INSTALLS += desktopfile 83 | 84 | # Icon File 85 | iconfile.path = $$PREFIX$$/share/pixmaps/ 86 | iconfile.files = linux/panini.png 87 | INSTALLS += iconfile 88 | 89 | # Appdata file 90 | appdatafile.path = $$PREFIX$$/share/metainfo/ 91 | appdatafile.files = linux/panini.appdata.xml 92 | INSTALLS += appdatafile 93 | } 94 | -------------------------------------------------------------------------------- /ui/CubeLimit_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 304 10 | 95 11 | 12 | 13 | 14 | Panini - Mac cube size limit 15 | 16 | 17 | 18 | 19 | -390 20 | 60 21 | 621 22 | 32 23 | 24 | 25 | 26 | Qt::Horizontal 27 | 28 | 29 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 30 | 31 | 32 | 33 | 34 | 35 | 230 36 | 20 37 | 61 38 | 22 39 | 40 | 41 | 42 | 512 43 | 44 | 45 | 4096 46 | 47 | 48 | 1536 49 | 50 | 51 | 52 | 53 | 54 | 30 55 | 20 56 | 191 57 | 21 58 | 59 | 60 | 61 | Maximum cube face dimension (pixels) 62 | 63 | 64 | 65 | 66 | 67 | 68 | buttonBox 69 | accepted() 70 | Dialog 71 | accept() 72 | 73 | 74 | 248 75 | 254 76 | 77 | 78 | 157 79 | 274 80 | 81 | 82 | 83 | 84 | buttonBox 85 | rejected() 86 | Dialog 87 | reject() 88 | 89 | 90 | 316 91 | 260 92 | 93 | 94 | 286 95 | 274 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /mac/complete-bundle.sh: -------------------------------------------------------------------------------- 1 | # complete-bundle.sh, May 2009, Harry van der Wolf 2 | # This shell script is neccessary to create a complete 3 | # and portable Panini for MacOSX 10.4 and newer. 4 | # Version 0.3, 2009-06-20 5 | 6 | #################################################################### 7 | # You should not need to modify anything below these comment lines # 8 | #################################################################### 9 | App_path=./build/Release/Panini.app 10 | Basic_path=./mac 11 | FrameW_path=$App_path/Contents/Frameworks 12 | plugins_path=$App_path/Contents/plugins 13 | Resources_path=$App_path/Contents/Resources 14 | 15 | # Copy QT frameworks into bundle 16 | mkdir -p $App_path/Contents/Frameworks 17 | rsync -av --exclude 'Headers/*' /Library/Frameworks/QtCore.framework $FrameW_path 18 | rsync -av --exclude 'Headers/*' /Library/Frameworks/QtGui.framework $FrameW_path 19 | rsync -av --exclude 'Headers/*' /Library/Frameworks/QtOpenGL.framework $FrameW_path 20 | 21 | # Copy QT plugins and config 22 | mkdir -p $plugins_path 23 | mkdir -p $plugins_path/imageformats 24 | #rsync -av /Developer/Applications/Qt/plugins/imageformats $plugins_path 25 | cp -f /Developer/Applications/Qt/plugins/imageformats/libqjpeg.dylib $plugins_path/imageformats 26 | cp -f /Developer/Applications/Qt/plugins/imageformats/libqtiff.dylib $plugins_path/imageformats 27 | cp -f /Developer/Applications/Qt/plugins/imageformats/libqmng.dylib $plugins_path/imageformats 28 | mkdir -p $Resources_path 29 | cp $Basic_path/qt.conf $Resources_path 30 | 31 | 32 | # Copy applications icons 33 | cp $Basic_path/appIcon.icns $Resources_path 34 | 35 | # Do the necessary install_name changes 36 | install_name_tool -id "@executable_path/../Frameworks/QtCore.framework/QtCore" "$FrameW_path/QtCore.framework/QtCore" 37 | install_name_tool -id "@executable_path/../Frameworks/QtGui.framework/QtGui" "$FrameW_path/QtGui.framework/QtGui" 38 | install_name_tool -id "@executable_path/../Frameworks/QtOpenGL.framework/QtOpenGL" "$FrameW_path/QtOpenGL.framework/QtOpenGL" 39 | 40 | binaries="$FrameW_path/QtCore.framework/QtCore $FrameW_path/QtOpenGL.framework/QtOpenGL $FrameW_path/QtGui.framework/QtGui $App_path/Contents/MacOS/*" 41 | # $plugins_path/imageformats/*.dylib 42 | #binaries="$App_path/Contents/MacOS/*" 43 | #old_install_name_dirname="Qt" 44 | new_install_name_dirname="@executable_path/../Frameworks" 45 | 46 | #First the Frameworks 47 | for exec_file in $binaries 48 | do 49 | # for lib in $(otool -L $exec_file | grep Qt | sed -e 's/ (.*$//' -e 's/^.*\///') 50 | for lib in $(otool -L $exec_file | grep Qt) 51 | do 52 | echo " Changing install name for: $lib" 53 | install_name_tool -change "$lib" "$new_install_name_dirname/$lib" $exec_file 54 | done 55 | done 56 | #Now the plugins 57 | binaries="$plugins_path/imageformats/*.dylib" 58 | for exec_file in $binaries 59 | do 60 | for lib in $(otool -L $exec_file | grep Qt) 61 | do 62 | echo " Changing install name for: $lib" 63 | install_name_tool -change "$lib" "$new_install_name_dirname/$lib" $exec_file 64 | done 65 | done 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/picTypeDialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * picTypeDialog.cpp for pvQt 0.5 24 Nov 2008 TKS 3 | (C) copyright 2008, 209 Thomas K Sharpless 4 | 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | */ 19 | 20 | #include "picTypeDialog.h" 21 | 22 | picTypeDialog::picTypeDialog( QWidget * parent) 23 | : QDialog( parent ) 24 | { 25 | setupUi( this ); 26 | setDims( QSize(0,0) ); 27 | thefov = QSizeF( 360, 360 ); 28 | setMinFOV( QSizeF( 5, 5 )); 29 | setMaxFOV( QSizeF( 360, 360 )); 30 | 31 | connect(typesBox, static_cast(&QComboBox::currentIndexChanged), this, &picTypeDialog::picTypeSelected); 32 | connect(nonSqOK, &QCheckBox::toggled, this, &picTypeDialog::freeToggled); 33 | connect(hfovBox, static_cast(&QDoubleSpinBox::valueChanged), this, &picTypeDialog::hFovChanged); 34 | connect(vfovBox, static_cast(&QDoubleSpinBox::valueChanged), this, &picTypeDialog::vFovChanged); 35 | } 36 | 37 | void picTypeDialog::setNameLabel( QString name ){ 38 | nameLabel->setText( name ); 39 | } 40 | 41 | void picTypeDialog::setPicTypes( QStringList types ){ 42 | typesBox->blockSignals( true ); 43 | typesBox->clear(); 44 | typesBox->addItems( types ); 45 | typesBox->blockSignals( false ); 46 | } 47 | 48 | void picTypeDialog::setDims( QSize wh ){ 49 | dims = wh; 50 | widPixels->setText(QString::number( wh.width())); 51 | hgtPixels->setText(QString::number( wh.height())); 52 | } 53 | 54 | void picTypeDialog::setMinFOV( QSizeF fovs ){ 55 | minfov = fovs; 56 | hfovBox->blockSignals(true); 57 | vfovBox->blockSignals(true); 58 | hfovBox->setMinimum( minfov.width() ); 59 | vfovBox->setMinimum( minfov.height() ); 60 | setFOV( thefov ); 61 | hfovBox->blockSignals(false); 62 | vfovBox->blockSignals(false); 63 | } 64 | 65 | void picTypeDialog::setMaxFOV( QSizeF fovs ){ 66 | maxfov = fovs; 67 | hfovBox->blockSignals(true); 68 | vfovBox->blockSignals(true); 69 | hfovBox->setMaximum( maxfov.width() ); 70 | vfovBox->setMaximum( maxfov.height() ); 71 | setFOV( thefov ); 72 | hfovBox->blockSignals(false); 73 | vfovBox->blockSignals(false); 74 | } 75 | 76 | void picTypeDialog::setFOV( QSizeF fovs ){ 77 | thefov = fovs.expandedTo(minfov).boundedTo(maxfov); 78 | hfovBox->setValue( thefov.width() ); 79 | vfovBox->setValue( thefov.height() ); 80 | } 81 | 82 | void picTypeDialog::setFreeFovs( bool val ){ 83 | nonSqOK->setChecked( val ); 84 | } 85 | 86 | bool picTypeDialog::freeFovs(){ 87 | return nonSqOK->isChecked(); 88 | } 89 | 90 | 91 | QSizeF picTypeDialog::getFOV(){ 92 | return thefov; 93 | } 94 | 95 | void picTypeDialog::selectPicType( int t, bool lock ){ 96 | typesBox->setCurrentIndex( t ); 97 | typesBox->setEnabled( !lock ); 98 | } 99 | 100 | int picTypeDialog::chosenType(){ 101 | return typesBox->currentIndex(); 102 | } 103 | 104 | void picTypeDialog::freeToggled( bool ckd ){ 105 | emit picTypeSelected( typesBox->currentIndex() ); 106 | } 107 | -------------------------------------------------------------------------------- /src/GLwindow.h: -------------------------------------------------------------------------------- 1 | /* GLwindow.h for pvQt_1 2 | * Copyright (C) 2008 Thomas K Sharpless 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This file is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this file; if not, write to Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | * 18 | 19 | A "glue widget", required to make pvQtView work inside QMainWindow. 20 | Also provides command line and input file handling. 21 | */ 22 | 23 | #include 24 | #include "pvQtPic.h" 25 | #include "picTypeDialog.h" 26 | #include "About.h" 27 | #include "TurnDialog.h" 28 | 29 | class pvQtView; 30 | class pvQtPic; 31 | 32 | class GLwindow : public QWidget { 33 | Q_OBJECT 34 | public: 35 | GLwindow(QWidget * parent = 0); 36 | bool isOK(){ return ok; } 37 | bool commandLine( int argc, char ** argv ); 38 | signals: 39 | void showTitle( QString msg ); 40 | void showProj( QString name ); 41 | void showFov( QSizeF fovs ); 42 | void showSurface( int surf ); 43 | 44 | public slots: 45 | // from mainwindow 46 | void newPicture( const char * type ); 47 | void about_pvQt(); 48 | void save_as(); 49 | void set_surface( int surf ); 50 | void turn90( int t ); 51 | void setCubeLimit( int ); 52 | // from picType dialog... 53 | void picTypeChanged( int t ); 54 | void hFovChanged( double h ); 55 | void vFovChanged( double v ); 56 | // from pvQtView... 57 | void OGLerror( QString msg); 58 | void reportTurn( int turn, double roll, double pitch, double yaw ); 59 | void reset_turn(); 60 | void overlayCtl( int c ); 61 | 62 | protected: 63 | void resizeEvent( QResizeEvent * ev ); 64 | 65 | private: 66 | bool QTVR_file( QString name ); 67 | bool choosePictureFiles( const char * picTypeName = 0 ); 68 | bool loadPictureFiles( QStringList names ); 69 | const QStringList picTypeDescrs(); 70 | const char * askPicType( QStringList files, 71 | const char * ptyp = 0 ); 72 | bool loadTypedFiles( const char * type, QStringList files ); 73 | void reportPic( bool ok = true, int c = -1, QStringList files = QStringList() ); 74 | void dragEnterEvent(QDragEnterEvent * event); 75 | void dropEvent(QDropEvent * event); 76 | 77 | pvQtAbout * aboutbox; 78 | TurnDialog turndialog; 79 | picTypeDialog ptd; 80 | // display widget 81 | pvQtView * glview; 82 | // picture maker 83 | pvQtPic * pvpic; 84 | // true if created w/o error 85 | bool ok; 86 | pictureTypes pictypes; 87 | 88 | // current picture type index, or -1 89 | int ipt; 90 | pvQtPic::PicType picType; 91 | // current FOV 92 | QSizeF picFov; 93 | // current size 94 | QSize picDim; 95 | QSizeF lastFOV[NpictureTypes]; 96 | int lastTurn[NpictureTypes]; 97 | double lastRoll[NpictureTypes]; 98 | double lastPitch[NpictureTypes]; 99 | double lastYaw[NpictureTypes]; 100 | QString errmsg; 101 | 102 | QString loaddir, savedir; 103 | //no. of image files now loaded 104 | int loadcount; 105 | //filename displayed in title 106 | QString loadname; 107 | 108 | // V0.7 support for overlay image 109 | QSize screenSize; 110 | QImage * ovlyImg; 111 | bool ovlyVisible; 112 | int ovlyFade; 113 | bool loadOverlayImage(); 114 | void setImgAlpha( QImage * pim, double alpha ); 115 | void diceImgAlpha( QImage * pim, double alpha, int dw ); 116 | }; 117 | -------------------------------------------------------------------------------- /src/TurnDialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TurnDialog.cpp for Panini 0.62 27 Jan 2009 3 | * Copyright (C) 2009 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | * 19 | */ 20 | 21 | #include "TurnDialog.h" 22 | 23 | TurnDialog::TurnDialog( QWidget * parent ) 24 | : QDialog( parent ) 25 | { 26 | setupUi( this ); 27 | 28 | connect(TurnList, static_cast(&QComboBox::currentIndexChanged), this, &TurnDialog::onTurnList_currentIndexChanged); 29 | connect(RollBox, static_cast(&QDoubleSpinBox::valueChanged), this, &TurnDialog::onRollBox_valueChanged); 30 | connect(PitchBox, static_cast(&QDoubleSpinBox::valueChanged), this, &TurnDialog::onPitchBox_valueChanged); 31 | connect(YawBox, static_cast(&QDoubleSpinBox::valueChanged), this, &TurnDialog::onYawBox_valueChanged); 32 | 33 | TurnList->addItem(tr(" Normal")); 34 | TurnList->addItem(tr(" 90 deg CW")); 35 | TurnList->addItem(tr(" Invert")); 36 | TurnList->addItem(tr(" 90 deg CCW")); 37 | enableTurn(); 38 | enablePitch(); 39 | setTurn( 0, 0, 0, 0 ); 40 | } 41 | 42 | void TurnDialog::getTurn( int& turn, double& roll, double& pitch, double &yaw ){ 43 | turn = TurnList->currentIndex(); 44 | roll = RollBox->value(); 45 | pitch = PitchBox->value(); 46 | yaw = YawBox->value(); 47 | } 48 | 49 | void TurnDialog::setTurn( int turn, double roll, double pitch, double yaw ){ 50 | blockSignals( true ); 51 | TurnList->setCurrentIndex( turn & 3 ); 52 | RollBox->setValue( roll ); 53 | PitchBox->setValue( pitch ); 54 | YawBox->setValue( yaw ); 55 | blockSignals( false ); 56 | } 57 | 58 | void TurnDialog::enableTurn( bool enb ){ 59 | TurnList->setEnabled( enb ); 60 | if( !enb || !turnEnb ) { 61 | TurnList->setCurrentIndex( 0 ); 62 | } 63 | turnEnb = enb; 64 | } 65 | 66 | void TurnDialog::enablePitch( bool enb ){ 67 | PitchBox->setEnabled( enb ); 68 | if( !enb || !pitchEnb ) { 69 | PitchBox->setValue( 0 ); 70 | } 71 | pitchEnb = enb; 72 | } 73 | 74 | void TurnDialog::enableYaw( bool enb ){ 75 | YawBox->setEnabled( enb ); 76 | if( !enb || !yawEnb ) { 77 | YawBox->setValue( 0 ); 78 | } 79 | yawEnb = enb; 80 | } 81 | 82 | void TurnDialog::onTurnList_currentIndexChanged(int i){ 83 | if( turnEnb ) { 84 | emit newTurn( TurnList->currentIndex(), 85 | RollBox->value(), PitchBox->value(), 86 | YawBox->value() ); 87 | } 88 | } 89 | 90 | void TurnDialog::onRollBox_valueChanged(double i){ 91 | emit newTurn( TurnList->currentIndex(), 92 | RollBox->value(), PitchBox->value(), 93 | YawBox->value() ); 94 | } 95 | 96 | void TurnDialog::onPitchBox_valueChanged(double i){ 97 | if( pitchEnb ) emit newTurn( TurnList->currentIndex(), 98 | RollBox->value(), PitchBox->value(), 99 | YawBox->value() ); 100 | } 101 | 102 | void TurnDialog::onYawBox_valueChanged(double i){ 103 | if( pitchEnb ) emit newTurn( TurnList->currentIndex(), 104 | RollBox->value(), PitchBox->value(), 105 | YawBox->value() ); 106 | } 107 | -------------------------------------------------------------------------------- /src/pvQt_QTVR.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Qt_QTVR.h for pvQt 03 Oct 2008 TKS 3 | * 4 | * To make QTVRDecoder work with Qt imaging components 5 | * 6 | * Adapted from QTVRDecoder.h, part of the freepv panoramic 7 | * viewer by Pablo d'Angelo 8 | * 9 | * $Id: QTVRDecoder.h 64 2006-10-08 10:54:23Z dangelo $ 10 | * 11 | * This file is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This file is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this file; if not, write to Free Software Foundation, Inc., 23 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 | * 25 | */ 26 | 27 | /* declares a slightly stripped down version of QTVRDecoder. */ 28 | 29 | #ifndef QT_QTVR_H 30 | #define QT_QTVR_H 31 | 32 | #include 33 | #include 34 | 35 | /** possible QTVR panorama types **/ 36 | enum PanoType { PANO_UNKNOWN, PANO_CUBIC, PANO_CYLINDRICAL }; 37 | 38 | // various typedefs required by the structs copied from various places 39 | typedef unsigned int uint32; 40 | typedef int int32; 41 | typedef short int16; 42 | typedef unsigned short uint16; 43 | 44 | #define MAX_TILE_DIMENSIONS 10 // this is the maximum # of tiles x/y that we'll support 45 | #define MAX_TILES_PER_FACE (MAX_TILE_DIMENSIONS * MAX_TILE_DIMENSIONS) 46 | 47 | #define MAX_IMAGE_OFFSETS (MAX_TILES_PER_FACE*6) 48 | 49 | #define MAX_REF_TRACKS 10 50 | 51 | struct SampleToChunkEntry 52 | { 53 | int32 startChunk; 54 | int32 samplesPerChunk; 55 | int32 sampleDescriptionID; 56 | }; 57 | 58 | 59 | // dangelo: wrap the parser into a class, this makes the parser 60 | // reentrant. Might be important if multiple plugin instances decode a qtvr 61 | // at the same time. 62 | class QTVRDecoder 63 | { 64 | 65 | public: 66 | 67 | QTVRDecoder(); 68 | ~QTVRDecoder(); 69 | 70 | // scan a file 71 | bool parseHeaders(const char * theDataFilePath); 72 | // get the type of pano it contains 73 | PanoType getType() { return m_type; } 74 | // get one image (new QImage) 75 | QImage * getImage( int face = 0 ); 76 | // get error message 77 | const char * getError(){ return m_error; } 78 | 79 | private: 80 | long ReadMovieAtom(void); 81 | long ReadQTMovieAtom(void); 82 | void ReadAtom_DCOM(long size); 83 | void ReadAtom_CMVD(long size); 84 | void ReadAtom_STCO(long size); 85 | void ReadAtom_HDLR(int size); 86 | void ReadAtom_STSZ(long size); 87 | void ReadAtom_STSC(long size); 88 | void ReadAtom_TKHD(long size); 89 | void ReadAtom_TREF(long size); 90 | void ReadAtom_QTVR_PDAT(long size); 91 | void ReadAtom_QTVR_TREF(long size); 92 | void ReadAtom_QTVR_CUFA(long size); 93 | bool extractCubeImage(int i, QImage * &img); 94 | bool extractCylImage(QImage * &img); 95 | bool SeekAndExtractImage_Tiled( int i, QImage * &img ); 96 | bool SeekAndExtractImageCyl_Tiled( QImage * &img ); 97 | void LoadTilesForFace(int chunkNum); 98 | void Swizzle(int32 *value); 99 | void Swizzle(uint32 *value); 100 | void Swizzle(int16 *value); 101 | void Swizzle(uint16 *value); 102 | /*********************/ 103 | /* VARIABLES */ 104 | /*********************/ 105 | uint32 gCurrentTrackMedia; // 'pano' or ... 106 | //Boolean gAlreadyGotVideoMedia; // we assume that the first video media track is what we want, 107 | // // anything after that is the fast-start track, so we ignore it. 108 | bool gFoundJPEGs; 109 | bool gImagesAreTiled; 110 | int gNumTilesPerImage; 111 | 112 | int32 gPanoChunkOffset; 113 | int32 gPanoSampleSize; 114 | int32 gVideoChunkOffset[MAX_IMAGE_OFFSETS]; 115 | int gVideoSampleSize[MAX_IMAGE_OFFSETS]; 116 | 117 | int32 gTileSize[MAX_TILES_PER_FACE]; 118 | 119 | FILE *gFile; 120 | FILE * m_mainFile; 121 | FILE * m_cmovFile; 122 | 123 | bool m_HostBigEndian; 124 | 125 | int32 m_imageRefTrackIndex; 126 | int32 m_panoType; 127 | 128 | int32 m_panoTracks[MAX_REF_TRACKS]; 129 | int32 m_mainTrack; 130 | bool m_currTrackIsImageTrack; 131 | 132 | std::vector m_sample2ChunkTable; 133 | char * m_error; 134 | 135 | bool m_horizontalCyl; 136 | bool m_cmovZLib; 137 | 138 | PanoType m_type; 139 | }; 140 | 141 | #endif //ndef QT_QTVR_H 142 | -------------------------------------------------------------------------------- /ui/TurnDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | TurnDialog 3 | 4 | 5 | Qt::ApplicationModal 6 | 7 | 8 | 9 | 0 10 | 0 11 | 211 12 | 184 13 | 14 | 15 | 16 | Panini Turn Image 17 | 18 | 19 | 20 | 21 | 20 22 | 150 23 | 161 24 | 21 25 | 26 | 27 | 28 | Qt::Horizontal 29 | 30 | 31 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 32 | 33 | 34 | 35 | 36 | 37 | 120 38 | 110 39 | 62 40 | 22 41 | 42 | 43 | 44 | true 45 | 46 | 47 | -45.000000000000000 48 | 49 | 50 | 45.000000000000000 51 | 52 | 53 | 0.100000000000000 54 | 55 | 56 | 57 | 58 | 59 | 120 60 | 80 61 | 62 62 | 22 63 | 64 | 65 | 66 | true 67 | 68 | 69 | -90.000000000000000 70 | 71 | 72 | 90.000000000000000 73 | 74 | 75 | 0.200000000000000 76 | 77 | 78 | 79 | 80 | 81 | 30 82 | 110 83 | 91 84 | 20 85 | 86 | 87 | 88 | Roll degrees 89 | 90 | 91 | 92 | 93 | 94 | 30 95 | 80 96 | 91 97 | 20 98 | 99 | 100 | 101 | Pitch degrees 102 | 103 | 104 | 105 | 106 | 107 | 100 108 | 10 109 | 81 110 | 22 111 | 112 | 113 | 114 | 115 | 116 | 117 | 30 118 | 10 119 | 61 120 | 21 121 | 122 | 123 | 124 | Orientation 125 | 126 | 127 | 128 | 129 | 130 | 120 131 | 50 132 | 62 133 | 22 134 | 135 | 136 | 137 | -180.000000000000000 138 | 139 | 140 | 180.000000000000000 141 | 142 | 143 | 144 | 145 | 146 | 30 147 | 50 148 | 61 149 | 21 150 | 151 | 152 | 153 | Yaw degrees 154 | 155 | 156 | 157 | 158 | 159 | 160 | buttonBox 161 | accepted() 162 | TurnDialog 163 | accept() 164 | 165 | 166 | 248 167 | 254 168 | 169 | 170 | 157 171 | 274 172 | 173 | 174 | 175 | 176 | buttonBox 177 | rejected() 178 | TurnDialog 179 | reject() 180 | 181 | 182 | 316 183 | 260 184 | 185 | 186 | 286 187 | 274 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/MainWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MainWindow.h for freepvQt 09Sep2008 3 | * Copyright (C) 2008 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | * 19 | 20 | Handles menu and keyboard input only, 21 | inner widgets handle graphical input. 22 | slot showStatus() puts message in status bar. 23 | 24 | NOTE GLwindow is an "insulating layer" for GLview, 25 | whose main function is to relay signals. This is 26 | required by the Qt architecture. 27 | */ 28 | 29 | #ifndef MAINWINDOW_H 30 | #define MAINWINDOW_H 31 | 32 | #include "ui_mainwindow.h" 33 | class GLwindow; 34 | class QActionGroup; 35 | class QErrorMessage; 36 | class QSettings; 37 | class CubeLimit_dialog; 38 | #include "pvQtMouseModes.h" 39 | 40 | class MainWindow : 41 | public QMainWindow, 42 | public Ui_MainWindow 43 | { 44 | Q_OBJECT 45 | public: 46 | MainWindow(QWidget * parent = 0); 47 | // send command line to glwindow 48 | bool postArgs( int argc, char **argv ); 49 | public slots: 50 | void showStatus( QString msg ); 51 | void showTitle( QString msg ); 52 | void showProj( QString name ); 53 | void showFov( QSizeF fovs ); 54 | void showSurface( int surf ); 55 | void showRecenter( bool ); 56 | signals: 57 | void step_pan( int d ); 58 | void step_tilt( int d ); 59 | void step_zoom( int d ); 60 | void step_roll( int d ); 61 | void step_dist( int d ); 62 | void step_hfov( int d ); 63 | void step_vfov( int d ); 64 | void step_iproj( int d ); 65 | void save_as(); 66 | void home_view(); 67 | void home_eyeXY(); 68 | void reset_view(); 69 | void reset_turn(); 70 | void super_wide(); 71 | void set_view( int v ); 72 | void turn90( int d ); 73 | void set_surface( int surf ); 74 | void step_eyex(int); 75 | void step_eyey(int); 76 | 77 | void newPicture( const char * pictype ); 78 | 79 | void about_pvQt(); 80 | void overlayCtl( int c ); 81 | void recenterMode( bool ckd ); 82 | 83 | protected: 84 | virtual void resizeEvent( QResizeEvent * ev ); 85 | virtual void closeEvent( QCloseEvent * ev ); 86 | QErrorMessage * errorMsgHandler; 87 | private: 88 | GLwindow * glwindow; 89 | QString imgFnm; 90 | QString projectFnm; 91 | pvQtMouseModes * pmm; 92 | // actions not created with Qt Designer 93 | QAction * actionToggleSurface; 94 | QAction * actionNext_iProj; 95 | // persistent settings 96 | QSettings * pqs; 97 | // Mac cube limit dialog 98 | CubeLimit_dialog * pcld; 99 | 100 | private slots: 101 | void verify(int i); 102 | void panLft(); 103 | void panRgt(); 104 | void tiltUp(); 105 | void tiltDwn(); 106 | void zoomIn(); 107 | void zoomOut(); 108 | void rollLeft(); 109 | void rollRight(); 110 | void eyeIn(); 111 | void eyeOut(); 112 | void homeView(); 113 | void resetView(); 114 | void on_actionPanini_proj_triggered(); 115 | void on_actionLinear_proj_triggered(); 116 | void on_actionOrtho_proj_triggered(); 117 | void on_actionSuper_wide_triggered(); 118 | void on_action90_deg_CW_triggered(); 119 | void on_actionQTVR_triggered(); 120 | void on_actionRectilinear_triggered(); 121 | void on_actionFisheye_triggered(); 122 | void on_actionSpherical_triggered(); 123 | void on_actionCylindrical_triggered(); 124 | void on_actionStereographic_triggered(); 125 | void on_actionMercator_triggered(); 126 | void on_actionEquirectangular_triggered(); 127 | void on_actionCube_faces_triggered(); 128 | void on_actionPT_script_triggered(); 129 | void on_actionAbout_pvQt_triggered(); 130 | void on_actionMouse_modes_triggered(); 131 | void on_actionSave_as_triggered(); 132 | void on_actionHFovUp_triggered(); 133 | void on_actionHFovDn_triggered(); 134 | void on_actionVFovUp_triggered(); 135 | void on_actionVFovDn_triggered(); 136 | void on_actionHome_Eye_X_Y_triggered(); 137 | void on_actionNone_wire_model_triggered(); 138 | void on_actionNext_iProj_triggered(); 139 | void on_actionToggleSurface_triggered( bool ckd ); 140 | void on_actionReset_turn_triggered(); 141 | void on_actionCube_limit_triggered(); 142 | // overlay menu 143 | void on_actionShow_Hide_triggered(); 144 | void on_actionLoad_overlay_triggered(); 145 | void on_actionRemove_triggered(); 146 | void on_actionFade_triggered(); 147 | 148 | void on_actionRecenter_mode_triggered( bool checked ); 149 | void on_actionEye_right_triggered(); 150 | void on_actionEye_left_triggered(); 151 | void on_actionEye_up_triggered(); 152 | void on_actionEye_down_triggered(); 153 | }; 154 | 155 | #endif //ndef MAINWINDOW_H 156 | -------------------------------------------------------------------------------- /src/panosurface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * panosurface.h for Panini 29 Jan 2009 TKS 3 | 4 | A panosurface is an array of vertices and their 2D 5 | texture coordinates for various source projections, 6 | plus arrays of linear indices that map the vertices 7 | to line segments and quadrilaterals. 8 | 9 | The common API and common source projection functions 10 | are defined here. Subclasses create usable surfaces. 11 | 12 | The texture coordinates are 2D (s,t) normalized 13 | to [0:1] <=> max valid fov for the type. Points 14 | outside the valid fov are set to 0 or 1. Smaller 15 | fovs can be mapped by scaling up the coordinates 16 | using the texture matrix. 17 | 18 | There are two index arrays, one for quads and one 19 | for line segments that form a wireframe drawing of 20 | the surface. 21 | 22 | All data are stored in one contiguous block, that 23 | could be copied to an OGL data buffer. The byte 24 | offsets to and sizes of the various components are 25 | available. 26 | 27 | The first nProjections entries int the pictureTypes 28 | table are the supported projections, which can be 29 | selected by ASCII names or pvQtPic PicType codes. 30 | * 31 | * Copyright (C) 2008-2009 Thomas K Sharpless 32 | * 33 | * This file is free software; you can redistribute it and/or modify 34 | * it under the terms of the GNU General Public License as published by 35 | * the Free Software Foundation; either version 3 of the License, or 36 | * (at your option) any later version. 37 | * 38 | * This file is distributed in the hope that it will be useful, 39 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 40 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 41 | * GNU General Public License for more details. 42 | * 43 | * You should have received a copy of the GNU General Public License 44 | * along with this file; if not, write to Free Software Foundation, Inc., 45 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 46 | * 47 | */ 48 | 49 | #ifndef PANOSURFACE_H 50 | #define PANOSURFACE_H 51 | #include "pvQtPic.h" 52 | #include 53 | 54 | class panosurface { 55 | public: 56 | panosurface(); 57 | ~panosurface(); 58 | /* * c'tor reports errors by posting an error message. 59 | errMsg returns 0 if there was no error. */ 60 | const char * errMsg(){ return errmsg; } 61 | // 3D sphere points 62 | const float * vertices(){ return verts; } 63 | unsigned int vertexOffset(){ return 0; } 64 | unsigned int vertexBytes(){ return 3 * vertpnts * sizeof(float); } 65 | // corresponding texture coordinates [0:1] 66 | const float * texCoords( const char * proj ); 67 | const float * texCoords( pvQtPic::PicType proj ); 68 | unsigned int texCoordOffset( const char * proj ); 69 | unsigned int texCoordOffset( pvQtPic::PicType proj ); 70 | unsigned int texCoordSize(){ return 2 * vertpnts * sizeof(float); } 71 | // index sequence for line drawing 72 | const unsigned int * lineIndices(){ return lineidx; } 73 | unsigned int lineIndexCount(){ return linewrds; } 74 | unsigned int lineIndexOffset(){ return (char *)lineidx - (char *)verts; } 75 | unsigned int lineIndexSize(){ return linewrds * sizeof(unsigned int); } 76 | // index sequence for quad drawing (CW inside) 77 | const unsigned int * quadIndices(){ return quadidx; } 78 | unsigned int quadIndexCount(){ return quadwrds; } 79 | unsigned int quadIndexOffset(){ return (char *)quadidx - (char *)verts; } 80 | unsigned int quadIndexSize(){ return quadwrds * sizeof(unsigned int); } 81 | // everything except the indices as a block of bytes 82 | char * dataBlockAddr(){ return (char *)words; } 83 | unsigned int dataBlockSize(){ return 15 * vertpnts * sizeof(float); } 84 | /* texture coordinate scale factors to correctly map 85 | an image of a given projection and angular size. 86 | xfov, yfov are full angular sizes in degrees 87 | xscale, yscale are factors by which the tabulated TC's should 88 | be multiplied. */ 89 | bool texScale( int pictypeindex, double xfov, double yfov, double& xscale, double& yscale); 90 | // convenience overloads 91 | bool texScale( const char * proj, double xfov, double yfov, double& xscale, double& yscale); 92 | bool texScale( pvQtPic::PicType proj, double xfov, double yfov, double& xscale, double& yscale ); 93 | 94 | protected: 95 | char * errmsg; 96 | pictureTypes pictypes; 97 | // all memory is allocated in one block 98 | float * words; 99 | unsigned int nwords; 100 | // array sizes 101 | unsigned int vertpnts; // vetices and TCs, in points 102 | unsigned int linewrds; // in words 103 | unsigned int quadwrds; // in words 104 | // array addrs 105 | float * verts; 106 | float * TCs; 107 | unsigned int * lineidx, * quadidx; 108 | // get memory and set pointers 109 | bool getMemory(); 110 | // compute texture coordinates 111 | // from the vertex coordinates 112 | void map_projections(); 113 | }; 114 | 115 | #ifdef PANOSURFACE_IMPLEMENTATION 116 | /* Stuff useful in implementation */ 117 | const float ninv = -0.01, pinv = 1.01; 118 | #define CLIP( x ) ( x < ninv ? ninv : x > pinv ? pinv : x ) 119 | #define INVAL( t ) (t > 0 ? pinv : ninv ) 120 | #define EVAL( t ) ( t < 0.5 ? t < 0 ? t : 0 : t > 1 ? t : 1 ) 121 | 122 | #ifndef Pi 123 | #define Pi 3.1415926535897932384626433832795 124 | #define DEG2RAD( x ) (( x ) * Pi / 180.0) 125 | #define RAD2DEG( x ) (( x ) * 180.0 / Pi) 126 | #endif 127 | 128 | #endif //def PANOSURFACE_IMPLEMENTATION 129 | 130 | #endif //ndef PANOSURFACE_H 131 | -------------------------------------------------------------------------------- /src/panocylinder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * panocylinder.cpp for pvQt 11 Dec 2008 3 | 4 | A cylinder tessellation with texture coordinates, 5 | line and quad incices, in arrays usable by OpenGl. 6 | 7 | * 8 | * Copyright (C) 2008 Thomas K Sharpless 9 | * 10 | * This file is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This file is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this file; if not, write to Free Software Foundation, Inc., 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 | * 24 | */ 25 | 26 | #define PANOSURFACE_IMPLEMENTATION 27 | #include "panocylinder.h" 28 | 29 | /* 30 | The cylinder is divided into quads of equal angular 31 | size (as seen from the center) so the heights increase 32 | away from equator. The width is 360 degrees and the 33 | full height is 150 degrees. 34 | 35 | The equatorial plane is XZ, with +Z at the center 36 | and +X at left; axis is Y, with +Y up. 37 | 38 | Quads are oriented CCW as seen from the inside. 39 | 40 | Each level is a circle of constant Y, and the XZ 41 | coordinates are same at all levels. So a very 42 | compact representation is possible; but I use 43 | a full array for simplicity. The vertical edge 44 | points are duplicated. 45 | */ 46 | 47 | panocylinder::panocylinder( int divs ){ 48 | // no error 49 | errmsg = 0; 50 | // make sure divs is even and > 3 51 | divs = 2 * ((divs + 1) / 2); 52 | 53 | if( divs < 4 ) { 54 | divs = 4; 55 | } 56 | 57 | /* 58 | The vertex and TC arrays have divs + 1 columns, 59 | and the smallest odd number of rows >= 0.5 * divs, 60 | for avg square quads at a vFov of 150 degrees. 61 | There are (rows - 1) * (cols -1) quads; each of the 62 | index arrays has 4 entries per quad. 63 | */ 64 | 65 | int cols = divs + 1; 66 | int rows = int( 0.5 * divs ); 67 | 68 | if( (rows & 1) == 0 ) { 69 | ++rows; // odd no. of rows 70 | } 71 | 72 | int quads = (rows - 1) * (cols - 1); 73 | vertpnts = rows * cols; // total vertices 74 | linewrds = 4 * quads; 75 | quadwrds = linewrds; 76 | 77 | // get memory or die 78 | if (!getMemory()) { 79 | return; 80 | } 81 | 82 | // local TC pntrs 83 | float * rects = (float *)texCoords("rect"); 84 | float * fishs = (float *)texCoords("fish"); 85 | float * cylis = (float *)texCoords("cyli"); 86 | float * equis = (float *)texCoords("equi"); 87 | float * sters = (float *)texCoords("ster"); 88 | float * mercs = (float *)texCoords("merc"); 89 | float * angls = (float *)texCoords("sphr"); 90 | 91 | /* 92 | * Build Vertex array 93 | * All rows have same X and Z coordinates 94 | * Array origin is lower left corner; X 95 | * points left, Y up when look along Z. 96 | */ 97 | 98 | int row, col; 99 | int r2 = rows / 2; 100 | float * pv0 = verts + 3 * r2 * cols; 101 | // center row 102 | double hs = 2 * Pi / double(cols - 1); 103 | float * pv = pv0; 104 | 105 | for( col = 0; col < cols; col++ ){ 106 | double a = col * hs - Pi; 107 | *pv++ = float(sin( a )); // X 108 | *pv++ = 0; // Y 109 | *pv++ = float(cos( a )); // Z 110 | } 111 | 112 | // rest of rows 113 | double vs = 0.5 * DEG2RAD( 150 ) / double(r2); 114 | 115 | for( int r = 0; r < r2; r++){ 116 | double t = tan( r * vs ); 117 | float * pv = pv0; 118 | float * pu = pv0 + 3 *( cols + r * cols ); 119 | float * pl = pv0 - 3 *( cols + r * cols ); 120 | for( col = 0; col < cols; col++){ 121 | *pu++ = *pv; 122 | *pl++ = *pv++; 123 | *pu++ = t; 124 | *pl++ = -t; 125 | ++pv; 126 | *pu++ = *pv; 127 | *pl++ = *pv++; 128 | } 129 | } 130 | 131 | /* 132 | quad indices 0 3 133 | 1 2 134 | */ 135 | 136 | unsigned int * pq = quadidx; 137 | int r, c; 138 | 139 | for( r = 0; r < rows - 1; r++ ){ 140 | unsigned int k = r * cols; // 1st index of row 141 | for( int c = 0; c < cols - 1; c++ ){ 142 | // CCW quad 143 | *pq++ = k + c; 144 | *pq++ = cols + k + c; 145 | *pq++ = cols + k + c + 1; 146 | *pq++ = k + c + 1; 147 | } 148 | } 149 | 150 | /* 151 | * line indices 152 | copy quad indices, replacing one index of each quad 153 | with a copy of another. Only 2 edges of each quad 154 | are drawn; the doubled index selects which ones. 155 | */ 156 | 157 | unsigned int * qq = quadidx; 158 | pq = lineidx; 159 | 160 | for( r = quads; r > 0; --r ){ 161 | pq[0] = qq[0]; 162 | pq[1] = qq[1]; 163 | pq[2] = qq[0]; 164 | pq[3] = qq[3]; 165 | pq += 4; 166 | qq += 4; 167 | } 168 | 169 | /* Texture coordinates */ 170 | map_projections(); 171 | 172 | /* fix up the +/- 180 wrap */ 173 | float * pc = cylis; 174 | float * pe = equis; 175 | float * pm = mercs; 176 | r = 2 * cols; 177 | c = r - 2; 178 | 179 | for( row = 0; row < rows; row++ ){ 180 | pc[0] = 1; pc[c] = 0; pc += r; 181 | pe[0] = 1; pe[c] = 0; pe += r; 182 | pm[0] = 1; pm[c] = 0; pm += r; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /ui/About.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | AboutDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 425 10 | 320 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 10 22 | 23 | 24 | 25 | About Panini 26 | 27 | 28 | true 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 30 37 | 150 38 | 371 39 | 151 40 | 41 | 42 | 43 | 44 | Sans Serif 45 | 10 46 | 50 47 | false 48 | 49 | 50 | 51 | QFrame::NoFrame 52 | 53 | 54 | QFrame::Sunken 55 | 56 | 57 | Your 58 | System's 59 | OpenGL 60 | Implementation 61 | Info... 62 | Qt vers 63 | 64 | 65 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 66 | 67 | 68 | true 69 | 70 | 71 | 5 72 | 73 | 74 | 0 75 | 76 | 77 | 78 | 79 | 80 | 160 81 | 50 82 | 191 83 | 41 84 | 85 | 86 | 87 | 88 | Sans Serif 89 | 10 90 | 50 91 | false 92 | false 93 | 94 | 95 | 96 | <html><head/><body><p>multiplatform panorama viewer<br/>and perspective adjustment tool</p></body></html> 97 | 98 | 99 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 100 | 101 | 102 | 103 | 104 | 105 | 160 106 | 20 107 | 51 108 | 31 109 | 110 | 111 | 112 | 113 | Arial 114 | 12 115 | 75 116 | true 117 | 118 | 119 | 120 | Qt::LeftToRight 121 | 122 | 123 | Panini 124 | 125 | 126 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 127 | 128 | 129 | 130 | 131 | 132 | 160 133 | 90 134 | 211 135 | 41 136 | 137 | 138 | 139 | 140 | Sans Serif 141 | 10 142 | 75 143 | true 144 | 145 | 146 | 147 | <html><head/><body><p>© 2008 - 2009 Thomas Sharpless<br/>© 2017 - 2019 Michael Vetter</p></body></html> 148 | 149 | 150 | Qt::RichText 151 | 152 | 153 | 154 | 155 | 156 | 220 157 | 20 158 | 101 159 | 31 160 | 161 | 162 | 163 | 164 | Arial 165 | 12 166 | 75 167 | true 168 | 169 | 170 | 171 | 0.7.88-89M 172 | 173 | 174 | 175 | 176 | false 177 | 178 | 179 | 180 | 30 181 | 30 182 | 111 183 | 111 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | :/Icons/panini-icon-blue.jpg 192 | :/Icons/panini-icon-blue.jpg:/Icons/panini-icon-blue.jpg 193 | 194 | 195 | 196 | 128 197 | 128 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /src/pictureTypes.cpp: -------------------------------------------------------------------------------- 1 | /* pictureTypes.cpp for pvQt 06Oct2008 TKS 2 | * Copyright (C) 2008 Thomas K Sharpless 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This file is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this file; if not, write to Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | * 18 | 19 | The picture type names visible to the user, and their attributes, 20 | one of which is the associated pvQtPic::PicType code. 21 | 22 | The first 7 names are the projection names used by quadsphere. 23 | These max FOVs set the ranges of the quadsphere mappings. 24 | */ 25 | #include "pvQtPic.h" 26 | 27 | pictureTypes::pictypnumdesc 28 | pictureTypes::pictypn[NpictureTypes] = { 29 | { "rect", pvQtPic::rec, 1, QString(), 10,10, 150,150, 165,165 }, 30 | { "fish", pvQtPic::eqs, 1, QString(), 10,10, 360,360, 360,360 }, 31 | { "sphr", pvQtPic::eqa, 1, QString(), 10,10, 360,360, 360,360 }, 32 | { "cyli", pvQtPic::cyl, 1, QString(), 10,10, 360,150, 360,165 }, 33 | { "equi", pvQtPic::eqr, 1, QString(), 10,10, 360,180, 360,180 }, 34 | { "ster", pvQtPic::stg, 1, QString(), 10,10, 310,310, 360,360 }, 35 | { "merc", pvQtPic::mrc, 1, QString(), 10,10, 360,150, 360,175 }, 36 | { "cube", pvQtPic::cub, 6, QString(), 90,90, 90,90, 90,90 }, 37 | { "proj", pvQtPic::nil, 1, QString(), 0,0,0,0,0,0 }, 38 | { "qtvr", pvQtPic::nil, 1, QString(), 0,0,0,0,0,0 } 39 | }; 40 | 41 | // need c'tor as tr() does not work outside a QObject 42 | pictureTypes::pictureTypes(){ 43 | pictypn[picTypeIndex("proj")].desc = 44 | tr("PanoTools script or project"); 45 | pictypn[picTypeIndex("qtvr")].desc = 46 | tr("QuickTime VR panorama"); 47 | pictypn[picTypeIndex("rect")].desc = 48 | tr("Normal rectilinear image"); 49 | pictypn[picTypeIndex("fish")].desc = 50 | tr("Fisheye or mirror ball image"); 51 | pictypn[picTypeIndex("sphr")].desc = 52 | tr("Spherical image"); 53 | pictypn[picTypeIndex("cyli")].desc = 54 | tr("Cylindrical panorama"); 55 | pictypn[picTypeIndex("equi")].desc = 56 | tr("Equirectangular panorama"); 57 | pictypn[picTypeIndex("cube")].desc = 58 | tr("1 to 6 Cube face images"); 59 | pictypn[picTypeIndex("ster")].desc = 60 | tr("Stereographic image"); 61 | pictypn[picTypeIndex("merc")].desc = 62 | tr("Mercator panorama"); 63 | } 64 | 65 | // return index of a pic type name, -1 if none 66 | int pictureTypes::picTypeIndex( const char * name ){ 67 | if( name == 0 ) return -1; 68 | int i; 69 | for( i = 0; i < NpictureTypes; i++ ){ 70 | if(!strcmp( name, pictypn[i].typ )) return i; 71 | } 72 | return -1; 73 | } 74 | 75 | // return index of a PicType code (Note nil => proj) 76 | int pictureTypes::picTypeIndex( pvQtPic::PicType t ){ 77 | int i; 78 | for( i = 0; i < NpictureTypes; i++ ){ 79 | if( t == pictypn[i].pictype ) return i; 80 | } 81 | return -1; 82 | } 83 | 84 | // given cstring type name, return file count, 0 if no such type 85 | int pictureTypes::picTypeCount( const char * name ){ 86 | int i = picTypeIndex( name ); 87 | if( i < 0 ) return 0; 88 | return pictypn[i].nfi; 89 | } 90 | 91 | // given type index, return cstring name, 0 if no such type 92 | const char * pictureTypes::picTypeName( int index ){ 93 | if( index < 0 || index >= NpictureTypes ) return 0; 94 | return pictypn[index].typ; 95 | } 96 | // get name for a pvQtPic type code 97 | const char * pictureTypes::picTypeName( pvQtPic::PicType t ){ 98 | int index = picTypeIndex( t ); 99 | if( index < 0 || index >= NpictureTypes ) return 0; 100 | return pictypn[index].typ; 101 | } 102 | 103 | 104 | // given type index, return file count, 0 if no such type 105 | int pictureTypes::picTypeCount( int index ){ 106 | if( index < 0 || index >= NpictureTypes ) return 0; 107 | return pictypn[index].nfi; 108 | } 109 | 110 | // given picture type name, return description 111 | QString pictureTypes::picTypeDescr( const char * name ){ 112 | int i = picTypeIndex( name ); 113 | if( i < 0 ) return tr("ERROR: nonexistent picture type"); 114 | return pictypn[i].desc; 115 | } 116 | 117 | // given picture type index, return description 118 | QString pictureTypes::picTypeDescr( int index ){ 119 | if( index < 0 || index >= NpictureTypes ) 120 | return tr("ERROR: nonexistent picture type"); 121 | return pictypn[index].desc; 122 | } 123 | 124 | QStringList pictureTypes::picTypeDescrs( ){ 125 | QStringList sl; 126 | for(int i = 0; i < NpictureTypes; i++ ){ 127 | sl.append(pictypn[i].desc); 128 | } 129 | return sl; 130 | } 131 | 132 | QSizeF pictureTypes::minFov( int index ){ 133 | if( index < 0 || index >= NpictureTypes ) return QSizeF(); 134 | return QSizeF( pictypn[index].minW, pictypn[index].minH ); 135 | } 136 | 137 | QSizeF pictureTypes::maxFov( int index ){ 138 | if( index < 0 || index >= NpictureTypes ) return QSizeF(); 139 | return QSizeF( pictypn[index].maxW, pictypn[index].maxH ); 140 | } 141 | 142 | QSizeF pictureTypes::absMaxFov( int index ){ 143 | if( index < 0 || index >= NpictureTypes ) return QSizeF(); 144 | return QSizeF( pictypn[index].AmaxW, pictypn[index].AmaxH ); 145 | } 146 | 147 | 148 | pvQtPic::PicType pictureTypes::PicType( const char * name ){ 149 | return PicType( picTypeIndex( name )); 150 | } 151 | 152 | pvQtPic::PicType pictureTypes::PicType( int i ){ 153 | if( i < 0 || i > NpictureTypes ) return pvQtPic::nil; 154 | return pictypn[i].pictype; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /MSVS-project/pvQt_1/pvQt_1.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 24 | 37 | 38 | 44 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 70 | 71 | 74 | 75 | 78 | 79 | 82 | 83 | 86 | 87 | 90 | 91 | 94 | 95 | 98 | 99 | 102 | 103 | 106 | 107 | 110 | 111 | 114 | 115 | 118 | 119 | 120 | 125 | 128 | 129 | 132 | 133 | 136 | 137 | 140 | 141 | 144 | 145 | 148 | 149 | 152 | 153 | 156 | 157 | 160 | 161 | 164 | 165 | 168 | 169 | 172 | 173 | 176 | 177 | 180 | 181 | 184 | 185 | 188 | 189 | 192 | 193 | 196 | 197 | 200 | 201 | 204 | 205 | 208 | 209 | 210 | 215 | 218 | 219 | 222 | 223 | 226 | 227 | 230 | 231 | 234 | 235 | 236 | 239 | 240 | 243 | 244 | 247 | 248 | 251 | 252 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /ui/picTypeDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | picTypeDialog 3 | 4 | 5 | 6 | 0 7 | 0 8 | 327 9 | 215 10 | 11 | 12 | 13 | panini - Picture Type and FOV 14 | 15 | 16 | 17 | 18 | 10 19 | 130 20 | 301 21 | 24 22 | 23 | 24 | 25 | 26 | 27 | 28 | Width, Height, degrees 29 | 30 | 31 | 32 | 33 | 34 | 35 | true 36 | 37 | 38 | 5.000000000000000 39 | 40 | 41 | 180.000000000000000 42 | 43 | 44 | 180.000000000000000 45 | 46 | 47 | 48 | 49 | 50 | 51 | true 52 | 53 | 54 | 5.000000000000000 55 | 56 | 57 | 180.000000000000000 58 | 59 | 60 | 180.000000000000000 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 10 70 | 100 71 | 301 72 | 24 73 | 74 | 75 | 76 | 77 | 78 | 79 | Width, Height, pixels 80 | 81 | 82 | 83 | 84 | 85 | 86 | QFrame::Box 87 | 88 | 89 | QFrame::Sunken 90 | 91 | 92 | 2 93 | 94 | 95 | TextLabel 96 | 97 | 98 | 99 | 100 | 101 | 102 | QFrame::Box 103 | 104 | 105 | QFrame::Sunken 106 | 107 | 108 | 2 109 | 110 | 111 | TextLabel 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 10 121 | 0 122 | 265 123 | 20 124 | 125 | 126 | 127 | 128 | 0 129 | 18 130 | 131 | 132 | 133 | Source 134 | 135 | 136 | 137 | 138 | 139 | 10 140 | 20 141 | 301 142 | 24 143 | 144 | 145 | 146 | QFrame::Box 147 | 148 | 149 | QFrame::Sunken 150 | 151 | 152 | 2 153 | 154 | 155 | TextLabel 156 | 157 | 158 | 159 | 160 | 161 | 10 162 | 50 163 | 265 164 | 20 165 | 166 | 167 | 168 | Picture type 169 | 170 | 171 | 172 | 173 | 174 | 40 175 | 70 176 | 241 177 | 20 178 | 179 | 180 | 181 | 182 | 183 | 184 | 174 185 | 170 186 | 141 187 | 25 188 | 189 | 190 | 191 | Qt::Horizontal 192 | 193 | 194 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 195 | 196 | 197 | 198 | 199 | 200 | 30 201 | 170 202 | 111 203 | 20 204 | 205 | 206 | 207 | unlock FOVs 208 | 209 | 210 | 211 | 212 | 213 | 214 | buttonBox 215 | accepted() 216 | picTypeDialog 217 | accept() 218 | 219 | 220 | 248 221 | 254 222 | 223 | 224 | 157 225 | 274 226 | 227 | 228 | 229 | 230 | buttonBox 231 | rejected() 232 | picTypeDialog 233 | reject() 234 | 235 | 236 | 316 237 | 260 238 | 239 | 240 | 286 241 | 274 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | # Panini 0.74.0 Release Notes 20 December 2023 2 | 3 | This is a Linux only release. 4 | 5 | Changes: 6 | 7 | * Use QErrorMessage instead of static QMessageBox in 8 | error handler (@luis-pereira #10) 9 | * Fix C++17 does not allow register storage class 10 | specifier (@listout #16) 11 | 12 | # Panini 0.73.0 Release Notes 15 January 2019 13 | 14 | This is a Linux only release. 15 | 16 | Changes: 17 | 18 | * Add AppData file (@sanjayankur31 gh#pr#8) 19 | * Enable mipmapping and anisotropic filtering. 20 | Greatly enhances rendering quality, especially 21 | for high resolution images. (@danpla gh#pr#9) 22 | 23 | # Panini 0.72.0 Release Notes 30 November 2017 24 | 25 | Panini will not use the svn revision in the version 26 | number anymore. 27 | 28 | Project moved from Sourceforge to GitHub: 29 | https://github.com/lazarus-pkgs/panini 30 | 31 | This is a Linux only release. 32 | 33 | Changes: 34 | 35 | * Fix project file by adding zlib and glu parameters. 36 | * Fix several memory leaks (#7) 37 | * Port to Qt5 38 | * Clean up coding style 39 | * Adjust About dialog 40 | 41 | For details look at the git history. 42 | 43 | # Panini 0.71.104 Release Notes 25 June 2009 44 | 45 | Version 0.71 improves the eye position controls and 46 | eliminates a difference between cubic and 2D panos in 47 | Recenter mode. 48 | 49 | Version 0.7 adds two new features requested by users: 50 | 51 | 1) Overlay (for Luca Vascon) * overlays a fixed image, 52 | with adjustable transparency, on the view. This is 53 | useful for matching the view to a reference such as 54 | a drawing, a painting or another photograph. 55 | 56 | 2) Recenter (for Peter Gawthrop) * an alternative 57 | viewing mode, in the Presets menu. The eye point can 58 | be placed anywhere inside the panosphere, and the view 59 | direction rotates around the eye point instead of the 60 | panosphere center. This gives views like Peter's 61 | "recentered equirectangular projections". 62 | 63 | The keyboard and mouse view controls are more rational 64 | and convenient. Specifically: 65 | * There are view menu items and keyboard shortcuts for 66 | Eye X and Y shifts and the "framing" shifts. 67 | * Shift right mouse now controls Eye X and Y, and the 68 | less useful hFov/vFov are now Shift left+right mouse. 69 | 70 | A couple of minor Mac bugs (may have been) fixed. 71 | 72 | At version 0.6, pvQt has been renamed "Panini". 73 | Update 0.61 (SVN 77) fixes some UI bugs. It uses native 74 | file selector dialogs on Windows and OSX because some 75 | Mac users have reported difficulty with the Qt dialog. 76 | 77 | Update 0.62 adds the ability to make Pannini views from 78 | cubic panoramas, and to control alignment of the image 79 | on the pancylinder. 80 | 81 | Update 0.63 adds a Mac-specific limit on the size of cubic 82 | images, and fixes a big memory leak related to cubic images. 83 | It also remembers the last-used window size and position. 84 | 85 | This is a beta release of software that is under active 86 | development and will be replaced by a better version in 87 | the not-too-distant future. So you should expect to find 88 | some bugs. Please report those, either on the hugin-et-al 89 | discussion group: 90 | http://groups.google.com/group/hugin-ptx 91 | or the pvQt project's bug tracker at SourceForge: 92 | http://sourceforge.net/tracker/?group_id=241632&atid=1116683 93 | You may find a newer release at: 94 | http://sourceforge.net/projects/pvqt/ 95 | 96 | The main new features in version 0.6 are: 97 | * cylindrical panosurface enables "Panini projection" for 98 | convincing perspective effects in wide angle views (not 99 | available for cubic source images). 100 | * The range of eye distance has been enlarged and the 101 | controls for it linearized. It is now possible to sweep 102 | the viewing projection from linear through stereographic 103 | or Panini, all the way to sinusoidal. 104 | * There are presets for the 3 principal viewing projections. 105 | * framing controls shift view center with respect to screen 106 | window. 107 | * can shift eye point sideways as well as radially, for 108 | "software view camera" effects. 109 | 110 | Some other improvements 111 | * fewer errors in input deprojection functions (some 112 | still need work) 113 | * a bug in cubic picture loading has ben fixed. 114 | 115 | Update 0.61 adds these bug fixes and UI improvements: 116 | * The menu for choosing source image format is now titled "Source" (was "Picture") 117 | * You can choose to see the wire frame panosphere models instead of an image 118 | * Panocylinder is automatically selected for non-cubic source formats; so just hit 119 | "P" to see the Pannini projection. 120 | * The current panosurface type is always displayed correctly 121 | * All window titles now say "Panini" 122 | * The program has a nice icon with a pano of the Erasmus Bridge in Rotterdam, 123 | that was perspective-corrected with Panini. 124 | 125 | Update 0.62 adds these enhancements: 126 | * Pannini projections of cubic panoramas can now be displayed. 127 | * Angular alignment of the image on the panosurface can be adjusted via a 128 | new "Turn" dialog. For cubic panos only, Yaw and pitch can be set to put 129 | the axis of the panosurface anywhere in the spherical image. 130 | * Panosurface is now selected by clicking a button at bottom of window. 131 | Another such button cycles through the non-cubic input projections. Menu 132 | items for these have been removed. 133 | * Mouse control can be made vertical-only by holding Alt. or horizontal-only 134 | by holding Control. This may not work on Linux, depending on X11 options. 135 | * Framing shifts are displayed on status line, and reset along with eye shifts. 136 | * More errors in the input mappings have been fixed. 137 | 138 | Update 0.63 addds two persistent settings: 139 | * window size and position are saved at shutdown and restored at launch. 140 | The initial default size is now smaller to accommodate the small wide- 141 | format screens on some newer laptops. 142 | * On Macs, cubic image resolution is limited to solve an OSX bug that causes 143 | garbled display or even system crashes with large cubic texture maps. 144 | The default limit of 1536 x 1536 (13.5 megapixels) should work on many recent 145 | Macs, but if yours needs a smaller limit, or you want to push the limit higher, 146 | you can change it via "Mac cube size" in the Presets menu. 147 | 148 | Version 0.70 fixes these bugs: 149 | * initial window position was under menu bar on Mac 150 | * save file name extension now defaults to ".jpg" on Mac as it does 151 | on Linux and Windows. 152 | 153 | Version 0.71 revises the controls as follows 154 | * in Recenter mode, eye Distance now sets the radial distance from 155 | panocenter, like normal mode, but maximum eye radius is 0.93. 156 | * in Recenter mode, eye X and Y now control Yaw and Pitch angles 157 | that specify the direction of the eye point shift. They are set to 158 | the current view direction when you switch to Recenter mode. 159 | * Status display shows eye Distance in radii as "eD". In normal mode 160 | it shows eye Shifts in radii as "eS(x, y)". In Recenter mode it 161 | shows eye Angles in degrees as "eA(yaw, pitch)"; this is the 162 | view direction along which the eye position is shifted. It now 163 | shows the Framing shifts, in radii, as "fS(x, y)". 164 | * Eye position now moves in mouse direction (was reversed). 165 | * The view no longer shifts much when eye position changes. 166 | * Homing the view direction no longer exits Recenter mode. 167 | * A secret mouse mode that could cause unexpected results has been 168 | removed. 169 | 170 | Known issues and limitations: 171 | * Due to a bug in OSX, it is not possible to display cubic images 172 | at full hardware resolution on a Mac. 173 | * There are still some small memory leaks that could cause crashes 174 | after very long periods of use. 175 | * Displayed vFov angle is correct only for the panosphere and for 176 | eye distances between 0 and 1. 177 | * still does not read PT scripts/projects 178 | * To save higher resolution views, your OpenGL must support off- 179 | screen framebuffers of arbitrary size. 180 | 181 | A set of test images, all made from one spherical panorama, is available 182 | for download as a separate package. 183 | 184 | The Win32 and Mac binary packages have been built with Qt 4.5.0 (LGPL), and contain 185 | executable Qt libraries from that version. If you build your own, Qt version 4.4.2 186 | or later should work. 187 | -------------------------------------------------------------------------------- /src/pvQtView.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pvQtView.h for pvQt 11 Sept 2008 3 | * Copyright (C) 2008 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | * 19 | 20 | pvQtView is an OpenGL display widget that shows images projected on 21 | a 3D spherical or cylindrical screen. 22 | 23 | This class is the only one in pvQt that issues OpenGL calls. 24 | 25 | App should call OpenGLOK() before using this widget, and terminate 26 | with error if it returns false (as nothing can be displayed). 27 | 28 | */ 29 | 30 | #ifndef PVQTVIEW_H 31 | #define PVQTVIEW_H 32 | 33 | #include 34 | #include "pvQtPic.h" 35 | #include "panosphere.h" 36 | #include "panocylinder.h" 37 | 38 | class pvQtView : public QGLWidget 39 | { 40 | Q_OBJECT 41 | public: 42 | pvQtView(QWidget *parent = 0); 43 | ~pvQtView(); 44 | 45 | QSize minimumSizeHint() const; 46 | QSize sizeHint() const; 47 | 48 | /** OpenGL attributes **/ 49 | 50 | bool OpenGLOK(); // false if OGL version too low 51 | 52 | QString OpenGLVersion(){ 53 | return QString( (const char *)glGetString(GL_VERSION) ); 54 | } 55 | QString OpenGLVendor(){ 56 | return QString( (const char *)glGetString(GL_VENDOR) ); 57 | } 58 | QString OpenGLHardware(){ 59 | return QString( (const char *)glGetString(GL_RENDERER) ); 60 | } 61 | QString OpenGLLimits(){ 62 | return QString("texPwr2 %1, texMax %2, cubeMax %3") 63 | .arg(texPwr2).arg(max2d).arg(maxcube); 64 | } 65 | 66 | /* 67 | Display an overlay image 68 | ovl = 0 stops overlay display 69 | Image format should be ARGB. Size should be moderate. 70 | Vertical dimension will fill the viewport. 71 | */ 72 | bool showOverlay( QImage * ovl ); 73 | 74 | /* 75 | Display a picture 76 | pic = 0 resets to base screen display. Otherwise 77 | *pic must be valid and undisturbed until showPic is next 78 | called (caller can then delete pic if appropriate). 79 | returns sucess or failure, with errmsg updated. 80 | */ 81 | bool showPic( pvQtPic * pic ); 82 | /* 83 | check whether picture displayed OK, get message if not 84 | The error flag is reset when a new picture is loaded. 85 | */ 86 | bool picOK( QString & errMsg ){ 87 | errMsg = errmsg; 88 | return picok; 89 | } 90 | 91 | /* 92 | Identify image face that contains a screen pixel 93 | pnt is in viewport mouse coordinates (ulc origin) 94 | returns front if pic type is not cubic 95 | */ 96 | pvQtPic::PicFace pickFace( QPoint pnt ); 97 | 98 | /* 99 | Save the current view to a file 100 | name is full pathname, with extension .jpg or .tif 101 | Default size is the current viewport size. Custom sizes 102 | need not be the same shape as the viewport (but may 103 | not be supported on a given system -- if not, viewport 104 | size is used) 105 | Returns true if image was rendered and written OK. 106 | */ 107 | bool saveView( QString name, QSize size = QSize()); 108 | // overload to save possibly scaled-up copy of viewport 109 | // scale is clipped to [1.0:5.0] 110 | bool saveView( QString name, double scale = 1.0 ); 111 | 112 | // get the current screen viewport size in pixels 113 | QSize screenSize(){ return QSize( Width, Height ); } 114 | 115 | public slots: 116 | /* 117 | Angles passed from/to GUI are integers in 16ths of a degree, 118 | so they can be reliably checked for equality. The zoom angle 119 | is the fov of the shorter window axis 120 | */ 121 | void setPan(int iangle); // unit = 1/16 degree 122 | void setTilt(int iangle); 123 | void setSpin(int iangle); 124 | void setZoom( int iangle ); 125 | void setDist( double dist ); 126 | // keypress steps -- dp should normally be +/- 1 127 | void step_pan( int dp ); 128 | void step_tilt( int dp ); 129 | void step_zoom( int dp ); 130 | void step_roll( int dp ); 131 | void step_dist( int dp ); 132 | void step_hfov( int dp ); 133 | void step_vfov( int dp ); 134 | void step_iproj( int dp ); 135 | // preset views 136 | void reset_view(); // reinit all params 137 | void set_view( int v ); // 0: lin, 1: proto, 2: ortho 138 | void home_view(); // zero view angles 139 | void home_eyeXY(); // zero eyepoint shifts 140 | void super_fish(); // Ez = 1.07, min zoom 141 | // update display of current picture 142 | void picChanged(); // from scratch 143 | void newFace( pvQtPic::PicFace face ); // one cube face 144 | // select panosurface 145 | void setSurface( int surf ); 146 | // orient image on panosurface turn(0:3)= 0,90,180,270 deg 147 | void setTurn( int turn, double roll, double pitch, double yaw ); 148 | // Mac cube texture dimension limit 149 | void setCubeLimit( int lim ); 150 | // V0.7 151 | void recenterMode( bool ); 152 | void step_eyex( int ); 153 | void step_eyey( int ); 154 | 155 | 156 | signals: 157 | void reportView( QString msg ); 158 | void OGLerror( QString msg ); 159 | void reportTurn( int turn, double roll, double pitch, double yaw ); 160 | void reportFov( QSizeF fovs ); 161 | void reportProj( QString name ); 162 | void reportSurface( int surf ); 163 | void reportRecenter( bool ); // when recenter changed internally 164 | protected: 165 | void initializeGL(); 166 | void paintGL(); 167 | void resizeGL(int width, int height); 168 | void mousePressEvent(QMouseEvent *pme ); 169 | void mouseMoveEvent(QMouseEvent *pme ); 170 | void mouseReleaseEvent( QMouseEvent *pme ); 171 | void mouseDoubleClickEvent( QMouseEvent *pme ); 172 | void wheelEvent(QWheelEvent *event); 173 | 174 | private slots: 175 | void mTimeout(); 176 | private: 177 | // GUI support 178 | double normalizeAngle(int &iangle, int istep, double lwr, double upr); 179 | int iAngle( double angle ); // real to integer coded angle 180 | void stepDangl( int dp, int stp ); 181 | void showview(); 182 | void setFOV( double fov = 0 ); 183 | void initView(); 184 | // current view parameters 185 | double hFOV, vFOV; // angular size at sphere center (deg) 186 | double minFOV, maxFOV; // limits on vFOV 187 | double wFOV; // vert angle at eye (deg) sets magnification 188 | double eyeDistance; // of eye from origin, in sphere radii 189 | // working eye position, in radii 190 | double eyex, eyey, eyez; 191 | // framing shifts 192 | double framex, framey, // user controlled 193 | fcompx, fcompy, // to compensate eye shifts 194 | framex0, framey0, 195 | fwf, fhf; 196 | 197 | int Width, Height; // screen pixel dimensions 198 | double portAR; // width/height 199 | double Znear, Zfar; // clipping plane distances from eye 200 | double panAngle, tiltAngle, spinAngle; // degrees 201 | double minpan, maxpan, mintilt,maxtilt; //image limits 202 | // imge orientation on panosurface 203 | int turn90; // 0:3 90 deg steps 204 | double turnRoll; // fine -45:45 deg 205 | double turnPitch; // -90:90 deg 206 | double turnYaw; // -180:180 deg 207 | 208 | int ipan, panstep; 209 | int itilt, tiltstep; 210 | int ispin, spinstep; 211 | int izoom, zoomstep; 212 | int idangl, danglstep; 213 | int ihangl, hanglstep; 214 | int ivangl, vanglstep; 215 | double hangle, vangle; // recenter eye dir 216 | int mx0, my0, mx1, my1; // mouse coordinates 217 | Qt::MouseButtons mb; 218 | Qt::KeyboardModifiers mk; 219 | QTimer mTimer; 220 | 221 | void setTexMag( double magx, double magy ); 222 | 223 | // display support 224 | void setPicType( pvQtPic::PicType pt ); 225 | bool setupPic( pvQtPic * pic ); 226 | void updatePic(); 227 | pvQtPic * thePic; 228 | pvQtPic::PicType picType; 229 | int ipicType; // index of picType 230 | // display lists for screens 231 | void makeSphere( GLuint list ); 232 | GLuint theScreen; // current screen list 233 | // textures 234 | GLenum textgt; // current target (2D or cube) 235 | GLuint texname; // current texture object 236 | GLuint texnms[2]; // bound textures: 0: 2d, 1: cube 237 | // OpenGL capabilities 238 | bool OGLisOK; // is usable 239 | bool OGLv20; // is version 2.0 or better 240 | bool texPwr2; // needs power-of-2 texture dimensions 241 | bool cubeMap; // has cube mapping (rq'd) 242 | bool vertBuf; // has vertex buffers (opt) 243 | GLint maxcube, max2d; // OGL's texture dim limits 244 | QSize maxTex2Dsqr, // largest feasible texture dims 245 | maxTex2Drec, 246 | maxTexCube; 247 | QSize maxTexSize( GLenum proxy, int tw, int th ); 248 | // status 249 | bool paintok; // most recent OGL error status 250 | bool picok; // sticky OGL error flag 251 | QString errmsg; // sticky OGL error message 252 | bool OGLok(const char * label); // check, post and signal OGL errors 253 | // tabulated sphere points and texture coordinates 254 | panosphere * pqs; 255 | panocylinder * ppc; 256 | bool QS_BUF; // put quadsphere data in OGL buffers 257 | double xtexmag, ytexmag; // tex coord scale factors 258 | 259 | pictureTypes pictypes; 260 | QSizeF curr_fovs; // current 261 | pvQtPic::PicType curr_pt; 262 | int curr_ipt; // index of curr_pt 263 | QSizeF stdTexScale; 264 | 265 | int surface; // 0: sphere, 1: cylinder 266 | 267 | int MacCubeLimit; 268 | 269 | // pointer to overlay image 270 | QImage * povly; 271 | // for recenter mode 272 | bool recenter; 273 | void clipEyePosition(); 274 | 275 | }; 276 | 277 | #endif //ndef PVQTVIEW_H 278 | -------------------------------------------------------------------------------- /src/panosurface.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * panosurface.cpp for Panini 29 Jan 2009 TKS 3 | * 4 | Common code for the projection screen classes 5 | 6 | * Copyright (C) 2008-2009 Thomas K Sharpless 7 | * 8 | * This file is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This file is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this file; if not, write to Free Software Foundation, Inc., 20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | * 22 | */ 23 | 24 | #define PANOSURFACE_IMPLEMENTATION 25 | #include "panosurface.h" 26 | #include 27 | 28 | /* c'tor sets null object */ 29 | panosurface::panosurface(){ 30 | errmsg = 0; 31 | nwords = vertpnts = linewrds = quadwrds = 0; 32 | words = 0; 33 | } 34 | 35 | /* 36 | * get memory and set up pointers 37 | call from subclass c'tor after setting 38 | vertpnts = total no. of vertices 39 | linewrds = 2 * no. of line index pairs 40 | quadwrds = 2 * no. of quad index pairs 41 | 42 | Allocates a pair of TCs for each vertex 43 | returns false if allocation fails 44 | */ 45 | bool panosurface::getMemory(){ 46 | nwords = (3 + 2 * Nprojections) * vertpnts + linewrds + quadwrds; 47 | words = new float[ nwords ]; 48 | 49 | if( words == 0 ){ 50 | errmsg = "insufficient memory for panosurface"; 51 | return false; 52 | } 53 | 54 | // set pointers to arrays 55 | verts = words; 56 | TCs = verts + 3 * vertpnts; 57 | lineidx = (unsigned int *)(TCs + Nprojections * 2 * vertpnts); 58 | quadidx = lineidx + linewrds; 59 | 60 | return true; 61 | } 62 | 63 | panosurface::~panosurface(){ 64 | if( words ) { 65 | delete[] words; 66 | } 67 | } 68 | 69 | /* 70 | * API calls to get data pointers and sizes 71 | */ 72 | 73 | const float * panosurface::texCoords( const char * proj ){ 74 | int i = pictypes.picTypeIndex( proj ); 75 | 76 | if( i < 0 || i >= Nprojections ) { 77 | return 0; 78 | } 79 | 80 | return TCs + i * 2 * vertpnts; 81 | } 82 | 83 | unsigned int panosurface::texCoordOffset( const char * proj ){ 84 | const float * p = texCoords( proj ); 85 | 86 | if( p == 0 ) { 87 | return 0; 88 | } 89 | 90 | return (char *)p - (char *)verts; 91 | } 92 | 93 | const float * panosurface::texCoords( pvQtPic::PicType proj ){ 94 | int i = pictypes.picTypeIndex( proj ); 95 | 96 | if( i < 0 || i >= Nprojections ) { 97 | return 0; 98 | } 99 | 100 | return TCs + i * 2 * vertpnts; 101 | } 102 | 103 | unsigned int panosurface::texCoordOffset( pvQtPic::PicType proj ){ 104 | const float * p = texCoords( proj ); 105 | 106 | if( p == 0 ) { 107 | return 0; 108 | } 109 | 110 | return (char *)p - (char *)verts; 111 | } 112 | 113 | /* 114 | Compute texture coordinates from vertex coordinates 115 | All memory must be allocated and vertices valid 116 | 117 | The supported mappings are listed in pictureTypes 118 | 119 | The valid range of TCs is [0:1]; invalid points 120 | get TCs just slightly outside that range. 121 | 122 | The TC ranges for a given projection correspond to 123 | the absolute max Fovs returned by pictureTypes. If 124 | the panosurface subtends a smaller angle, the TC 125 | range will be truncated accordingly. 126 | 127 | To correctly map images with angular sizes smaller than 128 | maxFov, TC's need to be scaled up appopriately. The 129 | function getTCScale() can be used for that. 130 | 131 | */ 132 | 133 | void panosurface::map_projections(){ 134 | 135 | // set angular limts from fovs in pictureTypes 136 | QSizeF fovs; 137 | fovs = pictypes.maxFov( pictypes.picTypeIndex( "rect" ) ); 138 | double amaxrect = DEG2RAD(0.5 * fovs.width()); 139 | double cminrect = cos( amaxrect ); 140 | double tmaxrect = tan( amaxrect ); 141 | 142 | fovs = pictypes.maxFov( pictypes.picTypeIndex( "cyli" ) ); 143 | double amaxcyli = DEG2RAD( 0.5 * fovs.height() ); 144 | double tmaxcyli = tan( amaxcyli ); 145 | 146 | fovs = pictypes.maxFov( pictypes.picTypeIndex( "sphr" ) ); 147 | double amaxsphr = DEG2RAD( 0.5 * fovs.width() ); 148 | 149 | fovs = pictypes.maxFov( pictypes.picTypeIndex( "fish" ) ); 150 | double amaxfish = DEG2RAD( 0.5 * fovs.width() ); 151 | 152 | fovs = pictypes.maxFov( pictypes.picTypeIndex( "merc" ) ); 153 | double amaxmerc = DEG2RAD( 0.5 * fovs.height() ); 154 | double smaxmerc = sin( amaxmerc ); 155 | double tmaxmerc = atanh( smaxmerc ); 156 | 157 | fovs = pictypes.maxFov( pictypes.picTypeIndex( "ster" ) ); 158 | double amaxster = DEG2RAD( 0.5 * fovs.width() ); 159 | double tmaxster = tan( 0.5 * amaxster ); 160 | 161 | // working TC array pntrs 162 | float * pr = (float *)texCoords("rect"); 163 | float * pf = (float *)texCoords("fish"); 164 | float * pc = (float *)texCoords("cyli"); 165 | float * pe = (float *)texCoords("equi"); 166 | float * pt = (float *)texCoords("ster"); 167 | float * pm = (float *)texCoords("merc"); 168 | float * pa = (float *)texCoords("sphr"); 169 | // working vertex array pointer 170 | float * ps = verts; 171 | 172 | // loop over all vertices 173 | for(int r = vertpnts; r > 0; --r ){ 174 | 175 | /* 176 | x,y,z is point on panosphere in direction of vertex 177 | xa is angle from +Z in XZ plane [-Pi:Pi], 178 | ya is angle out of XZ plane [-Pi/2 : Pi/2], 179 | za is angle away from +Z [0:Pi] 180 | Viewing 0->+Z, X left, Y up 181 | */ 182 | 183 | // unit direction vector 184 | double s = 1.0 / sqrt(ps[0]*ps[0] + ps[1]*ps[1] + ps[2]*ps[2]); 185 | double x = s * ps[0], 186 | y = s * ps[1], 187 | z = s * ps[2]; 188 | // important angles, their sines and cosines 189 | double xa = -atan2(x, z), // horiz from +Z 190 | ya = acos(y), // vert from -Y 191 | za = acos(z); // radial from +Z 192 | double sxa = -x, // sin(xa) 193 | cxa = z, // cos(xa) 194 | sya = y, // sin(ya) 195 | cya = sqrt( x * x + z * z ), 196 | sza = sqrt( x * x + y * y ), 197 | cza = z; 198 | // direction vector for radial fns (0: invalid) 199 | double sx = 0, sy = 0; 200 | if( sza >= 1.0e-4 ){ 201 | sx = sxa / sza; 202 | sy = -sya / sza; 203 | } 204 | 205 | // rectilinear 206 | if( za > 0.45 * Pi ){ 207 | pr[0] = INVAL( sx ); 208 | pr[1] = INVAL( sy ); 209 | } else { 210 | s = 0.5 * (sza/cza) / tmaxrect; 211 | double x = CLIP( 0.5 + s * sx ), 212 | y = CLIP( 0.5 + s * sy ); 213 | pr[0] = float( x ); 214 | pr[1] = float( y ); 215 | } 216 | 217 | // fisheye 218 | if( za > amaxfish ){ 219 | pf[0] = INVAL( xa ); 220 | pf[1] = INVAL( ya - 0.5 * Pi ); 221 | } else { 222 | s = 0.5 * sqrt(0.5 * ( 1 - cza )); 223 | pf[0] = float(CLIP(0.5 + s * sx) ); 224 | pf[1] = float(CLIP(0.5 + s * sy) ); 225 | } 226 | 227 | // equirectangular 228 | pe[0] = float( CLIP(0.5 + 0.5 * xa / Pi)); 229 | pe[1] = float( CLIP(ya / Pi)); 230 | 231 | // cylindrical 232 | s = ya - 0.5 * Pi; 233 | if( fabs(s) > amaxcyli ){ 234 | pc[0] = INVAL( xa ); 235 | pc[1] = INVAL( s ); 236 | } else { 237 | pc[0] = pe[0]; 238 | pc[1] = float(CLIP(0.5 - 0.5 * (sya/cya) / tmaxcyli)); 239 | } 240 | 241 | // equiangular sphere 242 | if( za > amaxsphr ){ 243 | pa[0] = INVAL( xa ); 244 | pa[1] = INVAL( ya - 0.5 * Pi ); 245 | } else { 246 | s = 0.5 * za / Pi; 247 | pa[0] = float( CLIP(0.5 + s * sx) ); 248 | pa[1] = float( CLIP(0.5 + s * sy) ); 249 | } 250 | 251 | // mercator 252 | pm[0] = pe[0]; 253 | s = ya - 0.5 * Pi; 254 | if( fabs(sya) > smaxmerc ) { 255 | pm[1] = INVAL( s ); 256 | } 257 | else { 258 | s = atanh(sya); 259 | pm[1] = float(CLIP( 0.5 - 0.5 * s / tmaxmerc )); 260 | } 261 | 262 | // stereographic 263 | if( za > amaxster ){ 264 | pt[0] = INVAL( sx ); 265 | pt[1] = INVAL( sy ); 266 | } else { 267 | s = 0.5 * tan( 0.5 * za ) / tmaxster; 268 | pt[0] = float(CLIP(0.5 + s * sx)); 269 | pt[1] = float(CLIP(0.5 + s * sy)); 270 | } 271 | 272 | 273 | // next point 274 | ps += 3; 275 | pr += 2; pf += 2; pc += 2; pe += 2; 276 | pa += 2; pm += 2; pt += 2; 277 | } 278 | } 279 | 280 | /* 281 | texture coordinate scale factors to correctly map 282 | an image of a given projection and angular size. 283 | xfov, yfov are full angular sizes in degrees 284 | xscale, yscale are factors by which the tabulated TC's should 285 | be multiplied. 286 | */ 287 | bool panosurface::texScale( int ipt, double xfov, double yfov, double& xscale, double& yscale) { 288 | xscale = yscale = 1; 289 | if( ipt < 0 || ipt >= Nprojections ) { 290 | return false; 291 | } 292 | 293 | QSizeF fovs = pictypes.maxFov( ipt ); 294 | pvQtPic::PicType ppt = pictypes.PicType( ipt ); 295 | int ptx, pty; 296 | pvQtPic::getxyproj( ppt, ptx, pty ); 297 | double d; 298 | d = pvQtPic::fov2rad( ptx, fovs.width() ); 299 | xscale = d / pvQtPic::fov2rad( ptx, xfov ); 300 | d = pvQtPic::fov2rad( pty, fovs.height() ); 301 | yscale = d / pvQtPic::fov2rad( ptx, yfov ); 302 | 303 | return true; 304 | } 305 | 306 | bool panosurface::texScale( const char * proj, double xfov, double yfov, double& xscale, double& yscale) { 307 | return texScale( pictypes.picTypeIndex( proj ), xfov, yfov, xscale, yscale ); 308 | } 309 | 310 | bool panosurface::texScale( pvQtPic::PicType proj, double xfov, double yfov, double& xscale, double& yscale ) { 311 | return texScale( pictypes.picTypeIndex( proj ), xfov, yfov, xscale, yscale ); 312 | } 313 | -------------------------------------------------------------------------------- /src/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MainWindow.cpp for pvQt 08Sep2008 3 | * Copyright (C) 2008 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "MainWindow.h" 25 | #include "GLwindow.h" 26 | #include "CubeLimit_dialog.h" 27 | 28 | /* 29 | * modal error message display 30 | Note QErrorMessage::qtHandler installs a non-modal handler, so 31 | you don't get to see the message before the program bombs 32 | */ 33 | static void errMsgHandler( QtMsgType type, const QMessageLogContext &context, const QString &msg){ 34 | QString s; 35 | bool die = false; 36 | QErrorMessage *errorMessage = new QErrorMessage(nullptr); 37 | errorMessage->setModal(true); 38 | 39 | switch( type ){ 40 | case QtDebugMsg: 41 | s = "Debug: "; 42 | case QtWarningMsg: 43 | s += msg; 44 | errorMessage->showMessage(s, "pvQt"); 45 | break; 46 | case QtFatalMsg: 47 | die = true; 48 | s = "Fatal: "; 49 | case QtCriticalMsg: 50 | s += msg; 51 | errorMessage->showMessage(s, "pvQt"); 52 | break; 53 | } 54 | 55 | if( die ) { 56 | exit( 3 ); 57 | } 58 | } 59 | 60 | MainWindow::MainWindow( QWidget * parent) 61 | { 62 | //install modal error message handler 63 | qInstallMessageHandler( errMsgHandler ); 64 | 65 | //create actions that aren't in menus 66 | //toggle panosurface type 67 | QAction * ats = new QAction(this); 68 | actionToggleSurface = ats; 69 | ats->setObjectName(QString::fromUtf8("actionToggleSurface")); 70 | ats->setCheckable( true ); 71 | ats->setChecked( true ); 72 | ats->setIconText(tr("panosphere")); 73 | 74 | #ifndef QT_NO_TOOLTIP 75 | ats->setToolTip(QApplication::translate("MainWindow", 76 | "Switch panosurface: sphere gives stereographic views; cylinder gives Pannini views", 77 | 0)); 78 | #endif // QT_NO_TOOLTIP 79 | 80 | // step thru source formats 81 | actionNext_iProj = new QAction(this); 82 | actionNext_iProj->setObjectName(QString::fromUtf8("actionNext_iProj")); 83 | 84 | #ifndef QT_NO_TOOLTIP 85 | actionNext_iProj->setToolTip(QApplication::translate("MainWindow", 86 | "Change assumed source image projection", 87 | 0)); 88 | #endif // QT_NO_TOOLTIP 89 | 90 | setupUi( this ); 91 | 92 | // load & apply persistent window settings 93 | pqs = new QSettings("PaniniPerspective", "Panini-0.6"); 94 | resize( pqs->value("window/size", QSize(400, 400) ).toSize() ); 95 | move( pqs->value("window/posn", QPoint(40, 40) ).toPoint() ); 96 | 97 | pmm = new pvQtMouseModes( this ); 98 | 99 | bool ok = true; 100 | if(ok) ok = connect(actionQuit, &QAction::triggered, 101 | qApp, &QApplication::quit); 102 | if(ok) ok = connect(actionPan_Left, &QAction::triggered, 103 | this, &MainWindow::panLft); 104 | if(ok) ok = connect(actionPan_Right, &QAction::triggered, 105 | this, &MainWindow::panRgt); 106 | if(ok) ok = connect(actionTilt_Up, &QAction::triggered, 107 | this, &MainWindow::tiltUp); 108 | if(ok) ok = connect(actionTilt_Down, &QAction::triggered, 109 | this, &MainWindow::tiltDwn); 110 | if(ok) ok = connect(actionZoom_In, &QAction::triggered, 111 | this, &MainWindow::zoomIn); 112 | if(ok) ok = connect(actionZoom_Out, &QAction::triggered, 113 | this, &MainWindow::zoomOut); 114 | if(ok) ok = connect(actionRoll_Right, &QAction::triggered, 115 | this, &MainWindow::rollRight); 116 | if(ok) ok = connect(actionRoll_Left, &QAction::triggered, 117 | this, &MainWindow::rollLeft); 118 | if(ok) ok = connect(actionEye_In, &QAction::triggered, 119 | this, &MainWindow::eyeIn); 120 | if(ok) ok = connect(actionEye_Out, &QAction::triggered, 121 | this, &MainWindow::eyeOut); 122 | if(ok) ok = connect(action_Home, &QAction::triggered, 123 | this, &MainWindow::homeView); 124 | if(ok) ok = connect(actionReset, &QAction::triggered, 125 | this, &MainWindow::resetView); 126 | 127 | // add actions to status buttons 128 | surfaceButton->setDefaultAction( actionToggleSurface ); 129 | iprojButton->setDefaultAction( actionNext_iProj ); 130 | 131 | // put labels and buttons in status bar 132 | statusbar->addPermanentWidget( iprojButton ); 133 | statusbar->addPermanentWidget( vfovLabel ); 134 | statusbar->addPermanentWidget( hfovLabel ); 135 | statusbar->addPermanentWidget( surfaceButton ); 136 | statusBar()->showMessage(tr("Ready")); 137 | 138 | if(ok){ 139 | glwindow = new GLwindow(this); 140 | ok = glwindow->isOK(); 141 | } 142 | 143 | if( !ok ) { 144 | qFatal("MainWindow setup failed"); 145 | } 146 | 147 | setCentralWidget( glwindow ); 148 | 149 | int cubelim = pqs->value("Mac/cube_limit", 1536 ).toInt(); 150 | pcld = new CubeLimit_dialog( cubelim, this ); 151 | glwindow->setCubeLimit( cubelim ); 152 | 153 | #ifdef __APPLE__ 154 | actionCube_limit->setEnabled( true ); 155 | #else 156 | actionCube_limit->setEnabled( false ); 157 | #endif 158 | 159 | // enable panosurface switch 160 | actionPanosphere->setEnabled(true); 161 | actionPanocylinder->setEnabled(true); 162 | } 163 | 164 | /* 165 | * handle command line argumnents 166 | called before GUI is activated 167 | if it returns false the run is aborted. 168 | */ 169 | bool MainWindow::postArgs( int argc, char **argv ){ 170 | return glwindow->commandLine( argc, argv ); 171 | } 172 | 173 | void MainWindow::resizeEvent( QResizeEvent * ev ){ 174 | } 175 | 176 | void MainWindow::closeEvent( QCloseEvent * ev ){ 177 | // save window size and position 178 | pqs->setValue("window/size", size()); 179 | pqs->setValue("window/posn", pos()); 180 | pqs->sync(); 181 | ev->accept(); 182 | } 183 | 184 | void MainWindow::showStatus( QString msg ){ 185 | statusBar()->showMessage(msg); 186 | } 187 | 188 | void MainWindow::verify( int i ){ 189 | QString s; 190 | showStatus( s.sprintf("verify: %d", i) ); 191 | } 192 | 193 | void MainWindow::showTitle( QString msg ){ 194 | setWindowTitle( msg ); 195 | } 196 | 197 | /* 198 | * View menu handlers 199 | */ 200 | 201 | void MainWindow::panLft(){ 202 | emit step_pan(-1); 203 | } 204 | void MainWindow::panRgt(){ 205 | emit step_pan(1); 206 | } 207 | 208 | void MainWindow::tiltDwn(){ 209 | emit step_tilt(-1); 210 | } 211 | void MainWindow::tiltUp(){ 212 | emit step_tilt(1); 213 | } 214 | 215 | void MainWindow::zoomOut(){ 216 | emit step_zoom(-1); 217 | } 218 | void MainWindow::zoomIn(){ 219 | emit step_zoom(1); 220 | } 221 | void MainWindow::rollLeft(){ 222 | emit step_roll(-1); 223 | } 224 | void MainWindow::rollRight(){ 225 | emit step_roll(1); 226 | } 227 | 228 | void MainWindow::eyeIn(){ 229 | emit step_dist(-1); 230 | } 231 | 232 | void MainWindow::eyeOut(){ 233 | emit step_dist(1); 234 | } 235 | 236 | void MainWindow::homeView(){ 237 | emit home_view(); 238 | } 239 | void MainWindow::resetView(){ 240 | emit reset_view(); 241 | } 242 | 243 | void MainWindow::on_actionLinear_proj_triggered(){ 244 | emit set_view( 0 ); 245 | } 246 | 247 | void MainWindow::on_actionPanini_proj_triggered(){ 248 | emit set_view( 1 ); 249 | } 250 | 251 | void MainWindow::on_actionOrtho_proj_triggered(){ 252 | emit set_view( 2 ); 253 | } 254 | 255 | void MainWindow::on_actionSuper_wide_triggered(){ 256 | emit super_wide(); 257 | } 258 | 259 | void MainWindow::on_action90_deg_CW_triggered(){ 260 | emit turn90( 1 ); 261 | } 262 | 263 | /* 264 | * Source Menu Handlers named so connectSlotsByName() will find them 265 | */ 266 | 267 | void MainWindow::on_actionNone_wire_model_triggered(){ 268 | emit newPicture("none"); 269 | } 270 | 271 | void MainWindow::on_actionQTVR_triggered(){ 272 | emit newPicture( "qtvr" ); 273 | } 274 | 275 | void MainWindow::on_actionRectilinear_triggered(){ 276 | emit newPicture( "rect" ); 277 | } 278 | 279 | void MainWindow::on_actionFisheye_triggered(){ 280 | emit newPicture( "fish" ); 281 | } 282 | 283 | void MainWindow::on_actionSpherical_triggered(){ 284 | emit newPicture( "sphr" ); 285 | } 286 | 287 | void MainWindow::on_actionCylindrical_triggered(){ 288 | emit newPicture( "cyli" ); 289 | } 290 | 291 | void MainWindow::on_actionStereographic_triggered(){ 292 | emit newPicture( "ster" ); 293 | } 294 | 295 | void MainWindow::on_actionMercator_triggered(){ 296 | emit newPicture( "merc" ); 297 | } 298 | 299 | void MainWindow::on_actionEquirectangular_triggered(){ 300 | emit newPicture( "equi" ); 301 | } 302 | 303 | void MainWindow::on_actionCube_faces_triggered(){ 304 | emit newPicture( "cube" ); 305 | } 306 | 307 | void MainWindow::on_actionPT_script_triggered(){ 308 | emit newPicture( "proj" ); 309 | } 310 | 311 | /* 312 | * Help menu handlers 313 | */ 314 | void MainWindow::on_actionAbout_pvQt_triggered(){ 315 | emit about_pvQt(); 316 | } 317 | 318 | void MainWindow::on_actionMouse_modes_triggered(){ 319 | pmm->show(); 320 | } 321 | 322 | /* 323 | * Display projection and magnification 324 | */ 325 | void MainWindow::showProj( QString name ){ 326 | iprojButton->setText(QString("iproj %1").arg(name)); 327 | } 328 | 329 | void MainWindow::showFov( QSizeF f ){ 330 | hfovLabel->setText(QString("hfov %1").arg(f.width(), 0, 'f', 2)); 331 | vfovLabel->setText(QString("vfov %1").arg(f.height(), 0, 'f', 2)); 332 | } 333 | 334 | void MainWindow::on_actionSave_as_triggered(){ 335 | emit save_as(); 336 | } 337 | 338 | void MainWindow::on_actionHFovUp_triggered(){ 339 | emit step_hfov( 1 ); 340 | } 341 | 342 | void MainWindow::on_actionHFovDn_triggered(){ 343 | emit step_hfov( -1 ); 344 | } 345 | 346 | void MainWindow::on_actionVFovUp_triggered(){ 347 | emit step_vfov( 1 ); 348 | } 349 | 350 | void MainWindow::on_actionVFovDn_triggered(){ 351 | emit step_vfov( -1 ); 352 | } 353 | 354 | // Note true <=> panosphere 355 | void MainWindow::on_actionToggleSurface_triggered( bool ckd ){ 356 | int surf = ckd? 0 : 1; 357 | 358 | showSurface( surf ); 359 | emit set_surface( surf ); 360 | } 361 | 362 | void MainWindow::on_actionNext_iProj_triggered(){ 363 | emit step_iproj( 1 ); 364 | } 365 | 366 | void MainWindow::on_actionHome_Eye_X_Y_triggered(){ 367 | emit home_eyeXY(); 368 | } 369 | 370 | void MainWindow::on_actionReset_turn_triggered(){ 371 | emit reset_turn(); 372 | } 373 | 374 | // Display panosurface. 0: sphere, 1: cylinder 375 | // also sets menu checks 376 | void MainWindow::showSurface( int surf ){ 377 | bool s = surf == 0; 378 | actionToggleSurface->setChecked( s ); 379 | actionToggleSurface->setIconText( s? tr("panosphere") : tr("panocylinder") ); 380 | } 381 | 382 | void MainWindow::on_actionCube_limit_triggered(){ 383 | int t = pcld->limit(); 384 | if( pcld->exec() ){ 385 | t = pcld->limit(); 386 | glwindow->setCubeLimit( t ); 387 | pqs->setValue("Mac/cube_limit", t ); 388 | pqs->sync(); 389 | } else { 390 | pcld->setLimit( t ); 391 | } 392 | } 393 | 394 | // Overlay menu handlers 395 | void MainWindow::on_actionRemove_triggered(){ 396 | emit overlayCtl( 0 ); 397 | } 398 | 399 | void MainWindow::on_actionLoad_overlay_triggered(){ 400 | emit overlayCtl( 1 ); 401 | } 402 | 403 | void MainWindow::on_actionShow_Hide_triggered(){ 404 | emit overlayCtl( 2 ); 405 | } 406 | 407 | void MainWindow::on_actionFade_triggered(){ 408 | emit overlayCtl( 3 ); 409 | } 410 | 411 | void MainWindow::on_actionRecenter_mode_triggered( bool ckd ){ 412 | emit recenterMode( ckd ); 413 | } 414 | 415 | void MainWindow::showRecenter( bool ckd ){ 416 | actionRecenter_mode->setChecked( ckd ); 417 | } 418 | 419 | void MainWindow::on_actionEye_right_triggered(){ 420 | emit step_eyex( 1 ); 421 | } 422 | 423 | void MainWindow::on_actionEye_left_triggered(){ 424 | emit step_eyex( -1 ); 425 | } 426 | 427 | void MainWindow::on_actionEye_up_triggered(){ 428 | emit step_eyey( 1 ); 429 | } 430 | 431 | void MainWindow::on_actionEye_down_triggered(){ 432 | emit step_eyey( -1 ); 433 | } 434 | -------------------------------------------------------------------------------- /src/pvQtPic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pvQtPic.h 3 | * Copyright (C) 2008 Thomas K Sharpless 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to Free Software Foundation, Inc., 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | * 19 | pvQt is a panoramic image viewer built on the Qt4 framework and OpenGL. 20 | 21 | Class pictureTypes defines the supported source formats. 22 | 23 | Class pvQtPic holds the specifications and data for a picture. It 24 | can be built with data already in memory, or local files, and can 25 | be subclassed to get the necessary information from other sources. 26 | 27 | Passing a pvQtPic to pvQtView::showPic() starts a picture display 28 | session, in which the view object is the active partner. It 29 | gets information it needs by calling pvQtPic methods, and may 30 | also modify the pvQtPic. 31 | 32 | The images passed to view are always QImages, in a size and 33 | format negotiated by view, which knows the OpenGL capabilities, 34 | and pic, which knows the picture format. So texture imagea are 35 | often resized or otherwise transformed from the source images. 36 | Source images are normally read form an external mediunm when 37 | called for, but may be cached. 38 | 39 | Source image files can be in any format readable by QImageReader. 40 | Their locations can be specified by path name or url. The base 41 | implementation will read local files specified by url (as passed 42 | by drag-n-drop, for example) but is not necessarily able to load 43 | remote files. 44 | 45 | Memory resident source images may be QImages or copies of files 46 | that QImageReader can decode (this includes most supported file 47 | types) or raster images whose format can be specified with the 48 | simple ad hoc interface defined here. 49 | 50 | The base class is a subclass of QObject, but does not define any 51 | signals or slots. 52 | 53 | The basic cycle of operation is: 54 | App calls 55 | setType() -- clear state & post fixed rqt's of new type 56 | setImageFOV() -- post the source image angular size 57 | setFaceImage() -- identify & verify the source image(s) 58 | [setLabel,setBorder(),setFill() -- set a nondefault style 59 | for empty frames, if desired] 60 | then passes pic to GLview::showPic(). 61 | PvQtView calls 62 | setFaceFOV() -- sets image cropping/padding ratios 63 | setFaceSize() -- sets final face image size 64 | FaceImage() -- to get the final texture image(s) 65 | PictureFOV() -- to get pan and tilt limits 66 | 67 | For cubic pictures only, you can call setFaceImage() even 68 | after the picture is displayed, to add, replace or delete 69 | face images. To delete a face, pass a null QImage *. 70 | To add or replace one, call any of the overloads. The 71 | new images will be shown at the existing face dimensions. 72 | */ 73 | 74 | #ifndef __PVQTPIC_H__ 75 | #define __PVQTPIC_H__ 76 | 77 | #include 78 | 79 | #define PVQT_PIC_FACE_FORMAT QImage::Format_ARGB32 80 | 81 | class pictureTypes; 82 | 83 | class pvQtPic : public QObject 84 | { Q_OBJECT 85 | public: 86 | 87 | /* Panosurface (projection screen) type */ 88 | enum { 89 | sphere = 0, 90 | cylinder 91 | }; 92 | 93 | /* Picture type implies the number of faces and the image projection */ 94 | typedef enum { 95 | nil = 0, // No picture 96 | cub, // cubic (1 to 6 rectilinear cube face images) 97 | rec, // rectilinear 98 | eqs, // equal solid angle 99 | eqa, // equal angle 100 | cyl, // cylindrical 101 | eqr, // equirectangular 102 | stg, // stereographic 103 | mrc // mercator 104 | } PicType; 105 | 106 | /* 107 | cube face IDs 108 | Use front for all single images, front and back for hemi pairs. 109 | These enumerated values should not be used as array indices. 110 | */ 111 | typedef enum { 112 | any = -1, 113 | front, 114 | right, 115 | back, 116 | left, 117 | top, 118 | bottom 119 | } PicFace; 120 | 121 | pvQtPic( PicType type = nil ); 122 | 123 | /* 124 | utility functions to interconvert pixels and angles 125 | proj is an axis projection type code, from getxyproj() 126 | fov in degrees, rad in radians 127 | */ 128 | static bool getxyproj( PicType t, int & xproj, int & yproj ); 129 | // radius / fl from full fov 130 | static double fov2rad( int proj, double fov ); 131 | // full fov from radius / fl 132 | static double rad2fov( int proj, double rad ); 133 | 134 | // new dimension from change in fov 135 | int scalepix( int proj, int pix, 136 | double fov, double tofov ); 137 | // new fov from change in dimension 138 | double scalefov( int proj, double fov, 139 | int pix, int topix ); 140 | 141 | // adjust a 2D FOV to match image dimensions 142 | // The larger axis fov sets the scale. If the other is 143 | // zero, the nonzero fov goes with the longer dimension 144 | QSizeF adjustFov( PicType t, QSizeF fovs, QSize dims ); 145 | // new 2D fov from change in one angle 146 | QSizeF changeFovAxis( PicType t, QSizeF fovs, double fov, int axis = -1 ); 147 | 148 | /* functions that rescale the cuurent picture */ 149 | 150 | // new 2D fov from rescaling of dimensions 151 | QSizeF picScale2Fov( QSizeF scls ); 152 | // new 2D fov from a change of pictype 153 | QSizeF changeFovType( PicType t, QSizeF fovs, PicType twas ); 154 | // limit fovs to max for type, preserving aspect ratio 155 | QSizeF legalFov( PicType t, QSizeF fovs ); 156 | 157 | /* Functions that return state info */ 158 | 159 | PicType Type(); // nil => cannot display 160 | int NumFaces(); // max for type, 0 for nil 161 | int NumImages(); // number of faces that have source images 162 | 163 | int Surface(){ return surface; } 164 | 165 | // size of texture image(s) 166 | QSize FaceSize(){ return facedims; } 167 | QSizeF FaceFOV(){ return facefovs; } 168 | // size of source image 169 | QSize ImageSize(){ return imagedims; } 170 | QSizeF ImageFOV(){ return imagefovs; } 171 | // Effective size of displayed image in megapixels 172 | double PictureSize(); 173 | 174 | QString FaceName( PicFace face = front ); // display name 175 | /* info about the default "empty image" for a face 176 | isEmpty( any ) is false if any face has a source image. 177 | Other fns return the empty string or black if face is 178 | any or has a source image. 179 | */ 180 | bool isEmpty( PicFace face = any ); 181 | QString getLabel( PicFace face = front ); 182 | QColor getBorder( PicFace face = front ); 183 | QColor getFill( PicFace face = front ); 184 | 185 | // normally called only from pvQtView: 186 | // set face size and display scaling 187 | bool fitFaceToImage( QSize maxdims, bool pwr2 = false ); 188 | // texcoord scale factors to give correct displayed FOV 189 | QSizeF getTexScale(){ return texscale; } 190 | // get a displayable image 191 | QImage * FaceImage( PicFace face = front ); // get face image 192 | // Apparent FOV for arbitrary projection and texcoord scale 193 | QSizeF texScale2Fov( QSizeF scl, PicType t ); 194 | 195 | /* 196 | programmed setup fns return true: success, false: failure. 197 | 198 | A pvQtPic with type nil cannot be displayed or accept any setup 199 | calls but setType(). 200 | 201 | setType() sets internal state to a legal default for the new type. 202 | The default angular sizes are also the largest ones allowed for 203 | the type; for some types this is the only legal value. 204 | 205 | Source images must be individually assigned to specific faces, 206 | legal for the type. 207 | 208 | call setType before setImageFov before setFaceImage 209 | */ 210 | 211 | // to be called only from app: 212 | bool setType( PicType pt ); // clears, sets all defaults 213 | bool setSurface( int s ); 214 | bool setImageFOV( QSizeF angles ); 215 | bool setFaceImage( PicFace face, QImage * img ); 216 | bool setFaceImage( PicFace face, int width, int height, void * addr, 217 | int bitsPerColor, int colorsPerPixel, 218 | bool floatValues = false, 219 | bool packedPixels = true, 220 | int alignBytes = 0 ); 221 | bool setFaceImage( PicFace face, QString path ); 222 | bool setFaceImage( PicFace face, QUrl url ); 223 | 224 | /* 225 | Set empty frame styles 226 | face = any sets all faces; label = "*" uses face names 227 | label color is black or white according to fill color 228 | NOTE setType() restores the default styles 229 | */ 230 | bool setLabel( PicFace face = any, QString label = QString("*") ); 231 | bool setBorder( PicFace face = any, QColor color = QColor() ); 232 | bool setFill( PicFace face = any, QColor color = QColor() ); 233 | 234 | private: 235 | pictureTypes * picTypes; // for max fovs 236 | PicType type; 237 | int surface; 238 | int ipt; // pictureTypes index of type 239 | int maxfaces; // 0 to 6 240 | int numimgs; // no. of faces with source images 241 | // display image and face properties 242 | QImage::Format faceformat; // pixel format 243 | /* dimensions and assigned FOVs of source image never modified here */ 244 | QSize imagedims; 245 | QSizeF imagefovs; 246 | /* texture image and scaling params set by fitFaceToImage() */ 247 | QSize facedims; 248 | QSizeF facefovs; 249 | QSizeF texscale; 250 | QRectF cliprect; // fractional image clip 251 | QRect imageclip; // same scaled to image dims 252 | // display limits for current projection 253 | QSizeF maxfovs, minfovs; 254 | // latest arguments to fitFaceToImage 255 | QSize l_maxdims; 256 | bool l_pwr2; 257 | // arrays indexed by face id, 0:maxfaces-1 258 | bool accept[6]; // usable image 259 | int kinds[6]; // coded source type 260 | int formats[6]; // QImage::Format, or a kcode 261 | QSize idims[6]; // source dimensions 262 | void * addrs[6]; // address if in core 263 | QString names[6]; // path or url 264 | QString labels[6]; // for empty images... 265 | QColor borders[6]; 266 | QColor fills[6]; 267 | // common logic for assigning an image to a face 268 | bool addimgsize( int iface, QSize dims ); 269 | // pixels <=> fov angle 270 | int xproj, yproj; // axis projection types 271 | 272 | // delete and zero a QImage* 273 | // used to remove cached source images 274 | void removeImg( int i ); 275 | // load local images for a face 276 | QImage * loadEmpty( int face ); 277 | QImage * loadFile( int face ); 278 | QImage * loadQImage( int face ); 279 | QImage * loadRaster( int face ); 280 | 281 | /* 282 | virtual functions for remote images -- 283 | reimplement in subclasses that can fetch these. 284 | gotURL is called when a non-local url is put in urls[face]. 285 | It must return true for a (probably) loadable image, else 286 | false. If possible it should put image dimensions in dims, 287 | however that can be deferred until the image is loaded. 288 | loadURL is called when it is time to fetch the image. 289 | */ 290 | virtual bool gotURL( QUrl url, QSize & dims ){ 291 | Q_UNUSED(url); 292 | Q_UNUSED(dims); 293 | return false; 294 | } 295 | virtual QImage * loadURL( QUrl url ){ 296 | Q_UNUSED(url); 297 | return 0; 298 | } 299 | }; 300 | 301 | /* 302 | Picture types visble to the user 303 | The first Nprojections of them correspond to projections 304 | supported by quadsphere (does not include cubic) 305 | */ 306 | #define NpictureTypes 10 307 | #define Nprojections 7 308 | 309 | class pictureTypes: 310 | public QObject 311 | { Q_OBJECT 312 | public: 313 | // constructor 314 | pictureTypes(); 315 | /* 316 | * access functions 317 | Picture type attributes are accessible by index or by 318 | the 4 letter picture type names visible to the user. 319 | For the subset corresponding to primitive projections, 320 | picture type names and pvQtPic::picType codes can be 321 | interconverted. 322 | */ 323 | // PicType code (nil if not valid) 324 | pvQtPic::PicType PicType( const char * name ); 325 | pvQtPic::PicType PicType( int index ); 326 | 327 | // table index from key 328 | int picTypeIndex( pvQtPic::PicType t ); // subset only 329 | int picTypeIndex( const char * name ); // all rows 330 | 331 | // attributes 332 | const char * picTypeName( int index ); 333 | const char * picTypeName( pvQtPic::PicType t ); 334 | int picTypeCount( const char * name ); 335 | int picTypeCount( int index ); 336 | QString picTypeDescr( const char * name ); 337 | QString picTypeDescr( int index ); 338 | QSizeF minFov( int index ); 339 | QSizeF maxFov( int index ); 340 | QSizeF absMaxFov( int index ); 341 | // all descriptors as a list of strings 342 | QStringList picTypeDescrs(); 343 | 344 | private: 345 | // picture type names, descriptions, and max file counts 346 | typedef struct { 347 | const char * typ; 348 | pvQtPic::PicType pictype; 349 | const int nfi; // max file count 350 | QString desc; 351 | double minW, minH, // smallest accepted 352 | maxW, maxH, // largest displayable 353 | AmaxW, AmaxH; // largest accepted 354 | } pictypnumdesc; 355 | 356 | static pictypnumdesc pictypn[NpictureTypes]; 357 | }; 358 | 359 | #endif // __PVQTPIC_H__ 360 | -------------------------------------------------------------------------------- /src/panosphere.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * panosphere.cpp for Panini 29 Jan 2009 3 | 4 | A sphere tessellation with texture coordinates, 5 | line and quad incices, in arrays usable by OpenGl 6 | 7 | Note the OGL coordinate system is left handed: 8 | X right, Y up, Z toward eye 9 | PvQt puts the picture center at +Z, and loads 10 | Y-reversed images, so these texture coordinates 11 | have X left, Y down (Z toward). 12 | * 13 | * Copyright (C) 2008-2009 Thomas K Sharpless 14 | * 15 | * This file is free software; you can redistribute it and/or modify 16 | * it under the terms of the GNU General Public License as published by 17 | * the Free Software Foundation; either version 3 of the License, or 18 | * (at your option) any later version. 19 | * 20 | * This file is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * You should have received a copy of the GNU General Public License 26 | * along with this file; if not, write to Free Software Foundation, Inc., 27 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 | * 29 | */ 30 | 31 | #define PANOSURFACE_IMPLEMENTATION 32 | #include "panosphere.h" 33 | 34 | static inline double dotp( double a[3], double b[3] ){ 35 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 36 | } 37 | 38 | /* split the great circle between 2 unit vectors into divs 39 | intervals, storing results in a general array (divs + 1 40 | results in all, including the end points). Results and 41 | stride are in float 3-vectors. 42 | */ 43 | static void slerp( int divs, double v0[3], double v1[3], 44 | float * pf, int stride ) 45 | { 46 | if( divs < 1 ) { 47 | return; 48 | } 49 | double om = dotp( v0, v1 ); // cos(angle between ends) 50 | double som = sqrt( 1.0 - om * om ); // sin ditto 51 | om = asin( som ); // the angle 52 | double s = 1.0 / divs ; // the t step 53 | 54 | for( int i = 0; i <= divs; i++ ){ 55 | double t = i * s; 56 | double a = sin(om*(1.0 - t)) / som, 57 | b = sin(om*t) / som; 58 | pf[0] = float(v0[0] * a + v1[0] * b); 59 | pf[1] = float(v0[1] * a + v1[1] * b); 60 | pf[2] = float(v0[2] * a + v1[2] * b); 61 | pf += 3 * stride; 62 | } 63 | } 64 | 65 | inline void setEdgeTCs( float * base, unsigned int ic, unsigned int id ){ 66 | base[ic] = EVAL( base[ic - 2]); 67 | base[id] = EVAL( base[ic + 2]); 68 | base[id + 1] = base[ic + 1]; 69 | } 70 | 71 | inline void copyTCs( float * base, unsigned int ic, unsigned int id ){ 72 | base[id] = base[ic]; 73 | base[id + 1] = base[ic + 1]; 74 | } 75 | 76 | /* Abandoning a long hard struggle to put together a 77 | usable sphere from the mimimum number of data points, 78 | I've decided in this third version to keep it simple, 79 | stupid. So here there are 6 full cube faces, 3 of 80 | which are split up the middle to span the wraparound 81 | line. The redundant edges add 12 * (divs + 1) points, 82 | but make it possible to draw the sphere with code I 83 | can write. 84 | 85 | The faces are stored in unform blocks as follows: 86 | 0: +Z (front) 3: -Z (back) 87 | 1: +y (top) 4: -Y (bottom) 88 | 2: +X (left) 5: -X (right) 89 | followed by the duplicate points that split faces 90 | 1, 3, and 4. The vertices and texture coordinate 91 | sets are stored separately in this format. 92 | 93 | The uniform face layout makes it easy to index the 94 | vertices and texture coordinates to create lines 95 | and quads. 96 | 97 | The faces are oriented as pvQt sees them -- that is, 98 | from the inside. Since the OGL coordinate system is 99 | left handed, the +Z face has +X left and +Y up, and 100 | so forth. This makes all quad indices run CCW. 101 | 102 | */ 103 | 104 | panosphere::panosphere( int divs ){ 105 | 106 | // make sure divs is even, or 1 (cube only) 107 | divs = 2 * ((divs + 1) / 2); 108 | 109 | if( divs < 1 ) { 110 | divs = 1; 111 | } 112 | 113 | /* calculate memory reauirements*/ 114 | int dp1 = divs + 1; // total points per row 115 | int qpf = divs * divs; // quads per face 116 | int ppf = dp1 * dp1; // points per face 117 | int d2 = divs/2; 118 | int ntb = d2; // top/bottom seam pnts to copy 119 | // top/bot centers handled specially, 2 xtra pnts each 120 | int dups = dp1 + 2 * ntb + 4; // extra pnts for seam fix 121 | 122 | vertpnts = 6 * ppf + dups; // total vertices 123 | linewrds = 24 * qpf; 124 | quadwrds = linewrds; 125 | 126 | // get memory or die 127 | if( ! getMemory() ) { 128 | return; 129 | } 130 | 131 | // local TC pntrs 132 | float * rects = (float *)texCoords("rect"); 133 | float * fishs = (float *)texCoords("fish"); 134 | float * cylis = (float *)texCoords("cyli"); 135 | float * equis = (float *)texCoords("equi"); 136 | float * sters = (float *)texCoords("ster"); 137 | float * mercs = (float *)texCoords("merc"); 138 | float * angls = (float *)texCoords("sphr"); 139 | 140 | /* 141 | * Build Vertex arrays 142 | +Z face is computed from cube corners using slerp; 143 | rest are rotations of that by multiples of 90 degrees. 144 | */ 145 | 146 | // cube corner coordinates for front face 147 | const double ccc = sqrt( 1.0 / 3.0 ); 148 | double vul[3] = { ccc, ccc, ccc }, // upper left 149 | vur[3] = { -ccc, ccc, ccc }, // upper rigt 150 | vll[3] = { ccc, -ccc, ccc }, // lower left 151 | vlr[3] = { -ccc, -ccc, ccc }; // lower right 152 | 153 | /* 154 | * compute front face row-by row with a double slerp. 155 | The outer one, done here, interpolates row endpoints 156 | at double precision, then slerp() computes the rows 157 | at float precision. 158 | Note posts the full face including all edge points. 159 | */ 160 | 161 | float * pd = verts; // running output addr 162 | int nr = divs + 1; // row length for 1st 4 faces 163 | unsigned int vcnt = 0; // debug check 164 | double om = dotp( vul, vll ); // cos(angle between ends) 165 | double som = sqrt( 1.0 - om * om ); // sin ditto 166 | om = asin( som ); // the angle 167 | double s = 1.0 / divs ; // the t step 168 | for(int i = 0; i <= divs; i++ ){ 169 | double v0[3], v1[3]; 170 | double t = i * s; 171 | double a = sin(om*(1.0 - t)) / som, 172 | b = sin(om*t) / som; 173 | v0[0] = vul[0] * a + vll[0] * b; 174 | v0[1] = vul[1] * a + vll[1] * b; 175 | v0[2] = vul[2] * a + vll[2] * b; 176 | v1[0] = vur[0] * a + vlr[0] * b; 177 | v1[1] = vur[1] * a + vlr[1] * b; 178 | v1[2] = vur[2] * a + vlr[2] * b; 179 | slerp( divs, v0, v1, pd, 1 ); // fill row 180 | pd += 3 * nr; 181 | vcnt += divs + 1; 182 | } 183 | 184 | /* 185 | * remaining faces 186 | Note this code depends on the symmetry of the 187 | faces around their center points. 188 | */ 189 | unsigned int jf = 3 * ppf; // words per face 190 | float * ps = verts; // -> front face 191 | 192 | for( int i = 0; i < ppf; i++ ){ 193 | float * p = ps; 194 | 195 | p += jf; // ->top 196 | p[0] = ps[0]; // x = x 197 | p[1] = ps[2]; // y = z 198 | p[2] = -ps[1]; // z = -y 199 | p += jf; // -> left 200 | p[0] = ps[2]; // x = z 201 | p[1] = ps[1]; // y = y 202 | p[2] = -ps[0]; // z = -x 203 | p += jf; // -> back 204 | p[0] = -ps[0]; // x = -x 205 | p[1] = ps[1]; // y = y 206 | p[2] = -ps[2]; // z = -z 207 | p += jf; // -> bottom 208 | p[0] = ps[0]; // x = x 209 | p[1] = -ps[2]; // y = -z 210 | p[2] = ps[1]; // z = y 211 | p += jf; // -> right 212 | p[0] = -ps[2]; // x = -z 213 | p[1] = ps[1]; // y = y 214 | p[2] = ps[0]; // z = x 215 | 216 | ps += 3; 217 | } 218 | 219 | /* 220 | * quad indices 221 | same pattern each face 0 3 222 | 1 2 223 | generate one face then copy with offset 224 | */ 225 | 226 | unsigned int * pq = quadidx; 227 | int r; 228 | 229 | for( r = 0; r < divs; r++ ){ 230 | unsigned int k = r * dp1; // 1st index of row 231 | for( int c = 0; c < divs; c++ ){ 232 | // CCW quad 233 | *pq++ = k + c; 234 | *pq++ = dp1 + k + c; 235 | *pq++ = dp1 + k + c + 1; 236 | *pq++ = k + c + 1; 237 | } 238 | } 239 | 240 | unsigned int * qq = quadidx; 241 | for( r = 20 * qpf; r > 0; --r ){ 242 | *pq++ = *qq++ + ppf; 243 | } 244 | 245 | /* 246 | * line indices 247 | copy quad indices, replacing one index of each quad 248 | with a copy of another. Only 2 edges of each quad 249 | are drawn; the doubled index selects which ones. The 250 | doubled corners are chosen so that all 12 cube edges 251 | get drawn: F, T, R : 0 => 2; K, L: 1 => 3; B: 3 => 1 252 | */ 253 | 254 | qq = quadidx; 255 | pq = lineidx; 256 | 257 | // front 258 | for( r = qpf; r > 0; --r ){ 259 | pq[0] = qq[0]; 260 | pq[1] = qq[1]; 261 | pq[2] = qq[0]; 262 | pq[3] = qq[3]; 263 | pq += 4; 264 | qq += 4; 265 | } 266 | 267 | // top 268 | for( r = qpf; r > 0; --r ){ 269 | pq[0] = qq[0]; 270 | pq[1] = qq[1]; 271 | pq[2] = qq[0]; 272 | pq[3] = qq[3]; 273 | pq += 4; 274 | qq += 4; 275 | } 276 | 277 | // left 278 | for( r = qpf; r > 0; --r ){ 279 | pq[0] = qq[0]; 280 | pq[1] = qq[1]; 281 | pq[2] = qq[2]; 282 | pq[3] = qq[1]; 283 | pq += 4; 284 | qq += 4; 285 | } 286 | 287 | // back 288 | for( r = qpf; r > 0; --r ){ 289 | pq[0] = qq[0]; 290 | pq[1] = qq[1]; 291 | pq[2] = qq[2]; 292 | pq[3] = qq[1]; 293 | pq += 4; 294 | qq += 4; 295 | } 296 | 297 | // bottom 298 | for( r = qpf; r > 0; --r ){ 299 | pq[0] = qq[0]; 300 | pq[1] = qq[3]; 301 | pq[2] = qq[2]; 302 | pq[3] = qq[3]; 303 | pq += 4; 304 | qq += 4; 305 | } 306 | 307 | // right 308 | for( r = qpf; r > 0; --r ){ 309 | pq[0] = qq[0]; 310 | pq[1] = qq[1]; 311 | pq[2] = qq[0]; 312 | pq[3] = qq[3]; 313 | pq += 4; 314 | qq += 4; 315 | } 316 | 317 | /* generate texture coordinates for all projections */ 318 | map_projections(); 319 | 320 | /* Fix up the wrap seam 321 | The wrap seam runs from top center thru bottom center 322 | down the middle of back, including top and bottom center 323 | points. There are 2 *divs + 3 points in all because the 324 | cube edge points are already doubled. 325 | 326 | These vertices are copied to the dupes area and the indices 327 | for their right adjacent quads adjusted in-place. Then the 328 | corresponding TCs -- both old and new -- are set low or high 329 | according to the values of the other TC's in their quads. 330 | 331 | Finally the top and bottom centers are split vertically by 332 | assigning a new split center point to their lower quads. 333 | */ 334 | 335 | // point and quad indices 336 | int qtb = ntb - 1; // whole quads top/bottom 337 | unsigned int ipt = ppf + d2, // top rear 338 | iqt = qpf + d2, 339 | ipk = 3 * ppf + d2, // back upper 340 | iqk = 3 * qpf + d2, 341 | ipb = 4 * ppf + (dp1 - ntb) * dp1 + d2, 342 | iqb = 4 * qpf + (divs - 1 - qtb) * divs + d2, 343 | idt = 6 * ppf, // dupe top 344 | idk = idt + ntb, // dupe back 345 | idb = idk + dp1, // dupe bottom 346 | idc = idb + ntb; // dupe centers 347 | // copy the vertices in ascending address order 348 | // and post corresponding correct TCs (old and new) 349 | unsigned int ic = 2 * ipt, id = 2 * idt; 350 | ps = verts + 3 * ipt; 351 | pd = verts + 3 * idt; 352 | 353 | for( r = 0; r < ntb; r++ ){ 354 | pd[0] = ps[0]; 355 | pd[1] = ps[1]; 356 | pd[2] = ps[2]; 357 | ps += 3 * dp1; 358 | pd += 3; 359 | 360 | copyTCs( angls, ic, id ); 361 | setEdgeTCs( rects, ic, id ); 362 | copyTCs( fishs, ic, id ); 363 | setEdgeTCs( cylis, ic, id ); 364 | setEdgeTCs( equis, ic, id ); 365 | copyTCs( sters, ic, id ); 366 | setEdgeTCs( mercs, ic, id ); 367 | ic += 2 * dp1; 368 | id += 2; 369 | } 370 | 371 | ic = 2 * ipk; 372 | id = 2 * idk; 373 | ps = verts + 3 * ipk; 374 | 375 | for( r = 0; r < dp1; r++ ){ 376 | pd[0] = ps[0]; 377 | pd[1] = ps[1]; 378 | pd[2] = ps[2]; 379 | ps += 3 * dp1; 380 | pd += 3; 381 | copyTCs( angls, ic, id ); 382 | setEdgeTCs( rects, ic, id ); 383 | copyTCs( fishs, ic, id ); 384 | setEdgeTCs( cylis, ic, id ); 385 | setEdgeTCs( equis, ic, id ); 386 | copyTCs( sters, ic, id ); 387 | setEdgeTCs( mercs, ic, id ); 388 | ic += 2 * dp1; 389 | id += 2; 390 | } 391 | 392 | ic = 2 * ipb; 393 | id = 2 * idb; 394 | ps = verts + 3 * ipb; 395 | 396 | for( r = 0; r < ntb; r++ ){ 397 | pd[0] = ps[0]; 398 | pd[1] = ps[1]; 399 | pd[2] = ps[2]; 400 | ps += 3 * dp1; 401 | pd += 3; 402 | copyTCs( angls, ic, id ); 403 | setEdgeTCs( rects, ic, id ); 404 | copyTCs( fishs, ic, id ); 405 | setEdgeTCs( cylis, ic, id ); 406 | setEdgeTCs( equis, ic, id ); 407 | copyTCs( sters, ic, id ); 408 | setEdgeTCs( mercs, ic, id ); 409 | ic += 2 * dp1; 410 | id += 2; 411 | } 412 | 413 | /* 414 | * change right quad indices of non-center pnts. 415 | The first and last points of each seam get 1 416 | new index, the others get 2. 417 | */ 418 | 419 | pq = quadidx + 4 * iqt; 420 | for( r = 0; r < qtb; r++ ){ 421 | pq[0] = idt++; 422 | pq[1] = idt; 423 | pq += 4 * divs; 424 | } 425 | 426 | pq[0] = idt; 427 | 428 | pq = quadidx + 4 * iqk; 429 | pq[0] = idk++; 430 | 431 | for( r = 0; r < divs - 1; r++ ){ 432 | pq[1] = idk; 433 | pq += 4 * divs; 434 | pq[0] = idk++; 435 | } 436 | pq[1] = idk; 437 | 438 | pq = quadidx + 4 * iqb; 439 | pq[1] = idb; 440 | for( r = 0; r < qtb ; r++ ){ 441 | pq += 4 * divs; 442 | pq[0] = idb++; 443 | pq[1] = idb; 444 | } 445 | 446 | /* 447 | * fix up the center points 448 | Centers get split vertically as well as horizontally, 449 | so need 2 new vertices and 2 new TC sets. The upper 450 | indices of the lower 2 quads point to the new data. 451 | The vertices are just copies of the center vertex; 452 | the new TCs have x = x of next row, y = center y. 453 | NOTE "next" is addr-- for top, addr++ for bottom, 454 | */ 455 | 456 | // point indices 457 | jf = (dp1 + 1) * d2; // edge to center 458 | ic = ppf + jf; // top center 459 | id = 4 * ppf + jf; // bot center 460 | 461 | // copy vertices 462 | ps = verts + 3 * ic; 463 | pd = verts + 3 * idc; 464 | *pd++ = ps[0]; *pd++ = ps[1]; *pd++ = ps[2]; 465 | *pd++ = ps[0]; *pd++ = ps[1]; *pd++ = ps[2]; 466 | ps = verts + 3 * id; 467 | *pd++ = ps[0]; *pd++ = ps[1]; *pd++ = ps[2]; 468 | *pd++ = ps[0]; *pd++ = ps[1]; *pd++ = ps[2]; 469 | 470 | // build new TC points via old quad indices 471 | // then change the indices 472 | 473 | #define makeCTCtop(ps, pd, pq ) \ 474 | *pd++ = ps[2 * pq[-1]]; \ 475 | *pd++ = ps[2 * pq[-2] + 1]; \ 476 | *pd++ = ps[2 * pq[0]]; \ 477 | *pd++ = ps[2 * pq[1] + 1] 478 | #if 1 479 | unsigned int jq = divs * (d2 - 1) + d2; // upr rgt quad 480 | pq = quadidx + 4 * ( qpf + jq ); 481 | jf = 2 * idc; 482 | ps = angls; pd = ps + jf; 483 | makeCTCtop(ps, pd, pq ); 484 | ps = rects; pd = ps + jf; 485 | makeCTCtop(ps, pd, pq ); 486 | ps = fishs; pd = ps + jf; 487 | makeCTCtop(ps, pd, pq ); 488 | ps = cylis; pd = ps + jf; 489 | makeCTCtop(ps, pd, pq ); 490 | ps = equis; pd = ps + jf; 491 | makeCTCtop(ps, pd, pq ); 492 | ps = sters; pd = ps + jf; 493 | makeCTCtop(ps, pd, pq ); 494 | ps = mercs; pd = ps + jf; 495 | makeCTCtop(ps, pd, pq ); 496 | // change indices 497 | pq[-2] = idc; pq[1] = idc + 1; 498 | #endif 499 | 500 | #define makeCTCbot(ps, pd, pq ) \ 501 | *pd++ = ps[2 * pq[-2]]; \ 502 | *pd++ = ps[2 * pq[-1] + 1]; \ 503 | *pd++ = ps[2 * pq[1]]; \ 504 | *pd++ = ps[2 * pq[0] + 1] 505 | #if 1 506 | jq = (divs + 1) * d2; // lwr rgt quad 507 | pq = quadidx + 4 * ( 4 * qpf + jq ); // bot... 508 | jf = 2 * idc + 4; 509 | ps = angls; pd = ps + jf; 510 | makeCTCbot(ps, pd, pq ); 511 | ps = rects; pd = ps + jf; 512 | makeCTCbot(ps, pd, pq ); 513 | ps = fishs; pd = ps + jf; 514 | makeCTCbot(ps, pd, pq ); 515 | ps = cylis; pd = ps + jf; 516 | makeCTCbot(ps, pd, pq ); 517 | ps = equis; pd = ps + jf; 518 | makeCTCbot(ps, pd, pq ); 519 | ps = sters; pd = ps + jf; 520 | makeCTCbot(ps, pd, pq ); 521 | ps = mercs; pd = ps + jf; 522 | makeCTCbot(ps, pd, pq ); 523 | // change indices 524 | pq[-1] = idc + 2; pq[0] = idc + 3; 525 | #endif 526 | } 527 | -------------------------------------------------------------------------------- /ui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 211 11 | 12 | 13 | 14 | Panini 15 | 16 | 17 | 18 | :/Icons/panini-icon-blue.jpg:/Icons/panini-icon-blue.jpg 19 | 20 | 21 | 22 | 23 | 24 | 230 25 | 150 26 | 61 27 | 16 28 | 29 | 30 | 31 | QFrame::Panel 32 | 33 | 34 | QFrame::Sunken 35 | 36 | 37 | hfov 360.0 38 | 39 | 40 | 41 | 42 | true 43 | 44 | 45 | 46 | 300 47 | 150 48 | 61 49 | 16 50 | 51 | 52 | 53 | QFrame::Panel 54 | 55 | 56 | QFrame::Sunken 57 | 58 | 59 | vfov 180.0 60 | 61 | 62 | 63 | 64 | 65 | 384 66 | 150 67 | 61 68 | 20 69 | 70 | 71 | 72 | iproj: cube 73 | 74 | 75 | 76 | 77 | 78 | 464 79 | 150 80 | 71 81 | 20 82 | 83 | 84 | 85 | 86 | 0 87 | 0 88 | 89 | 90 | 91 | panocylinder 92 | 93 | 94 | 95 | 96 | 97 | 98 | 0 99 | 0 100 | 800 101 | 22 102 | 103 | 104 | 105 | 106 | &View 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | &Source 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | Help 152 | 153 | 154 | 155 | 156 | 157 | 158 | Presets 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Overlay 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | Home Y,P,R 193 | 194 | 195 | Home 196 | 197 | 198 | 199 | 200 | Zoom &in 201 | 202 | 203 | Ctrl+Up 204 | 205 | 206 | 207 | 208 | Zoom &out 209 | 210 | 211 | Ctrl+Down 212 | 213 | 214 | 215 | 216 | Yaw &left 217 | 218 | 219 | Left 220 | 221 | 222 | 223 | 224 | Yaw &right 225 | 226 | 227 | Right 228 | 229 | 230 | 231 | 232 | Pitch &up 233 | 234 | 235 | Up 236 | 237 | 238 | 239 | 240 | Pitch &down 241 | 242 | 243 | Down 244 | 245 | 246 | 247 | 248 | Eye &near 249 | 250 | 251 | Ctrl+Right 252 | 253 | 254 | 255 | 256 | Eye &far 257 | 258 | 259 | Ctrl+Left 260 | 261 | 262 | 263 | 264 | Fullframe 265 | 266 | 267 | PgDown 268 | 269 | 270 | 271 | 272 | SuperFish 273 | 274 | 275 | PgUp 276 | 277 | 278 | 279 | 280 | Home all 281 | 282 | 283 | End 284 | 285 | 286 | 287 | 288 | rectilinear 289 | 290 | 291 | 292 | 293 | fisheye 294 | 295 | 296 | 297 | 298 | cylindrical 299 | 300 | 301 | 302 | 303 | equirectangular 304 | 305 | 306 | 307 | 308 | cube faces 309 | 310 | 311 | 312 | 313 | Quit 314 | 315 | 316 | 317 | 318 | QTVR 319 | 320 | 321 | 322 | 323 | PT script 324 | 325 | 326 | 327 | 328 | About... 329 | 330 | 331 | 332 | 333 | Panini proj 334 | 335 | 336 | P 337 | 338 | 339 | 340 | 341 | Super fish 342 | 343 | 344 | S 345 | 346 | 347 | 348 | 349 | Turn image... 350 | 351 | 352 | Turn image on panosurface 353 | 354 | 355 | T 356 | 357 | 358 | 359 | 360 | spherical 361 | 362 | 363 | 364 | 365 | stereographic 366 | 367 | 368 | 369 | 370 | mercator 371 | 372 | 373 | 374 | 375 | Frame right 376 | 377 | 378 | Shift+Right 379 | 380 | 381 | 382 | 383 | Frame left 384 | 385 | 386 | Shift+Left 387 | 388 | 389 | 390 | 391 | Frame up 392 | 393 | 394 | Shift+Up 395 | 396 | 397 | 398 | 399 | Frame down 400 | 401 | 402 | Shift+Down 403 | 404 | 405 | 406 | 407 | Save as... 408 | 409 | 410 | Ctrl+S 411 | 412 | 413 | 414 | 415 | Next iProj 416 | 417 | 418 | Shift+PgUp 419 | 420 | 421 | 422 | 423 | Prev iProj 424 | 425 | 426 | Shift+PgDown 427 | 428 | 429 | 430 | 431 | Mouse modes... 432 | 433 | 434 | 435 | 436 | Roll CW 437 | 438 | 439 | Ctrl+Alt+Right 440 | 441 | 442 | 443 | 444 | Roll CCW 445 | 446 | 447 | Ctrl+Alt+Left 448 | 449 | 450 | 451 | 452 | true 453 | 454 | 455 | true 456 | 457 | 458 | false 459 | 460 | 461 | panosphere 462 | 463 | 464 | project on sphere 465 | 466 | 467 | 468 | 469 | true 470 | 471 | 472 | false 473 | 474 | 475 | panocylinder 476 | 477 | 478 | project on cylinder 479 | 480 | 481 | 482 | 483 | Home Eye, Frame 484 | 485 | 486 | Shift+Home 487 | 488 | 489 | 490 | 491 | Linear proj 492 | 493 | 494 | L 495 | 496 | 497 | 498 | 499 | Ortho proj 500 | 501 | 502 | O 503 | 504 | 505 | 506 | 507 | none (wire model) 508 | 509 | 510 | 511 | 512 | Reset turn 513 | 514 | 515 | Alt+End 516 | 517 | 518 | 519 | 520 | false 521 | 522 | 523 | Mac cube size... 524 | 525 | 526 | Cube face size limit for Mac OSX 527 | 528 | 529 | false 530 | 531 | 532 | 533 | 534 | Load image... 535 | 536 | 537 | 538 | 539 | Show/Hide 540 | 541 | 542 | V 543 | 544 | 545 | 546 | 547 | Hide 548 | 549 | 550 | 551 | 552 | Fade 553 | 554 | 555 | Ctrl+V 556 | 557 | 558 | 559 | 560 | Remove 561 | 562 | 563 | 564 | 565 | true 566 | 567 | 568 | Recenter mode 569 | 570 | 571 | Ctrl+R 572 | 573 | 574 | 575 | 576 | Eye right 577 | 578 | 579 | Alt+Right 580 | 581 | 582 | 583 | 584 | Eye left 585 | 586 | 587 | Alt+Left 588 | 589 | 590 | 591 | 592 | Eye up 593 | 594 | 595 | Alt+Up 596 | 597 | 598 | 599 | 600 | Eye down 601 | 602 | 603 | Alt+Down 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Panini is a tool for creating perspective views from panoramic and wide angle images. 4 | 5 | Panini can load most common photo and panoramic formats from image files or QuickTime VR (.mov) files. Like all pano viewers, it then shows a linear perspective view that can be panned and zoomed. But Panini can also display a range of wide angle perspectives via the stereographic and "Pannini" families of projections, and shift, rotate, and stretch the image like a software view camera. 6 | 7 | Panini can do those things because it paints the picture on a three dimensional surface, either a sphere or a cylinder, which you then view in perspective. Shifting the point of view changes the apparent perspective of the image, and other controls let you frame the view to your liking. Then you can save the screen image to a file at higher-than-screen resolution. 8 | 9 | All of this works interactively at "mouse speed" thanks to the OpenGL video graphics system. And thanks to the Qt application framework, it runs on Windows, Mac OSX, and most flavors of Linux and Unix. 10 | 11 | ## Why "Panini"? 12 | 13 | The name honors 18th century artist and professor of perspective Gian Paolo Pannini (also spelled "Panini"), who painted grand views of Rome and taught a generation of artists that included Canaletto and Piranesi. Their impressive wide angle views of building interiors and urban landscapes were famous throughout Europe. These pictures seem to be in true perspective, but can't be: the standard linear perspective projection creates serious distortions at much smaller view angles. Instead, they used clever combinations of cylindrical and linear projections. These techniques were probably invented in Holland a century or more earlier, which is one reason why Panini's icon is a picture of the Erasmus Bridge in Rotterdam. 14 | 15 | Bruno Postle recently deduced one very simple but effective way of combining the cylindrical and linear projections by studying a Pannini painting of St. Peter's; so we are calling that the "Pannini projection". It is a linear perspective view of a cylindrical image -- the cylindrical analog of the stereographic projection of a sphere. The Baroque artists may or may not have used this construction explictly, but it produces images that look a lot like some of the ones they drew. 16 | 17 | # System requirements and limitations 18 | 19 | To run Panini you need several Qt runtime libraries. 20 | 21 | To display pictures Panini uses OpenGL, a low level graphics API that is tightly integrated with the system's video drivers. OpenGL cannot be installed as a separate piece of software. If the version of your OpenGL implementation is less than 1.2 or does not support cubic texture mapping, Panini will not run. If the OpenGL version is less than 1.5 it may not be able to display all pictures correctly. 22 | 23 | You will get best results with OpenGL version 2.0 and above. That means you should have either a "gaming" video card, or a recent "multimedia" one. But ultimate gaming performance is not needed; a basic version 2 system should be adequate. Cards to upgrade most desktop PCs to that level now cost under $50. 24 | 25 | The "About" dialog shows information about your system's OpenGL facilities as well as the version of Panini you are running. 26 | It reports these limits: 27 | ``` 28 | texPwr2: must texture dimensions be powers of 2? 0 (false) preferred. 29 | texMax: maximum dimension of 2D textures, the bigger the better. 30 | cubeMax: maximum dimension of cubic textures, the bigger the better. 31 | ``` 32 | 33 | The maximum resolution Panini can display or render depends on the amount of texture memory available. Cubic images generally support somewhat larger texture maps than the flat formats. Saved image resolution is normally several times screen resolution, however that too may be limited by OpenGL resources. If your OpenGL does not support offscreen frame buffers of arbitrary size, all saved views will be at screen resolution. 34 | 35 | Panini places no special demands on CPU speed or memory; if you can stitch panoramas, you will have no trouble viewing them. 36 | 37 | # Basic viewing controls 38 | 39 | Most viewing controls have keyboard shortcuts, shown in the *View* menu. You can operate all viewing controls with the mouse. Help menu item *mouse modes...* shows how. 40 | 41 | When you start Panini without a command line argument, for example by double clicking an icon, it displays a wire frame model of the panosphere (you can switch to the panocylinder by clicking the button that shows the panosurface name). Try out the basic viewing controls on these wire models. 42 | 43 | The *Yaw*, *Pitch* and *Roll* controls rotate your view with respect to the panosurface, as in a flight simulator. Yaw and Pitch let you look in different directions; Roll adjusts which way is up, and changes the directions of the Yaw and Pitch axes accordingly. The status line shows these angles as "Y", "P", and "R", in degrees. 44 | 45 | To control Yaw and Pitch with the mouse, hold the left button and move the pointer in the direction you wish to look. Or you can hold both mouse buttons and "fly" using Roll and Pitch. 46 | 47 | Zoom controls magnification by changing the vertical field of view of the screen window. When you resize the window, the displayed image grows or shrinks to maintain the selected vertical field. The mouse wheel (if present) adjusts zoom in coarse steps. For finer control, move the mouse vertically while holding the right button. Zoom is reported on the status line as "V", in degrees. This is the vertical field of view on the panosphere when eye distance is <= 1, but has a more complicated relationship to visible vfov at larger eye distances and on the panocylinder. 48 | 49 | "Eye in" and "Eye out" change perspective by moving the point of view toward or away from the center of the panosurface. The basic effect of moving the eye out is to compress the periphery of the image. With the eye at the center, the viewing perspective is linear, like a normal camera lens or pano viewer. As the eye moves out, the perspective becomes more fisheye-like, and you can zoom farther out to see wider views. But you can zoom in to the same level of image detail at any perspective setting. When the right button is held, horizontal mouse motion controls eye distance. Eye distance is reported on the status line as "D" in units of the panosurface radius. 50 | 51 | With the panosphere, the perspective projections are members of a family whose protoype (at Ez = 1) is the stereographic projection. With the panocylinder, the projections are members of a family whose prototype is the recently rediscovered "Panini projection", a combination of cylindrical and linear projections. 52 | 53 | The linear perspective projection, at Ez = 0, belongs to both families. As eye distance increases past 1, the viewing projection becomes more nearly parallel, and at Ez = 26, it is very close to the orthographic spherical or cylindrical projection. 54 | 55 | Becase the effect of Ez on the image is quite nonlinear, the controls that adjust it have a compensating nonlinear response built in. 56 | 57 | The following view controls, new in version 0.6, can only be operated with the mouse. 58 | 59 | To move the eye point perpendicular to the projection axis, hold down the Shift key and both mouse buttons, and move the mouse toward the desired eye position. The effects are similar to "swing" and "tilt" on a view camera. Eye position is reported on the status line as "Ex" and "Ey" in units of the panosurface radius. "Shift-Home" resets the eyepoint shifts to zero, as does "End". 60 | 61 | To move the whole view horizontally or vertically without changing its size or shape, hold down the Shift key and the left mouse button. These "framing shifts" are reported on the status line as Fx and Fy. "Shift-Home" or "End" resets them to zero. 62 | 63 | # Presets 64 | 65 | Several preset views are available via single keystrokes. "Home" resets the yaw, pitch and roll angles to zero. "Shift + Home" resets the eyepoint and framing shifts. "End" resets everything, restoring the standard view you got when the image was first loaded. "F" gives the widest possible full frame view (a stereographic or Panini projection) and "S" gives a "super wide" view, obtained by moving the eye point slightly outside the panosurface. 66 | 67 | The Turn controls dialog ("Turn image" in preset menu, or T key) lets you rotate the image on the panosurface. For cubic panoramas this is a full 3D rotation, including Yaw and Pitch angles, so you can align the axis of the panocylinder in any direction in space. For non-cubic sources, only the Roll angle (around source center) can be adjusted. The "orientation" and "Roll" controls both affect this angle, orientation in steps of 90 degrees. The Turn angles are reset by "Alt+End" but not by "End". 68 | 69 | Hint: The main use of Turn is to align the cylinder axis with lines that you want to keep straight in the Pannini projections. The projection axes do not turn along with the image, however you can get rather messy "transverse" forms of some projections by adjusting the hfov and vfov. A future release will have proper transverse projections. 70 | 71 | For convenience in viewing series of related pictures, Panini remembers the last selected Turn angles and FOV for each source format (but version 0.6 does not save them when you exit the program). 72 | 73 | Version 0.63 does save a few items at exit, and restore them at startup. One is a limit on the displayed size of cube faces, effective only on Macs, to work around a bug in OSX that can cause system crashes as well as garbled display of cubic images. You can see and change this limit via the "cube limit" item on the Presets menu. The default limit will probably work for you, but if it gives bad displays of large cubic QTVRs, reduce it until they are displayed correctly. Otherwise you may want to increase the limit until you find the largest size your system will display comfortably (note that the source cube face size also limits the displayed size). 74 | 75 | Version 0.63 also saves and restores the window size. The initial default size is smaller than before so that the window will not overflow small laptop screens, which can be awkward to correct on some systems. 76 | 77 | # Loading a source image 78 | 79 | You can load an image into Panini by naming it on the command line, by selecting it with a file browser (after choosing a format from the "Source" menu) or by dragging it into the Panini window. Details below. 80 | 81 | To show a picture, Panini needs to know the format (projection), angular size (field of view, FOV), image dimensions and where to find the image data. All supported sources supply image dimensions and data, and some also define projection and angular size; but in many cases you must specify the projection and field of view yourself. 82 | 83 | You do this in a dialog that shows the file name, projection, dimensions in pixels, and horizontal and vertical fields of view in degrees. If you have not already chosen a format, you can do that; and in all cases you can enter the field of view. 84 | 85 | You can specify either the horizontal or vertical field of view. They are coupled via the dimensions and projection, so that when you change one the other changes to match. You can uncouple the FOVs when necessary, for example to load an image with non-square pixels, or one with a "transverse" projection, by checking the box. 86 | 87 | When you select a different projection, the last FOV used for that projection is recalled. If it does not match the current image's dimensions, the larger FOV is kept and the other is adjusted. 88 | 89 | Each projection has a maximum FOV, that you can not exceed. These limits are generous, and may let you specify an FOV larger than the maximum that can actually be displayed. In that case the image will be projected according to the given FOV, but cropped to the display limit. 90 | 91 | When an image is loaded, the Panini window's title bar shows the file name and a size in megapixels, which will almost always be smaller than the source image size. This is not the size of the view on screen, but of the texture image used to generate it. In case loading or display fails, the title bar will show an error message instead. 92 | 93 | You can load images by naming them on the command line, by selecting them via the Source menu, or by dragging them into the Panini window. 94 | 95 | ## via Command Line 96 | 97 | When you start Panini from the command line, you can optionally specify an image to be displayed. If you give only a partial specification, Panini will ask you for the rest of the information. 98 | 99 | The first command line argument can be a 4 letter format name, one of: 100 | 101 | ``` 102 | proj PanoTools script or project 103 | qtvr QuickTime VR panorama (cubic or cylindrical) 104 | cube 1 to 6 linear cube face images 105 | rect Rectilinear (normal lens) projection 106 | fish Fisheye lens or mirror ball projection 107 | sphr Equal angle spherical projection 108 | ster Stereographic (super wide) projection 109 | cyli Cylindrical panorama 110 | equi Equirectangular panorama 111 | merc Mercator panorama 112 | ``` 113 | 114 | The first 3 names are rarely needed, because project and qtvr files are normally recognized by their name extensions, and we usually give multiple file names for a cubic format. 115 | 116 | For the other formats, which imply specific input projections, you need to specify the angular size; so the next argument can be a field of view, in degrees, as a floating point number. This is always the fov of the longer image axis, if the image is not square. 117 | 118 | The last thing on the command line can be a file name, or up to six file names for a cubic panorama. Panini can read tiff, jpeg and png image files, identified by the conventional file name extensions. 119 | 120 | If you give only an image file name or names on the command line, Panini will assume that two or more square images are cube faces (to load a single cube face you must give the type name) and that an image file whose width is exactly twice its height is a 360 degree equirectangular panorama. Otherwise it will ask you to specify the projection and FOV. On Windows and Mac, dragging files onto the Panini program icon generates a files-only command line. 121 | 122 | If the command line is only a format name (other than proj or qtvr) Panini displays labelled empty frames (just a "front" frame for most formats; or six cube faces). 123 | 124 | ## via Source Menu 125 | 126 | The Source menu lets you select a format, then Panini asks for files and fov. If you cancel the file selector dialog, empty frames will be displayed. If you cancel the fov dialog, the selected file will not be loaded and the previous picture will remain. 127 | 128 | You can choose "none" to get back the wire frame views of the panosurfaces. 129 | 130 | The "rect" format is appropriate for normal photos. You can also use it to display any kind of image that you want to see all at once. Hint: you can then do serious linear "perspective correction", in the Photoshop sense, with the Yaw and Pitch controls. 131 | 132 | The "fish" format uses the equisolid angle (also known as mirror sphere) projection, a classic panoramic format that is approximated by most modern fisheye lenses. So you can use "fish" to display mirror sphere photos and computed panoramas up to 360 degrees in diameter. 133 | 134 | The "sphr" format uses the equal-angle spherical projection. Most stitchers can generate this projection, and some older or more technical fisheye lenses approximate it. 135 | 136 | You can load 1 to 6 image files for the "cube" format. They must be square images (but need not be the same size). Their sorted names must fall in the conventional cube face order: front, right, back, left, top, bottom. So if you have names like foo_front.tif, foo_right.tif,..., you would have to change them to something like foo_1.tif, foo_2.tif,.... If you give fewer than 6 cube face files, Panini will display empty frames for the missing ones. Nonexistent or unreadable files will also generate empty frames. 137 | 138 | But the easy way to load cube faces is... 139 | 140 | ## via Drag-and-Drop 141 | 142 | You can load any kind of image by dragging it into the Panini window. If a cubic image (or empty cube faces) is currently displayed, and the dropped image is square, it will be put in the cube face on which it was dropped, replacing any image already there. Otherwise, dropped files are handled as described for files named on the command line. 143 | 144 | # Projections 145 | 146 | A photographic image is a projection of part of the world, as seen from a single point, onto a flat plane, according to some mathematical rule. That is also true of most images created by photo processing software, which however provides a much bigger choice of projection rules than camera lenses can. For mathematical purposes, the original view of the world is equivalent to its linear projection onto a spherical surface, so the various photographic projections are considered as projections of the sphere onto the image plane. Most were known to map makers and astronomers long before the invention of photography. 147 | 148 | The first job of a panorama viewer is to undo the source image projection, in effect recreating the spherical image, so that you see the orignal view of the world as the camera saw it. Panini literally "de-projects" source images onto a sphere or cylinder. 149 | 150 | Each image is assigned a projection when it is loaded. If that really is the projection used to make the image, then Panini can show you a "correct" world image, as well as various perspective transformations of that. If a different projection was used, then none of the views displayed by Panini would be "correct" -- but some of them might be interesting or useful images anyhow. 151 | 152 | In fact, all source projections except the cubic are interchangeable -- Panini will happily display the same image according to any of them, and you can cycle through them without reloading the image by clicking the button at the bottom of the window (Shift-double-click also cycles through the projections). The "End" key restores the projection assigned when the image was loaded. 153 | 154 | The current source projection is shown on the status bar. Please remember that this is not the projection you are viewing, but the assumed projection of the input image. 155 | 156 | # Field of view 157 | 158 | The angular size, or field of view of an image determines how much of the panosurface it covers. Each projection has a maximum displayable FOV, however you can specify a different FOV for any non-cubic image. 159 | An image is assigned a horizontal and a vertical field of view when loaded. But for non-cubic formats, you can adjust the apparent fields of view afterward, using the hFov and vFov controls in the View menu (also Shift-right-mouse). These controls change the apparent horizontal and vertical FOVs independently, within a range of 10% to 100% of the displayable FOV for the current projection. The "End" key restores the assigned FOVs and projection. 160 | 161 | Changing an FOV changes not only the apparent width or height of the picture but also the intrinsic perspective, because the image displayed through a different part of the projection function. 162 | 163 | The apparent horizontal and vertical FOVs, shown in the status bar, also change when you select a different input projection, since each projection has different relationships of image dimension to angular size. Hint: don't worry about the "true" fovs; adjust them so the view looks right. 164 | 165 | # Saving views 166 | 167 | You can save the current view to a jpeg image file at any time ("Save as..." in View menu, or Ctrl-S). This is an exact copy of the displayed view, with the resolution increased 2.5 times (5.25 saved pixels for each screen pixel) if possible, typically giving a 3 to 8 megapixel image suitable for proof printing. If your OpenGL does not support offscreen rendering buffers of arbitrary size, the filed view will be at screen resolution instead. You can control the size and shape of the saved image by resizing the screen window, and center it in the frame with Shift-left mouse. 168 | --------------------------------------------------------------------------------