You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

424 lines
14 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 1992-2012 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. * @file 3d_draw_basic_functions.cpp
  26. */
  27. #include <fctsys.h>
  28. #include <trigo.h>
  29. #include <convert_basic_shapes_to_polygon.h>
  30. #include <3d_viewer.h>
  31. #include <info3d_visu.h>
  32. #include <3d_draw_basic_functions.h>
  33. // Imported function:
  34. extern void Set_Object_Data( std::vector<S3D_VERTEX>& aVertices, double aBiuTo3DUnits );
  35. extern void CheckGLError();
  36. // Number of segments to approximate a circle by segments
  37. #define SEGM_PER_CIRCLE 16
  38. #ifndef CALLBACK
  39. #define CALLBACK
  40. #endif
  41. // CALLBACK functions for GLU_TESS
  42. static void CALLBACK tessBeginCB( GLenum which );
  43. static void CALLBACK tessEndCB();
  44. static void CALLBACK tessErrorCB( GLenum errorCode );
  45. static void CALLBACK tessCPolyPt2Vertex( const GLvoid* data );
  46. // 2 helper functions to set the current normal vector for gle items
  47. static inline void SetNormalZpos()
  48. {
  49. glNormal3f( 0.0, 0.0, 1.0 );
  50. }
  51. static inline void SetNormalZneg()
  52. {
  53. glNormal3f( 0.0, 0.0, -1.0 );
  54. }
  55. /* Draw3D_VerticalPolygonalCylinder is a helper function.
  56. *
  57. * draws a "vertical cylinder" having a polygon shape
  58. * from Z position = aZpos to aZpos + aHeight
  59. * Used to create the vertical sides of 3D horizontal shapes with thickness.
  60. */
  61. static void Draw3D_VerticalPolygonalCylinder( const std::vector<CPolyPt>& aPolysList,
  62. int aHeight, int aZpos,
  63. bool aInside, double aBiuTo3DUnits )
  64. {
  65. if( aHeight == 0 )
  66. return;
  67. std::vector<S3D_VERTEX> coords;
  68. coords.resize( 4 );
  69. // Init Z position of the 4 points of a GL_QUAD
  70. if( aInside )
  71. {
  72. coords[0].z = aZpos;
  73. coords[1].z = aZpos + aHeight;
  74. }
  75. else
  76. {
  77. coords[0].z = aZpos + aHeight;
  78. coords[1].z = aZpos;
  79. }
  80. coords[2].z = coords[1].z;
  81. coords[3].z = coords[0].z;
  82. // Draw the vertical polygonal side
  83. int startContour = 0;
  84. for( unsigned ii = 0; ii < aPolysList.size(); ii++ )
  85. {
  86. unsigned jj = ii + 1;
  87. if( aPolysList[ii].end_contour || jj >= aPolysList.size() )
  88. {
  89. jj = startContour;
  90. startContour = ii + 1;
  91. }
  92. // Build the 4 vertices of each GL_QUAD
  93. coords[0].x = aPolysList[ii].x;
  94. coords[0].y = -aPolysList[ii].y;
  95. coords[1].x = coords[0].x;
  96. coords[1].y = coords[0].y; // only z change
  97. coords[2].x = aPolysList[jj].x;
  98. coords[2].y = -aPolysList[jj].y;
  99. coords[3].x = coords[2].x;
  100. coords[3].y = coords[2].y; // only z change
  101. // Creates the GL_QUAD
  102. Set_Object_Data( coords, aBiuTo3DUnits );
  103. }
  104. }
  105. void SetGLColor( EDA_COLOR_T color )
  106. {
  107. double red, green, blue;
  108. const StructColors &colordata = g_ColorRefs[ColorGetBase( color )];
  109. red = colordata.m_Red / 255.0;
  110. blue = colordata.m_Blue / 255.0;
  111. green = colordata.m_Green / 255.0;
  112. glColor3f( red, green, blue );
  113. }
  114. /* draw all solid polygons found in aPolysList
  115. * aZpos = z position in board internal units
  116. * aThickness = thickness in board internal units
  117. * If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos.
  118. * If aThickness > 0, a solid object is drawn.
  119. * The top side is located at aZpos + aThickness / 2
  120. * The bottom side is located at aZpos - aThickness / 2
  121. */
  122. void Draw3D_SolidHorizontalPolyPolygons( const std::vector<CPolyPt>& aPolysList,
  123. int aZpos, int aThickness, double aBiuTo3DUnits )
  124. {
  125. GLUtesselator* tess = gluNewTess();
  126. gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )tessBeginCB );
  127. gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )tessEndCB );
  128. gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )tessErrorCB );
  129. gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )tessCPolyPt2Vertex );
  130. GLdouble v_data[3];
  131. double zpos = ( aZpos + (aThickness / 2) ) * aBiuTo3DUnits;
  132. g_Parm_3D_Visu.m_CurrentZpos = zpos;
  133. v_data[2] = aZpos + (aThickness / 2);
  134. // Set normal to toward positive Z axis, for a solid object only (to draw the top side)
  135. if( aThickness )
  136. SetNormalZpos();
  137. // gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
  138. // Draw solid areas contained in this list
  139. std::vector<CPolyPt> polylist = aPolysList; // temporary copy for gluTessVertex
  140. for( int side = 0; side < 2; side++ )
  141. {
  142. int startContour = 1;
  143. for( unsigned ii = 0; ii < polylist.size(); ii++ )
  144. {
  145. if( startContour == 1 )
  146. {
  147. gluTessBeginPolygon( tess, NULL );
  148. gluTessBeginContour( tess );
  149. startContour = 0;
  150. }
  151. v_data[0] = polylist[ii].x * aBiuTo3DUnits;
  152. v_data[1] = -polylist[ii].y * aBiuTo3DUnits;
  153. // gluTessVertex store pointers on data, not data, so do not store
  154. // different corners values in a temporary variable
  155. // but send pointer on each CPolyPt value in polylist
  156. // before calling gluDeleteTess
  157. gluTessVertex( tess, v_data, &polylist[ii] );
  158. if( polylist[ii].end_contour == 1 )
  159. {
  160. gluTessEndContour( tess );
  161. gluTessEndPolygon( tess );
  162. startContour = 1;
  163. }
  164. }
  165. if( aThickness == 0 )
  166. break;
  167. // Prepare the bottom side of solid areas
  168. zpos = ( aZpos - (aThickness / 2) ) * aBiuTo3DUnits;
  169. g_Parm_3D_Visu.m_CurrentZpos = zpos;
  170. v_data[2] = zpos;
  171. // Now;, set normal to toward negative Z axis, for the solid object bottom side
  172. SetNormalZneg();
  173. }
  174. gluDeleteTess( tess );
  175. if( aThickness == 0 )
  176. return;
  177. // Build the 3D data : vertical side
  178. Draw3D_VerticalPolygonalCylinder( polylist, aThickness, aZpos - (aThickness / 2), false, aBiuTo3DUnits );
  179. }
  180. /* draw the solid polygon found in aPolysList
  181. * The first polygon is the main polygon, others are holes
  182. * See Draw3D_SolidHorizontalPolyPolygons for more info
  183. */
  184. void Draw3D_SolidHorizontalPolygonWithHoles( const std::vector<CPolyPt>& aPolysList,
  185. int aZpos, int aThickness,
  186. double aBiuTo3DUnits )
  187. {
  188. std::vector<CPolyPt> polygon;
  189. ConvertPolysListWithHolesToOnePolygon( aPolysList, polygon );
  190. Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, aThickness, aBiuTo3DUnits );
  191. }
  192. /* draw a cylinder (a tube) using 3D primitives.
  193. * the cylinder axis is parallel to the Z axis
  194. * If aHeight = height of the cylinder is 0, only one ring will be drawn
  195. * If aThickness = 0, only one cylinder will be drawn
  196. */
  197. void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
  198. int aHeight, int aThickness,
  199. int aZpos, double aBiuTo3DUnits )
  200. {
  201. const int slice = SEGM_PER_CIRCLE;
  202. std::vector <CPolyPt> outer_cornerBuffer;
  203. TransformCircleToPolygon( outer_cornerBuffer, aCenterPos,
  204. aRadius + (aThickness / 2), slice );
  205. std::vector<S3D_VERTEX> coords;
  206. coords.resize( 4 );
  207. std::vector <CPolyPt> inner_cornerBuffer;
  208. if( aThickness ) // build the the vertical inner polygon (hole)
  209. TransformCircleToPolygon( inner_cornerBuffer, aCenterPos,
  210. aRadius - (aThickness / 2), slice );
  211. if( aHeight )
  212. {
  213. // Draw the vertical outer side
  214. Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer,
  215. aHeight, aZpos, false, aBiuTo3DUnits );
  216. if( aThickness )
  217. // Draws the vertical inner side (hole)
  218. Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer,
  219. aHeight, aZpos, true, aBiuTo3DUnits );
  220. }
  221. if( aThickness )
  222. {
  223. // draw top (front) and bottom (back) horizontal sides (rings)
  224. SetNormalZpos();
  225. outer_cornerBuffer.insert( outer_cornerBuffer.end(),
  226. inner_cornerBuffer.begin(), inner_cornerBuffer.end() );
  227. std::vector<CPolyPt> polygon;
  228. ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
  229. // draw top (front) horizontal ring
  230. Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits );
  231. if( aHeight )
  232. {
  233. // draw bottom (back) horizontal ring
  234. SetNormalZneg();
  235. Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, 0, aBiuTo3DUnits );
  236. }
  237. }
  238. SetNormalZpos();
  239. }
  240. /*
  241. * Function Draw3D_ZaxisOblongCylinder:
  242. * draw a segment with an oblong hole.
  243. * Used to draw oblong holes
  244. * If aHeight = height of the cylinder is 0, only one ring will be drawn
  245. * If aThickness = 0, only one cylinder will be drawn
  246. */
  247. void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
  248. int aRadius, int aHeight, int aThickness,
  249. int aZpos, double aBiuTo3DUnits )
  250. {
  251. const int slice = SEGM_PER_CIRCLE;
  252. // Build the points to approximate oblong cylinder by segments
  253. std::vector <CPolyPt> outer_cornerBuffer;
  254. int segm_width = (aRadius * 2) + aThickness;
  255. TransformRoundedEndsSegmentToPolygon( outer_cornerBuffer, aAxis1Pos,
  256. aAxis2Pos, slice, segm_width );
  257. // Draw the oblong outer cylinder
  258. if( aHeight )
  259. Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer, aHeight, aZpos,
  260. false, aBiuTo3DUnits );
  261. if( aThickness )
  262. {
  263. std::vector <CPolyPt> inner_cornerBuffer;
  264. segm_width = aRadius * 2;
  265. TransformRoundedEndsSegmentToPolygon( inner_cornerBuffer, aAxis1Pos,
  266. aAxis2Pos, slice, segm_width );
  267. // Draw the oblong inner cylinder
  268. if( aHeight )
  269. Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer, aHeight,
  270. aZpos, true, aBiuTo3DUnits );
  271. // Build the horizontal full polygon shape
  272. // (outer polygon shape - inner polygon shape)
  273. outer_cornerBuffer.insert( outer_cornerBuffer.end(),
  274. inner_cornerBuffer.begin(),
  275. inner_cornerBuffer.end() );
  276. std::vector<CPolyPt> polygon;
  277. ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
  278. // draw top (front) horizontal side (ring)
  279. SetNormalZpos();
  280. Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits );
  281. if( aHeight )
  282. {
  283. // draw bottom (back) horizontal side (ring)
  284. SetNormalZneg();
  285. Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, 0, aBiuTo3DUnits );
  286. }
  287. }
  288. SetNormalZpos();
  289. }
  290. /* draw a thick segment using 3D primitives, in a XY plane
  291. * wxPoint aStart, wxPoint aEnd = YX position of end in board units
  292. * aWidth = width of segment in board units
  293. * aThickness = thickness of segment in board units
  294. * aZpos = z position of segment in board units
  295. */
  296. void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
  297. int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits )
  298. {
  299. std::vector <CPolyPt> cornerBuffer;
  300. const int slice = SEGM_PER_CIRCLE;
  301. TransformRoundedEndsSegmentToPolygon( cornerBuffer, aStart, aEnd, slice, aWidth );
  302. Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits );
  303. }
  304. void Draw3D_ArcSegment( const wxPoint& aCenterPos, const wxPoint& aStartPoint,
  305. int aArcAngle, int aWidth, int aThickness,
  306. int aZpos, double aBiuTo3DUnits )
  307. {
  308. const int slice = SEGM_PER_CIRCLE;
  309. std::vector <CPolyPt> cornerBuffer;
  310. TransformArcToPolygon( cornerBuffer, aCenterPos, aStartPoint, aArcAngle,
  311. slice, aWidth );
  312. Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits );
  313. }
  314. // /////////////////////////////////////////////////////////////////////////////
  315. // GLU_TESS CALLBACKS
  316. // /////////////////////////////////////////////////////////////////////////////
  317. void CALLBACK tessBeginCB( GLenum which )
  318. {
  319. glBegin( which );
  320. }
  321. void CALLBACK tessEndCB()
  322. {
  323. glEnd();
  324. }
  325. void CALLBACK tessCPolyPt2Vertex( const GLvoid* data )
  326. {
  327. // cast back to double type
  328. const CPolyPt* ptr = (const CPolyPt*) data;
  329. glVertex3d( ptr->x * g_Parm_3D_Visu.m_BiuTo3Dunits,
  330. -ptr->y * g_Parm_3D_Visu.m_BiuTo3Dunits,
  331. g_Parm_3D_Visu.m_CurrentZpos );
  332. }
  333. void CALLBACK tessErrorCB( GLenum errorCode )
  334. {
  335. #if defined(DEBUG)
  336. const GLubyte* errorStr;
  337. errorStr = gluErrorString( errorCode );
  338. // DEBUG //
  339. D( printf( "Tess ERROR: %s\n", errorStr ); )
  340. #endif
  341. }