├── .clang-format
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── Plama.pro
├── README.md
├── app
├── app.pro
├── data
│ ├── project.cpp
│ └── project.h
├── gui
│ ├── plot.cpp
│ ├── plot.h
│ ├── windowmain.cpp
│ └── windowmain.h
├── main.cpp
├── render
│ ├── axis.cpp
│ ├── axis.h
│ ├── bar.cpp
│ ├── bar.h
│ ├── common.cpp
│ ├── common.h
│ ├── engine.cpp
│ ├── engine.h
│ ├── gradient.cpp
│ ├── gradient.h
│ ├── model.cpp
│ └── model.h
├── res
│ ├── icons
│ │ ├── document-export.svg
│ │ ├── document-open.svg
│ │ ├── plama.svg
│ │ ├── plot-gradient.svg
│ │ ├── plot-shader.svg
│ │ └── plot-step.svg
│ ├── render
│ │ ├── flat.fsh
│ │ ├── flat.vsh
│ │ ├── plain.fsh
│ │ └── plain.vsh
│ ├── res.qrc
│ └── script
│ │ └── plugins.py
└── util
│ ├── util.cpp
│ └── util.h
└── doc
├── Plama.ico
├── Report
├── fig
│ ├── comp1.jpg
│ ├── comp2.pdf
│ ├── comp3.jpg
│ ├── comp4.pdf
│ ├── comp5.jpg
│ ├── comp6.pdf
│ ├── datamodule.pdf
│ ├── datamodule.xml
│ ├── datastruct.pdf
│ ├── datastruct.xml
│ ├── diagram1.pdf
│ ├── diagram1.svg
│ ├── diagram2.pdf
│ ├── diagram2.svg
│ ├── diagram3.pdf
│ ├── diagram3.svg
│ ├── dir.pdf
│ ├── dir.svg
│ ├── echarts.png
│ ├── example1.pdf
│ ├── example2.pdf
│ ├── excel1.png
│ ├── excel2.png
│ ├── gantt.pdf
│ ├── grid.pdf
│ ├── grid.svg
│ ├── index1.pdf
│ ├── index2.pdf
│ ├── interface.png
│ ├── interp1.jpg
│ ├── interp2.jpg
│ ├── light1.jpg
│ ├── light2.jpg
│ ├── matlab0.pdf
│ ├── matlab1.pdf
│ ├── matlab2.pdf
│ ├── matrix.pdf
│ ├── matrix.svg
│ ├── plasimo.png
│ ├── sample1.pdf
│ ├── sample1.svg
│ ├── sample2.pdf
│ ├── sample2.svg
│ ├── sample3.pdf
│ ├── sample3.svg
│ ├── signature.png
│ ├── toplevel.pdf
│ ├── toplevel.xml
│ ├── unit1.pdf
│ ├── unit2.pdf
│ ├── vec1.pdf
│ └── vec2.pdf
├── final-report_juntong-liu_201219267.pdf
├── reference.bib
├── report.tex
└── res
│ ├── header.tex
│ ├── myieee.bst
│ ├── titlepage.tex
│ └── univcrest.pdf
├── demo.7z
├── demo.png
├── dummy.py
├── logo.svg
├── plama.svg
└── title.svg
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | AlignAfterOpenBracket: DontAlign
3 | AlignConsecutiveAssignments: 'false'
4 | AlignConsecutiveDeclarations: 'false'
5 | AlignEscapedNewlinesLeft: 'false'
6 | AlignOperands: 'false'
7 | AlignTrailingComments: 'false'
8 | AllowShortCaseLabelsOnASingleLine: 'true'
9 | AllowShortFunctionsOnASingleLine: All
10 | AllowShortIfStatementsOnASingleLine: 'true'
11 | AllowShortLoopsOnASingleLine: 'true'
12 | AccessModifierOffset: -4
13 | ColumnLimit: 90
14 | IndentWidth: 4
15 | SpaceAfterTemplateKeyword: 'false'
16 | ...
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Plama.pro.user
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Plama.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += app
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | This is Plama, a program for data visualization. It is designed to render the data in plasma simulation, but it should work well for different formats of data.
4 |
5 | Here is the main interface:
6 |
7 | 
8 |
9 | These are three main formats of rendering supported by this program as you can find in the screenshot. All the plots can be rotated and rendered in 3D space, toggling shader effect, and exported into jpg, svg and pdf images. The main advantage of this program is it has one variable dimension (the slider bar at the top), typically being the time, so you can easily check different quantities of one model at any time. For time-variant data, it can be also exported into videos.
10 |
11 | ## Why Use It
12 |
13 | The purpose of this project is to provide a universal interface and user-friendly workflow for basic visualization of most data formats. You can read, view and export data with several clicks. It also provides descent performance and rendering quality.
14 |
15 | So the typical usage is to load some formatted data. You need to install / write the plugin for this format, and enjoy. If you are a big fan of MATLAB or other similar tools, it might not be attractive at all. But to me, it provides better performance in rendering, simpler usage, optimized details and to be honest, the rendering style is closer to my taste. In addition, it uses python for data processing, which is more convenient for text and file processing, and it is more like the industrial standard in programming community.
16 |
17 | In one sentence, it is a light-weighted, free and flexible data visualizing tool.
18 |
19 | ## How to Use
20 |
21 | This program has built-in support for a plasma simulation file format called `MD2D`, so you can directly open projects in this format without any configuration. Simply choose open, and select all the output data, and the input model file, then it will handle all the things.
22 |
23 | If you do not have MD2D format data, [here](doc/demo.7z) is a simple example to help you run it an test most of the features. The data files are given in data folder and `pdp_demo.md2d` is the input model file.
24 |
25 | #### Installing the Plugin
26 |
27 | This program aims to provide zero-configuration experience for any data format, through a plugin system. When you have the plugin to process the data format, you only needs to click open, then select the files, easy.
28 |
29 | But you might have already thought about, you need to install the plugin first. A plugin is basically a python file. Simply throw it into the config folder , then the program will load it automatically if it runs. The config location is given below:
30 |
31 | - Linux: `/home/username/.config/Plama`
32 | - Windows: `C:/Users/username/AppData/Local/Plama`
33 |
34 | #### Write a Plugin
35 |
36 | This program implements possibly the easiest way to write plugins. For the description of plugin protocol, please find corresponding sections in the [full report](doc/Report/final-report_juntong-liu_201219267.pdf).
37 |
38 | ## How to Compile
39 |
40 | #### Linux
41 |
42 | First you need the dependencies (python3 please):
43 |
44 | `sudo pacman -S qt5-base qt5-svg python ffmpeg`
45 |
46 | Then the typical way for Qt projects:
47 |
48 | `qmake && make && make install`
49 |
50 | #### Windows
51 |
52 | Well, I don't suggest compile it yourself on Windows, not only because I use QtCreator to compile and run it on Windows, but also for the complicities of the dependencies. Why not use the released executable? By the way, I use mingw to compile it on Windows.
53 |
54 | #### OSX
55 |
56 | Not tested, not supported, have fun yourself.
57 |
58 | ## About the Project Name
59 |
60 | "Plama" sounds to be a strange name, and here is the reason. As stated before, the main purpose of this project is to display plasma properties, so it takes 5 letters from the word "plasma". And since I'm Chinese, and the animal "lama" has very popular jokes in Chinese (not so suitable for work), so it also provides 4 letters. In addition, "Plama" is a Polish word meaning stain, which is the logo of this project (No offense, Ubuntu!). And finally as a Linux user, here is the recursive abbreviation: Plama leaks all memory available! Just try not being a boring person.
61 |
62 | ## Credits
63 |
64 | - Project supervisor: Dr Mark Bowden
65 | - Project assessor: Dr James Walsh
66 | - Some icons from [Numix Project](https://github.com/numixproject) under GPLv3
67 | - [Qt5](https://www.qt.io/) under LGPLv3
68 | - [FFmpeg](https://www.ffmpeg.org/) under LGPLv3
69 | - [Python 3](https://www.python.org/) under PSF LICENSE
70 |
--------------------------------------------------------------------------------
/app/app.pro:
--------------------------------------------------------------------------------
1 | QT += core gui widgets svg
2 | TARGET = Plama
3 | TEMPLATE = app
4 |
5 | CONFIG += no_keywords c++14
6 |
7 | SOURCES += \
8 | main.cpp \
9 | gui/windowmain.cpp \
10 | render/engine.cpp \
11 | render/model.cpp \
12 | render/axis.cpp \
13 | util/util.cpp \
14 | gui/plot.cpp \
15 | data/project.cpp \
16 | render/bar.cpp \
17 | render/gradient.cpp \
18 | render/common.cpp
19 |
20 | HEADERS += \
21 | gui/windowmain.h \
22 | render/engine.h \
23 | render/model.h \
24 | render/axis.h \
25 | util/util.h \
26 | gui/plot.h \
27 | data/project.h \
28 | render/bar.h \
29 | render/gradient.h \
30 | render/common.h
31 |
32 | RESOURCES += \
33 | res/res.qrc
34 |
35 | unix: LIBS += -lpython3.6m
36 | unix: INCLUDEPATH += /usr/include/python3.6m
37 | win32: LIBS += -L $$(PY_LIBS) -lpython36
38 | win32:INCLUDEPATH += -I $$(PY_INCLUDE)
39 |
--------------------------------------------------------------------------------
/app/data/project.cpp:
--------------------------------------------------------------------------------
1 | #include "project.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | using namespace std;
12 |
13 | ProjectLoader::ProjectLoader() : globals(PyImport_AddModule("__main__")), list() {
14 | // read script
15 | QFile f(":script/plugins.py");
16 |
17 | f.open(QFile::ReadOnly);
18 | QTextStream ts(&f);
19 | QString code = ts.readAll();
20 | f.close();
21 | QByteArray ba = code.toUtf8();
22 | char *cmd = ba.data();
23 | // config dir to PyObject
24 | QStringList dirs =
25 | QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);
26 | PyObject *pDirs = PyList_New(0);
27 | for (QString s : dirs)
28 | PyList_Append(pDirs, PyUnicode_FromString(s.toLocal8Bit().data()));
29 | PyObject *args = Py_BuildValue("(O)", pDirs);
30 | // run python
31 | PyObject *rec;
32 | PyObject *variables = PyModule_GetDict(globals);
33 | rec = PyRun_String(cmd, Py_file_input, variables, variables);
34 | Py_DecRef(rec);
35 | PyObject *init = PyObject_GetAttrString(globals, "init");
36 | PyObject *plugins = PyObject_CallObject(init, args);
37 |
38 | Py_DECREF(args);
39 | Py_DECREF(init);
40 | // PyObject to QStringList
41 | int len = PyObject_Length(plugins);
42 | QStringList ret;
43 | for (int i = 0; i < len; i++) {
44 | PyObject *name = PyList_GetItem(plugins, i);
45 | char *plugin = PyUnicode_AsUTF8(name);
46 | list << QString(plugin);
47 | }
48 | Py_DECREF(plugins);
49 | }
50 |
51 | const QStringList &ProjectLoader::plugins() const { return list; }
52 |
53 | std::unique_ptr ProjectLoader::load(QString name) const {
54 | PyObject *fArgs = PyObject_GetAttrString(globals, "args");
55 | PyObject *fLoad = PyObject_GetAttrString(globals, "load");
56 | PyObject *args = Py_BuildValue("(s)", name.toLocal8Bit().data());
57 | PyObject *types = PyObject_CallObject(fArgs, args);
58 | Py_DECREF(args);
59 | int len = PyObject_Length(types);
60 | vector> typesConverted(len);
61 | for (int i = 0; i < len; i++) {
62 | PyObject *arg = PyList_GetItem(types, i);
63 | QString argName = QString(PyUnicode_AsUTF8(PyTuple_GetItem(arg, 0)));
64 | int argType = PyLong_AsLong(PyTuple_GetItem(arg, 1));
65 | typesConverted[i] = {argName, argType};
66 | }
67 | PyObject *pluginArgs = buildArgs(typesConverted);
68 | args = Py_BuildValue("(s, O)", name.toLocal8Bit().data(), pluginArgs);
69 | PyObject *data = PyObject_CallObject(fLoad, args);
70 | Py_DECREF(pluginArgs);
71 | Py_DECREF(args);
72 | Py_DECREF(types);
73 | return make_unique(data);
74 | }
75 |
76 | PyObject *ProjectLoader::buildArgs(const std::vector> &types) {
77 | PyObject *builder = PyList_New(0);
78 | for (auto &i : types) {
79 | switch (i.second) {
80 | case STRING_LIST:
81 | PyList_Append(builder,
82 | buildStringList(QFileDialog::getOpenFileNames(nullptr, i.first)));
83 | QApplication::processEvents();
84 | break;
85 | default: Q_ASSERT(true);
86 | }
87 | }
88 | return builder;
89 | }
90 |
91 | PyObject *ProjectLoader::buildStringList(const QStringList &value) {
92 | PyObject *ret = PyList_New(0);
93 | for (const auto &i : value)
94 | PyList_Append(ret, PyUnicode_FromString(i.toLocal8Bit().data()));
95 | return ret;
96 | }
97 |
98 | Project::Project(PyObject *data)
99 | : nodes(), data(data, [](PyObject *o) { //
100 | Py_DECREF(o);
101 | }) {
102 | PyObject *value = PyTuple_GetItem(data, 0);
103 | PyObject *error = PyTuple_GetItem(data, 1);
104 |
105 | if (error == Py_None) {
106 | Py_ssize_t len = PyList_Size(value);
107 | for (Py_ssize_t i = 0; i < len; i++) nodes.emplace_back(PyList_GetItem(value, i));
108 | }
109 | }
110 |
111 | QString Project::getError() const {
112 | PyObject *err = PyTuple_GetItem(data.get(), 1);
113 | return err == Py_None ? "" : QString(PyUnicode_AsUTF8(err));
114 | }
115 |
116 | const vector &Project::getTopLevelNodes() const { return nodes; }
117 |
118 | SimTreeNode::SimTreeNode(PyObject *data)
119 | : raw(data), name(PyUnicode_AsUTF8(PyDict_GetItemString(data, "name"))),
120 | abbr(PyUnicode_AsUTF8(PyDict_GetItemString(data, "abbr"))), quantities(),
121 | children() {
122 | PyObject *pChildren = PyDict_GetItemString(data, "children");
123 | PyObject *pQuantities = PyDict_GetItemString(data, "quantities");
124 | Py_ssize_t len = PyList_Size(pChildren);
125 |
126 | for (Py_ssize_t i = 0; i < len; i++)
127 | children.emplace_back(PyList_GetItem(pChildren, i));
128 | len = PyList_Size(pQuantities);
129 | for (Py_ssize_t i = 0; i < len; i++)
130 | quantities.emplace_back(PyList_GetItem(pQuantities, i));
131 | }
132 |
133 | const QString &SimTreeNode::getName() const { return name; }
134 | const QString &SimTreeNode::getAbbr() const { return abbr; }
135 | const vector &SimTreeNode::getData() const { return quantities; }
136 | const vector &SimTreeNode::getChildren() const { return children; }
137 |
138 | SimQuantity::SimQuantity(PyObject *data)
139 | : raw(data), name(PyUnicode_AsUTF8(PyDict_GetItemString(data, "name"))),
140 | dimData(PyLong_AsLong(PyDict_GetItemString(data, "dimData"))), times(), sizeModel(),
141 | sizeData(), labels(), data(), max(0), min(0), initialized(false) {
142 | PyObject *pTimes = PyDict_GetItemString(data, "times");
143 | PyObject *pSizeData = PyDict_GetItemString(data, "sizeData");
144 | PyObject *pSizeModel = PyDict_GetItemString(data, "sizeModel");
145 | PyObject *pLabels = PyDict_GetItemString(data, "labels");
146 | Py_ssize_t len = PyList_Size(pTimes);
147 |
148 | for (Py_ssize_t i = 0; i < len; i++)
149 | times.push_back(PyFloat_AsDouble(PyList_GetItem(pTimes, i)));
150 | len = PyList_Size(pSizeModel);
151 | for (Py_ssize_t i = 0; i < len; i++) {
152 | PyObject *o = PyList_GetItem(pSizeModel, i);
153 | sizeModel.emplace_back(PyFloat_AsDouble(PyList_GetItem(o, 0)),
154 | PyFloat_AsDouble(PyList_GetItem(o, 1)));
155 | }
156 |
157 | len = PyList_Size(pSizeData);
158 | for (Py_ssize_t i = 0; i < len; i++)
159 | sizeData.push_back(PyLong_AsLong(PyList_GetItem(pSizeData, i)));
160 |
161 | for (Py_ssize_t i = 0; i < PyList_Size(pSizeData) + 2; i++) {
162 | labels.emplace_back(PyUnicode_AsUTF8(PyList_GetItem(pLabels, i)));
163 | }
164 | }
165 |
166 | const QString &SimQuantity::getName() const { return name; }
167 | const vector &SimQuantity::getTimes() const { return times; }
168 | const vector &SimQuantity::getSizeModel() const { return sizeModel; }
169 | const vector &SimQuantity::getSizeData() const { return sizeData; }
170 |
171 | const vector &SimQuantity::getDataAt(double time, int dim) {
172 | auto t = lower_bound(times.begin(), times.end(), time);
173 | int d = t - times.begin();
174 | const vector &ret = getData()[dimData * d + dim];
175 | return ret;
176 | }
177 |
178 | const vector> &SimQuantity::getData() {
179 | if (!initialized) initData();
180 | return data;
181 | }
182 |
183 | const std::vector &SimQuantity::getLabels() const { return labels; }
184 |
185 | VectorD2D SimQuantity::getExtreme() {
186 | if (!initialized) initData();
187 | return VectorD2D(min, max);
188 | }
189 |
190 | int SimQuantity::getDim() const { return dimData; }
191 |
192 | QString SimQuantity::getError() const {
193 | if (!initialized) return "Data not initialized";
194 | return error;
195 | }
196 |
197 | QString SimQuantity::getError() {
198 | if (!initialized) initData();
199 | return error;
200 | }
201 |
202 | void SimQuantity::initData() {
203 |
204 | PyObject *pDataFunc = PyDict_GetItemString(raw, "data");
205 | PyObject *pDataRet = PyObject_CallObject(pDataFunc, NULL);
206 | PyObject *pData = PyTuple_GetItem(pDataRet, 0);
207 | PyObject *pErr = PyTuple_GetItem(pDataRet, 1);
208 | error = pErr == Py_None ? "" : QString(PyUnicode_AsUTF8(pErr));
209 | initialized = true;
210 |
211 | if (!error.isEmpty()) return;
212 |
213 | Py_ssize_t lenI = PyList_Size(pData);
214 | for (Py_ssize_t i = 0; i < lenI; i++) {
215 | vector buf;
216 | PyObject *pBuf = PyList_GetItem(pData, i);
217 | Py_ssize_t lenJ = PyList_Size(pBuf);
218 | for (Py_ssize_t j = 0; j < lenJ; j++)
219 | buf.push_back(PyFloat_AsDouble(PyList_GetItem(pBuf, j)));
220 | data.push_back(buf);
221 | }
222 |
223 | if (dimData == 1) {
224 | max = data[0][0];
225 | min = max;
226 | for (auto i : data) {
227 | for (auto j : i) {
228 | if (j > max) max = j;
229 | if (j < min) min = j;
230 | }
231 | }
232 | } else if (dimData == 2) {
233 | size_t sizeSection = data.size() / 2;
234 | size_t sizeNumber = 1;
235 | for (auto i : sizeData) sizeNumber *= i;
236 |
237 | max = magnitude(data[0][0], data[1][0]);
238 | min = 0;
239 | for (size_t i = 0; i < sizeSection; i++) {
240 | vector &vx = data[2 * i];
241 | vector &vy = data[2 * i + 1];
242 | for (size_t j = 0; j < sizeNumber; j++) {
243 | float x = vx[j];
244 | float y = vy[j];
245 | float m = magnitude(x, y);
246 | if (m > max) max = m;
247 | }
248 | }
249 | }
250 |
251 | Py_DECREF(pDataRet);
252 | }
253 |
--------------------------------------------------------------------------------
/app/data/project.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT_H
2 | #define PROJECT_H
3 |
4 | #include "util/util.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class SimTreeNode;
12 |
13 | class SimQuantity {
14 | public:
15 | SimQuantity(PyObject *data);
16 | const QString &getName() const;
17 | const std::vector &getTimes() const;
18 | const std::vector &getSizeModel() const;
19 | const std::vector &getSizeData() const;
20 | const std::vector &getDataAt(double time, int dim = 0);
21 | const std::vector> &getData();
22 | const std::vector &getLabels() const;
23 | VectorD2D getExtreme();
24 | int getDim() const;
25 | QString getError() const;
26 | QString getError();
27 |
28 | private:
29 | PyObject *raw;
30 | QString name, error;
31 | int dimData;
32 | bool uniform;
33 | std::vector times;
34 | std::vector sizeModel;
35 | std::vector sizeData;
36 | std::vector labels;
37 | std::vector> data;
38 | float max, min;
39 | bool initialized;
40 | void initData();
41 | };
42 |
43 | class SimTreeNode {
44 | public:
45 | SimTreeNode(PyObject *data);
46 | SimTreeNode(SimTreeNode &&) = default;
47 | SimTreeNode &operator=(const SimTreeNode &) = delete;
48 | const QString &getName() const;
49 | const QString &getAbbr() const;
50 | const std::vector &getData() const;
51 | const std::vector &getChildren() const;
52 |
53 | private:
54 | PyObject *raw;
55 | QString name;
56 | QString abbr;
57 | std::vector quantities;
58 | std::vector children;
59 | };
60 |
61 | class Project {
62 | public:
63 | Project(PyObject *data);
64 | QString getError() const;
65 | const std::vector &getTopLevelNodes() const;
66 |
67 | private:
68 | std::vector nodes;
69 | std::unique_ptr> data;
70 | };
71 |
72 | class ProjectLoader {
73 | public:
74 | ProjectLoader();
75 | const QStringList &plugins() const;
76 | std::unique_ptr load(QString name) const;
77 |
78 | private:
79 | const static int STRING_LIST = 1;
80 | PyObject *globals;
81 | QStringList list;
82 | static PyObject *buildArgs(const std::vector> &types);
83 | static PyObject *buildStringList(const QStringList &value);
84 | };
85 |
86 | #endif // PROJECT_H
87 |
--------------------------------------------------------------------------------
/app/gui/plot.cpp:
--------------------------------------------------------------------------------
1 | #include "plot.h"
2 | #include "render/bar.h"
3 | #include "render/model.h"
4 | #include "util/util.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | using namespace std;
25 |
26 | int mouseFuncForward(int x) {
27 | const int width = 20;
28 | const float slope = 0.5;
29 | if (x < width)
30 | return 0;
31 | else if (x < 90 / slope + width)
32 | return (x - width) * slope;
33 | else
34 | return 90;
35 | }
36 |
37 | int mouseFuncInverse(int x) {
38 | const int width = 20;
39 | const float slope = 0.5;
40 | if (x == 0)
41 | return 0;
42 | else if (x == 90)
43 | return 2 * width + 90 / slope;
44 | else
45 | return width + x / slope;
46 | }
47 |
48 | int mouseFuncForwardLoop(int x) {
49 | const int width = 20;
50 | const float slope = 0.5;
51 | const int radix = 2 * width + 90 / slope;
52 | QPair p = unify(x, radix);
53 | return p.first * 90 + mouseFuncForward(p.second);
54 | }
55 |
56 | int mouseFuncInverseLoop(int x) {
57 | const int width = 20;
58 | const float slope = 0.5;
59 | const int radix = 90;
60 | QPair p = unify(x, radix);
61 | return p.first * (2 * width + (int)(90 / slope)) + mouseFuncInverse(p.second);
62 | }
63 |
64 | void Plot::setTime(double t, bool update) {
65 | const vector × = quantity->getTimes();
66 | double t1 = times[0];
67 | double t2 = times[times.size() - 1];
68 | if (plot->setQuantity(*quantity, t, step, update))
69 | plot->setLabel((t - t1) / (t2 - t1), update);
70 | time = t;
71 | }
72 |
73 | Plot::Plot(SimQuantity &quantity) : time(quantity.getTimes()[0]), step(1) {
74 | static vector, function>> types =
75 | {
76 | {"JPEG image (*.jpg *.jpeg *.jpe)", {"jpg", "jpeg", "jpe"},
77 | [](QString s, Plot &p) {
78 | QImage image(3000, 3000, QImage::Format_ARGB32);
79 | image.fill(Qt::white);
80 | p.plot->setEnLabel(false, false);
81 | p.plot->renderTo(image);
82 | p.plot->setEnLabel(true, false);
83 | QImage scaled = image.scaled(
84 | 1500, 1500, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
85 | scaled.save(s, "JPG", 100);
86 | }},
87 | {"PNG image (*.png)", {"png"},
88 | [](QString s, Plot &p) {
89 | QImage image(3000, 3000, QImage::Format_ARGB32);
90 | p.plot->setEnLabel(false, false);
91 | p.plot->renderTo(image);
92 | p.plot->setEnLabel(true, false);
93 | QImage scaled = image.scaled(
94 | 1500, 1500, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
95 | scaled.save(s, "PNG", 100);
96 | }},
97 | {"SVG image (*.svg)", {"svg"},
98 | [](QString s, Plot &p) {
99 | QSvgGenerator generator;
100 | generator.setFileName(s);
101 | generator.setSize(QSize(800, 800));
102 | generator.setViewBox(QRect(0, 0, 800, 800));
103 | p.plot->setEnLabel(false, false);
104 | p.plot->renderTo(generator);
105 | p.plot->setEnLabel(true, false);
106 | }},
107 | {"PDF image (*.pdf)", {"pdf"},
108 | [](QString s, Plot &p) {
109 | QPdfWriter writer(s);
110 | QSizeF size(100, 100);
111 | writer.setPageSizeMM(size);
112 | writer.setResolution(300);
113 | p.plot->setEnLabel(false, false);
114 | p.plot->renderTo(writer);
115 | p.plot->setEnLabel(true, false);
116 | }},
117 | {"AVI video (*.avi)", {"avi"},
118 | [](QString s, Plot &p) { p.renderVideo(s, 800, 800, 10, 20); }},
119 | };
120 |
121 | auto range = make_unique>();
122 | auto labels = make_unique>();
123 | if (quantity.getSizeModel().size() == 0) {
124 | *range = {
125 | {quantity.getTimes()[0], quantity.getTimes()[quantity.getTimes().size() - 1]},
126 | quantity.getExtreme(), {0, 1}};
127 | *labels = {quantity.getLabels()[0], quantity.getLabels()[1], ""};
128 | } else {
129 | *range = {quantity.getSizeModel()[0], quantity.getSizeModel()[1],
130 | quantity.getExtreme()};
131 | *labels = {
132 | quantity.getLabels()[2], quantity.getLabels()[3], quantity.getLabels()[1]};
133 | }
134 |
135 | QVBoxLayout *l = new QVBoxLayout;
136 | l->setMargin(0);
137 | QToolBar *bar = new QToolBar;
138 | QSpinBox *nStep = new QSpinBox;
139 | nStep->setMinimum(1);
140 | nStep->setEnabled(false);
141 | QAction *aExport = new QAction(getIcon("document-export"), "Export");
142 | QAction *aShader = new QAction(getIcon("plot-shader"), "Enable shader");
143 | QAction *aBar = new QAction(getIcon("plot-gradient"), "Enable color bar");
144 | QAction *aStep = new QAction(getIcon("plot-step"), "Set grid simplification");
145 | aShader->setCheckable(true);
146 | aBar->setCheckable(true);
147 | aStep->setCheckable(true);
148 | connect(aExport, &QAction::triggered, [&]() {
149 | QString selected;
150 | QStringList filters;
151 | for (auto &i : types) filters << i.a;
152 | QString filter = filters.join(";;");
153 | QString name =
154 | QFileDialog::getSaveFileName(this, "Export file", "", filter, &selected);
155 | if (name.isEmpty()) return;
156 |
157 | for (auto &i : types) {
158 | if (i.a == selected) {
159 | if (QFileInfo::exists(name))
160 | i.c(name, *this);
161 | else {
162 | bool ext = false;
163 | for (auto &j : i.b)
164 | if (name.endsWith(j)) ext = true;
165 | if (!ext) name += "." + i.b[0];
166 | i.c(name, *this);
167 | }
168 | }
169 | }
170 | });
171 | connect(aShader, &QAction::toggled, [=](bool b) { plot->setShader(b); });
172 | connect(aBar, &QAction::toggled, [=](bool b) { plot->setEnBar(b); });
173 | connect(aStep, &QAction::toggled, [=](bool b) {
174 | nStep->setEnabled(b);
175 | if (b)
176 | this->setStep(nStep->value());
177 | else
178 | this->setStep(1);
179 | });
180 | connect(nStep, QOverload::of(&QSpinBox::valueChanged),
181 | [=](int v) { this->setStep(v); });
182 | bar->addAction(aExport);
183 | bar->addAction(aShader);
184 | bar->addAction(aBar);
185 | bar->addAction(aStep);
186 | bar->addWidget(nStep);
187 | l->addWidget(bar);
188 |
189 | plot = new PlotInternal(make_unique(5, 5, 5),
190 | make_unique(Gradient::HEIGHT_MAP, 5), std::move(range), std::move(labels));
191 | plot->setQuantity(quantity, quantity.getTimes()[0], step);
192 | l->addWidget(QWidget::createWindowContainer(plot));
193 | l->setMargin(0);
194 | setLayout(l);
195 | this->quantity = &quantity;
196 | }
197 |
198 | void Plot::setRotation(int x, int y, bool update) { plot->setRotation(x, y, update); }
199 |
200 | void Plot::setPartition(float p, bool update) {
201 | const vector × = quantity->getTimes();
202 | double t1 = times[0];
203 | double t2 = times[times.size() - 1];
204 | double td = t2 - t1;
205 | setTime(t1 + td * p, update);
206 | }
207 |
208 | void Plot::setStep(int s, bool update) {
209 | step = s;
210 | plot->setQuantity(*quantity, time, step, update);
211 | }
212 |
213 | QSize Plot::sizeHint() const { return QSize(1000, 1000); }
214 | QSize Plot::minimumSizeHint() const { return QSize(100, 100); }
215 |
216 | void Plot::renderVideo(QString dir, int sizeX, int sizeY, int len, int fps) {
217 | QProgressDialog *progress = new QProgressDialog(this);
218 | progress->setRange(0, 0);
219 | progress->setLabelText("Processing...");
220 | progress->show();
221 |
222 | QTemporaryDir *tmp = new QTemporaryDir();
223 | int total = len * fps;
224 | float lenStep = 1.0 / total;
225 | int sizeName = QString::number(total).length();
226 | QString format = "%1.jpg";
227 | QString f1, f2;
228 | f2 = format.arg(int(0), sizeName, 10, QLatin1Char('0'));
229 | float t = time;
230 | for (int i = 0; i < total; i++) {
231 | f1 = f2;
232 | f2 = format.arg(int(i), sizeName, 10, QLatin1Char('0'));
233 | setPartition(i * lenStep, false);
234 | QImage image(sizeX * 2, sizeY * 2, QImage::Format_ARGB32);
235 | image.fill(Qt::white);
236 | QString filePath = tmp->filePath(f2);
237 | plot->renderTo(image);
238 | image.save(filePath, Q_NULLPTR, 100);
239 | QApplication::processEvents();
240 | }
241 | setTime(t, false);
242 |
243 | QProcess *process = new QProcess(this);
244 | process->setWorkingDirectory(tmp->path());
245 | connect(progress, &QProgressDialog::canceled, [=]() { process->kill(); });
246 | connect(process, QOverload::of(&QProcess::finished), [=]() {
247 | delete tmp;
248 | progress->close();
249 | });
250 | QString cmd = "ffmpeg -y -r 10 -i %0" + QString::number(sizeName) +
251 | "d.jpg -c:v libx264 -crf 12 -s " + QString::number(sizeX) + "x" +
252 | QString::number(sizeY) + " " + dir;
253 | process->start(cmd);
254 | }
255 |
256 | PlotInternal::PlotInternal(unique_ptr &&axis, unique_ptr &&bar,
257 | unique_ptr> &&size, unique_ptr> &&labels)
258 | : model(new Model()) {
259 | shared_ptr pa = move(axis);
260 | shared_ptr> ps = move(size);
261 | shared_ptr pb = move(bar);
262 | shared_ptr> pl = move(labels);
263 |
264 | engineGL = make_unique(model, pa, pb, ps, pl);
265 | engineQt = make_unique(model, pa, pb, ps, pl);
266 | }
267 |
268 | void PlotInternal::setRotation(int x, int y, bool update) {
269 | engineGL->setRotation(x, y);
270 | engineQt->setRotation(x, y);
271 | if (update) requestUpdate();
272 | }
273 |
274 | void PlotInternal::setLabel(float pos, bool update) {
275 | engineGL->setLine(pos);
276 | engineQt->setLine(pos);
277 | if (update) requestUpdate();
278 | }
279 |
280 | void PlotInternal::setShader(bool en, bool update) {
281 | engineGL->setShader(en);
282 | engineQt->setShader(en);
283 | if (update) requestUpdate();
284 | }
285 |
286 | void PlotInternal::setEnBar(bool en, bool update) {
287 | engineGL->setEnBar(en);
288 | engineQt->setEnBar(en);
289 | if (update) requestUpdate();
290 | }
291 |
292 | void PlotInternal::setEnLabel(bool en, bool update) {
293 | engineGL->setEnLabel(en);
294 | engineQt->setEnLabel(en);
295 | if (update) requestUpdate();
296 | }
297 |
298 | bool PlotInternal::setQuantity(SimQuantity &sq, float time, int step, bool update) {
299 | bool ret = model->setQuantity(sq, time, step);
300 | if (update) requestUpdate();
301 | return ret;
302 | }
303 |
304 | void PlotInternal::renderTo(QPaintDevice &d) {
305 | QPainter p(&d);
306 | engineQt->resize(d.width(), d.height());
307 | engineQt->render(p);
308 | p.end();
309 | }
310 |
311 | void PlotInternal::mouseMoveEvent(QMouseEvent *event) {
312 | if ((event->buttons() & Qt::LeftButton) == 0) return;
313 |
314 | QPoint diff = event->pos() - mouse;
315 | int mx = mouseFuncInverseLoop(rotation.x()) + diff.x();
316 | int my = mouseFuncInverseLoop(rotation.y()) + diff.y();
317 | int ry = mouseFuncForwardLoop(my);
318 | if (ry > 90)
319 | ry = 90;
320 | else if (ry < 0)
321 | ry = 0;
322 | setRotation(unify(mouseFuncForwardLoop(mx), 360).second, ry);
323 | }
324 |
325 | void PlotInternal::mousePressEvent(QMouseEvent *event) {
326 | if ((event->buttons() & Qt::LeftButton) == 0) return;
327 | mouse = event->pos();
328 | rotation = engineGL->getRotation();
329 | }
330 |
331 | void PlotInternal::mouseReleaseEvent(QMouseEvent *) { setMouseGrabEnabled(false); }
332 |
333 | void PlotInternal::initializeGL() { engineGL->initialize(); }
334 |
335 | void PlotInternal::resizeGL(int w, int h) {
336 | engineGL->resize(w, h);
337 | engineQt->resize(w, h);
338 | requestUpdate();
339 | }
340 |
341 | void PlotInternal::paintGL() {
342 | QPainter p(this);
343 | engineGL->render(p);
344 | }
345 |
--------------------------------------------------------------------------------
/app/gui/plot.h:
--------------------------------------------------------------------------------
1 | #ifndef PLOT_H
2 | #define PLOT_H
3 |
4 | #include "data/project.h"
5 | #include "render/engine.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | class PlotInternal : public QOpenGLWindow {
13 | public:
14 | PlotInternal(std::unique_ptr &&axis, std::unique_ptr &&bar,
15 | std::unique_ptr> &&size,
16 | std::unique_ptr> &&labels);
17 | void setRotation(int x, int y, bool update = true);
18 | void setLabel(float pos, bool update = true);
19 | void setShader(bool en, bool update = true);
20 | void setEnBar(bool en, bool update = true);
21 | void setEnLabel(bool en, bool update = true);
22 | bool setQuantity(SimQuantity &sq, float time, int step, bool update = true);
23 | void renderTo(QPaintDevice &d);
24 |
25 | private:
26 | std::shared_ptr model;
27 | std::unique_ptr engineGL;
28 | std::unique_ptr engineQt;
29 | QPoint mouse;
30 | QPoint rotation;
31 |
32 | protected:
33 | void mouseMoveEvent(QMouseEvent *) override;
34 | void mousePressEvent(QMouseEvent *) override;
35 | void mouseReleaseEvent(QMouseEvent *) override;
36 | void initializeGL() override;
37 | void resizeGL(int w, int h) override;
38 | void paintGL() override;
39 | };
40 |
41 | class Plot : public QWidget {
42 | public:
43 | Plot(SimQuantity &quantity);
44 | void setRotation(int x, int y, bool update = true);
45 | void setTime(double t, bool update = true);
46 | void setPartition(float p, bool update = true);
47 | void setStep(int s, bool update = true);
48 | QSize sizeHint() const override;
49 | QSize minimumSizeHint() const override;
50 |
51 | private:
52 | PlotInternal *plot;
53 | SimQuantity *quantity;
54 | double time;
55 | int step;
56 |
57 | void renderVideo(QString dir, int sizeX, int sizeY, int len, int fps);
58 | };
59 |
60 | #endif // PLOT_H
61 |
--------------------------------------------------------------------------------
/app/gui/windowmain.cpp:
--------------------------------------------------------------------------------
1 | #include "windowmain.h"
2 | #include "data/project.h"
3 | #include "gui/plot.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | using namespace std;
19 |
20 | QTreeWidgetItem *generateTree(const SimTreeNode &n) {
21 | QTreeWidgetItem *ret = new QTreeWidgetItem(QStringList(n.getName()));
22 |
23 | ret->setData(0, 0x0101, QVariant::fromValue((void *)&n));
24 | for (const SimTreeNode &i : n.getChildren()) ret->addChild(generateTree(i));
25 | for (const SimQuantity &i : n.getData()) {
26 | QTreeWidgetItem *q = new QTreeWidgetItem(QStringList(i.getName()));
27 | q->setData(0, 0x0100, QVariant::fromValue((void *)&i));
28 | ret->addChild(q);
29 | }
30 | return ret;
31 | }
32 |
33 | QList generateTree(const Project &a) {
34 | const vector &nodes = a.getTopLevelNodes();
35 |
36 | QList ret;
37 | for (auto &i : nodes) ret.append(generateTree(i));
38 | return ret;
39 | }
40 |
41 | WindowMain::WindowMain(QWidget *parent) : QMainWindow(parent), data() {
42 | static ProjectLoader m;
43 | static set activePlots;
44 | static set activeDocks;
45 |
46 | QToolBar *toolbar = new QToolBar("Hello");
47 | QSlider *slider = new QSlider(Qt::Horizontal);
48 | slider->setMaximum(10000);
49 | slider->setMinimum(0);
50 | toolbar->addWidget(slider);
51 |
52 | addToolBar(toolbar);
53 | QWidget *empty = new QWidget(this);
54 | setCentralWidget(empty);
55 | resize(700, 500);
56 |
57 | setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks |
58 | QMainWindow::AllowTabbedDocks);
59 | QDockWidget *dock = new QDockWidget("Data list", this);
60 | QTreeWidget *tree = new QTreeWidget(dock);
61 | dock->setWidget(tree);
62 | tree->header()->close();
63 | dock->setAllowedAreas(Qt::LeftDockWidgetArea);
64 | dock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
65 | addDockWidget(Qt::LeftDockWidgetArea, dock);
66 |
67 | connect(tree, &QTreeWidget::itemDoubleClicked, [=](QTreeWidgetItem *i) {
68 | QVariant q = i->data(0, 0x0100);
69 | if (q.isValid()) {
70 | QVariant p = i->parent()->data(0, 0x0101);
71 | SimTreeNode *sp = (SimTreeNode *)p.value();
72 | SimQuantity *sq = (SimQuantity *)q.value();
73 | vector size = sq->getSizeData();
74 |
75 | if (sq->getError().isEmpty()) {
76 | QDockWidget *d =
77 | new QDockWidget(sp->getAbbr() + '>' + sq->getName(), this);
78 | d->setAttribute(Qt::WA_DeleteOnClose);
79 | Plot *plot = new Plot(*sq);
80 | plot->setRotation(90, 90);
81 | plot->setPartition(slider->value() / (float)10000);
82 | d->setWidget(plot);
83 |
84 | d->setAllowedAreas(Qt::LeftDockWidgetArea);
85 | addDockWidget(Qt::LeftDockWidgetArea, d);
86 | if (activePlots.size() == 0)
87 | splitDockWidget(dock, d, Qt::Horizontal);
88 | else {
89 | QDockWidget *dst = *activeDocks.begin();
90 | QSize s = dst->size();
91 | splitDockWidget(
92 | dst, d, s.width() > s.height() ? Qt::Horizontal : Qt::Vertical);
93 | }
94 |
95 | connect(d, &QDockWidget::visibilityChanged, [=](bool v) {
96 | if (v) {
97 | activeDocks.insert(d);
98 | activePlots.insert(plot);
99 | } else {
100 | activeDocks.erase(d);
101 | activePlots.erase(plot);
102 | if (activePlots.size() == 0) {
103 | QWidget *empty = new QWidget(this);
104 | setCentralWidget(empty);
105 | }
106 | }
107 | });
108 |
109 | } else
110 | QMessageBox::warning(this, "Data Reading Error", sq->getError());
111 | }
112 | });
113 |
114 | connect(slider, &QSlider::sliderMoved, [](int i) {
115 | for (auto p : activePlots) p->setPartition(i / (float)10000);
116 | });
117 |
118 | QMenu *mFile = menuBar()->addMenu("File");
119 | QMenu *mFOpen = mFile->addMenu("Open");
120 | for (QString i : m.plugins()) {
121 | QAction *a = new QAction(i, this);
122 | mFOpen->addAction(a);
123 | connect(a, &QAction::triggered, [=]() {
124 | unique_ptr tmp = m.load(i);
125 | if (tmp->getError().isEmpty()) {
126 | data = std::move(tmp);
127 | tree->clear();
128 | tree->addTopLevelItems(generateTree(*data));
129 | auto set = activeDocks;
130 | for (auto i : set) i->close();
131 | } else
132 | QMessageBox::warning(this, "File Loading Error", tmp->getError());
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/gui/windowmain.h:
--------------------------------------------------------------------------------
1 | #ifndef WINDOWMAIN_H
2 | #define WINDOWMAIN_H
3 |
4 | #include "data/project.h"
5 | #include
6 | #include
7 |
8 | class WindowMain : public QMainWindow {
9 | Q_OBJECT
10 |
11 | public:
12 | explicit WindowMain(QWidget *parent = 0);
13 | ~WindowMain() = default;
14 |
15 | private:
16 | std::unique_ptr data;
17 | };
18 |
19 | #endif // WINDOWMAIN_H
20 |
--------------------------------------------------------------------------------
/app/main.cpp:
--------------------------------------------------------------------------------
1 | #include "gui/windowmain.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | int main(int argc, char *argv[]) {
11 | Py_Initialize();
12 |
13 | int ret;
14 | {
15 | QSurfaceFormat format;
16 |
17 | format.setSamples(2);
18 | format.setSwapInterval(0);
19 | format.setDepthBufferSize(16);
20 | format.setRenderableType(QSurfaceFormat::OpenGL);
21 | QSurfaceFormat::setDefaultFormat(format);
22 |
23 | QApplication a(argc, argv);
24 | a.setWindowIcon(getIcon("plama"));
25 | if (QSysInfo::productType() == "windows") {
26 | QFont font("Segoe UI");
27 | a.setFont(font);
28 | }
29 |
30 | WindowMain w(nullptr);
31 | w.show();
32 | ret = a.exec();
33 | }
34 | Py_Finalize();
35 | return ret;
36 | }
37 |
--------------------------------------------------------------------------------
/app/render/axis.cpp:
--------------------------------------------------------------------------------
1 | #include "axis.h"
2 | #include
3 | #include
4 |
5 | using namespace std;
6 |
7 | Axis::Axis(int sizeX, int sizeY, int sizeZ)
8 | : point(12 * (sizeX + sizeY + sizeZ + 1)), color(12 * (sizeX + sizeY + sizeZ + 1)),
9 | index((sizeX + sizeY + sizeY + 3) * 16 + 12), sizeX(sizeX), sizeY(sizeY),
10 | sizeZ(sizeZ), offset(2 * OFFSET) {
11 |
12 | int countPnt = 0;
13 | int countIdx = 0;
14 | int tmp;
15 |
16 | for (int j = 0; j < 2; j++) {
17 | float diffX = 1.0f / sizeX;
18 | float diffY = 1.0f / sizeY;
19 | float diffZ = 1.0f / sizeZ;
20 |
21 | int xyStart = countPnt;
22 | for (int i = 0; i < sizeX; i++) {
23 | point[countPnt] = QVector3D(diffX * i, 0, -DIST);
24 | color[countPnt++] = GREY;
25 | }
26 | for (int i = 0; i < sizeY; i++) {
27 | point[countPnt] = QVector3D(1, diffY * i, -DIST);
28 | color[countPnt++] = GREY;
29 | }
30 | tmp = countPnt;
31 | for (int i = 0; i < sizeX; i++) {
32 | point[countPnt] = QVector3D(1 - diffX * i, 1, -DIST);
33 | color[countPnt++] = GREY;
34 | }
35 | for (int i = 0; i < sizeY; i++) {
36 | point[countPnt] = QVector3D(0, 1 - diffY * i, -DIST);
37 | color[countPnt++] = GREY;
38 | }
39 | color[tmp] = BLACK;
40 |
41 | int markXStrt = countPnt;
42 | for (int i = 0; i < sizeX + 1; i++) {
43 | point[countPnt] = QVector3D(diffX * i, 1 + EXTRA, -DIST);
44 | color[countPnt++] = BLACK;
45 | }
46 | int markXRight = countPnt;
47 | for (int i = 0; i < sizeX + 1; i++) {
48 | point[countPnt] = QVector3D(diffX * i, 1, -DIST - EXTRA);
49 | color[countPnt++] = BLACK;
50 | }
51 | int markYStrt = countPnt;
52 | for (int i = 0; i < sizeY + 1; i++) {
53 | point[countPnt] = QVector3D(1 + EXTRA, diffY * i, -DIST);
54 | color[countPnt++] = BLACK;
55 | }
56 | int markYRight = countPnt;
57 | for (int i = 0; i < sizeY + 1; i++) {
58 | point[countPnt] = QVector3D(1, diffY * i, -DIST - EXTRA);
59 | color[countPnt++] = BLACK;
60 | }
61 |
62 | int xzStart = countPnt;
63 | for (int i = 0; i < sizeX; i++) {
64 | point[countPnt] = QVector3D(diffX * i, -DIST, 0);
65 | color[countPnt++] = GREY;
66 | }
67 | for (int i = 0; i < sizeZ; i++) {
68 | point[countPnt] = QVector3D(1, -DIST, diffZ * i);
69 | color[countPnt++] = GREY;
70 | }
71 | tmp = countPnt;
72 | for (int i = 0; i < sizeX; i++) {
73 | point[countPnt] = QVector3D(1 - diffX * i, -DIST, 1);
74 | color[countPnt++] = GREY;
75 | }
76 | for (int i = 0; i < sizeZ; i++) {
77 | point[countPnt] = QVector3D(0, -DIST, 1 - diffZ * i);
78 | color[countPnt++] = GREY;
79 | }
80 | color[tmp] = BLACK;
81 |
82 | int markZStrt = countPnt;
83 | for (int i = 0; i < sizeZ + 1; i++) {
84 | point[countPnt] = QVector3D(1 + EXTRA, -DIST, diffZ * i);
85 | color[countPnt++] = BLACK;
86 | }
87 | int markZRight = countPnt;
88 | for (int i = 0; i < sizeZ + 1; i++) {
89 | point[countPnt] = QVector3D(1, -DIST - EXTRA, diffZ * i);
90 | color[countPnt++] = BLACK;
91 | }
92 |
93 | int yzStart = countPnt;
94 | for (int i = 0; i < sizeY; i++) {
95 | point[countPnt] = QVector3D(-DIST, diffY * i, 0);
96 | color[countPnt++] = GREY;
97 | }
98 | for (int i = 0; i < sizeZ; i++) {
99 | point[countPnt] = QVector3D(-DIST, 1, diffZ * i);
100 | color[countPnt++] = GREY;
101 | }
102 | for (int i = 0; i < sizeY; i++) {
103 | point[countPnt] = QVector3D(-DIST, 1 - diffY * i, 1);
104 | color[countPnt++] = GREY;
105 | }
106 | for (int i = 0; i < sizeZ; i++) {
107 | point[countPnt] = QVector3D(-DIST, 0, 1 - diffZ * i);
108 | color[countPnt++] = GREY;
109 | }
110 |
111 | offset[XY_X_B + j * OFFSET] = countIdx;
112 | index[countIdx++] = xyStart + 2 * sizeX + sizeY;
113 | index[countIdx++] = xyStart + sizeX + sizeY;
114 | offset[XY_X_G + j * OFFSET] = countIdx;
115 | tmp = countIdx;
116 | for (int i = 0; i < sizeY + 1; i++) {
117 | index[countIdx++] = xyStart + sizeX + i;
118 | index[countIdx++] = xyStart + 2 * sizeX + 2 * sizeY - i;
119 | }
120 | index[tmp + 1] = xyStart;
121 | offset[XY_Y_B + j * OFFSET] = countIdx;
122 | index[countIdx++] = xyStart + sizeX;
123 | index[countIdx++] = xyStart + sizeX + sizeY;
124 | offset[XY_Y_G + j * OFFSET] = countIdx;
125 | for (int i = 0; i < sizeX + 1; i++) {
126 | index[countIdx++] = xyStart + 2 * sizeX + sizeY - i;
127 | index[countIdx++] = xyStart + i;
128 | }
129 |
130 | offset[X_S + j * OFFSET] = countIdx;
131 | for (int i = 0; i < sizeX + 1; i++) {
132 | index[countIdx++] = xyStart + 2 * sizeX + sizeY - i;
133 | index[countIdx++] = markXStrt + i;
134 | }
135 | offset[X_R + j * OFFSET] = countIdx;
136 | for (int i = 0; i < sizeX + 1; i++) {
137 | index[countIdx++] = xyStart + 2 * sizeX + sizeY - i;
138 | index[countIdx++] = markXRight + i;
139 | }
140 | offset[Y_S + j * OFFSET] = countIdx;
141 | for (int i = 0; i < sizeY + 1; i++) {
142 | index[countIdx++] = xyStart + sizeX + i;
143 | index[countIdx++] = markYStrt + i;
144 | }
145 | offset[Y_R + j * OFFSET] = countIdx;
146 | for (int i = 0; i < sizeY + 1; i++) {
147 | index[countIdx++] = xyStart + sizeX + i;
148 | index[countIdx++] = markYRight + i;
149 | }
150 |
151 | offset[XZ + j * OFFSET] = countIdx;
152 | index[countIdx++] = xzStart + sizeX;
153 | index[countIdx++] = xzStart + sizeX + sizeZ;
154 | for (int i = 0; i < sizeX; i++) {
155 | index[countIdx++] = xzStart + 2 * sizeX + sizeZ - i;
156 | index[countIdx++] = xzStart + i;
157 | }
158 | tmp = countIdx;
159 | for (int i = 0; i < sizeZ + 1; i++) {
160 | index[countIdx++] = xzStart + sizeX + i;
161 | index[countIdx++] = xzStart + 2 * sizeX + 2 * sizeZ - i;
162 | }
163 | index[tmp + 1] = xzStart;
164 |
165 | offset[Z_S + j * OFFSET] = countIdx;
166 | for (int i = 0; i < sizeZ + 1; i++) {
167 | index[countIdx++] = xzStart + sizeX + i;
168 | index[countIdx++] = markZStrt + i;
169 | }
170 | offset[Z_R + j * OFFSET] = countIdx;
171 | for (int i = 0; i < sizeZ + 1; i++) {
172 | index[countIdx++] = xzStart + sizeX + i;
173 | index[countIdx++] = markZRight + i;
174 | }
175 |
176 | offset[YZ + j * OFFSET] = countIdx;
177 | tmp = countIdx;
178 | for (int i = 0; i < sizeZ + 1; i++) {
179 | index[countIdx++] = yzStart + sizeY + i;
180 | index[countIdx++] = yzStart + 2 * sizeY + 2 * sizeZ - i;
181 | }
182 | index[tmp + 1] = yzStart;
183 | for (int i = 0; i < sizeY + 1; i++) {
184 | index[countIdx++] = yzStart + 2 * sizeY + sizeZ - i;
185 | index[countIdx++] = yzStart + i;
186 | }
187 |
188 | tmp = sizeX;
189 | sizeX = sizeY;
190 | sizeY = tmp;
191 | }
192 | }
193 |
194 | const vector &Axis::getPoint() const { return point; }
195 | const vector &Axis::getColor() const { return color; }
196 | const vector &Axis::getIndex() const { return index; }
197 |
198 | vector> Axis::getSlice(int rotX, int rotY) {
199 | int rx = (360 - rotX) % 90;
200 | int dir = (360 - rotX) / 45 % 8;
201 | return getSlice(rx > 20 || rotY > 20, rx < 70 || rotY > 20, rotY<70, rx> 45,
202 | rotY > 20, dir % 4 > 1);
203 | }
204 |
205 | vector> Axis::getSlice(
206 | bool xEnable, bool yEnable, bool zEnable, bool zStrait, bool xyStrait, bool invert) {
207 | typedef QPair P;
208 | vector> ret;
209 | int i = invert ? OFFSET : 0;
210 | int x = invert ? sizeY : sizeX;
211 | int y = invert ? sizeX : sizeY;
212 | ret.push_back(P(offset[(xEnable ? XY_X_B : XY_X_G) + i], y + 1));
213 | ret.push_back(P(offset[(yEnable ? XY_Y_B : XY_Y_G) + i], x + 1));
214 | if (xEnable) ret.push_back(P(offset[(xyStrait ? X_S : X_R) + i], x + 1));
215 | if (yEnable) ret.push_back(P(offset[(xyStrait ? Y_S : Y_R) + i], y + 1));
216 | if (zEnable) ret.push_back(P(offset[XZ + i], x + sizeZ + 2));
217 | if (zEnable) ret.push_back(P(offset[(zStrait ? Z_S : Z_R) + i], sizeZ + 1));
218 | if (zEnable) ret.push_back(P(offset[YZ + i], sizeZ + y + 2));
219 | return ret;
220 | }
221 |
222 | const vector>> &Axis::getNumber(
223 | int rotX, int rotY) const {
224 | int rx = (360 - rotX) % 90;
225 | int dir = (360 - rotX) / 45 % 8;
226 | return getNumber(dir, rx > 20 || rotY > 20, rx < 70 || rotY > 20, rotY<70, rx> 45,
227 | rotY > 20, rx == 0);
228 | }
229 |
230 | // position, base line dir, expand dir
231 | const vector &Axis::getLabel(int dir, bool xEnable, bool yEnable,
232 | bool zEnable, bool zStrait, bool xyStrait, bool front) const {
233 | static vector ret(3);
234 | float xy1 = xyStrait ? 1 + 2 * EXTRA : 1;
235 | float xy2 = xyStrait ? -DIST : -DIST - 2 * EXTRA;
236 | float z1 = zStrait ? 1 + 2 * EXTRA : 1;
237 | float z2 = zStrait ? -DIST : -DIST - 2 * EXTRA;
238 | int i1 = dir % 4 > 1 ? 1 : 0;
239 | int i2 = dir % 4 > 1 ? 0 : 1;
240 |
241 | if (zEnable) {
242 | ret[i1] = {xEnable ? (front ? PARALLEL : CENTER) : DISABLED, {0.5, xy1, xy2},
243 | {1, 0, 0}, xyStrait ? QVector3D{0, 1, 0} : QVector3D{0, 1, -1}};
244 | ret[i2] = {yEnable ? (front ? PARALLEL : CENTER) : DISABLED, {xy1, 0.5, xy2},
245 | {0, 1, 0}, xyStrait ? QVector3D{1, 0, 0} : QVector3D{1, 0, -1}};
246 | ret[2] = {zEnable ? PARALLEL : DISABLED, {z1, z2, 0.5}, {0, 0, 1},
247 | zStrait ? QVector3D{1, 0, 0} : QVector3D{0, -1, 0}};
248 | } else {
249 | ret[i1] = {xEnable ? PARALLEL : DISABLED, {0.5, xy1, xy2}, {1, 0, 0},
250 | xyStrait ? QVector3D{0, 1, 0} : QVector3D{0, 1, -1}};
251 | ret[i2] = {yEnable ? PARALLEL : DISABLED, {xy1, 0.5, xy2}, {0, 1, 0},
252 | xyStrait ? QVector3D{1, 0, 0} : QVector3D{1, 0, -1}};
253 | ret[2] = {DISABLED, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
254 | }
255 |
256 | return ret;
257 | }
258 |
259 | const vector &Axis::getLabel(int rotX, int rotY) const {
260 | int rx = (360 - rotX) % 90;
261 | int dir = (360 - rotX) / 45 % 8;
262 | return getLabel(dir, rx > 20 || rotY > 20, rx < 70 || rotY > 20, rotY<70, rx> 45,
263 | rotY > 20, rx == 0);
264 | }
265 |
266 | bool Axis::getDir(int rotX, int rotY) const {
267 | (void)rotX;
268 | return rotY < 70;
269 | }
270 |
271 | QMatrix4x4 Axis::getTransform(int rotX, int rotY) const {
272 | return getTransform((360 - rotX) / 45 % 8, rotY >= 70);
273 | }
274 |
275 | QMatrix4x4 Axis::getTransform(int dir, bool flipX) const {
276 | QMatrix4x4 ret, tmp;
277 | ret.rotate(90 * (dir / 2), 0, 0, 1);
278 | if (flipX) tmp.data()[5] = -1;
279 | return ret * tmp;
280 | }
281 |
282 | const vector>> &Axis::getNumber(int dir,
283 | bool xEnable, bool yEnable, bool zEnable, bool zStrait, bool xyStrait,
284 | bool front) const {
285 | static vector>> ret(3); // true for right align
286 | for (int i = 0; i < 3; i++) ret[i].second.clear();
287 | float diffX = 1.0f / sizeX;
288 | float diffY = 1.0f / sizeY;
289 | float diffZ = 1.0f / sizeZ;
290 | float xyEx = xyStrait ? 2 * EXTRA : 0;
291 | float zEx = xyStrait ? 0 : -2 * EXTRA;
292 | float xEx = zStrait ? 2 * EXTRA : 0;
293 | float yEx = -DIST + (zStrait ? 0 : -2 * EXTRA);
294 |
295 | int d = dir / 2;
296 | if (d == 0) {
297 | if (xEnable)
298 | for (int i = 0; i < sizeX + 1; i++)
299 | ret[0].second.push_back(QVector3D(diffX * i, 1 + xyEx, -DIST + zEx));
300 | if (yEnable) {
301 | if (zEnable)
302 | for (int i = 0; i < sizeY + 1; i++)
303 | ret[1].second.push_back(QVector3D(1 + xyEx, diffY * i, -DIST + zEx));
304 | else
305 | for (int i = 0; i < sizeY + 1; i++)
306 | ret[1].second.push_back(
307 | QVector3D(1 + xyEx, 1 - diffY * i, -DIST + zEx));
308 | }
309 | ret[0].first = zEnable ? RIGHT : LEFT;
310 | ret[1].first = !front && xEnable ? LEFT : CENTER;
311 | } else if (d == 1) {
312 | if (xEnable)
313 | for (int i = 0; i < sizeY + 1; i++)
314 | ret[1].second.push_back(QVector3D(diffY * i, 1 + xyEx, -DIST + zEx));
315 | if (yEnable) {
316 | if (zEnable)
317 | for (int i = 0; i < sizeX + 1; i++)
318 | ret[0].second.push_back(
319 | QVector3D(1 + xyEx, 1 - diffX * i, -DIST + zEx));
320 | else
321 | for (int i = 0; i < sizeX + 1; i++)
322 | ret[0].second.push_back(QVector3D(1 + xyEx, diffX * i, -DIST + zEx));
323 | }
324 | ret[0].first = !front && xEnable ? LEFT : CENTER;
325 | ret[1].first = zEnable ? RIGHT : LEFT;
326 | } else if (d == 2) {
327 | if (xEnable)
328 | for (int i = 0; i < sizeX + 1; i++)
329 | ret[0].second.push_back(QVector3D(1 - diffX * i, 1 + xyEx, -DIST + zEx));
330 | if (yEnable) {
331 | if (zEnable)
332 | for (int i = 0; i < sizeY + 1; i++)
333 | ret[1].second.push_back(
334 | QVector3D(1 + xyEx, 1 - diffY * i, -DIST + zEx));
335 | else
336 | for (int i = 0; i < sizeY + 1; i++)
337 | ret[1].second.push_back(QVector3D(1 + xyEx, diffY * i, -DIST + zEx));
338 | }
339 | ret[0].first = zEnable ? RIGHT : LEFT;
340 | ret[1].first = !front && xEnable ? LEFT : CENTER;
341 | } else {
342 | if (xEnable)
343 | for (int i = 0; i < sizeY + 1; i++)
344 | ret[1].second.push_back(QVector3D(1 - diffY * i, 1 + xyEx, -DIST + zEx));
345 | if (yEnable) {
346 | if (zEnable)
347 | for (int i = 0; i < sizeX + 1; i++)
348 | ret[0].second.push_back(QVector3D(1 + xyEx, diffX * i, -DIST + zEx));
349 | else
350 | for (int i = 0; i < sizeX + 1; i++)
351 | ret[0].second.push_back(
352 | QVector3D(1 + xyEx, 1 - diffX * i, -DIST + zEx));
353 | }
354 | ret[0].first = !front && xEnable ? LEFT : CENTER;
355 | ret[1].first = zEnable ? RIGHT : LEFT;
356 | }
357 | if (zEnable)
358 | for (int i = 0; i < sizeZ + 1; i++)
359 | ret[2].second.push_back(QVector3D(1 + xEx, yEx, diffZ * i));
360 | ret[2].first = LEFT;
361 |
362 | return ret;
363 | }
364 |
--------------------------------------------------------------------------------
/app/render/axis.h:
--------------------------------------------------------------------------------
1 | #ifndef AXIS_H
2 | #define AXIS_H
3 |
4 | #include "common.h"
5 | #include "util/util.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | class Axis {
13 | public:
14 | Axis(int sizeX, int sizeY, int sizeZ);
15 | const std::vector &getPoint() const;
16 | const std::vector &getColor() const;
17 | const std::vector &getIndex() const;
18 | std::vector> getSlice(int rotX, int rotY);
19 | std::vector> getSlice(bool xEnable, bool yEnable, bool zEnable,
20 | bool zStrait, bool xyStrait, bool invert);
21 | const std::vector>> &getNumber(int dir,
22 | bool xEnable, bool yEnable, bool zEnable, bool zStrait, bool xyStrait,
23 | bool front) const;
24 | const std::vector>> &getNumber(
25 | int rotX, int rotY) const;
26 | const std::vector &getLabel(int dir, bool xEnable, bool yEnable,
27 | bool zEnable, bool zStrait, bool xyStrait, bool front) const;
28 | const std::vector &getLabel(int rotX, int rotY) const;
29 | bool getDir(int rotX, int rotY) const;
30 |
31 | QMatrix4x4 getTransform(int rotX, int rotY) const;
32 | QMatrix4x4 getTransform(int dir, bool flipX) const;
33 |
34 | private:
35 | std::vector point;
36 | std::vector color;
37 | std::vector index;
38 | int sizeX, sizeY, sizeZ;
39 | std::vector offset;
40 | static constexpr float DIST = 0.07f;
41 | static constexpr float EXTRA = 0.04f;
42 | static constexpr float LABEL = 0.5f;
43 | static constexpr QVector3D BLACK = QVector3D(0.4, 0.4, 0.4);
44 | static constexpr QVector3D GREY = QVector3D(0.8, 0.8, 0.8);
45 | static const int XY_X_B = 0;
46 | static const int XY_X_G = 1;
47 | static const int XY_Y_B = 2;
48 | static const int XY_Y_G = 3;
49 | static const int XZ = 4;
50 | static const int YZ = 5;
51 | static const int X_S = 6;
52 | static const int X_R = 7;
53 | static const int Y_S = 8;
54 | static const int Y_R = 9;
55 | static const int Z_S = 10;
56 | static const int Z_R = 11;
57 | static const int OFFSET = 12;
58 | };
59 |
60 | #endif // AXIS_H
61 |
--------------------------------------------------------------------------------
/app/render/bar.cpp:
--------------------------------------------------------------------------------
1 | #include "bar.h"
2 | #include "util/util.h"
3 |
4 | const float Bar::DIFF = -0.00001;
5 | const QVector3D Bar::BLACK = QVector3D(0.4, 0.4, 0.4);
6 |
7 | Bar::Bar(const Gradient &gradient, int steps)
8 | : point(104 + 2 * steps), index(310 + 2 * steps), color(104 + 2 * steps),
9 | number(steps + 1), pos{PARALLEL, {1.3, 0.5, 0}, {0, 1, 0}, {1, 0, 0}},
10 | steps(steps) {
11 | int count = 0;
12 | float step = 1.0 / 50;
13 | for (int j = 0; j < 2; j++) {
14 | for (int i = 0; i < 51; i++) {
15 | point[count] = QVector3D(j, i * step, 0);
16 | color[count] = gradient.getColor(step / 2 + i * step);
17 | count++;
18 | }
19 | }
20 | for (int j = 0; j < 2; j++) {
21 | for (int i = 0; i < 2; i++) {
22 | point[count] = QVector3D(j, i, DIFF);
23 | color[count++] = BLACK;
24 | }
25 | }
26 | step = 1.0 / steps;
27 | for (int i = 1; i < steps; i++) {
28 | for (int j = 0; j < 2; j++) {
29 | point[count] = QVector3D(0.8 + 0.2 * j, i * step, DIFF);
30 | color[count++] = BLACK;
31 | }
32 | }
33 | count = 0;
34 | for (int i = 0; i < 50; i++) {
35 | index[count++] = 52 + i;
36 | index[count++] = 1 + i;
37 | index[count++] = i;
38 | index[count++] = 52 + i;
39 | index[count++] = i;
40 | index[count++] = 51 + i;
41 | }
42 | index[count++] = 102;
43 | index[count++] = 103;
44 | index[count++] = 103;
45 | index[count++] = 105;
46 | index[count++] = 105;
47 | index[count++] = 104;
48 | index[count++] = 104;
49 | index[count++] = 102;
50 | for (int i = 0; i < steps - 1; i++) {
51 | index[count++] = 106 + 2 * i;
52 | index[count++] = 107 + 2 * i;
53 | }
54 |
55 | for (int i = 0; i < steps + 1; i++) {
56 | number[i] = QVector3D(1.3, i * step, 0);
57 | }
58 | }
59 |
60 | const std::vector &Bar::getPoint() const { return point; }
61 | const std::vector &Bar::getColor() const { return color; }
62 | const std::vector &Bar::getIndex() const { return index; }
63 | const std::vector &Bar::getNumber() const { return number; }
64 | const PositionInfo &Bar::getLabel() const { return pos; }
65 | QPair Bar::getSliceL() const { return QPair(300, steps + 3); }
66 | QPair Bar::getSliceT() const { return QPair(0, 100); }
67 |
--------------------------------------------------------------------------------
/app/render/bar.h:
--------------------------------------------------------------------------------
1 | #ifndef BAR_H
2 | #define BAR_H
3 |
4 | #include "common.h"
5 | #include "render/gradient.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | class Bar {
14 | public:
15 | Bar(const Gradient &gradient, int steps);
16 | const std::vector &getPoint() const;
17 | const std::vector &getColor() const;
18 | const std::vector &getIndex() const;
19 | const std::vector &getNumber() const;
20 | const PositionInfo &getLabel() const;
21 | QPair getSliceL() const;
22 | QPair getSliceT() const;
23 |
24 | private:
25 | std::vector point;
26 | std::vector index;
27 | std::vector color;
28 | std::vector number;
29 | PositionInfo pos;
30 | int steps;
31 | static const float DIFF;
32 | static const QVector3D BLACK;
33 | };
34 |
35 | #endif // BAR_H
36 |
--------------------------------------------------------------------------------
/app/render/common.cpp:
--------------------------------------------------------------------------------
1 | #include "common.h"
2 |
--------------------------------------------------------------------------------
/app/render/common.h:
--------------------------------------------------------------------------------
1 | #ifndef COMMON_H
2 | #define COMMON_H
3 | #include
4 |
5 | enum EnumPosition { LEFT, RIGHT, CENTER, PARALLEL, DISABLED };
6 |
7 | struct PositionInfo {
8 | EnumPosition align;
9 | QVector3D base;
10 | QVector3D line;
11 | QVector3D expand;
12 | };
13 |
14 | #endif // COMMON_H
15 |
--------------------------------------------------------------------------------
/app/render/engine.h:
--------------------------------------------------------------------------------
1 | #ifndef ENGINE_H
2 | #define ENGINE_H
3 |
4 | #include "render/axis.h"
5 | #include "render/bar.h"
6 | #include "render/model.h"
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | class Engine {
16 | public:
17 | Engine(std::shared_ptr &model, std::shared_ptr &axis,
18 | std::shared_ptr &bar, std::shared_ptr> &size,
19 | std::shared_ptr> &labels);
20 | virtual ~Engine() = default;
21 | virtual void initialize() = 0;
22 | virtual void render(QPainter &p) = 0;
23 | virtual void resize(int sizeX, int sizeY);
24 | QPoint getRotation();
25 | void setModel(std::shared_ptr &model);
26 | void setRotation(int rotX, int rotY);
27 | void setLine(float pos);
28 | void setShader(bool en);
29 | void setEnBar(bool en);
30 | void setEnLabel(bool en);
31 |
32 | protected:
33 | int rotX, rotY;
34 | int sizeX, sizeY;
35 | std::shared_ptr model;
36 | std::shared_ptr axis;
37 | std::shared_ptr bar;
38 | std::shared_ptr> size;
39 | std::shared_ptr> labels;
40 | float line;
41 | bool enShader, enBar, enLine;
42 | };
43 |
44 | class EngineGL : public Engine, protected QOpenGLFunctions {
45 | public:
46 | EngineGL(std::shared_ptr &model, std::shared_ptr &axis,
47 | std::shared_ptr &bar, std::shared_ptr> &size,
48 | std::shared_ptr> &labels);
49 | void initialize() override;
50 | void render(QPainter &p) override;
51 |
52 | private:
53 | int argFlatVecPos;
54 | int argFlatVecPnt;
55 | int argFlatVecNormal;
56 | int argFlatVecColor;
57 | int argFlatVecView;
58 | int argFlatVecLight;
59 | int argFlatMatTrans;
60 | int argFlatMatModel;
61 | int argFlatMatNormal;
62 |
63 | int argPlainVecPnt;
64 | int argPlainVecColor;
65 | int argPlainMatModel;
66 | int argPlainMatTrans;
67 |
68 | std::unique_ptr programFlat;
69 | std::unique_ptr programPlain;
70 | };
71 |
72 | class EngineQt : public Engine {
73 | public:
74 | EngineQt(std::shared_ptr &model, std::shared_ptr &axis,
75 | std::shared_ptr &bar, std::shared_ptr> size,
76 | std::shared_ptr> &labels);
77 | void initialize() override{};
78 | void render(QPainter &p) override;
79 | void render(QPaintDevice &p);
80 | };
81 |
82 | #endif // ENGINE_H
83 |
--------------------------------------------------------------------------------
/app/render/gradient.cpp:
--------------------------------------------------------------------------------
1 | #include "gradient.h"
2 | #include "util/util.h"
3 | #include
4 |
5 | using namespace std;
6 |
7 | Gradient Gradient::HEIGHT_MAP(
8 | {//
9 | {Qt::blue, 0.0}, {Qt::cyan, 0.2f}, {Qt::green, 0.4f}, {Qt::yellow, 0.6f},
10 | {Qt::red, 0.8f}, {Qt::darkRed, 1.0f}},
11 | 200);
12 |
13 | Gradient::Gradient(const vector> &data, int steps)
14 | : cache(steps), step(1.0f / steps) {
15 | QVector3D c1 = toV3D(data[0].first);
16 | QVector3D c2 = c1;
17 | float p1 = 0;
18 | float p2 = 0;
19 | int cnt = 0;
20 | for (auto i : data) {
21 | c1 = c2;
22 | p1 = p2;
23 | c2 = toV3D(i.first);
24 | p2 = i.second;
25 | for (float pos = cnt * step; pos < p2 && cnt < steps; cnt++, pos = cnt * step)
26 | cache[cnt] = (c1 * (p2 - pos) + c2 * (pos - p1)) / (p2 - p1);
27 | }
28 | }
29 |
30 | const QVector3D &Gradient::getColor(float pos) const {
31 | int index = pos / step;
32 | int size = cache.size();
33 | return cache[index >= size ? size - 1 : index];
34 | }
35 |
--------------------------------------------------------------------------------
/app/render/gradient.h:
--------------------------------------------------------------------------------
1 | #ifndef GRADIENT_H
2 | #define GRADIENT_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class Gradient {
10 | public:
11 | Gradient(const std::vector> &data, int steps = 50);
12 | const QVector3D &getColor(float pos) const;
13 |
14 | static Gradient HEIGHT_MAP;
15 |
16 | private:
17 | std::vector cache;
18 | float step;
19 | };
20 |
21 | #endif // GRADIENT_H
22 |
--------------------------------------------------------------------------------
/app/render/model.cpp:
--------------------------------------------------------------------------------
1 | #include "render/model.h"
2 | #include "util/util.h"
3 | #include
4 |
5 | using namespace std;
6 |
7 | function &, int, int)> f0 = //
8 | [](function &f, int x, int y) {
9 | for (int i = 0; i < x; i++)
10 | for (int j = 0; j < y; j++) f(j * x + i);
11 | };
12 |
13 | function &, int, int)> f1 = //
14 | [](function &f, int x, int y) {
15 | for (int i = x - 1; i >= 0; i--)
16 | for (int j = 0; j < y; j++) f(j * x + i);
17 | };
18 |
19 | function &, int, int)> f2 = //
20 | [](function &f, int x, int y) {
21 | for (int i = 0; i < x; i++)
22 | for (int j = y - 1; j >= 0; j--) f(j * x + i);
23 | };
24 |
25 | function &, int, int)> f3 = //
26 | [](function &f, int x, int y) {
27 | for (int i = x - 1; i >= 0; i--)
28 | for (int j = y - 1; j >= 0; j--) f(j * x + i);
29 | };
30 |
31 | vector &, int, int)>> Model::indexFunc{
32 | f0, f0, f1, f1, f3, f3, f2, f2};
33 |
34 | Model::Model() : indexT(8, vector()), indexL(8, vector()) {}
35 | const vector &Model::getIndexT(int dir) const { return indexT[dir]; }
36 | const vector &Model::getIndexL(int dir) const { return indexL[dir]; }
37 | const vector &Model::getPoint() const { return point; }
38 | const vector &Model::getNormal() const { return normal; }
39 | const vector &Model::getColor() const { return color; }
40 | const vector &Model::getPosition() const { return position; }
41 |
42 | bool Model::setQuantity(SimQuantity &sq, float time, int step) {
43 | VectorD2D extreme = sq.getExtreme();
44 | const vector &size = sq.getSizeModel();
45 | auto dataAt = [&](int dim = 0) {
46 | return Sampler::gen(sq.getDataAt(time, dim), step, sq.getSizeData()[0]);
47 | };
48 | auto data = [&](const auto &d) { return Sampler::gen(d, step); };
49 | double ratio = sq.getSizeData().size() == 2
50 | ? (size[0].second - size[0].first) / (size[1].second - size[1].first)
51 | : 0;
52 |
53 | switch (sq.getSizeData().size()) {
54 | case 0: genLine(data(sq.getTimes()), data(sq.getData()), extreme); return true;
55 | case 2:
56 | switch (sq.getDim()) {
57 | case 1: genHeight(dataAt(), extreme); return false;
58 | case 2: genVector(dataAt(0), dataAt(1), extreme, ratio); return false;
59 | }
60 | break;
61 | }
62 | return false;
63 | }
64 |
65 | void Model::genLine(DATA x, DATA y, VectorD2D extreme) {
66 | if (checkSame(LINE, {x, y})) return; // TODO
67 | checkSize(x->sizeXI(), 0, (x->sizeXI() - 1) * 2);
68 | float xMin = x->get(0);
69 | float xMax = x->get(-1);
70 | float xDiff = xMax - xMin;
71 | double yMin = extreme.first;
72 | double yMax = extreme.second;
73 | double yDiff = yMax - yMin;
74 |
75 | if (yDiff == 0) {
76 | if (yMin == 0) {
77 | yMin = -1;
78 | yDiff = 2;
79 | } else {
80 | yMin *= 0.9;
81 | yMax *= 1.1;
82 | yDiff = yMax - yMin;
83 | }
84 | }
85 |
86 | for (int i = 0; i < x->sizeXI(); i++) {
87 | point[i] = QVector3D((x->get(i) - xMin) / xDiff, (y->get(i) - yMin) / yDiff, 0);
88 | normal[i] = QVector3D(0, 0, 1);
89 | color[i] = QVector3D(0, 0, 1);
90 | position[i] = point[i];
91 | }
92 |
93 | for (int j = 0; j < 8; j++) {
94 | for (int i = 0; i < x->sizeXI() - 1; i++) {
95 | indexL[j][2 * i] = i;
96 | indexL[j][2 * i + 1] = i + 1;
97 | }
98 | }
99 | }
100 |
101 | void Model::genHeight(DATA data, VectorD2D extreme) {
102 | if (checkSame(HEIGHT, {data})) return;
103 | int sxi = data->sizeXI();
104 | int syi = data->sizeYI();
105 | float sxf = data->sizeXF();
106 | float syf = data->sizeYF();
107 | checkSize((sxi - 1) * (syi - 1) * 9, (sxi - 1) * (syi - 1) * 24, 0);
108 |
109 | static vector> order{
110 | {0, 4, 3, 1, 4, 0, 3, 4, 6, 6, 4, 7, 2, 4, 1, 5, 4, 2, 7, 4, 8, 8, 4, 5},
111 | {1, 4, 0, 0, 4, 3, 3, 4, 6, 6, 4, 7, 2, 4, 1, 5, 4, 2, 8, 4, 5, 7, 4, 8},
112 | {2, 4, 1, 5, 4, 2, 1, 4, 0, 0, 4, 3, 8, 4, 5, 7, 4, 8, 3, 4, 6, 6, 4, 7},
113 | {5, 4, 2, 2, 4, 1, 1, 4, 0, 0, 4, 3, 8, 4, 5, 7, 4, 8, 6, 4, 7, 3, 4, 6},
114 | {8, 4, 5, 7, 4, 8, 5, 4, 2, 2, 4, 1, 6, 4, 7, 3, 4, 6, 1, 4, 0, 0, 4, 3},
115 | {7, 4, 8, 8, 4, 5, 5, 4, 2, 2, 4, 1, 6, 4, 7, 3, 4, 6, 0, 4, 3, 1, 4, 0},
116 | {6, 4, 7, 3, 4, 6, 7, 4, 8, 8, 4, 5, 0, 4, 3, 1, 4, 0, 5, 4, 2, 2, 4, 1},
117 | {3, 4, 6, 6, 4, 7, 7, 4, 8, 8, 4, 5, 0, 4, 3, 1, 4, 0, 2, 4, 1, 5, 4, 2}};
118 |
119 | float height = extreme.second - extreme.first;
120 | float offsetX = data->offsetX();
121 | float offsetY = data->offsetY();
122 | for (int y = 0; y < syi - 1; y++) {
123 | for (int x = 0; x < sxi - 1; x++) {
124 | double d[9];
125 | float px[3];
126 | float py[3];
127 | QVector3D p[9];
128 |
129 | int offsetPoint = (y * (sxi - 1) + x) * 9;
130 |
131 | if (height == 0) {
132 | for (int i = 0; i < 9; i++) d[i] = data->get(0);
133 | } else {
134 | d[0] = data->get(x, y);
135 | d[2] = data->get(x + 1, y);
136 | d[6] = data->get(x, y + 1);
137 | d[8] = data->get(x + 1, y + 1);
138 | d[0] = (d[0] - extreme.first) / height;
139 | d[2] = (d[2] - extreme.first) / height;
140 | d[6] = (d[6] - extreme.first) / height;
141 | d[8] = (d[8] - extreme.first) / height;
142 | d[1] = (d[0] + d[2]) / 2;
143 | d[3] = (d[0] + d[6]) / 2;
144 | d[5] = (d[2] + d[8]) / 2;
145 | d[7] = (d[6] + d[8]) / 2;
146 | d[4] = (d[3] + d[5]) / 2;
147 | }
148 |
149 | for (int i = 0; i < 3; i++) {
150 | px[i] = (x + 0.5f * i + offsetX) / sxf;
151 | py[i] = (y + 0.5f * i + offsetY) / syf;
152 | }
153 |
154 | for (int yy = 0; yy < 3; yy++) {
155 | for (int xx = 0; xx < 3; xx++) {
156 | int i = yy * 3 + xx;
157 | p[i] = QVector3D(px[xx], py[yy], d[i]);
158 | }
159 | }
160 |
161 | for (int i = 0; i < 9; i++) point[offsetPoint + i] = p[i];
162 |
163 | normal[offsetPoint] = QVector3D::crossProduct(p[1] - p[0], p[4] - p[0]);
164 | normal[offsetPoint + 1] = QVector3D::crossProduct(p[2] - p[1], p[4] - p[1]);
165 | normal[offsetPoint + 2] = QVector3D::crossProduct(p[5] - p[2], p[4] - p[2]);
166 | normal[offsetPoint + 3] = QVector3D::crossProduct(p[0] - p[3], p[4] - p[3]);
167 | normal[offsetPoint + 4] = QVector3D(1, 1, 1);
168 | normal[offsetPoint + 5] = QVector3D::crossProduct(p[8] - p[5], p[4] - p[5]);
169 | normal[offsetPoint + 6] = QVector3D::crossProduct(p[3] - p[6], p[4] - p[6]);
170 | normal[offsetPoint + 7] = QVector3D::crossProduct(p[6] - p[7], p[4] - p[7]);
171 | normal[offsetPoint + 8] = QVector3D::crossProduct(p[7] - p[8], p[4] - p[8]);
172 |
173 | color[offsetPoint] = Gradient::HEIGHT_MAP.getColor(d[0]);
174 | color[offsetPoint + 3] = color[offsetPoint];
175 | color[offsetPoint + 1] = Gradient::HEIGHT_MAP.getColor(d[2]);
176 | color[offsetPoint + 2] = color[offsetPoint + 1];
177 | color[offsetPoint + 5] = Gradient::HEIGHT_MAP.getColor(d[8]);
178 | color[offsetPoint + 8] = color[offsetPoint + 5];
179 | color[offsetPoint + 7] = Gradient::HEIGHT_MAP.getColor(d[6]);
180 | color[offsetPoint + 6] = color[offsetPoint + 7];
181 | color[offsetPoint + 4] = Gradient::HEIGHT_MAP.getColor(0);
182 |
183 | position[offsetPoint] = (p[0] + p[1] + p[4]) / 3;
184 | position[offsetPoint + 1] = (p[1] + p[2] + p[4]) / 3;
185 | position[offsetPoint + 2] = (p[2] + p[5] + p[4]) / 3;
186 | position[offsetPoint + 3] = (p[0] + p[3] + p[4]) / 3;
187 | position[offsetPoint + 4] = p[4];
188 | position[offsetPoint + 5] = (p[5] + p[8] + p[4]) / 3;
189 | position[offsetPoint + 6] = (p[6] + p[3] + p[4]) / 3;
190 | position[offsetPoint + 7] = (p[6] + p[7] + p[4]) / 3;
191 | position[offsetPoint + 8] = (p[7] + p[8] + p[4]) / 3;
192 | }
193 | }
194 |
195 | for (int i = 0; i < 8; i++) {
196 | int cnt = 0;
197 | function func = [&](int j) {
198 | int offsetPoint = j * 9;
199 | for (int j = 0; j < 24; j++) indexT[i][cnt++] = order[i][j] + offsetPoint;
200 | };
201 | indexFunc[i](func, sxi - 1, syi - 1);
202 | }
203 | }
204 |
205 | void Model::genVector(
206 | Model::DATA dataX, Model::DATA dataY, VectorD2D extreme, float ratio) {
207 | static vector> polar;
208 | static float angle1 = PI * 13 / 12;
209 | static float angle2 = PI * 11 / 12;
210 | static vector bufY;
211 |
212 | int sizeX = dataX->sizeXI();
213 | int sizeY = dataX->sizeYI();
214 | int sizeT = sizeX * sizeY;
215 |
216 | if (checkSame(VECTOR, {dataX, dataY})) return; // TODO
217 | checkSize(sizeX * sizeY * 5, sizeX * sizeY * 3, sizeX * sizeY * 2);
218 | polar.resize(sizeT);
219 | bufY.resize(sizeT);
220 | const DATA &convX = dataX;
221 | DATA convY = Sampler::gen(bufY, 1, sizeX);
222 | for (int i = 0; i < sizeT; i++) bufY[i] = dataY->get(i) * ratio;
223 |
224 | double maxActual = extreme.second;
225 | double maxScaled = maxActual * (ratio > 1 ? ratio : 1 / ratio);
226 |
227 | VectorD2D pnt;
228 | for (int i = 0; i < sizeT; i++) {
229 | toPolar(convX->get(i), convY->get(i), pnt);
230 | QPair &pair = polar[i];
231 | pair.first = pnt;
232 | pair.second = magnitude(dataX->get(i), dataY->get(i));
233 | }
234 | int divs = sizeX > sizeY ? sizeX : sizeY;
235 | double toLenth = maxScaled == 0 ? 0 : 0.7 / maxScaled / divs;
236 | double toUnity = 1 / maxActual;
237 | float sizeL = 0.6 / (sizeX > sizeY ? sizeX + 1 : sizeY + 1);
238 | float sizeA = sizeL * cos(PI / 12);
239 | float marginX = 0.5 / dataX->sizeXO();
240 | float marginY = 0.5 / dataX->sizeYO();
241 | float diffX = dataX->sizeXI() == 1 ? 1 : (1.0 - 2 * marginX) / dataX->sizeXF();
242 | float diffY = dataX->sizeYI() == 1 ? 1 : (1.0 - 2 * marginY) / dataX->sizeYF();
243 | for (auto &i : polar) {
244 | i.first.first *= toLenth;
245 | i.second *= toUnity;
246 | }
247 | QVector2D line0;
248 | QVector2D line1;
249 | QVector2D line2;
250 | float offsetX = dataX->offsetX();
251 | float offsetY = dataX->offsetY();
252 | for (int i = 0; i < sizeY; i++) {
253 | for (int j = 0; j < sizeX; j++) {
254 | int idx = i * sizeX + j;
255 | float offX = marginX + diffX * (j + offsetX);
256 | float offY = marginY + diffY * (i + offsetY);
257 | VectorD2D &p = polar[idx].first;
258 | QVector3D base(offX, offY, polar[idx].second);
259 | toCatsn(p.first, p.second, line0);
260 | QVector3D top = base + line0;
261 | point[idx * 5] = base;
262 | point[idx * 5 + 1] = top;
263 | toCatsn(sizeA, p.second, line0);
264 | toCatsn(sizeL, p.second + angle1, line1);
265 | toCatsn(sizeL, p.second + angle2, line2);
266 | top = top + line0;
267 | point[idx * 5 + 2] = top;
268 | point[idx * 5 + 3] = top + line1;
269 | point[idx * 5 + 4] = top + line2;
270 | QVector3D c = Gradient::HEIGHT_MAP.getColor(polar[idx].second);
271 | for (int i = 0; i < 5; i++) color[idx * 5 + i] = c;
272 | }
273 | }
274 | for (size_t i = 0; i < normal.size(); i++) normal[i] = QVector3D(0, 0, 1);
275 | for (int i = 0; i < 8; i++) {
276 | int cntT = 0;
277 | int cntL = 0;
278 | function