Solvespace
256 строк · 7.2 Кб
1//-----------------------------------------------------------------------------
2// Triangle mesh file reader. Reads an STL file triangle mesh and creates
3// a SovleSpace SMesh from it. Supports only Linking, not import.
4//
5// Copyright 2020 Paul Kahler.
6//-----------------------------------------------------------------------------
7#include "solvespace.h"
8#include "sketch.h"
9#include <vector>
10
11#define MIN_POINT_DISTANCE 0.001
12
13// we will check for duplicate vertices and keep all their normals
14class vertex {
15public:
16Vector p;
17std::vector<Vector> normal;
18};
19
20static bool isEdgeVertex(vertex &v) {
21unsigned int i,j;
22bool result = false;
23for(i=0;i<v.normal.size();i++) {
24for(j=i;j<v.normal.size();j++) {
25if(v.normal[i].Dot(v.normal[j]) < 0.9) {
26result = true;
27}
28}
29}
30return result;
31}
32// This function has poor performance, used inside a loop it is O(n**2)
33static void addUnique(std::vector<vertex> &lv, Vector &p, Vector &n) {
34unsigned int i;
35for(i=0; i<lv.size(); i++) {
36if(lv[i].p.Equals(p, MIN_POINT_DISTANCE)) {
37break;
38}
39}
40if(i==lv.size()) {
41vertex v;
42v.p = p;
43lv.push_back(v);
44}
45// we could improve a little by only storing unique normals
46lv[i].normal.push_back(n);
47};
48
49// Make a new point - type doesn't matter since we will make a copy later
50static hEntity newPoint(EntityList *el, int *id, Vector p) {
51Entity en = {};
52en.type = Entity::Type::POINT_N_COPY;
53en.extraPoints = 0;
54en.timesApplied = 0;
55en.group.v = 462;
56en.actPoint = p;
57en.construction = false;
58en.style.v = Style::DATUM;
59en.actVisible = true;
60en.forceHidden = false;
61
62en.h.v = *id + en.group.v*65536;
63*id = *id+1;
64el->Add(&en);
65return en.h;
66}
67
68// check if a vertex is unique and add it via newPoint if it is.
69static void addVertex(EntityList *el, Vector v) {
70if(el->n < 15000) {
71int id = el->n;
72newPoint(el, &id, v);
73}
74}
75
76static hEntity newNormal(EntityList *el, int *id, Quaternion normal, hEntity p) {
77// normals have parameters, but we don't need them to make a NORMAL_N_COPY from this
78Entity en = {};
79en.type = Entity::Type::NORMAL_N_COPY;
80en.extraPoints = 0;
81en.timesApplied = 0;
82en.group.v = 472;
83en.actNormal = normal;
84en.construction = false;
85en.style.v = Style::NORMALS;
86// to be visible we need to add a point.
87// en.point[0] = newPoint(el, id, Vector::From(0,0,0));
88en.point[0] = p;
89en.actVisible = true;
90en.forceHidden = false;
91
92*id = *id+1;
93en.h.v = *id + en.group.v*65536;
94el->Add(&en);
95return en.h;
96}
97
98static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1) {
99Entity en = {};
100en.type = Entity::Type::LINE_SEGMENT;
101en.point[0] = p0;
102en.point[1] = p1;
103en.extraPoints = 0;
104en.timesApplied = 0;
105en.group.v = 493;
106en.construction = true;
107en.style.v = Style::CONSTRUCTION;
108en.actVisible = true;
109en.forceHidden = false;
110
111en.h.v = *id + en.group.v*65536;
112*id = *id + 1;
113el->Add(&en);
114return en.h;
115}
116
117namespace SolveSpace {
118
119bool LinkStl(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
120dbp("\nLink STL triangle mesh.");
121el->Clear();
122std::string data;
123if(!ReadFile(filename, &data)) {
124Error("Couldn't read from '%s'", filename.raw.c_str());
125return false;
126}
127
128std::stringstream f(data);
129
130char str[80] = {};
131f.read(str, 80);
132
133if(0==memcmp("solid", str, 5)) {
134// just returning false will trigger the warning that linked file is not present
135// best solution is to add an importer for text STL.
136Message(_("Text-formated STL files are not currently supported"));
137return false;
138}
139
140uint32_t n;
141uint32_t color;
142
143f.read((char*)&n, 4);
144dbp("%d triangles", n);
145
146float x,y,z;
147float xn,yn,zn;
148
149std::vector<vertex> verts = {};
150
151for(uint32_t i = 0; i<n; i++) {
152STriangle tr = {};
153
154// read the triangle normal
155f.read((char*)&xn, 4);
156f.read((char*)&yn, 4);
157f.read((char*)&zn, 4);
158tr.an = Vector::From(xn,yn,zn);
159tr.bn = tr.an;
160tr.cn = tr.an;
161
162f.read((char*)&x, 4);
163f.read((char*)&y, 4);
164f.read((char*)&z, 4);
165tr.a.x = x;
166tr.a.y = y;
167tr.a.z = z;
168
169f.read((char*)&x, 4);
170f.read((char*)&y, 4);
171f.read((char*)&z, 4);
172tr.b.x = x;
173tr.b.y = y;
174tr.b.z = z;
175
176f.read((char*)&x, 4);
177f.read((char*)&y, 4);
178f.read((char*)&z, 4);
179tr.c.x = x;
180tr.c.y = y;
181tr.c.z = z;
182
183f.read((char*)&color,2);
184if(color & 0x8000) {
185tr.meta.color.red = (color >> 7) & 0xf8;
186tr.meta.color.green = (color >> 2) & 0xf8;
187tr.meta.color.blue = (color << 3);
188tr.meta.color.alpha = 255;
189} else {
190tr.meta.color.red = 90;
191tr.meta.color.green = 120;
192tr.meta.color.blue = 140;
193tr.meta.color.alpha = 255;
194}
195
196m->AddTriangle(&tr);
197Vector normal = tr.Normal().WithMagnitude(1.0);
198addUnique(verts, tr.a, normal);
199addUnique(verts, tr.b, normal);
200addUnique(verts, tr.c, normal);
201}
202dbp("%d vertices", verts.size());
203
204int id = 1;
205
206//add the STL origin and normals
207hEntity origin = newPoint(el, &id, Vector::From(0.0, 0.0, 0.0));
208newNormal(el, &id, Quaternion::From(Vector::From(1,0,0),Vector::From(0,1,0)), origin);
209newNormal(el, &id, Quaternion::From(Vector::From(0,1,0),Vector::From(0,0,1)), origin);
210newNormal(el, &id, Quaternion::From(Vector::From(0,0,1),Vector::From(1,0,0)), origin);
211
212BBox box = {};
213box.minp = verts[0].p;
214box.maxp = verts[0].p;
215
216// determine the bounding box for all vertexes
217for(unsigned int i=1; i<verts.size(); i++) {
218box.Include(verts[i].p);
219}
220
221hEntity p[8];
222p[0] = newPoint(el, &id, Vector::From(box.minp.x, box.minp.y, box.minp.z));
223p[1] = newPoint(el, &id, Vector::From(box.maxp.x, box.minp.y, box.minp.z));
224p[2] = newPoint(el, &id, Vector::From(box.minp.x, box.maxp.y, box.minp.z));
225p[3] = newPoint(el, &id, Vector::From(box.maxp.x, box.maxp.y, box.minp.z));
226p[4] = newPoint(el, &id, Vector::From(box.minp.x, box.minp.y, box.maxp.z));
227p[5] = newPoint(el, &id, Vector::From(box.maxp.x, box.minp.y, box.maxp.z));
228p[6] = newPoint(el, &id, Vector::From(box.minp.x, box.maxp.y, box.maxp.z));
229p[7] = newPoint(el, &id, Vector::From(box.maxp.x, box.maxp.y, box.maxp.z));
230
231newLine(el, &id, p[0], p[1]);
232newLine(el, &id, p[0], p[2]);
233newLine(el, &id, p[3], p[1]);
234newLine(el, &id, p[3], p[2]);
235
236newLine(el, &id, p[4], p[5]);
237newLine(el, &id, p[4], p[6]);
238newLine(el, &id, p[7], p[5]);
239newLine(el, &id, p[7], p[6]);
240
241newLine(el, &id, p[0], p[4]);
242newLine(el, &id, p[1], p[5]);
243newLine(el, &id, p[2], p[6]);
244newLine(el, &id, p[3], p[7]);
245
246for(unsigned int i=0; i<verts.size(); i++) {
247// create point entities for edge vertexes
248if(isEdgeVertex(verts[i])) {
249addVertex(el, verts[i].p);
250}
251}
252
253return true;
254}
255
256}
257