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