diff --git a/3d-viewer/3d_draw.cpp b/3d-viewer/3d_draw.cpp index 4f0301e449..4317241ec9 100644 --- a/3d-viewer/3d_draw.cpp +++ b/3d-viewer/3d_draw.cpp @@ -30,7 +30,6 @@ #include #include #include -//include #include #include @@ -40,9 +39,8 @@ #include #include #include -#include -//#include #include +#include #include <3d_viewer.h> #include @@ -51,15 +49,21 @@ // angle increment to draw a circle, approximated by segments #define ANGLE_INC(x) (3600/(x)) +// Thickness of copper +// TODO: use the actual copper thickness to draw copper items with thickness +#define COPPER_THICKNESS (int)(0.035 * IU_PER_MM) + extern void CheckGLError(); -/* draw a segment using 3D primitives, in a XY plane - * aStartPos = 3D position of the starting point (3D units) - * aEndx, aEndy = 3D position of the ending point (3D units) - * aWidth = width of the segment (3D units) +/* draw a thick segment using 3D primitives, in a XY plane + * wxPoint aStart, wxPoint aEnd = YX position of ends in board units + * aWidth = width of segment in board units + * aThickness = thickness of segment in board units + * aZpos = z position of segment in board units */ -static void Draw3D_FilledSegment( const S3D_VERTEX& aStartPos, - double aEndx, double aEndy, double aWidth ); +static void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd, + int aWidth, int aThickness, int aZpos, + double aBiuTo3DUnits ); static void Draw3D_SegmentWithHole( double startx, double starty, double endx, double endy, @@ -116,12 +120,12 @@ static void Draw3D_ZaxisCylinder( const S3D_VERTEX& aPos, double aRadius, dou /* draw an oblong cylinder (oblong hole) using 3D primitives. * the cylinder axis are parallel to the Z axis - * aCenterPos = 3D position of the first axis cylinder + * aStartPos = 3D position of the first axis cylinder * aEndx, aEndy = 3D position of the second axis cylinder * aRadius = radius of the cylinder (3D units) * aHeight = height of the cylinder (3D units) */ -static void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos, +static void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aStartPos, double aEndx, double aEndy, double aRadius, double aHeight ); @@ -410,8 +414,7 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone ) if( layer == LAST_COPPER_LAYER ) layer = g_Parm_3D_Visu.m_CopperLayersCount - 1; - double zpos = g_Parm_3D_Visu.m_LayerZcoord[layer]; - double width = aZone->m_ZoneMinThickness * g_Parm_3D_Visu.m_BoardScale; + int zpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale ); SetGLColor( color ); glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) ); @@ -428,14 +431,10 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone ) { // segments are used to fill areas for( unsigned iseg = 0; iseg < aZone->m_FillSegmList.size(); iseg++ ) - { - double ox = aZone->m_FillSegmList[iseg].m_Start.x * g_Parm_3D_Visu.m_BoardScale; - double oy = aZone->m_FillSegmList[iseg].m_Start.y * g_Parm_3D_Visu.m_BoardScale; - double fx = aZone->m_FillSegmList[iseg].m_End.x * g_Parm_3D_Visu.m_BoardScale; - double fy = aZone->m_FillSegmList[iseg].m_End.y * g_Parm_3D_Visu.m_BoardScale; - S3D_VERTEX pos( ox, -oy, zpos ); - Draw3D_FilledSegment( pos, fx, -fy, width ); - } + Draw3D_SolidSegment( aZone->m_FillSegmList[iseg].m_Start, + aZone->m_FillSegmList[iseg].m_End, + aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos, + g_Parm_3D_Visu.m_BoardScale ); } // Draw copper area outlines @@ -458,12 +457,11 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone ) if( begincorner->m_utility == 0 ) { // Draw only basic outlines, not extra segments - double ox = begincorner->x * g_Parm_3D_Visu.m_BoardScale; - double oy = begincorner->y * g_Parm_3D_Visu.m_BoardScale; - double fx = endcorner->x * g_Parm_3D_Visu.m_BoardScale; - double fy = endcorner->y * g_Parm_3D_Visu.m_BoardScale; - S3D_VERTEX pos( ox, -oy, zpos ); - Draw3D_FilledSegment( pos, fx, -fy, width ); + wxPoint start( begincorner->x, begincorner->y ); + wxPoint end( endcorner->x, endcorner->y ); + Draw3D_SolidSegment( start, end, + aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos, + g_Parm_3D_Visu.m_BoardScale ); } if( (endcorner->end_contour) || (ic == imax) ) @@ -472,12 +470,11 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone ) if( endcorner->m_utility == 0 ) { // Draw only basic outlines, not extra segments - double ox = endcorner->x * g_Parm_3D_Visu.m_BoardScale; - double oy = endcorner->y * g_Parm_3D_Visu.m_BoardScale; - double fx = firstcorner->x * g_Parm_3D_Visu.m_BoardScale; - double fy = firstcorner->y * g_Parm_3D_Visu.m_BoardScale; - S3D_VERTEX pos( ox, -oy, zpos ); - Draw3D_FilledSegment( pos, fx, -fy, width ); + wxPoint start( endcorner->x, endcorner->y ); + wxPoint end( firstcorner->x, firstcorner->y ); + Draw3D_SolidSegment( start, end, + aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos, + g_Parm_3D_Visu.m_BoardScale ); } ic++; @@ -632,19 +629,14 @@ void EDA_3D_CANVAS::Draw3D_Track( TRACK* aTrack ) if( layer == LAST_COPPER_LAYER ) layer = g_Parm_3D_Visu.m_CopperLayersCount - 1; - double zpos = g_Parm_3D_Visu.m_LayerZcoord[layer]; + int zpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale ); SetGLColor( color ); glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 ); - double w = aTrack->m_Width * g_Parm_3D_Visu.m_BoardScale; - double ox = aTrack->m_Start.x * g_Parm_3D_Visu.m_BoardScale; - double oy = aTrack->m_Start.y * g_Parm_3D_Visu.m_BoardScale; - double fx = aTrack->m_End.x * g_Parm_3D_Visu.m_BoardScale; - double fy = aTrack->m_End.y * g_Parm_3D_Visu.m_BoardScale; - - S3D_VERTEX pos( ox, -oy, zpos ); - Draw3D_FilledSegment( pos, fx, -fy, w ); + Draw3D_SolidSegment( aTrack->m_Start, aTrack->m_End, + aTrack->m_Width, COPPER_THICKNESS, zpos, + g_Parm_3D_Visu.m_BoardScale ); } @@ -801,10 +793,11 @@ void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment ) break; default: - { - S3D_VERTEX pos( x, -y, zpos ); - Draw3D_FilledSegment( pos, xf, -yf, w ); - } + Draw3D_SolidSegment( segment->GetStart(), segment->GetEnd(), + segment->GetWidth(), COPPER_THICKNESS, + KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / + g_Parm_3D_Visu.m_BoardScale ), + g_Parm_3D_Visu.m_BoardScale ); break; } } @@ -833,10 +826,11 @@ void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment ) break; default: - { - S3D_VERTEX pos( x, -y, zpos ); - Draw3D_FilledSegment( pos , xf, -yf, w ); - } + Draw3D_SolidSegment( segment->GetStart(), segment->GetEnd(), + segment->GetWidth(), COPPER_THICKNESS, + KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / + g_Parm_3D_Visu.m_BoardScale ), + g_Parm_3D_Visu.m_BoardScale ); break; } } @@ -847,18 +841,14 @@ void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment ) // These variables are used in Draw3dTextSegm. // But Draw3dTextSegm is a call back function, so we cannot send them as arguments, // so they are static. -static double s_Text3DWidth, s_Text3DZPos; +int s_Text3DWidth, s_Text3DZPos; // This is a call back function, used by DrawGraphicText to draw the 3D text shape: static void Draw3dTextSegm( int x0, int y0, int xf, int yf ) { - double startx = x0 * g_Parm_3D_Visu.m_BoardScale; - double starty = y0 * g_Parm_3D_Visu.m_BoardScale; - double endx = xf * g_Parm_3D_Visu.m_BoardScale; - double endy = yf * g_Parm_3D_Visu.m_BoardScale; - - S3D_VERTEX pos( startx, -starty, s_Text3DZPos ); - Draw3D_FilledSegment( pos, endx, -endy, s_Text3DWidth ); + Draw3D_SolidSegment( wxPoint( x0, y0), wxPoint( xf, yf ), + s_Text3DWidth, COPPER_THICKNESS, s_Text3DZPos, + g_Parm_3D_Visu.m_BoardScale ); } @@ -868,8 +858,8 @@ void EDA_3D_CANVAS::Draw3D_DrawText( TEXTE_PCB* text ) int color = g_ColorsSettings.GetLayerColor( layer ); SetGLColor( color ); - s_Text3DZPos = g_Parm_3D_Visu.m_LayerZcoord[layer]; - s_Text3DWidth = text->GetThickness() * g_Parm_3D_Visu.m_BoardScale; + s_Text3DZPos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale ); + s_Text3DWidth = text->GetThickness(); glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) ); wxSize size = text->m_Size; @@ -993,7 +983,7 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas ) { wxString s; int dx, dy; - double x, y, fx, fy, w, zpos; + double x, y, fx, fy, w; if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( m_Layer ) == false ) return; @@ -1016,27 +1006,26 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas ) for( int layer = 0; layer < g_Parm_3D_Visu.m_CopperLayersCount; layer++ ) { glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 ); - zpos = g_Parm_3D_Visu.m_LayerZcoord[layer]; + int izpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale ); switch( m_Shape ) { case S_SEGMENT: - { - S3D_VERTEX pos( x, -y, zpos ); - Draw3D_FilledSegment( pos, fx, -fy, w ); - } + Draw3D_SolidSegment( m_Start, m_End, m_Width, + COPPER_THICKNESS, izpos, + g_Parm_3D_Visu.m_BoardScale ); break; case S_CIRCLE: { - S3D_VERTEX pos( x, -y, zpos ); + S3D_VERTEX pos( x, -y, g_Parm_3D_Visu.m_LayerZcoord[layer] ); Draw3D_ThickCircle( pos, hypot( x - fx, y - fy ), w); } break; case S_ARC: { - S3D_VERTEX pos( fx, -fy, zpos ); + S3D_VERTEX pos( fx, -fy, g_Parm_3D_Visu.m_LayerZcoord[layer] ); Draw3D_ArcSegment( pos, x, -y, (double) m_Angle, w ); } break; @@ -1059,7 +1048,7 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas ) pt += module->m_Pos; } - glcanvas->Draw3D_Polygon( points, zpos ); + glcanvas->Draw3D_Polygon( points, g_Parm_3D_Visu.m_LayerZcoord[layer] ); } break; @@ -1073,27 +1062,26 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas ) else { glNormal3f( 0.0, 0.0, (m_Layer == LAYER_N_BACK) ? -1.0 : 1.0 ); - zpos = g_Parm_3D_Visu.m_LayerZcoord[m_Layer]; + int izpos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[m_Layer] / g_Parm_3D_Visu.m_BoardScale ); switch( m_Shape ) { case S_SEGMENT: - { - S3D_VERTEX pos( x, -y, zpos ); - Draw3D_FilledSegment( pos, fx, -fy, w ); - } + Draw3D_SolidSegment( m_Start, m_End, m_Width, + COPPER_THICKNESS, izpos, + g_Parm_3D_Visu.m_BoardScale ); break; case S_CIRCLE: { - S3D_VERTEX pos( x, -y, zpos ); + S3D_VERTEX pos( x, -y, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] ); Draw3D_ThickCircle( pos, hypot( x - fx, y - fy ), w ); } break; case S_ARC: { - S3D_VERTEX pos( fx, -fy, zpos ); + S3D_VERTEX pos( fx, -fy, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] ); Draw3D_ArcSegment( pos, x, -y, (double) m_Angle, w ); } break; @@ -1116,7 +1104,7 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas ) pt += module->m_Pos; } - glcanvas->Draw3D_Polygon( points, zpos ); + glcanvas->Draw3D_Polygon( points, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] ); } break; @@ -1460,57 +1448,73 @@ static void Draw3D_ZaxisCylinder( const S3D_VERTEX& aPos, double aRadius, double } -// Draw a polygon similar to a segment has rounded tips -static void Draw3D_FilledSegment( const S3D_VERTEX& aStartPos, - double aEndx, double aEndy, double aWidth ) +/* draw a thick segment using 3D primitives, in a XY plane + * wxPoint aStart, wxPoint aEnd = YX position of end in board units + * aWidth = width of segment in board units + * aThickness = thickness of segment in board units + * aZpos = z position of segment in board units + */ +void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd, + int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits ) { - double dx, dy, x, y, firstx = 0, firsty = 0; - int ii, angle; - - // Calculate the coordinates of the segment assumed horizontal. - // Then turn the strips of the desired angle. - dx = aEndx - aStartPos.x; - dy = aEndy - aStartPos.y; - angle = (int) ( ( atan2( dy, dx ) * 1800 / M_PI ) + 0.5 ); - - RotatePoint( &dx, &dy, angle ); - aWidth /= 2; - - glBegin( GL_POLYGON ); - - // Trace the flare to right (1st half polygon at the end of the segment) + std::vector cornerBuffer; const int slice = 16; - for( ii = 0; ii <= slice/2; ii++ ) - { - x = 0.0; - y = -aWidth; - RotatePoint( &x, &y, -ii * ANGLE_INC(slice) ); - x += dx; - RotatePoint( &x, &y, -angle ); - glVertex3f( aStartPos.x + x, aStartPos.y + y, aStartPos.z ); + TransformRoundedEndsSegmentToPolygon(cornerBuffer, aStart, aEnd, slice, aWidth ); - if( ii == 0 ) - { - firstx = aStartPos.x + x; - firsty = aStartPos.y + y; - } + //Build the 3D data : upper side then lower side + double zupperpos = ( aZpos + (aThickness/2) ) * aBiuTo3DUnits; + double zlowerpos = ( aZpos - (aThickness/2) ) * aBiuTo3DUnits; + if( aThickness ) + glNormal3f( 0.0, 0.0, 1.0 ); // Normale is Z axis + + double zpos = zupperpos; // start with upper side + for( int face = 0; face < 2; face ++ ) + { + glBegin( GL_POLYGON ); + for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ ) + glVertex3f( cornerBuffer[ii].x * aBiuTo3DUnits, + - cornerBuffer[ii].y * aBiuTo3DUnits, zpos ); + + // Close the polygon shape + glVertex3f( cornerBuffer[0].x * aBiuTo3DUnits, + - cornerBuffer[0].y * aBiuTo3DUnits, zpos ); + glEnd(); + + if( aThickness == 0 ) + return; + + // Prepare the creation of lower side + glNormal3f( 0.0, 0.0, -1.0 ); // Normale now is -Z axis + zpos = zlowerpos; } - // Rounding the left (2nd half polygon is the origin of the segment) - for( ii = 0; ii <= slice/2; ii++ ) + //Build the 3D data : vertical sides + std::vector< S3D_VERTEX > vertices; + vertices.resize(4); + for( int ii = 0; ii < slice; ii++ ) { - int jj = ii * ANGLE_INC(slice); - x = 0.0; - y = aWidth; - RotatePoint( &x, &y, -angle - jj ); - glVertex3f( aStartPos.x + x, aStartPos.y + y, aStartPos.z ); + int jj = ii+1; + if( jj >=slice ) + jj = 0; + vertices[0].x = cornerBuffer[ii].x; + vertices[0].y = -cornerBuffer[ii].y; + vertices[0].z = aZpos + (aThickness/2); + vertices[1].x = cornerBuffer[ii].x; + vertices[1].y = -cornerBuffer[ii].y; + vertices[1].z = aZpos - (aThickness/2); + vertices[2].x = cornerBuffer[jj].x; + vertices[2].y = -cornerBuffer[jj].y; + vertices[2].z = aZpos - (aThickness/2); + vertices[3].x = cornerBuffer[jj].x; + vertices[3].y = -cornerBuffer[jj].y; + vertices[3].z = aZpos + (aThickness/2); + + Set_Object_Data( vertices ); } - glVertex3f( firstx, firsty, aStartPos.z ); - glEnd(); + glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis } - /* Draw a polygon similar to a segment ends with round hole */ static void Draw3D_SegmentWithHole( double startx, double starty, @@ -1646,7 +1650,7 @@ static void Draw3D_ThickCircle( const S3D_VERTEX& aCenterPos, double aRadius, do * draw a segment with an oblong hole. * Used to draw oblong holes */ -void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos, +void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aStartPos, double aEndx, double aEndy, double aRadius, double aHeight ) { @@ -1659,20 +1663,20 @@ void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos, DataScale3D = 1.0; // Coordinate is already in range for Set_Object_Data(); - double dx = aEndx - aCenterPos.x; - double dy = aEndy - aCenterPos.y; + double dx = aEndx - aStartPos.x; + double dy = aEndy - aStartPos.y; double angle = atan2( dy, dx ) * 1800 / M_PI; dx = 0; dy = aRadius; // draws the first rectangle between half cylinders RotatePoint( &dx, &dy, angle ); - coords[0].x = coords[1].x = aCenterPos.x + dx; - coords[0].y = coords[1].y = aCenterPos.y - dy; + coords[0].x = coords[1].x = aStartPos.x + dx; + coords[0].y = coords[1].y = aStartPos.y - dy; coords[2].x = coords[3].x = aEndx + dx; coords[2].y = coords[3].y = aEndy - dy; - coords[0].z = coords[3].z = aCenterPos.z; - coords[1].z = coords[2].z = aCenterPos.z + aHeight; + coords[0].z = coords[3].z = aStartPos.z; + coords[1].z = coords[2].z = aStartPos.z + aHeight; Set_Object_Data( coords ); // Draw the first half cylinder @@ -1693,10 +1697,10 @@ void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos, // draws the second rectangle between half cylinders coords[0].x = coords[1].x = aEndx - dx; coords[0].y = coords[1].y = aEndy + dy; - coords[2].x = coords[3].x = aCenterPos.x - dx; - coords[2].y = coords[3].y = aCenterPos.y + dy; - coords[0].z = coords[3].z = aCenterPos.z; - coords[1].z = coords[2].z = aCenterPos.z + aHeight; + coords[2].x = coords[3].x = aStartPos.x - dx; + coords[2].y = coords[3].y = aStartPos.y + dy; + coords[0].z = coords[3].z = aStartPos.z; + coords[1].z = coords[2].z = aStartPos.z + aHeight; Set_Object_Data( coords ); // Draw the second half cylinder @@ -1709,8 +1713,8 @@ void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos, coords[0].y = coords[2].y; coords[1].x = coords[3].x; coords[1].y = coords[3].y; - coords[2].x = coords[3].x = aCenterPos.x + ddx; - coords[2].y = coords[3].y = aCenterPos.y + ddy; + coords[2].x = coords[3].x = aStartPos.x + ddx; + coords[2].y = coords[3].y = aStartPos.y + ddy; Set_Object_Data( coords ); } diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a5b4c5e417..5137f20b28 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -87,6 +87,7 @@ set(PCB_COMMON_SRCS base_screen.cpp eda_text.cpp class_page_info.cpp + convert_basic_shapes_to_polygon.cpp pcbcommon.cpp footprint_info.cpp ../pcbnew/basepcbframe.cpp diff --git a/common/convert_basic_shapes_to_polygon.cpp b/common/convert_basic_shapes_to_polygon.cpp new file mode 100644 index 0000000000..1e6e7766a9 --- /dev/null +++ b/common/convert_basic_shapes_to_polygon.cpp @@ -0,0 +1,196 @@ +/** + * @file convert_basic_shapes_to_polygon.cpp + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include + +#include +#include +#include +#include + +/** + * Function TransformCircleToPolygon + * convert a circle to a polygon, using multiple straight lines + * @param aCornerBuffer = a buffer to store the polygon + * @param aCenter = the center of the circle + * @param aRadius = the radius of the circle + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * Note: the polygon is inside the circle, so if you want to have the polygon + * outside the circle, you should give aRadius calculated with a corrrection factor + */ +void TransformCircleToPolygon( std::vector & aCornerBuffer, + wxPoint aCenter, int aRadius, + int aCircleToSegmentsCount ) +{ + wxPoint corner_position; + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + int halfstep = 1800 / aCircleToSegmentsCount; // the starting value for rot angles + + for( int ii = 0; ii < aCircleToSegmentsCount; ii++ ) + { + corner_position.x = aRadius; + corner_position.y = 0; + int angle = (ii * delta) + halfstep; + RotatePoint( &corner_position.x, &corner_position.y, angle ); + corner_position += aCenter; + CPolyPt polypoint( corner_position.x, corner_position.y ); + aCornerBuffer.push_back( polypoint ); + } + + aCornerBuffer.back().end_contour = true; +} + + +/** + * Function TransformRoundedEndsSegmentToPolygon + * convert a segment with rounded ends to a polygon + * Convert arcs to multiple straight lines + * @param aCornerBuffer = a buffer to store the polygon + * @param aStart = the segment start point coordinate + * @param aEnd = the segment end point coordinate + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = the segment width + * Note: the polygon is inside the arc ends, so if you want to have the polygon + * outside the circle, you should give aStart and aEnd calculated with a correction factor + */ +void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, + wxPoint aStart, wxPoint aEnd, + int aCircleToSegmentsCount, + int aWidth ) +{ + int radius = aWidth / 2; + wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) + wxPoint startp = aStart; + wxPoint corner; + int seg_len; + CPolyPt polypoint; + + // normalize the position in order to have endp.x >= 0; + if( endp.x < 0 ) + { + endp = aStart - aEnd; + startp = aEnd; + } + + int delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees + seg_len = (int) sqrt( ( (double) endp.y * endp.y ) + ( (double) endp.x * endp.x ) ); + + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + + // Compute the outlines of the segment, and creates a polygon + // add right rounded end: + for( int ii = 0; ii < 1800; ii += delta ) + { + corner = wxPoint( 0, radius ); + RotatePoint( &corner, ii ); + corner.x += seg_len; + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); + } + + // Finish arc: + corner = wxPoint( seg_len, -radius ); + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); + + // add left rounded end: + for( int ii = 0; ii < 1800; ii += delta ) + { + corner = wxPoint( 0, -radius ); + RotatePoint( &corner, ii ); + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); + } + + // Finish arc: + corner = wxPoint( 0, radius ); + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.push_back( polypoint ); + + aCornerBuffer.back().end_contour = true; +} + + +/** + * Function TransformArcToPolygon + * Creates a polygon from an Arc + * Convert arcs to multiple straight segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aCentre = centre of the arc or circle + * @param aStart = start point of the arc, or a point on the circle + * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600 + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = width (thickness) of the line + */ +void TransformArcToPolygon( std::vector & aCornerBuffer, + wxPoint aCentre, wxPoint aStart, int aArcAngle, + int aCircleToSegmentsCount, int aWidth ) +{ + wxPoint arc_start, arc_end; + int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree + + arc_end = arc_start = aStart; + + if( aArcAngle != 3600 ) + { + RotatePoint( &arc_end, aCentre, -aArcAngle ); + } + + if( aArcAngle < 0 ) + { + EXCHG( arc_start, arc_end ); + NEGATE( aArcAngle ); + } + + // Compute the ends of segments and creates poly + wxPoint curr_end = arc_start; + wxPoint curr_start = arc_start; + + for( int ii = delta; ii < aArcAngle; ii += delta ) + { + curr_end = arc_start; + RotatePoint( &curr_end, aCentre, -ii ); + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end, + aCircleToSegmentsCount, aWidth ); + curr_start = curr_end; + } + + if( curr_end != arc_end ) + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, + curr_end, arc_end, aCircleToSegmentsCount, aWidth ); +} diff --git a/include/convert_basic_shapes_to_polygon.h b/include/convert_basic_shapes_to_polygon.h new file mode 100644 index 0000000000..18fa2b8d39 --- /dev/null +++ b/include/convert_basic_shapes_to_polygon.h @@ -0,0 +1,85 @@ +/** + * @file convert_basic_shapes_to_polygon.h + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef CONVERT_BASIC_SHAPES_TO_POLYGON_H +#define CONVERT_BASIC_SHAPES_TO_POLYGON_H + +#include + +#include +#include +#include +#include + +/** + * Function TransformCircleToPolygon + * convert a circle to a polygon, using multiple straight lines + * @param aCornerBuffer = a buffer to store the polygon + * @param aCenter = the center of the circle + * @param aRadius = the radius of the circle + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * Note: the polygon is inside the circle, so if you want to have the polygon + * outside the circle, you should give aRadius calculated with a correction factor + */ +void TransformCircleToPolygon( std::vector & aCornerBuffer, + wxPoint aCenter, int aRadius, + int aCircleToSegmentsCount ); + +/** + * Function TransformRoundedEndsSegmentToPolygon + * convert a segment with rounded ends to a polygon + * Convert arcs to multiple straight lines + * @param aCornerBuffer = a buffer to store the polygon + * @param aStart = the segment start point coordinate + * @param aEnd = the segment end point coordinate + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = the segment width + * Note: the polygon is inside the arc ends, so if you want to have the polygon + * outside the circle, you should give aStart and aEnd calculated with a correction factor + */ +void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, + wxPoint aStart, wxPoint aEnd, + int aCircleToSegmentsCount, + int aWidth ); + + +/** + * Function TransformArcToPolygon + * Creates a polygon from an Arc + * Convert arcs to multiple straight segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aCentre = centre of the arc or circle + * @param aStart = start point of the arc, or a point on the circle + * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600 + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = width (thickness) of the line + */ +void TransformArcToPolygon( std::vector & aCornerBuffer, + wxPoint aCentre, wxPoint aStart, int aArcAngle, + int aCircleToSegmentsCount, int aWidth ); + +#endif // CONVERT_BASIC_SHAPES_TO_POLYGON_H diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 4598512775..1e4b6dc8c5 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -19,75 +19,7 @@ #include #include #include - - -// Exported functions - -/** - * Function TransformRoundedEndsSegmentToPolygon - * convert a segment with rounded ends to a polygon - * Convert arcs to multiple straight lines - * @param aCornerBuffer = a buffer to store the polygon - * @param aStart = the segment start point coordinate - * @param aEnd = the segment end point coordinate - * @param aCircleToSegmentsCount = the number of segments to approximate a circle - * @param aWidth = the segment width - */ -void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, - wxPoint aStart, wxPoint aEnd, - int aCircleToSegmentsCount, - int aWidth ); - - -/** - * Function TransformArcToPolygon - * Creates a polygon from an Arc - * Convert arcs to multiple straight segments - * @param aCornerBuffer = a buffer to store the polygon - * @param aCentre = centre of the arc or circle - * @param aStart = start point of the arc, or a point on the circle - * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600 - * @param aCircleToSegmentsCount = the number of segments to approximate a circle - * @param aWidth = width (thickness) of the line - */ -void TransformArcToPolygon( std::vector & aCornerBuffer, - wxPoint aCentre, wxPoint aStart, int aArcAngle, - int aCircleToSegmentsCount, int aWidth ) -{ - wxPoint arc_start, arc_end; - int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree - - arc_end = arc_start = aStart; - - if( aArcAngle != 3600 ) - { - RotatePoint( &arc_end, aCentre, -aArcAngle ); - } - - if( aArcAngle < 0 ) - { - EXCHG( arc_start, arc_end ); - NEGATE( aArcAngle ); - } - - // Compute the ends of segments and creates poly - wxPoint curr_end = arc_start; - wxPoint curr_start = arc_start; - - for( int ii = delta; ii < aArcAngle; ii += delta ) - { - curr_end = arc_start; - RotatePoint( &curr_end, aCentre, -ii ); - TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end, - aCircleToSegmentsCount, aWidth ); - curr_start = curr_end; - } - - if( curr_end != arc_end ) - TransformRoundedEndsSegmentToPolygon( aCornerBuffer, - curr_end, arc_end, aCircleToSegmentsCount, aWidth ); -} - +#include /** * Function TransformShapeWithClearanceToPolygon @@ -276,29 +208,14 @@ void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor int aCircleToSegmentsCount, double aCorrectionFactor ) { - wxPoint corner_position; - int ii, angle; - int dx = (m_Width / 2) + aClearanceValue; - int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree - switch( Type() ) { case PCB_VIA_T: - dx = (int) ( dx * aCorrectionFactor ); - - for( ii = 0; ii < aCircleToSegmentsCount; ii++ ) - { - corner_position = wxPoint( dx, 0 ); - RotatePoint( &corner_position.x, &corner_position.y, (1800 / aCircleToSegmentsCount) ); - angle = ii * delta; - RotatePoint( &corner_position.x, &corner_position.y, angle ); - corner_position.x += m_Start.x; - corner_position.y += m_Start.y; - CPolyPt polypoint( corner_position.x, corner_position.y ); - aCornerBuffer.push_back( polypoint ); - } - - aCornerBuffer.back().end_contour = true; + { + int radius = (m_Width / 2) + aClearanceValue; + radius = KiROUND( radius * aCorrectionFactor ); + TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aCircleToSegmentsCount ); + } break; default: @@ -311,78 +228,6 @@ void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor } -/* Function TransformRoundedEndsSegmentToPolygon - */ -void TransformRoundedEndsSegmentToPolygon( std::vector & aCornerBuffer, - wxPoint aStart, wxPoint aEnd, - int aCircleToSegmentsCount, - int aWidth ) -{ - int radius = aWidth / 2; - wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) - wxPoint startp = aStart; - wxPoint corner; - int seg_len; - CPolyPt polypoint; - - // normalize the position in order to have endp.x >= 0; - if( endp.x < 0 ) - { - endp = aStart - aEnd; - startp = aEnd; - } - - int delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees - seg_len = (int) sqrt( ( (double) endp.y * endp.y ) + ( (double) endp.x * endp.x ) ); - - int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree - - // Compute the outlines of the segment, and creates a polygon - // add right rounded end: - for( int ii = 0; ii < 1800; ii += delta ) - { - corner = wxPoint( 0, radius ); - RotatePoint( &corner, ii ); - corner.x += seg_len; - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.push_back( polypoint ); - } - - // Finish arc: - corner = wxPoint( seg_len, -radius ); - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.push_back( polypoint ); - - // add left rounded end: - for( int ii = 0; ii < 1800; ii += delta ) - { - corner = wxPoint( 0, -radius ); - RotatePoint( &corner, ii ); - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.push_back( polypoint ); - } - - // Finish arc: - corner = wxPoint( 0, radius ); - RotatePoint( &corner, -delta_angle ); - corner += startp; - polypoint.x = corner.x; - polypoint.y = corner.y; - aCornerBuffer.push_back( polypoint ); - - aCornerBuffer.back().end_contour = true; -} - - /** * Function TransformShapeWithClearanceToPolygon * Convert the pad shape to a closed polygon @@ -401,7 +246,7 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor double aCorrectionFactor ) { wxPoint corner_position; - int ii, angle; + int angle; int dx = (m_Size.x / 2) + aClearanceValue; int dy = (m_Size.y / 2) + aClearanceValue; @@ -416,99 +261,37 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor { case PAD_CIRCLE: dx = (int) ( dx * aCorrectionFactor ); - - for( ii = 0; ii < aCircleToSegmentsCount; ii++ ) - { - corner_position = wxPoint( dx, 0 ); - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); - - // Half increment offset to get more space between - angle = ii * delta; - RotatePoint( &corner_position, angle ); - corner_position += PadShapePos; - CPolyPt polypoint( corner_position.x, corner_position.y ); - aCornerBuffer.push_back( polypoint ); - } - - aCornerBuffer.back().end_contour = true; + TransformCircleToPolygon( aCornerBuffer, PadShapePos, dx, + aCircleToSegmentsCount ); break; case PAD_OVAL: + // An oval pad has the same shape as a segment with rounded ends angle = m_Orient; - if( dy > dx ) // Oval pad X/Y ratio for choosing translation axles + { + int width; + wxPoint shape_offset; + if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis { dy = (int) ( dy * aCorrectionFactor ); - int angle_pg; // Polygon angle - wxPoint shape_offset = wxPoint( 0, dy - dx ); - - RotatePoint( &shape_offset, angle ); // Rotating shape offset vector with component - - for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Half circle end cap... - { - corner_position = wxPoint( dx, 0 ); - - // Coordinate translation +dx - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); - RotatePoint( &corner_position, angle ); - angle_pg = ii * delta; - RotatePoint( &corner_position, angle_pg ); - corner_position += PadShapePos - shape_offset; - CPolyPt polypoint( corner_position.x, corner_position.y ); - aCornerBuffer.push_back( polypoint ); - } - - for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Second half circle end cap... - { - corner_position = wxPoint( -dx, 0 ); - - // Coordinate translation -dx - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); - RotatePoint( &corner_position, angle ); - angle_pg = ii * delta; - RotatePoint( &corner_position, angle_pg ); - corner_position += PadShapePos + shape_offset; - CPolyPt polypoint( corner_position.x, corner_position.y ); - aCornerBuffer.push_back( polypoint ); - } - - aCornerBuffer.back().end_contour = true; - break; + shape_offset.y = dy - dx; + width = dx * 2; } else //if( dy <= dx ) { dx = (int) ( dx * aCorrectionFactor ); - int angle_pg; // Polygon angle - wxPoint shape_offset = wxPoint( (dy - dx), 0 ); - RotatePoint( &shape_offset, angle ); - - for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) - { - corner_position = wxPoint( 0, dy ); - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); - RotatePoint( &corner_position, angle ); - angle_pg = ii * delta; - RotatePoint( &corner_position, angle_pg ); - corner_position += PadShapePos - shape_offset; - CPolyPt polypoint( corner_position.x, corner_position.y ); - aCornerBuffer.push_back( polypoint ); - } - - for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) - { - corner_position = wxPoint( 0, -dy ); - RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) ); - RotatePoint( &corner_position, angle ); - angle_pg = ii * delta; - RotatePoint( &corner_position, angle_pg ); - corner_position += PadShapePos + shape_offset; - CPolyPt polypoint( corner_position.x, corner_position.y ); - aCornerBuffer.push_back( polypoint ); - } - - aCornerBuffer.back().end_contour = true; - break; + shape_offset.x = dy - dx; + width = dy * 2; } + RotatePoint( &shape_offset, angle ); + wxPoint start = PadShapePos - shape_offset; + wxPoint end = PadShapePos + shape_offset; + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, start, end, + aCircleToSegmentsCount, width ); + } + break; + default: case PAD_TRAPEZOID: psize.x += ABS( m_DeltaSize.y );