1 /*************************************************************************************
2  *  Copyright (C) 2016 Aleix Pol Gonzalez <aleixpol@kde.org>                         *
3  *                                                                                   *
4  *  This program is free software; you can redistribute it and/or                    *
5  *  modify it under the terms of the GNU General Public License                      *
6  *  as published by the Free Software Foundation; either version 2                   *
7  *  of the License, or (at your option) any later version.                           *
8  *                                                                                   *
9  *  This program 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 program; if not, write to the Free Software                      *
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
17  *************************************************************************************/
18 
19 #include "export3d.h"
20 
21 #include <QFile>
22 #include <QDebug>
23 #include <QVector3D>
24 #include <cmath>
25 
26 #include "surface.h"
27 #include "plotitem.h"
28 #include "plotsmodel.h"
29 
30 using namespace Analitza;
31 
32 template <typename T>
fromNumbers(const QVector<T> & input)33 static QByteArray fromNumbers(const QVector<T>& input)
34 {
35     QByteArray ret;
36     foreach(qreal r, input) {
37         ret += QByteArray::number(r)+' ';
38     }
39     ret.chop(1);
40     return ret;
41 }
42 
fromVector3D(const QVector3D & r)43 static QByteArray fromVector3D(const QVector3D &r)
44 {
45     return QByteArray::number(r.x())+' '
46          + QByteArray::number(r.y())+' '
47          + QByteArray::number(r.z());
48 }
49 
fromNumbers(const QVector<QVector3D> & input)50 static QByteArray fromNumbers(const QVector<QVector3D>& input)
51 {
52     QByteArray ret;
53     foreach(const QVector3D &r, input) {
54         ret += fromVector3D(r);
55     }
56     ret.chop(1);
57     return ret;
58 }
59 
makeTriangles(const QVector<uint> & input)60 static QVector<int> makeTriangles(const QVector<uint>& input)
61 {
62     QVector<int> ret;
63     int i = 0;
64     foreach(uint val, input) {
65         ret += val;
66         if(i==2) {
67             ret += -1;
68             i = 0;
69         } else
70             ++i;
71     }
72     ret += -1;
73     return ret;
74 }
75 
exportX3D(const QString & path,QAbstractItemModel * model)76 void Export3D::exportX3D(const QString& path, QAbstractItemModel* model)
77 {
78     QFile f(path);
79     if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
80         qWarning() << "couldn't open" << path;
81         return;
82     }
83 
84     f.write(QByteArrayLiteral("<?xml version='1.0' encoding='UTF-8'?>\n"
85     "<!DOCTYPE X3D PUBLIC 'ISO//Web3D//DTD X3D 3.2//EN' 'http://www.web3d.org/specifications/x3d-3.2.dtd'>\n"
86     "<X3D profile='Interchange' version='3.2' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' "
87         "xsd:noNamespaceSchemaLocation='http://www.web3d.org/specifications/x3d-3.2.xsd'>\n"
88         "<Scene>\n"));
89     for (int i = 0; i < model->rowCount(); ++i)
90     {
91         const QModelIndex pi = model->index(i, 0);
92 
93         if (!pi.isValid())
94             continue;
95 
96         PlotItem* item = pi.data(PlotsModel::PlotRole).value<PlotItem*>();
97 
98         Surface *surf = dynamic_cast<Surface*>(item);
99         if (!surf || !surf->isVisible())
100             continue;
101 
102         f.write(QByteArrayLiteral(
103             "<Shape>\n"
104                 "<Appearance><Material diffuseColor='1 0 0' specularColor='0.8 0.7 0.5'/></Appearance>\n"
105                 "<IndexedFaceSet solid='false' normalPerVertex='false' coordIndex='"));
106         f.write(fromNumbers(makeTriangles(surf->indexes())));
107         f.write(QByteArrayLiteral("'>\n"
108                     "<Coordinate point='"));
109         f.write(fromNumbers(surf->vertices()));
110         f.write(QByteArrayLiteral("'/>\n"
111         "<Normal vector='"));
112         f.write(fromNumbers(surf->normals()));
113         f.write(QByteArrayLiteral(
114                 "'/>\n"
115                 "</IndexedFaceSet>\n"
116             "</Shape>\n"));
117     }
118     f.write(QByteArrayLiteral(
119         "</Scene>\n"
120     "</X3D>\n"));
121 }
122 
exportSTL(const QString & path,QAbstractItemModel * model)123 void Export3D::exportSTL(const QString& path, QAbstractItemModel* model)
124 {
125     QFile f(path);
126     if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
127         qWarning() << "couldn't open" << path;
128         return;
129     }
130 
131     f.write(QByteArrayLiteral("solid myplot\n"));
132     for (int i = 0; i < model->rowCount(); ++i)
133     {
134         const QModelIndex pi = model->index(i, 0);
135 
136         if (!pi.isValid())
137             continue;
138 
139         PlotItem* item = pi.data(PlotsModel::PlotRole).value<PlotItem*>();
140 
141         Surface *surf = dynamic_cast<Surface*>(item);
142         if (!surf || !surf->isVisible())
143             continue;
144 
145         const auto vertices = surf->vertices();
146         const auto indexes = surf->indexes();
147 
148         for (int i = 0, c = indexes.count()/3; i<c; ++i) {
149 //             f.write("  facet normal " + fromVector3D(normals[i]) + '\n');
150             const QVector3D v1 = vertices[indexes[i*3 + 0]]
151                           , v2 = vertices[indexes[i*3 + 1]]
152                           , v3 = vertices[indexes[i*3 + 2]];
153 
154             //TODO: should be using the normals from Surface
155             f.write("  facet normal " + fromVector3D(QVector3D::normal(v1, v2, v3)) + '\n');
156             f.write("     outer loop\n");
157             f.write("       vertex " + fromVector3D(v1) + '\n');
158             f.write("       vertex " + fromVector3D(v2) + '\n');
159             f.write("       vertex " + fromVector3D(v3) + '\n');
160             f.write("     endloop\n");
161             f.write("  endfacet\n");
162         }
163         f.write("\n");
164     }
165     f.write("endsolid myplot\n");
166 }
167