KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sg_helpers.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2017 Cirilo Bernardo <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include <iomanip>
23#include <iostream>
24#include <map>
25#include <sstream>
26#include <utility>
27#include <wx/log.h>
28
30#include "3d_cache/sg/sg_node.h"
31
32
33void S3D::FormatFloat( std::string& result, double value )
34{
35 if( value < 1e-8 && value > -1e-8 )
36 {
37 result = "0";
38 return;
39 }
40
41 // note: many VRML implementations use float so we use the max.
42 // precision here of 8 digits.
43 std::ostringstream out;
44 out << std::setprecision( 8 ) << value;
45
46 result = out.str();
47
48 size_t p = result.find( '.' );
49
50 // trim trailing 0 if appropriate
51 if( std::string::npos == p )
52 return;
53
54 p = result.find_first_of( "eE" );
55
56 if( std::string::npos == p )
57 {
58 while( '0' == *( result.rbegin() ) )
59 result.erase( result.size() - 1 );
60
61 return;
62 }
63
64 if( '0' != result.at( p - 1 ) )
65 return;
66
67 // trim all 0 to the left of 'p'
68 std::string tmp = result.substr( p );
69 result = result.substr( 0, p );
70
71 while( '0' == *( result.rbegin() ) )
72 result.erase( result.size() - 1 );
73
74 result.append( tmp );
75}
76
77
78void S3D::FormatOrientation( std::string& result, const SGVECTOR& axis, double rotation )
79{
80 double aX;
81 double aY;
82 double aZ;
83
84 axis.GetVector( aX, aY, aZ );
85 FormatFloat( result, aX );
86 std::string tmp;
87 FormatFloat( tmp, aY );
88 result.append( " " );
89 result.append( tmp );
90 FormatFloat( tmp, aZ );
91 result.append( " " );
92 result.append( tmp );
93 FormatFloat( tmp, rotation );
94 result.append( " " );
95 result.append( tmp );
96}
97
98
99void S3D::FormatPoint( std::string& result, const SGPOINT& point )
100{
101 FormatFloat( result, point.x );
102
103 std::string tmp;
104 FormatFloat( tmp, point.y );
105 result.append( " " );
106 result.append( tmp );
107
108 FormatFloat( tmp, point.z );
109 result.append( " " );
110 result.append( tmp );
111}
112
113
114void S3D::FormatVector( std::string& result, const SGVECTOR& aVector )
115{
116 double X, Y, Z;
117 aVector.GetVector( X, Y, Z );
118 FormatFloat( result, X );
119
120 std::string tmp;
121 FormatFloat( tmp, Y );
122 result.append( " " );
123 result.append( tmp );
124
125 FormatFloat( tmp, Z );
126 result.append( " " );
127 result.append( tmp );
128}
129
130
131void S3D::FormatColor( std::string& result, const SGCOLOR& aColor )
132{
133 float R, G, B;
134 aColor.GetColor( R, G, B );
135 FormatFloat( result, R );
136
137 std::string tmp;
138 FormatFloat( tmp, G );
139 result.append( " " );
140 result.append( tmp );
141
142 FormatFloat( tmp, B );
143 result.append( " " );
144 result.append( tmp );
145}
146
147
148bool S3D::WritePoint( std::ostream& aFile, const SGPOINT& aPoint )
149{
150 aFile.write( (char*) &aPoint.x, sizeof( aPoint.x ) );
151 aFile.write( (char*) &aPoint.y, sizeof( aPoint.y ) );
152 aFile.write( (char*) &aPoint.z, sizeof( aPoint.z ) );
153
154 if( aFile.fail() )
155 return false;
156
157 return true;
158}
159
160
161bool S3D::WriteVector( std::ostream& aFile, const SGVECTOR& aVector )
162{
163 double x, y, z;
164 aVector.GetVector( x, y, z );
165 aFile.write( (char*) &x, sizeof( double ) );
166 aFile.write( (char*) &y, sizeof( double ) );
167 aFile.write( (char*) &z, sizeof( double ) );
168
169 if( aFile.fail() )
170 return false;
171
172 return true;
173}
174
175
176bool S3D::WriteColor( std::ostream& aFile, const SGCOLOR& aColor )
177{
178 float r, g, b;
179 aColor.GetColor( r, g, b );
180 aFile.write( (char*) &r, sizeof( float ) );
181 aFile.write( (char*) &g, sizeof( float ) );
182 aFile.write( (char*) &b, sizeof( float ) );
183
184 if( aFile.fail() )
185 return false;
186
187 return true;
188}
189
190
191S3D::SGTYPES S3D::ReadTag( std::istream& aFile, std::string& aName )
192{
193 char schar;
194 aFile.get( schar );
195
196 if( '[' != schar )
197 {
198 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; missing left bracket at "
199 "position %d" ),
200 __FILE__, __FUNCTION__, __LINE__,
201 static_cast<int>( aFile.tellg() ) );
202
203 return S3D::SGTYPE_END;
204 }
205
206 std::string name;
207 aFile.get( schar );
208
209 while( ']' != schar && aFile.good() )
210 {
211 name.push_back( schar );
212 aFile.get( schar );
213 }
214
215 if( schar != ']' )
216 {
217 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; could not find right "
218 "bracket" ),
219 __FILE__, __FUNCTION__, __LINE__ );
220
221 return S3D::SGTYPE_END;
222 }
223
224 aName = name;
225 size_t upos = name.find( '_' );
226
227 if( std::string::npos == upos )
228 {
229 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; no underscore in name '%s'" ),
230 __FILE__, __FUNCTION__, __LINE__,
231 name );
232
233 return S3D::SGTYPE_END;
234 }
235
236 name = name.substr( 0, upos );
247 };
248
249 for( int i = 0; i < S3D::SGTYPE_END; ++i )
250 {
251 if( !name.compare( S3D::GetNodeTypeName( types[i] ) ) )
252 return types[i];
253 }
254
255 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; no node type matching '%s'" ),
256 __FILE__, __FUNCTION__, __LINE__,
257 name );
258
259 return S3D::SGTYPE_END;
260}
261
262
263bool S3D::ReadPoint( std::istream& aFile, SGPOINT& aPoint )
264{
265 aFile.read( (char*) &aPoint.x, sizeof( aPoint.x ) );
266 aFile.read( (char*) &aPoint.y, sizeof( aPoint.y ) );
267 aFile.read( (char*) &aPoint.z, sizeof( aPoint.z ) );
268
269 if( aFile.fail() )
270 return false;
271
272 return true;
273}
274
275
276bool S3D::ReadVector( std::istream& aFile, SGVECTOR& aVector )
277{
278 double x, y, z;
279 aFile.read( (char*) &x, sizeof( double ) );
280 aFile.read( (char*) &y, sizeof( double ) );
281 aFile.read( (char*) &z, sizeof( double ) );
282 aVector.SetVector( x, y, z );
283
284 if( aFile.fail() )
285 return false;
286
287 return true;
288}
289
290
291bool S3D::ReadColor( std::istream& aFile, SGCOLOR& aColor )
292{
293 float r, g, b;
294 aFile.read( (char*) &r, sizeof( float ) );
295 aFile.read( (char*) &g, sizeof( float ) );
296 aFile.read( (char*) &b, sizeof( float ) );
297 aColor.SetColor( r, g, b );
298
299 if( aFile.fail() )
300 return false;
301
302 return true;
303}
304
305
306bool S3D::degenerate( glm::dvec3* pts ) noexcept
307{
308 double dx, dy, dz;
309
310 dx = pts[1].x - pts[0].x;
311 dy = pts[1].y - pts[0].y;
312 dz = pts[1].z - pts[0].z;
313
314 if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
315 return true;
316
317 dx = pts[2].x - pts[0].x;
318 dy = pts[2].y - pts[0].y;
319 dz = pts[2].z - pts[0].z;
320
321 if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
322 return true;
323
324 dx = pts[2].x - pts[1].x;
325 dy = pts[2].y - pts[1].y;
326 dz = pts[2].z - pts[1].z;
327
328 if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
329 return true;
330
331 return false;
332}
333
334
335static void calcTriad( glm::dvec3* pts, glm::dvec3& tri )
336{
337 if( S3D::degenerate( pts ) )
338 {
339 // degenerate points should contribute nothing to the result
340 tri = glm::dvec3( 0.0, 0.0, 0.0 );
341 return;
342 }
343
344 // normal * 2 * area
345 tri = glm::cross( pts[1] - pts[0], pts[2] - pts[0] );
346}
347
348
349bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords, std::vector< int >& index,
350 std::vector< SGVECTOR >& norms )
351{
352 size_t vsize = coords.size();
353
354 if( vsize < 3 )
355 {
356 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] invalid vertex set (fewer than 3 "
357 "vertices)" ),
358 __FILE__, __FUNCTION__, __LINE__ );
359
360 return false;
361 }
362
363 size_t isize = index.size();
364
365 if( 0 != isize % 3 || index.empty() )
366 {
367 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] invalid index set (not multiple of 3)" ),
368 __FILE__, __FUNCTION__, __LINE__ );
369
370 return false;
371 }
372
373 if( !norms.empty() )
374 {
375 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] normals set is not empty" ),
376 __FILE__, __FUNCTION__, __LINE__ );
377
378 return false;
379 }
380
381 std::map< int, std::list< glm::dvec3 > >vmap;
382
383 int p1, p2, p3;
384
385 // create the map of indices to facet sets
386 for( size_t i = 0; i < isize; )
387 {
388 p1 = index[i++];
389 p2 = index[i++];
390 p3 = index[i++];
391
392 if( p1 < 0 || p1 >= (int)vsize || p2 < 0 || p2 >= (int)vsize || p3 < 0 || p3 >= (int)vsize )
393 {
394#ifdef DEBUG
395 std::ostringstream ostr;
396 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
397 ostr << " * [INFO] invalid index set; index out of bounds";
398 wxLogTrace( MASK_3D_SG, wxT( "%s\n" ), ostr.str().c_str() );
399#endif
400
401 return false;
402 }
403
404 glm::dvec3 tri;
405 glm::dvec3 trip[3];
406 trip[0] = glm::dvec3( coords[p1].x, coords[p1].y, coords[p1].z );
407 trip[1] = glm::dvec3( coords[p2].x, coords[p2].y, coords[p2].z );
408 trip[2] = glm::dvec3( coords[p3].x, coords[p3].y, coords[p3].z );
409 calcTriad( trip, tri );
410
411 std::map< int, std::list< glm::dvec3 > >::iterator ip = vmap.find( p1 );
412
413 if( ip != vmap.end() )
414 {
415 ip->second.push_back( tri );
416 }
417 else
418 {
419 vmap.emplace( p1, std::list < glm::dvec3 >( 1, tri ) );
420 }
421
422 ip = vmap.find( p2 );
423
424 if( ip != vmap.end() )
425 {
426 ip->second.push_back( tri );
427 }
428 else
429 {
430 vmap.emplace( p2, std::list < glm::dvec3 >( 1, tri ) );
431 }
432
433 ip = vmap.find( p3 );
434
435 if( ip != vmap.end() )
436 {
437 ip->second.push_back( tri );
438 }
439 else
440 {
441 vmap.emplace( p3, std::list < glm::dvec3 >( 1, tri ) );
442 }
443 }
444
445 std::map< int, std::list< glm::dvec3 > >::iterator sM = vmap.begin();
446 std::map< int, std::list< glm::dvec3 > >::iterator eM = vmap.end();
447 size_t idx = 0;
448
449 while( sM != eM )
450 {
451 size_t item = sM->first;
452
453 // assign any skipped coordinates a normal of (0,0,1)
454 while( item > idx )
455 {
456 norms.emplace_back( 0, 0, 1 );
457 ++idx;
458 }
459
460 std::list< glm::dvec3 >::iterator sT = sM->second.begin();
461 std::list< glm::dvec3 >::iterator eT = sM->second.end();
462 glm::dvec3 norm( 0.0, 0.0, 0.0 );
463
464 while( sT != eT )
465 {
466 norm += *sT;
467 ++sT;
468 }
469
470 norms.emplace_back( norm.x, norm.y, norm.z );
471
472 ++idx;
473 ++sM;
474 }
475
476 if( norms.size() != coords.size() )
477 {
478 wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] number of normals does not equal number "
479 "of vertices" ),
480 __FILE__, __FUNCTION__, __LINE__ );
481
482 return false;
483 }
484
485 return true;
486}
int index
const char * name
bool SetColor(float aRedVal, float aGreenVal, float aBlueVal)
Definition sg_base.cpp:81
void GetColor(float &aRedVal, float &aGreenVal, float &aBlueVal) const noexcept
Definition sg_base.cpp:55
double z
Definition sg_base.h:68
double x
Definition sg_base.h:66
double y
Definition sg_base.h:67
void GetVector(double &aXVal, double &aYVal, double &aZVal) const noexcept
Definition sg_base.cpp:221
void SetVector(double aXVal, double aYVal, double aZVal)
Definition sg_base.cpp:229
#define G(x, y, z)
Definition md5_hash.cpp:16
bool ReadVector(std::istream &aFile, SGVECTOR &aVector)
bool ReadPoint(std::istream &aFile, SGPOINT &aPoint)
bool WritePoint(std::ostream &aFile, const SGPOINT &aPoint)
char const * GetNodeTypeName(S3D::SGTYPES aType) noexcept
Return the name of the given type of node.
Definition sg_node.cpp:49
bool WriteColor(std::ostream &aFile, const SGCOLOR &aColor)
bool CalcTriangleNormals(std::vector< SGPOINT > coords, std::vector< int > &index, std::vector< SGVECTOR > &norms)
void FormatColor(std::string &result, const SGCOLOR &aColor)
void FormatFloat(std::string &result, double value)
S3D::SGTYPES ReadTag(std::istream &aFile, std::string &aName)
Read the text tag of a binary cache file which is the NodeTag and unique ID number combined.
bool WriteVector(std::ostream &aFile, const SGVECTOR &aVector)
void FormatVector(std::string &result, const SGVECTOR &aVector)
void FormatPoint(std::string &result, const SGPOINT &point)
bool degenerate(glm::dvec3 *pts) noexcept
SGTYPES
Definition sg_types.h:32
@ SGTYPE_COLORS
Definition sg_types.h:35
@ SGTYPE_SHAPE
Definition sg_types.h:41
@ SGTYPE_FACESET
Definition sg_types.h:37
@ SGTYPE_APPEARANCE
Definition sg_types.h:34
@ SGTYPE_COLORINDEX
Definition sg_types.h:36
@ SGTYPE_NORMALS
Definition sg_types.h:40
@ SGTYPE_END
Definition sg_types.h:42
@ SGTYPE_COORDS
Definition sg_types.h:38
@ SGTYPE_COORDINDEX
Definition sg_types.h:39
@ SGTYPE_TRANSFORM
Definition sg_types.h:33
bool ReadColor(std::istream &aFile, SGCOLOR &aColor)
void FormatOrientation(std::string &result, const SGVECTOR &axis, double rotation)
static void calcTriad(glm::dvec3 *pts, glm::dvec3 &tri)
Define a number of macros to aid in repetitious code which is probably best expressed as a preprocess...
wxString result
Test unit parsing edge cases and error handling.