kicad/pcbnew/plotps.cpp

811 lines
24 KiB
C++

/*************************************/
/**** Pcbnew: Routine de trace PS ****/
/*************************************/
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "plot_common.h"
#include "pcbnew.h"
#include "pcbplot.h"
#include "trigo.h"
#include "protos.h"
#include "wx/defs.h"
// Routines Locales
static void PrintDrillMark( BOARD* Pcb );
static Ki_PageDescr* SheetPS;
// variables locales:
const int DRILL_MARK = 1;
/****************************************************************************/
void WinEDA_BasePcbFrame::Genere_PS( const wxString& FullFileName, int Layer, bool useA4 )
/****************************************************************************/
/* Genere un fichier POSTSCRIPT (*.ps) de trace du circuit, couche layer
* if layer < 0: all layers
*/
{
int modetrace, tracevia;
wxSize PcbSheetSize;
wxSize PaperSize;
wxSize BoardSize;
wxPoint BoardCenter;
bool Center = FALSE;
Ki_PageDescr* currentsheet = GetScreen()->m_CurrentSheetDesc;
double scale_format; // Facteur correctif pour conversion forlat Ax->A4
double scale_x, scale_y;
int PlotMarge_in_mils = 0;
MsgPanel->EraseMsgBox();
dest = wxFopen( FullFileName, wxT( "wt" ) );
if( dest == NULL )
{
wxString msg = _( "Unable to create file " ) + FullFileName;
DisplayError( this, msg );
return;
}
SetLocaleTo_C_standard( );
Affiche_1_Parametre( this, 0, _( "File" ), FullFileName, CYAN );
if( g_PlotScaleOpt != 1 )
Center = TRUE; // Echelle != 1 donc trace centree du PCB
modetrace = Plot_Mode;
scale_format = 1.0;
// Set default line width
if( g_PlotLine_Width < 1 )
g_PlotLine_Width = 1;
// calcul en unites internes des dimensions des feuilles ( connues en 1/1000 pouce )
PcbSheetSize.x = currentsheet->m_Size.x * U_PCB;
PcbSheetSize.y = currentsheet->m_Size.y * U_PCB;
if( useA4 )
{
SheetPS = &g_Sheet_A4;
PaperSize.x = g_Sheet_A4.m_Size.x * U_PCB;
PaperSize.y = g_Sheet_A4.m_Size.y * U_PCB;
scale_format = (float) PaperSize.x / PcbSheetSize.x;
}
else
{
SheetPS = currentsheet;
PaperSize = PcbSheetSize;
}
// calcul de l'offset de trace:
// calcul du cadrage horizontal du mode paysage ( val algebr. plus grande = decalage a gauche )
g_PlotOffset.x = PlotMarge_in_mils * U_PCB;
// cadrage vertical du mode paysage ( val algebr. plus grande = decalage vers le haut )
g_PlotOffset.y = PaperSize.y - PlotMarge_in_mils * U_PCB;
int BBox[4];
BBox[0] = BBox[1] = PlotMarge_in_mils;
BBox[2] = SheetPS->m_Size.x - PlotMarge_in_mils;
BBox[3] = SheetPS->m_Size.y - PlotMarge_in_mils;
scale_x = scale_y = 1.0;
InitPlotParametresPS( g_PlotOffset, SheetPS, 1.0 / m_InternalUnits, 1.0 / m_InternalUnits );
SetDefaultLineWidthPS( g_PlotLine_Width );
PrintHeaderPS( dest, wxT( "PCBNEW-PS" ), FullFileName, 1, BBox, wxLANDSCAPE );
if( Plot_Sheet_Ref )
{
int tmp = g_PlotOrient;
g_PlotOrient = 0;
SetPlotScale( 1.0, 1.0 );
PlotWorkSheet( PLOT_FORMAT_POST, GetScreen() );
g_PlotOrient = tmp;
}
// calcul des dimensions et centre du PCB
m_Pcb->ComputeBoundaryBox();
BoardSize = m_Pcb->m_BoundaryBox.GetSize();
BoardCenter = m_Pcb->m_BoundaryBox.Centre();
scale_x = Scale_X;
scale_y = Scale_Y;
if( g_PlotScaleOpt == 0 ) // Optimum scale
{
float Xscale, Yscale;
int noprint_size = 2 * PlotMarge_in_mils * U_PCB;
if( Plot_Sheet_Ref )
noprint_size += 500 * U_PCB;
Xscale = (float) ( PaperSize.x - noprint_size ) / BoardSize.x;
Yscale = (float) ( PaperSize.y - noprint_size ) / BoardSize.y;
scale_x = scale_y = MIN( Xscale, Yscale );
}
BoardCenter.x = (int) (BoardCenter.x * scale_x);
BoardCenter.y = (int) (BoardCenter.y * scale_y);
// Calcul du cadrage (echelle != 1 donc recadrage du trace)
if( Center )
{
g_PlotOffset.x -= PaperSize.x / 2 - BoardCenter.x + PlotMarge_in_mils * U_PCB;
g_PlotOffset.y = PaperSize.y / 2 + BoardCenter.y; // cadrage horizontal du mode paysage
}
if( g_PlotOrient == PLOT_MIROIR )
{
if( Center )
g_PlotOffset.y = -PaperSize.y / 2 + BoardCenter.y;
else
g_PlotOffset.y = -PaperSize.y + m_Pcb->m_BoundaryBox.GetBottom()
+ m_Pcb->m_BoundaryBox.GetY() + PlotMarge_in_mils * U_PCB;
}
InitPlotParametresPS( g_PlotOffset, SheetPS, scale_x, scale_y, g_PlotOrient );
// If plot a negative board:
// Draw a black rectangle (background for plot board in white)
// and switch the current color to WHITE
if( g_Plot_PS_Negative )
{
int Rectangle[10]; // Put here the board corners
int margin = 500; // Add a 0.1 inch margin around the board
Rectangle[0] = m_Pcb->m_BoundaryBox.GetX() - margin;
Rectangle[1] = m_Pcb->m_BoundaryBox.GetY() - margin;
Rectangle[2] = m_Pcb->m_BoundaryBox.GetRight() + margin;
Rectangle[3] = Rectangle[1];
Rectangle[4] = Rectangle[2];
Rectangle[5] = m_Pcb->m_BoundaryBox.GetBottom() + margin;
Rectangle[6] = Rectangle[0];
Rectangle[7] = Rectangle[5];
Rectangle[8] = Rectangle[0];
Rectangle[9] = Rectangle[1];
SetColorMapPS( BLACK );
PlotPolyPS( 5, Rectangle, TRUE );
SetColorMapPS( WHITE );
}
// Specify that the contents of the "Edges Pcb" layer are to be plotted
// in addition to the contents of the currently specified layer.
int layer_mask = g_TabOneLayerMask[Layer] | EDGE_LAYER;
switch( Layer )
{
case - 1:
Plot_Layer_PS( dest, layer_mask, 0, 1, modetrace );
break;
case FIRST_COPPER_LAYER:
case LAYER_N_2:
case LAYER_N_3:
case LAYER_N_4:
case LAYER_N_5:
case LAYER_N_6:
case LAYER_N_7:
case LAYER_N_8:
case LAYER_N_9:
case LAYER_N_10:
case LAYER_N_11:
case LAYER_N_12:
case LAYER_N_13:
case LAYER_N_14:
case LAYER_N_15:
case LAST_COPPER_LAYER:
Plot_Layer_PS( dest, layer_mask, 0, 1, modetrace );
break;
case SILKSCREEN_N_CU:
case SILKSCREEN_N_CMP:
Plot_Serigraphie( PLOT_FORMAT_POST, dest, layer_mask );
break;
case SOLDERMASK_N_CU:
case SOLDERMASK_N_CMP: // Trace du vernis epargne
if( g_DrawViaOnMaskLayer )
tracevia = 1;
else
tracevia = 0;
Plot_Layer_PS( dest, layer_mask, g_DesignSettings.m_MaskMargin,
tracevia, modetrace );
break;
case SOLDERPASTE_N_CU:
case SOLDERPASTE_N_CMP: // Trace du masque de pate de soudure
Plot_Layer_PS( dest, layer_mask, 0, 0, modetrace );
break;
default:
Plot_Serigraphie( PLOT_FORMAT_POST, dest, layer_mask );
break;
}
// fin
CloseFilePS( dest );
SetLocaleTo_Default( );
}
/********************************************************************/
void WinEDA_BasePcbFrame::Plot_Layer_PS( FILE* File, int masque_layer,
int garde, int tracevia, int modetrace )
/********************************************************************/
/* Trace en format POSTSCRIPT d'une couche cuivre ou masque
*/
{
wxPoint pos, end;
wxSize size;
MODULE* Module;
D_PAD* PtPad;
TRACK* pts;
BOARD_ITEM* PtStruct;
wxString msg;
// (Following command has been superceded by new command on line 173.)
// masque_layer |= EDGE_LAYER; // Les elements de la couche EDGE sont tj traces
// trace des elements type Drawings Pcb :
PtStruct = m_Pcb->m_Drawings;
for( ; PtStruct != NULL; PtStruct = PtStruct->Next() )
{
switch( PtStruct->Type() )
{
case TYPEDRAWSEGMENT:
PlotDrawSegment( (DRAWSEGMENT*) PtStruct, PLOT_FORMAT_POST,
masque_layer );
break;
case TYPETEXTE:
PlotTextePcb( (TEXTE_PCB*) PtStruct, PLOT_FORMAT_POST,
masque_layer );
break;
case TYPECOTATION:
PlotCotation( (COTATION*) PtStruct, PLOT_FORMAT_POST,
masque_layer );
break;
case TYPEMIRE:
PlotMirePcb( (MIREPCB*) PtStruct, PLOT_FORMAT_POST,
masque_layer );
break;
case TYPEMARKER:
break;
default:
DisplayError( this,
wxT( "WinEDA_BasePcbFrame::Plot_Layer_PS() : Unexpected Draw Type" ) );
break;
}
}
// Trace des Elements des modules autres que pads
nb_items = 0;
Affiche_1_Parametre( this, 48, wxT( "DrawMod" ), wxEmptyString, GREEN );
Module = m_Pcb->m_Modules;
for( ; Module != NULL; Module = (MODULE*) Module->Pnext )
{
PtStruct = Module->m_Drawings;
for( ; PtStruct != NULL; PtStruct = PtStruct->Next() )
{
switch( PtStruct->Type() )
{
case TYPEEDGEMODULE:
if( masque_layer & g_TabOneLayerMask[ PtStruct->GetLayer() ] )
Plot_1_EdgeModule( PLOT_FORMAT_POST, (EDGE_MODULE*) PtStruct );
break;
default:
break;
}
}
}
// Trace des Elements des modules : Pastilles
nb_items = 0;
Affiche_1_Parametre( this, 48, wxT( "Pads " ), wxEmptyString, GREEN );
Module = m_Pcb->m_Modules;
for( ; Module != NULL; Module = (MODULE*) Module->Pnext )
{
PtPad = (D_PAD*) Module->m_Pads;
for( ; PtPad != NULL; PtPad = (D_PAD*) PtPad->Pnext )
{
if( (PtPad->m_Masque_Layer & masque_layer) == 0 )
continue;
wxPoint shape_pos = PtPad->ReturnShapePos();
pos = shape_pos;
size.x = PtPad->m_Size.x + garde * 2;
size.y = PtPad->m_Size.y + garde * 2;
nb_items++;
switch( PtPad->m_PadShape )
{
case PAD_CIRCLE:
trace_1_pastille_RONDE_POST( pos, size.x, modetrace );
break;
case PAD_OVAL:
trace_1_pastille_OVALE_POST( pos, size, PtPad->m_Orient, modetrace );
break;
case PAD_TRAPEZOID:
{
wxSize delta;
delta = PtPad->m_DeltaSize;
trace_1_pad_TRAPEZE_POST( pos, size, delta,
PtPad->m_Orient, modetrace );
break;
}
case PAD_RECT:
default:
trace_1_pad_rectangulaire_POST( pos, size, PtPad->m_Orient, modetrace );
break;
}
msg.Printf( wxT( "%d" ), nb_items );
Affiche_1_Parametre( this, 48, wxT( "Pads" ), msg, GREEN );
}
}
// trace des VIAS :
if( tracevia )
{
nb_items = 0;
Affiche_1_Parametre( this, 56, _( "Vias" ), wxEmptyString, RED );
for( pts = m_Pcb->m_Track; pts != NULL; pts = pts->Next() )
{
if( pts->Type() != TYPEVIA )
continue;
SEGVIA* Via = (SEGVIA*) pts;
// vias not plotted if not on selected layer, but if layer
// == SOLDERMASK_LAYER_CU or SOLDERMASK_LAYER_CMP, vias are drawn,
// if they are on a external copper layer
int via_mask_layer = Via->ReturnMaskLayer();
if( via_mask_layer & CUIVRE_LAYER )
via_mask_layer |= SOLDERMASK_LAYER_CU;
if( via_mask_layer & CMP_LAYER )
via_mask_layer |= SOLDERMASK_LAYER_CMP;
if( (via_mask_layer & masque_layer) == 0 )
continue;
pos = Via->m_Start;
size.x = size.y = Via->m_Width + garde * 2;
trace_1_pastille_RONDE_POST( pos, size.x, modetrace );
nb_items++;
msg.Printf( wxT( "%d" ), nb_items );
Affiche_1_Parametre( this, 56, wxEmptyString, msg, RED );
}
}
// trace des pistes et zones:
nb_items = 0;
Affiche_1_Parametre( this, 64, _( "Tracks" ), wxEmptyString, YELLOW );
for( pts = m_Pcb->m_Track; pts != NULL; pts = (TRACK*) pts->Pnext )
{
if( pts->Type() == TYPEVIA )
continue;
if( (g_TabOneLayerMask[pts->GetLayer()] & masque_layer) == 0 )
continue;
size.x = size.y = pts->m_Width;
pos = pts->m_Start;
end = pts->m_End;
PlotFilledSegmentPS( pos, end, size.x );
nb_items++;
msg.Printf( wxT( "%d" ), nb_items );
Affiche_1_Parametre( this, 64, wxEmptyString, msg, YELLOW );
}
nb_items = 0;
Affiche_1_Parametre( this, 64, wxT( "Zones " ), wxEmptyString, YELLOW );
for( pts = m_Pcb->m_Zone; pts != NULL; pts = (TRACK*) pts->Pnext )
{
if( (g_TabOneLayerMask[pts->GetLayer()] & masque_layer) == 0 )
continue;
size.x = size.y = pts->m_Width;
pos = pts->m_Start;
end = pts->m_End;
PlotFilledSegmentPS( pos, end, size.x );
nb_items++;
msg.Printf( wxT( "%d" ), nb_items );
Affiche_1_Parametre( this, 64, wxEmptyString, msg, YELLOW );
}
/* Plot filled ares */
for( int ii = 0; ii < m_Pcb->GetAreaCount(); ii++ )
{
ZONE_CONTAINER* edge_zone = m_Pcb->GetArea(ii);
if( ( (1 << edge_zone->GetLayer()) & masque_layer ) == 0 )
continue;
PlotFilledAreas(edge_zone, PLOT_FORMAT_POST);
}
// Trace des trous de percage
if( modetrace == FILLED )
PrintDrillMark( m_Pcb );
}
/*************************************/
static void PrintDrillMark( BOARD* Pcb )
/*************************************/
/* Draw a drill mark for pads and vias.
* Must be called after all drawings, because it
* redraw the drill mark on a pad or via, as a negative (i.e. white) shape
*/
{
const int SMALL_DRILL = 150;
wxPoint pos;
wxSize diam;
MODULE* Module;
D_PAD* PtPad;
TRACK* pts;
if( g_DrillShapeOpt == 0 )
return;
if( g_Plot_PS_Negative )
fprintf( dest, " 0 setgray\n" );
else
fprintf( dest, " 1 setgray\n" );
diam.x = diam.y = (g_DrillShapeOpt == DRILL_MARK) ? SMALL_DRILL :
g_DesignSettings.m_ViaDrill;
for( pts = Pcb->m_Track; pts != NULL; pts = pts->Next() )
{
if( pts->Type() != TYPEVIA )
continue;
pos = pts->m_Start;
if( g_DrillShapeOpt == DRILL_MARK )
diam.x = diam.y = SMALL_DRILL;
else
diam.x = diam.y = pts->GetDrillValue();
trace_1_pastille_RONDE_POST( pos, diam.x, FILLED );
}
Module = Pcb->m_Modules;
for( ; Module != NULL; Module = (MODULE*) Module->Pnext )
{
PtPad = Module->m_Pads;
for( ; PtPad != NULL; PtPad = PtPad->Next() )
{
if( PtPad->m_Drill.x == 0 )
continue;
// Output hole shapes:
pos = PtPad->m_Pos;
if( PtPad->m_DrillShape == PAD_OVAL )
{
diam = PtPad->m_Drill;
trace_1_pastille_OVALE_POST( pos, diam, PtPad->m_Orient, FILLED );
}
else
{
diam.x = (g_DrillShapeOpt == DRILL_MARK) ? SMALL_DRILL :
PtPad->m_Drill.x;
trace_1_pastille_RONDE_POST( pos, diam.x, FILLED );
}
}
}
fprintf( dest, " 0 setgray\n" );
}
/***********************************************************************************/
void trace_1_pastille_OVALE_POST( wxPoint pos, wxSize size, int orient, int modetrace )
/************************************************************************************/
/* Trace 1 pastille PAD_OVAL en position pos_X,Y:
* dimensions dx,dy,
* orientation orient
* La forme est tracee comme un segment
*/
{
int x0, y0, x1, y1, delta;
int thickness, rayon;
// la pastille est ramenee a une pastille ovale avec dy > dx
if( size.x > size.y )
{
EXCHG( size.x, size.y );
orient += 900;
if( orient >= 3600 )
orient -= 3600;
}
delta = size.y - size.x;
x0 = 0;
y0 = -delta / 2;
x1 = 0;
y1 = delta / 2;
RotatePoint( &x0, &y0, orient );
RotatePoint( &x1, &y1, orient );
if( modetrace == FILLED )
{
PlotFilledSegmentPS( wxPoint( pos.x + x0, pos.y + y0 ),
wxPoint( pos.x + x1, pos.y + y1 ), size.x );
}
else
{
thickness = g_PlotLine_Width;
rayon = (size.x - thickness) / 2;
if( rayon < 1 )
rayon = 1;
if( rayon < thickness )
thickness = rayon;
PlotArcPS( wxPoint( pos.x + x1, pos.y + y1 ), -orient, -orient + 1800, rayon, false, thickness);
PlotArcPS( wxPoint( pos.x + x0, pos.y + y0 ), -orient + 1800, -orient, rayon, false, thickness );
x0 = -rayon;
y0 = -delta / 2;
x1 = -rayon;
y1 = delta / 2;
RotatePoint( &x0, &y0, orient );
RotatePoint( &x1, &y1, orient );
PlotFilledSegmentPS( wxPoint( pos.x + x0, pos.y + y0 ),
wxPoint( pos.x + x1, pos.y + y1 ), thickness );
x0 = rayon;
y0 = -delta / 2;
x1 = rayon;
y1 = delta / 2;
RotatePoint( &x0, &y0, orient );
RotatePoint( &x1, &y1, orient );
PlotFilledSegmentPS( wxPoint( pos.x + x0, pos.y + y0 ),
wxPoint( pos.x + x1, pos.y + y1 ), thickness );
}
}
/*******************************************************************************/
void trace_1_pastille_RONDE_POST( wxPoint centre, int diametre, int modetrace )
/*******************************************************************************/
/* Trace 1 pastille RONDE (via,pad rond) en position pos_X,Y
*/
{
int rayon, w;
wxSize diam( diametre, diametre );
UserToDeviceCoordinate( centre );
UserToDeviceSize( diam );
if( modetrace == FILLED )
{
SetCurrentLineWidthPS( 0 );
rayon = diam.x / 2;
if( rayon < 1 )
rayon = 1;
fprintf( dest, "newpath %d %d %d 0 360 arc fill stroke\n",
centre.x, centre.y, rayon );
}
else
{
w = g_PlotLine_Width;
rayon = (diam.x - w) / 2;
if( rayon < 1 )
rayon = 1;
if( rayon < w )
w = rayon;
SetCurrentLineWidthPS( w );
fprintf( dest, "newpath %d %d %d 0 360 arc stroke\n",
centre.x, centre.y, rayon );
}
}
/**************************************************************************/
void trace_1_pad_rectangulaire_POST( wxPoint centre,
wxSize size, int orient, int modetrace )
/**************************************************************************/
/*
* Trace 1 pad rectangulaire d'orientation quelconque
* donne par son centre, ses dimensions,
* et son orientation orient
*/
{
int x0, y0, x1, y1, w;
if( modetrace == FILLED )
{
x0 = centre.x - size.x / 2;
x1 = centre.x + size.x / 2;
y0 = y1 = centre.y;
w = size.y;
RotatePoint( &x0, &y0, centre.x, centre.y, orient );
RotatePoint( &x1, &y1, centre.x, centre.y, orient );
fprintf( dest, "0 setlinewidth 0 setlinecap 0 setlinejoin\n" );
ForcePenReinit(); // Force init line width for PlotFilledSegmentPS
PlotFilledSegmentPS( wxPoint( x0, y0 ), wxPoint( x1, y1 ), w );
ForcePenReinit();
SetCurrentLineWidthPS( 0 ); // Force init line width to default
fprintf( dest, "1 setlinecap 1 setlinejoin\n" );
}
else
{
w = g_PlotLine_Width;
size.x -= w;
if( size.x < 1 )
size.x = 1;
size.y -= w;
if( size.y < 1 )
size.y = 1;
trace_1_contour_POST( centre, size, wxSize( 0, 0 ), w, orient );
}
}
/**************************************************************/
void trace_1_contour_POST( wxPoint centre, wxSize size, wxSize delta,
int dim_trait, int orient )
/**************************************************************/
/*
* Trace 1 contour rectangulaire ou trapezoidal d'orientation quelconque
* donne par son centre centre,
* ses dimensions size,
* ses variations delta
* epaisseur de trait dim_trait
* et son orientation orient (en 0.1 degres)
*/
{
int ii;
int dx, dy, lpen;
int ddx, ddy;
wxPoint coord[4];
lpen = dim_trait;
dx = size.x / 2;
dy = size.y / 2;
ddx = delta.x >> 1;
ddy = delta.y >> 1; // demi dim dx et dy
coord[0].x = centre.x - dx - ddy;
coord[0].y = centre.y + dy + ddx;
coord[1].x = centre.x - dx + ddy;
coord[1].y = centre.y - dy - ddx;
coord[2].x = centre.x + dx - ddy;
coord[2].y = centre.y - dy + ddx;
coord[3].x = centre.x + dx + ddy;
coord[3].y = centre.y + dy - ddx;
for( ii = 0; ii < 4; ii++ )
{
RotatePoint( &coord[ii], centre, orient );
}
PlotFilledSegmentPS( coord[0], coord[1], lpen );
PlotFilledSegmentPS( coord[1], coord[2], lpen );
PlotFilledSegmentPS( coord[2], coord[3], lpen );
PlotFilledSegmentPS( coord[3], coord[0], lpen );
}
/*******************************************************************/
void trace_1_pad_TRAPEZE_POST( wxPoint centre, wxSize size, wxSize delta,
int orient, int modetrace )
/*******************************************************************/
/*
* Trace 1 pad trapezoidal donne par :
* son centre centre
* ses dimensions size
* les variations delta ( 1 des deux au moins doit etre nulle)
* son orientation orient en 0.1 degres
* le mode de trace (FILLED, SKETCH, FILAIRE)
*
* Le trace n'est fait que pour un trapeze, c.a.d que deltaX ou deltaY
* = 0.
*
* les notation des sommets sont ( vis a vis de la table tracante )
*
* " 0 ------------- 3 "
* " . . "
* " . O . "
* " . . "
* " 1 ---- 2 "
*
*
* exemple de Disposition pour deltaY > 0, deltaX = 0
* " 1 ---- 2 "
* " . . "
* " . O . "
* " . . "
* " 0 ------------- 3 "
*
*
* exemple de Disposition pour deltaY = 0, deltaX > 0
* " 0 "
* " . . "
* " . . "
* " . 3 "
* " . . "
* " . O . "
* " . . "
* " . 2 "
* " . . "
* " . . "
* " 1 "
*/
{
int ii;
int dx, dy;
wxPoint polygone[4]; // coord des sommets / centre du pad
int ddx, ddy;
int l_pen; // diam spot (plume)
l_pen = 1;
if( modetrace == FILAIRE || Plot_Mode == FILAIRE )
{
wxSize lsize( g_PlotLine_Width, g_PlotLine_Width );
UserToDeviceSize( lsize );
l_pen = lsize.x;
}
dx = size.x / 2;
dy = size.y / 2;
ddx = delta.x / 2;
ddy = delta.y / 2;
polygone[0].x = -dx - ddy;
polygone[0].y = +dy + ddx;
polygone[1].x = -dx + ddy;
polygone[1].y = -dy - ddx;
polygone[2].x = +dx - ddy;
polygone[2].y = -dy + ddx;
polygone[3].x = +dx + ddy;
polygone[3].y = +dy - ddx;
for( ii = 0; ii < 4; ii++ )
{
RotatePoint( &polygone[ii].x, &polygone[ii].y, orient );
polygone[ii].x += centre.x;
polygone[ii].y += centre.y;
}
SetCurrentLineWidthPS( l_pen );
UserToDeviceCoordinate( polygone[0] );
fprintf( dest, "newpath %d %d moveto\n", polygone[0].x, polygone[0].y );
for( ii = 1; ii < 4; ii++ )
{
UserToDeviceCoordinate( polygone[ii] );
fprintf( dest, "%d %d lineto\n", polygone[ii].x, polygone[ii].y );
}
fprintf( dest, "%d %d lineto ", polygone[0].x, polygone[0].y );
if( modetrace == FILLED )
fprintf( dest, "fill " );
fprintf( dest, "stroke\n" );
}