3d-viewer and polygon functions: remove duplicate code about conversion from basic shapes to polygons.

This commit is contained in:
jean-pierre charras 2012-08-23 21:15:58 +02:00
parent 2dbb428b23
commit b19cc14ef2
5 changed files with 443 additions and 374 deletions

View File

@ -30,7 +30,6 @@
#include <trigo.h> #include <trigo.h>
#include <pcbstruct.h> #include <pcbstruct.h>
#include <drawtxt.h> #include <drawtxt.h>
//include <confirm.h>
#include <layers_id_colors_and_visibility.h> #include <layers_id_colors_and_visibility.h>
#include <class_board.h> #include <class_board.h>
@ -40,9 +39,8 @@
#include <class_zone.h> #include <class_zone.h>
#include <class_drawsegment.h> #include <class_drawsegment.h>
#include <class_pcb_text.h> #include <class_pcb_text.h>
#include <class_board_design_settings.h>
//#include <class_marker_pcb.h>
#include <colors_selection.h> #include <colors_selection.h>
#include <convert_basic_shapes_to_polygon.h>
#include <3d_viewer.h> #include <3d_viewer.h>
#include <info3d_visu.h> #include <info3d_visu.h>
@ -51,15 +49,21 @@
// angle increment to draw a circle, approximated by segments // angle increment to draw a circle, approximated by segments
#define ANGLE_INC(x) (3600/(x)) #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(); extern void CheckGLError();
/* draw a segment using 3D primitives, in a XY plane /* draw a thick segment using 3D primitives, in a XY plane
* aStartPos = 3D position of the starting point (3D units) * wxPoint aStart, wxPoint aEnd = YX position of ends in board units
* aEndx, aEndy = 3D position of the ending point (3D units) * aWidth = width of segment in board units
* aWidth = width of the segment (3D units) * aThickness = thickness of segment in board units
* aZpos = z position of segment in board units
*/ */
static void Draw3D_FilledSegment( const S3D_VERTEX& aStartPos, static void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
double aEndx, double aEndy, double aWidth ); int aWidth, int aThickness, int aZpos,
double aBiuTo3DUnits );
static void Draw3D_SegmentWithHole( double startx, double starty, static void Draw3D_SegmentWithHole( double startx, double starty,
double endx, double endy, 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. /* draw an oblong cylinder (oblong hole) using 3D primitives.
* the cylinder axis are parallel to the Z axis * 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 * aEndx, aEndy = 3D position of the second axis cylinder
* aRadius = radius of the cylinder (3D units) * aRadius = radius of the cylinder (3D units)
* aHeight = height 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 aEndx, double aEndy,
double aRadius, double aHeight ); double aRadius, double aHeight );
@ -410,8 +414,7 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone )
if( layer == LAST_COPPER_LAYER ) if( layer == LAST_COPPER_LAYER )
layer = g_Parm_3D_Visu.m_CopperLayersCount - 1; 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 );
double width = aZone->m_ZoneMinThickness * g_Parm_3D_Visu.m_BoardScale;
SetGLColor( color ); SetGLColor( color );
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) ); 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 // segments are used to fill areas
for( unsigned iseg = 0; iseg < aZone->m_FillSegmList.size(); iseg++ ) for( unsigned iseg = 0; iseg < aZone->m_FillSegmList.size(); iseg++ )
{ Draw3D_SolidSegment( aZone->m_FillSegmList[iseg].m_Start,
double ox = aZone->m_FillSegmList[iseg].m_Start.x * g_Parm_3D_Visu.m_BoardScale; aZone->m_FillSegmList[iseg].m_End,
double oy = aZone->m_FillSegmList[iseg].m_Start.y * g_Parm_3D_Visu.m_BoardScale; aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos,
double fx = aZone->m_FillSegmList[iseg].m_End.x * g_Parm_3D_Visu.m_BoardScale; 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 );
}
} }
// Draw copper area outlines // Draw copper area outlines
@ -458,12 +457,11 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone )
if( begincorner->m_utility == 0 ) if( begincorner->m_utility == 0 )
{ {
// Draw only basic outlines, not extra segments // Draw only basic outlines, not extra segments
double ox = begincorner->x * g_Parm_3D_Visu.m_BoardScale; wxPoint start( begincorner->x, begincorner->y );
double oy = begincorner->y * g_Parm_3D_Visu.m_BoardScale; wxPoint end( endcorner->x, endcorner->y );
double fx = endcorner->x * g_Parm_3D_Visu.m_BoardScale; Draw3D_SolidSegment( start, end,
double fy = endcorner->y * g_Parm_3D_Visu.m_BoardScale; aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos,
S3D_VERTEX pos( ox, -oy, zpos ); g_Parm_3D_Visu.m_BoardScale );
Draw3D_FilledSegment( pos, fx, -fy, width );
} }
if( (endcorner->end_contour) || (ic == imax) ) if( (endcorner->end_contour) || (ic == imax) )
@ -472,12 +470,11 @@ void EDA_3D_CANVAS::Draw3D_Zone( ZONE_CONTAINER* aZone )
if( endcorner->m_utility == 0 ) if( endcorner->m_utility == 0 )
{ {
// Draw only basic outlines, not extra segments // Draw only basic outlines, not extra segments
double ox = endcorner->x * g_Parm_3D_Visu.m_BoardScale; wxPoint start( endcorner->x, endcorner->y );
double oy = endcorner->y * g_Parm_3D_Visu.m_BoardScale; wxPoint end( firstcorner->x, firstcorner->y );
double fx = firstcorner->x * g_Parm_3D_Visu.m_BoardScale; Draw3D_SolidSegment( start, end,
double fy = firstcorner->y * g_Parm_3D_Visu.m_BoardScale; aZone->m_ZoneMinThickness, COPPER_THICKNESS, zpos,
S3D_VERTEX pos( ox, -oy, zpos ); g_Parm_3D_Visu.m_BoardScale );
Draw3D_FilledSegment( pos, fx, -fy, width );
} }
ic++; ic++;
@ -632,19 +629,14 @@ void EDA_3D_CANVAS::Draw3D_Track( TRACK* aTrack )
if( layer == LAST_COPPER_LAYER ) if( layer == LAST_COPPER_LAYER )
layer = g_Parm_3D_Visu.m_CopperLayersCount - 1; 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 ); SetGLColor( color );
glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 ); glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
double w = aTrack->m_Width * g_Parm_3D_Visu.m_BoardScale; Draw3D_SolidSegment( aTrack->m_Start, aTrack->m_End,
double ox = aTrack->m_Start.x * g_Parm_3D_Visu.m_BoardScale; aTrack->m_Width, COPPER_THICKNESS, zpos,
double oy = aTrack->m_Start.y * g_Parm_3D_Visu.m_BoardScale; 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 );
} }
@ -801,10 +793,11 @@ void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment )
break; break;
default: default:
{ Draw3D_SolidSegment( segment->GetStart(), segment->GetEnd(),
S3D_VERTEX pos( x, -y, zpos ); segment->GetWidth(), COPPER_THICKNESS,
Draw3D_FilledSegment( pos, xf, -yf, w ); KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] /
} g_Parm_3D_Visu.m_BoardScale ),
g_Parm_3D_Visu.m_BoardScale );
break; break;
} }
} }
@ -833,10 +826,11 @@ void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment )
break; break;
default: default:
{ Draw3D_SolidSegment( segment->GetStart(), segment->GetEnd(),
S3D_VERTEX pos( x, -y, zpos ); segment->GetWidth(), COPPER_THICKNESS,
Draw3D_FilledSegment( pos , xf, -yf, w ); KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] /
} g_Parm_3D_Visu.m_BoardScale ),
g_Parm_3D_Visu.m_BoardScale );
break; break;
} }
} }
@ -847,18 +841,14 @@ void EDA_3D_CANVAS::Draw3D_DrawSegment( DRAWSEGMENT* segment )
// These variables are used in Draw3dTextSegm. // These variables are used in Draw3dTextSegm.
// But Draw3dTextSegm is a call back function, so we cannot send them as arguments, // But Draw3dTextSegm is a call back function, so we cannot send them as arguments,
// so they are static. // 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: // 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 ) static void Draw3dTextSegm( int x0, int y0, int xf, int yf )
{ {
double startx = x0 * g_Parm_3D_Visu.m_BoardScale; Draw3D_SolidSegment( wxPoint( x0, y0), wxPoint( xf, yf ),
double starty = y0 * g_Parm_3D_Visu.m_BoardScale; s_Text3DWidth, COPPER_THICKNESS, s_Text3DZPos,
double endx = xf * g_Parm_3D_Visu.m_BoardScale; 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 );
} }
@ -868,8 +858,8 @@ void EDA_3D_CANVAS::Draw3D_DrawText( TEXTE_PCB* text )
int color = g_ColorsSettings.GetLayerColor( layer ); int color = g_ColorsSettings.GetLayerColor( layer );
SetGLColor( color ); SetGLColor( color );
s_Text3DZPos = g_Parm_3D_Visu.m_LayerZcoord[layer]; s_Text3DZPos = KiROUND( g_Parm_3D_Visu.m_LayerZcoord[layer] / g_Parm_3D_Visu.m_BoardScale );
s_Text3DWidth = text->GetThickness() * g_Parm_3D_Visu.m_BoardScale; s_Text3DWidth = text->GetThickness();
glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) ); glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( layer ) );
wxSize size = text->m_Size; wxSize size = text->m_Size;
@ -993,7 +983,7 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
{ {
wxString s; wxString s;
int dx, dy; 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 ) if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( m_Layer ) == false )
return; 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++ ) 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 ); 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 ) switch( m_Shape )
{ {
case S_SEGMENT: case S_SEGMENT:
{ Draw3D_SolidSegment( m_Start, m_End, m_Width,
S3D_VERTEX pos( x, -y, zpos ); COPPER_THICKNESS, izpos,
Draw3D_FilledSegment( pos, fx, -fy, w ); g_Parm_3D_Visu.m_BoardScale );
}
break; break;
case S_CIRCLE: 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); Draw3D_ThickCircle( pos, hypot( x - fx, y - fy ), w);
} }
break; break;
case S_ARC: 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 ); Draw3D_ArcSegment( pos, x, -y, (double) m_Angle, w );
} }
break; break;
@ -1059,7 +1048,7 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
pt += module->m_Pos; pt += module->m_Pos;
} }
glcanvas->Draw3D_Polygon( points, zpos ); glcanvas->Draw3D_Polygon( points, g_Parm_3D_Visu.m_LayerZcoord[layer] );
} }
break; break;
@ -1073,27 +1062,26 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
else else
{ {
glNormal3f( 0.0, 0.0, (m_Layer == LAYER_N_BACK) ? -1.0 : 1.0 ); 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 ) switch( m_Shape )
{ {
case S_SEGMENT: case S_SEGMENT:
{ Draw3D_SolidSegment( m_Start, m_End, m_Width,
S3D_VERTEX pos( x, -y, zpos ); COPPER_THICKNESS, izpos,
Draw3D_FilledSegment( pos, fx, -fy, w ); g_Parm_3D_Visu.m_BoardScale );
}
break; break;
case S_CIRCLE: 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 ); Draw3D_ThickCircle( pos, hypot( x - fx, y - fy ), w );
} }
break; break;
case S_ARC: 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 ); Draw3D_ArcSegment( pos, x, -y, (double) m_Angle, w );
} }
break; break;
@ -1116,7 +1104,7 @@ void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
pt += module->m_Pos; pt += module->m_Pos;
} }
glcanvas->Draw3D_Polygon( points, zpos ); glcanvas->Draw3D_Polygon( points, g_Parm_3D_Visu.m_LayerZcoord[m_Layer] );
} }
break; break;
@ -1460,56 +1448,72 @@ static void Draw3D_ZaxisCylinder( const S3D_VERTEX& aPos, double aRadius, double
} }
// Draw a polygon similar to a segment has rounded tips /* draw a thick segment using 3D primitives, in a XY plane
static void Draw3D_FilledSegment( const S3D_VERTEX& aStartPos, * wxPoint aStart, wxPoint aEnd = YX position of end in board units
double aEndx, double aEndy, double aWidth ) * 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; std::vector <CPolyPt> cornerBuffer;
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)
const int slice = 16; const int slice = 16;
for( ii = 0; ii <= slice/2; ii++ ) TransformRoundedEndsSegmentToPolygon(cornerBuffer, aStart, aEnd, slice, aWidth );
{
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 );
if( ii == 0 ) //Build the 3D data : upper side then lower side
{ double zupperpos = ( aZpos + (aThickness/2) ) * aBiuTo3DUnits;
firstx = aStartPos.x + x; double zlowerpos = ( aZpos - (aThickness/2) ) * aBiuTo3DUnits;
firsty = aStartPos.y + y; if( aThickness )
} glNormal3f( 0.0, 0.0, 1.0 ); // Normale is Z axis
}
// Rounding the left (2nd half polygon is the origin of the segment) double zpos = zupperpos; // start with upper side
for( ii = 0; ii <= slice/2; ii++ ) for( int face = 0; face < 2; face ++ )
{ {
int jj = ii * ANGLE_INC(slice); glBegin( GL_POLYGON );
x = 0.0; for( unsigned ii = 0; ii < cornerBuffer.size(); ii++ )
y = aWidth; glVertex3f( cornerBuffer[ii].x * aBiuTo3DUnits,
RotatePoint( &x, &y, -angle - jj ); - cornerBuffer[ii].y * aBiuTo3DUnits, zpos );
glVertex3f( aStartPos.x + x, aStartPos.y + y, aStartPos.z );
}
glVertex3f( firstx, firsty, aStartPos.z ); // Close the polygon shape
glVertex3f( cornerBuffer[0].x * aBiuTo3DUnits,
- cornerBuffer[0].y * aBiuTo3DUnits, zpos );
glEnd(); 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;
}
//Build the 3D data : vertical sides
std::vector< S3D_VERTEX > vertices;
vertices.resize(4);
for( int ii = 0; ii < slice; ii++ )
{
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 );
}
glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
}
/* Draw a polygon similar to a segment ends with round hole /* Draw a polygon similar to a segment ends with round hole
*/ */
@ -1646,7 +1650,7 @@ static void Draw3D_ThickCircle( const S3D_VERTEX& aCenterPos, double aRadius, do
* draw a segment with an oblong hole. * draw a segment with an oblong hole.
* Used to draw oblong holes * Used to draw oblong holes
*/ */
void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos, void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aStartPos,
double aEndx, double aEndy, double aEndx, double aEndy,
double aRadius, double aHeight ) 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(); DataScale3D = 1.0; // Coordinate is already in range for Set_Object_Data();
double dx = aEndx - aCenterPos.x; double dx = aEndx - aStartPos.x;
double dy = aEndy - aCenterPos.y; double dy = aEndy - aStartPos.y;
double angle = atan2( dy, dx ) * 1800 / M_PI; double angle = atan2( dy, dx ) * 1800 / M_PI;
dx = 0; dx = 0;
dy = aRadius; dy = aRadius;
// draws the first rectangle between half cylinders // draws the first rectangle between half cylinders
RotatePoint( &dx, &dy, angle ); RotatePoint( &dx, &dy, angle );
coords[0].x = coords[1].x = aCenterPos.x + dx; coords[0].x = coords[1].x = aStartPos.x + dx;
coords[0].y = coords[1].y = aCenterPos.y - dy; coords[0].y = coords[1].y = aStartPos.y - dy;
coords[2].x = coords[3].x = aEndx + dx; coords[2].x = coords[3].x = aEndx + dx;
coords[2].y = coords[3].y = aEndy - dy; coords[2].y = coords[3].y = aEndy - dy;
coords[0].z = coords[3].z = aCenterPos.z; coords[0].z = coords[3].z = aStartPos.z;
coords[1].z = coords[2].z = aCenterPos.z + aHeight; coords[1].z = coords[2].z = aStartPos.z + aHeight;
Set_Object_Data( coords ); Set_Object_Data( coords );
// Draw the first half cylinder // Draw the first half cylinder
@ -1693,10 +1697,10 @@ void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos,
// draws the second rectangle between half cylinders // draws the second rectangle between half cylinders
coords[0].x = coords[1].x = aEndx - dx; coords[0].x = coords[1].x = aEndx - dx;
coords[0].y = coords[1].y = aEndy + dy; coords[0].y = coords[1].y = aEndy + dy;
coords[2].x = coords[3].x = aCenterPos.x - dx; coords[2].x = coords[3].x = aStartPos.x - dx;
coords[2].y = coords[3].y = aCenterPos.y + dy; coords[2].y = coords[3].y = aStartPos.y + dy;
coords[0].z = coords[3].z = aCenterPos.z; coords[0].z = coords[3].z = aStartPos.z;
coords[1].z = coords[2].z = aCenterPos.z + aHeight; coords[1].z = coords[2].z = aStartPos.z + aHeight;
Set_Object_Data( coords ); Set_Object_Data( coords );
// Draw the second half cylinder // Draw the second half cylinder
@ -1709,8 +1713,8 @@ void Draw3D_ZaxisOblongCylinder( const S3D_VERTEX& aCenterPos,
coords[0].y = coords[2].y; coords[0].y = coords[2].y;
coords[1].x = coords[3].x; coords[1].x = coords[3].x;
coords[1].y = coords[3].y; coords[1].y = coords[3].y;
coords[2].x = coords[3].x = aCenterPos.x + ddx; coords[2].x = coords[3].x = aStartPos.x + ddx;
coords[2].y = coords[3].y = aCenterPos.y + ddy; coords[2].y = coords[3].y = aStartPos.y + ddy;
Set_Object_Data( coords ); Set_Object_Data( coords );
} }

View File

@ -87,6 +87,7 @@ set(PCB_COMMON_SRCS
base_screen.cpp base_screen.cpp
eda_text.cpp eda_text.cpp
class_page_info.cpp class_page_info.cpp
convert_basic_shapes_to_polygon.cpp
pcbcommon.cpp pcbcommon.cpp
footprint_info.cpp footprint_info.cpp
../pcbnew/basepcbframe.cpp ../pcbnew/basepcbframe.cpp

View File

@ -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 <vector>
#include <fctsys.h>
#include <trigo.h>
#include <macros.h>
#include <convert_basic_shapes_to_polygon.h>
/**
* 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 <CPolyPt>& 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 <CPolyPt>& 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 <CPolyPt>& 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 );
}

View File

@ -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 <vector>
#include <fctsys.h>
#include <trigo.h>
#include <macros.h>
#include <PolyLine.h>
/**
* 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 <CPolyPt>& 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 <CPolyPt>& 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 <CPolyPt>& aCornerBuffer,
wxPoint aCentre, wxPoint aStart, int aArcAngle,
int aCircleToSegmentsCount, int aWidth );
#endif // CONVERT_BASIC_SHAPES_TO_POLYGON_H

View File

@ -19,75 +19,7 @@
#include <class_drawsegment.h> #include <class_drawsegment.h>
#include <class_pcb_text.h> #include <class_pcb_text.h>
#include <class_zone.h> #include <class_zone.h>
#include <convert_basic_shapes_to_polygon.h>
// 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 <CPolyPt>& 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 <CPolyPt>& 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 );
}
/** /**
* Function TransformShapeWithClearanceToPolygon * Function TransformShapeWithClearanceToPolygon
@ -276,29 +208,14 @@ void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor
int aCircleToSegmentsCount, int aCircleToSegmentsCount,
double aCorrectionFactor ) 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() ) switch( Type() )
{ {
case PCB_VIA_T: case PCB_VIA_T:
dx = (int) ( dx * aCorrectionFactor );
for( ii = 0; ii < aCircleToSegmentsCount; ii++ )
{ {
corner_position = wxPoint( dx, 0 ); int radius = (m_Width / 2) + aClearanceValue;
RotatePoint( &corner_position.x, &corner_position.y, (1800 / aCircleToSegmentsCount) ); radius = KiROUND( radius * aCorrectionFactor );
angle = ii * delta; TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aCircleToSegmentsCount );
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;
break; break;
default: default:
@ -311,78 +228,6 @@ void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor
} }
/* Function TransformRoundedEndsSegmentToPolygon
*/
void TransformRoundedEndsSegmentToPolygon( std::vector <CPolyPt>& 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 * Function TransformShapeWithClearanceToPolygon
* Convert the pad shape to a closed polygon * Convert the pad shape to a closed polygon
@ -401,7 +246,7 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor
double aCorrectionFactor ) double aCorrectionFactor )
{ {
wxPoint corner_position; wxPoint corner_position;
int ii, angle; int angle;
int dx = (m_Size.x / 2) + aClearanceValue; int dx = (m_Size.x / 2) + aClearanceValue;
int dy = (m_Size.y / 2) + aClearanceValue; int dy = (m_Size.y / 2) + aClearanceValue;
@ -416,98 +261,36 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCor
{ {
case PAD_CIRCLE: case PAD_CIRCLE:
dx = (int) ( dx * aCorrectionFactor ); dx = (int) ( dx * aCorrectionFactor );
TransformCircleToPolygon( aCornerBuffer, PadShapePos, dx,
for( ii = 0; ii < aCircleToSegmentsCount; ii++ ) aCircleToSegmentsCount );
{
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;
break; break;
case PAD_OVAL: case PAD_OVAL:
// An oval pad has the same shape as a segment with rounded ends
angle = m_Orient; 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 ); dy = (int) ( dy * aCorrectionFactor );
int angle_pg; // Polygon angle shape_offset.y = dy - dx;
wxPoint shape_offset = wxPoint( 0, dy - dx ); width = dx * 2;
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;
} }
else //if( dy <= dx ) else //if( dy <= dx )
{ {
dx = (int) ( dx * aCorrectionFactor ); dx = (int) ( dx * aCorrectionFactor );
int angle_pg; // Polygon angle shape_offset.x = dy - dx;
wxPoint shape_offset = wxPoint( (dy - dx), 0 ); width = dy * 2;
}
RotatePoint( &shape_offset, angle ); RotatePoint( &shape_offset, angle );
wxPoint start = PadShapePos - shape_offset;
for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) wxPoint end = PadShapePos + shape_offset;
{ TransformRoundedEndsSegmentToPolygon( aCornerBuffer, start, end,
corner_position = wxPoint( 0, dy ); aCircleToSegmentsCount, width );
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; break;
}
default: default:
case PAD_TRAPEZOID: case PAD_TRAPEZOID: