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