summaryrefslogtreecommitdiff
path: root/common/common_plotHPGL_functions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/common_plotHPGL_functions.cpp')
-rw-r--r--common/common_plotHPGL_functions.cpp740
1 files changed, 740 insertions, 0 deletions
diff --git a/common/common_plotHPGL_functions.cpp b/common/common_plotHPGL_functions.cpp
new file mode 100644
index 0000000..ba99b67
--- /dev/null
+++ b/common/common_plotHPGL_functions.cpp
@@ -0,0 +1,740 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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
+ */
+
+/**
+ * @file common_plotHPGL_functions.cpp
+ * @brief KiCad: Common plot HPGL Routines
+ * Filled primitive are not supported, but some could be using HPGL/2
+ * Since this plot engine is mostly intended for import in external programs,
+ * sadly HPGL/2 isn't supported a lot... some of the primitives use overlapped
+ * strokes to fill the shape
+ */
+
+/* Some HPGL commands:
+ * Note: the HPGL unit is 25 micrometers
+ * All commands MUST be terminated by a semi-colon or a linefeed.
+ * Spaces can NOT be substituted for required commas in the syntax of a command.
+ *
+ *
+ * AA (Arc Absolute): Angle is a floating point # (requires non integer value)
+ * Draws an arc with the center at (X,Y).
+ * A positive angle creates a counter-clockwise arc.
+ * If the chord angle is specified,
+ * this will be the number of degrees used for stepping around the arc.
+ * If no value is given then a default value of five degrees is used.
+ * AA x, y, a {,b};
+ *
+ * AR (Arc Relative):
+ * AR Dx, Dy, a {, b};
+ *
+ * CA (Alternate Character Set):
+ * CA {n};
+ *
+ * CI (Circle):
+ * CI r {,b};
+ *
+ * CP (Character Plot):
+ * CP {h, v};
+ * h [-127.9999 .. 127.9999] Anzahl der Zeichen horizontal
+ * v [-127.9999 .. 127.9999] Anzahl der Zeichen vertikal
+ *
+ * CS (Standard Character Set):
+ * CS {n};
+ *
+ * DR (Relative Direction for Label Text):
+ * DR s, a;
+ *
+ * DI (Absolute Direction for Label Text):
+ * DI {s, a};
+ *
+ * DT (Define Terminator - this character becomes unavailable except to terminate a label string.
+ * Default is ^C control-C):
+ * DT t;
+ *
+ * EA (rEctangle Absolute - Unfilled, from current position to diagonal x,y):
+ * EA x, y;
+ *
+ * ER (rEctangle Relative - Unfilled, from current position to diagonal x,y):
+ * ER x,y;
+ *
+ * FT (Fill Type):
+ * FT {s {,l {a}}};
+ *
+ * IM (Input Mask):
+ * IM {f};
+ *
+ * IN (Initialize): This command instructs the controller to begin processing the HPGL plot file.
+ * Without this, the commands in the file are received but never executed.
+ * If multiple IN s are found during execution of the file,
+ * the controller performs a Pause/Cancel operation.
+ * All motion from the previous job, yet to be executed, is lost,
+ * and the new information is executed.
+ * IN;
+ *
+ * IP Input P1 and P2:
+ * IP {P1x, P1y {, P2x, P2y}};
+ *
+ * IW (Input Window):
+ * IW {XUL, YUL, XOR, YOR};
+ *
+ * LB (Label):
+ * LB c1 .. cn t;
+ *
+ * PA (Plot Absolute): Moves to an absolute HPGL position and sets absolute mode for
+ * future PU and PD commands. If no arguments follow the command,
+ * only absolute mode is set.
+ * PA {x1, y1 {{PU|PD|,} ..., ..., xn, yn}};
+ * P1x, P1y, P2x, P2y [Integer in ASCII]
+ *
+ * PD (Pen Down): Executes <current pen> pen then moves to the requested position
+ * if one is specified. This position is dependent on whether absolute
+ * or relative mode is set. This command performs no motion in 3-D mode,
+ * but the outputs and feedrates are affected.
+ * PD {x, y};
+ *
+ * PR (Plot Relative): Moves to the relative position specified and sets relative mode
+ * for future PU and PD commands.
+ * If no arguments follow the command, only relative mode is set.
+ * PR {Dx1, Dy1 {{PU|PD|,} ..., ..., Dxn, Dyn}};
+ *
+ * PS (Paper Size):
+ * PS {n};
+ *
+ * PT (Pen Thickness):
+ * PT {l};
+ *
+ * PU (Pen Up): Executes <current pen> pen then moves to the requested position
+ * if one is specified. This position is dependent on whether absolute
+ * or relative mode is set.
+ * This command performs no motion in 3-D mode, but the outputs
+ * and feedrates are affected.
+ * PU {x, y};
+ *
+ * RA (Rectangle Absolute - Filled, from current position to diagonal x,y):
+ * RA x, y;
+ *
+ * RO (Rotate Coordinate System):
+ * RO;
+ *
+ * RR (Rectangle Relative - Filled, from current position to diagonal x,y):
+ * RR x, y;
+ *
+ * SA (Select Alternate Set):
+ * SA;
+ *
+ * SC (Scale):
+ * SC {Xmin, Xmax, Ymin, Ymax};
+ *
+ * SI (Absolute Character Size):
+ * SI b, h;
+ * b [-127.9999 .. 127.9999, keine 0]
+ * h [-127.9999 .. 127.9999, keine 0]
+ *
+ * SL (Character Slant):
+ * SL {a};
+ * a [-3.5 .. -0.5, 0.5 .. 3.5]
+*
+ * SP (Select Pen): Selects a new pen or tool for use.
+ * If no pen number or a value of zero is given,
+ * the controller performs an EOF (end of file command).
+ * Once an EOF is performed, no motion is executed,
+ * until a new IN command is received.
+ * SP n;
+ *
+ * SR (Relative Character Size):
+ * SR {b, h};
+ * b [-127.9999 .. 127.9999, keine 0]
+ * h [-127.9999 .. 127.9999, keine 0]
+ *
+ * SS (Select Standard Set):
+ * SS;
+ *
+ * TL (Tick Length):
+ * TL {tp {, tm}};
+ *
+ * UC (User Defined Character):
+ * UC {i,} x1, y1, {i,} x2, y2, ... {i,} xn, yn;
+ *
+ * VS (Velocity Select):
+ * VS {v {, n}};
+ * v [1 .. 40]
+ * n [1 .. 8, je nach Ausstattung]
+ *
+ * XT (X Tick):
+ * XT;
+ *
+ * YT (Y Tick):
+ * YT;
+ */
+
+
+#include <fctsys.h>
+#include <gr_basic.h>
+#include <trigo.h>
+#include <wxstruct.h>
+#include <base_struct.h>
+#include <plot_common.h>
+#include <macros.h>
+#include <kicad_string.h>
+
+// HPGL scale factor (1 PLU = 1/40mm = 25 micrometers)
+static const double PLUsPERDECIMIL = 0.102041;
+
+HPGL_PLOTTER::HPGL_PLOTTER()
+{
+ SetPenSpeed( 40 ); // Default pen speed = 40 cm/s; Pen speed is *always* in cm
+ SetPenNumber( 1 ); // Default pen num = 1
+ SetPenDiameter( 0.0 );
+ SetPenOverlap( 0.0 );
+}
+
+void HPGL_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
+ double aScale, bool aMirror )
+{
+ plotOffset = aOffset;
+ plotScale = aScale;
+ m_IUsPerDecimil = aIusPerDecimil;
+ iuPerDeviceUnit = PLUsPERDECIMIL / aIusPerDecimil;
+ /* Compute the paper size in IUs */
+ paperSize = pageInfo.GetSizeMils();
+ paperSize.x *= 10.0 * aIusPerDecimil;
+ paperSize.y *= 10.0 * aIusPerDecimil;
+ SetDefaultLineWidth( 0 ); // HPGL has pen sizes instead
+ m_plotMirror = aMirror;
+}
+
+
+/**
+ * At the start of the HPGL plot pen speed and number are requested
+ */
+bool HPGL_PLOTTER::StartPlot()
+{
+ wxASSERT( outputFile );
+ fprintf( outputFile, "IN;VS%d;PU;PA;SP%d;\n", penSpeed, penNumber );
+ return true;
+}
+
+
+/**
+ * HPGL end of plot: pen return and release
+ */
+bool HPGL_PLOTTER::EndPlot()
+{
+ wxASSERT( outputFile );
+ fputs( "PU;PA;SP0;\n", outputFile );
+ fclose( outputFile );
+ outputFile = NULL;
+ return true;
+}
+
+
+/**
+ * HPGL rectangle: fill not supported
+ */
+void HPGL_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
+{
+ wxASSERT( outputFile );
+ DPOINT p2dev = userToDeviceCoordinates( p2 );
+ MoveTo( p1 );
+ fprintf( outputFile, "EA %.0f,%.0f;\n", p2dev.x, p2dev.y );
+ PenFinish();
+}
+
+
+/**
+ * HPGL circle: fill not supported
+ */
+void HPGL_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill,
+ int width )
+{
+ wxASSERT( outputFile );
+ double radius = userToDeviceSize( diameter / 2 );
+
+ if( radius > 0 )
+ {
+ MoveTo( centre );
+ fprintf( outputFile, "CI %g;\n", radius );
+ PenFinish();
+ }
+}
+
+
+/**
+ * HPGL polygon: fill not supported (but closed, at least)
+ */
+void HPGL_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
+ FILL_T aFill, int aWidth )
+{
+ if( aCornerList.size() <= 1 )
+ return;
+
+ SetCurrentLineWidth( aWidth );
+
+ MoveTo( aCornerList[0] );
+
+ for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
+ LineTo( aCornerList[ii] );
+
+ // Close polygon if filled.
+ if( aFill )
+ {
+ int ii = aCornerList.size() - 1;
+
+ if( aCornerList[ii] != aCornerList[0] )
+ LineTo( aCornerList[0] );
+ }
+
+ PenFinish();
+}
+
+
+/**
+ * Pen control logic (remove redundant pen activations)
+ */
+void HPGL_PLOTTER::penControl( char plume )
+{
+ wxASSERT( outputFile );
+
+ switch( plume )
+ {
+ case 'U':
+
+ if( penState != 'U' )
+ {
+ fputs( "PU;", outputFile );
+ penState = 'U';
+ }
+
+ break;
+
+ case 'D':
+
+ if( penState != 'D' )
+ {
+ fputs( "PD;", outputFile );
+ penState = 'D';
+ }
+
+ break;
+
+ case 'Z':
+ fputs( "PU;", outputFile );
+ penState = 'U';
+ penLastpos.x = -1;
+ penLastpos.y = -1;
+ break;
+ }
+}
+
+
+void HPGL_PLOTTER::PenTo( const wxPoint& pos, char plume )
+{
+ wxASSERT( outputFile );
+
+ if( plume == 'Z' )
+ {
+ penControl( 'Z' );
+ return;
+ }
+
+ penControl( plume );
+ DPOINT pos_dev = userToDeviceCoordinates( pos );
+
+ if( penLastpos != pos )
+ fprintf( outputFile, "PA %.0f,%.0f;\n", pos_dev.x, pos_dev.y );
+
+ penLastpos = pos;
+}
+
+
+/**
+ * HPGL supports dashed lines
+ */
+void HPGL_PLOTTER::SetDash( bool dashed )
+{
+ wxASSERT( outputFile );
+
+ if( dashed )
+ fputs( "LI 2;\n", outputFile );
+ else
+ fputs( "LI;\n", outputFile );
+}
+
+
+void HPGL_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end,
+ int width, EDA_DRAW_MODE_T tracemode )
+{
+ wxPoint center;
+ wxSize size;
+
+ // Suppress overlap if pen is too big
+ if( penDiameter >= width )
+ {
+ MoveTo( start );
+ FinishTo( end );
+ }
+ else
+ segmentAsOval( start, end, width, tracemode );
+}
+
+
+/* Plot an arc:
+ * Center = center coord
+ * Stangl, endAngle = angle of beginning and end
+ * Radius = radius of the arc
+ * Command
+ * PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, NbSegm; PU;
+ * Or PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, PU;
+ */
+void HPGL_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
+ FILL_T fill, int width )
+{
+ wxASSERT( outputFile );
+ double angle;
+
+ if( radius <= 0 )
+ return;
+
+ DPOINT centre_dev = userToDeviceCoordinates( centre );
+
+ if( m_plotMirror )
+ angle = StAngle - EndAngle;
+ else
+ angle = EndAngle - StAngle;
+
+ NORMALIZE_ANGLE_180( angle );
+ angle /= 10;
+
+ // Calculate arc start point:
+ wxPoint cmap;
+ cmap.x = centre.x + KiROUND( cosdecideg( radius, StAngle ) );
+ cmap.y = centre.y - KiROUND( sindecideg( radius, StAngle ) );
+ DPOINT cmap_dev = userToDeviceCoordinates( cmap );
+
+ fprintf( outputFile,
+ "PU;PA %.0f,%.0f;PD;AA %.0f,%.0f,",
+ cmap_dev.x, cmap_dev.y,
+ centre_dev.x, centre_dev.y );
+ fprintf( outputFile, "%.0f", angle );
+ fprintf( outputFile, ";PU;\n" );
+ PenFinish();
+}
+
+
+/* Plot oval pad.
+ */
+void HPGL_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
+ EDA_DRAW_MODE_T trace_mode )
+{
+ int deltaxy, cx, cy;
+ wxSize size( aSize );
+
+ /* The pad is reduced to an oval with size.y > size.x
+ * (Oval vertical orientation 0)
+ */
+ if( size.x > size.y )
+ {
+ std::swap( size.x, size.y );
+ orient = AddAngles( orient, 900 );
+ }
+
+ deltaxy = size.y - size.x; // distance between centers of the oval
+
+ if( trace_mode == FILLED )
+ {
+ FlashPadRect( pos, wxSize( size.x, deltaxy + KiROUND( penDiameter ) ),
+ orient, trace_mode );
+ cx = 0; cy = deltaxy / 2;
+ RotatePoint( &cx, &cy, orient );
+ FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode );
+ cx = 0; cy = -deltaxy / 2;
+ RotatePoint( &cx, &cy, orient );
+ FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode );
+ }
+ else // Plot in SKETCH mode.
+ {
+ sketchOval( pos, size, orient, KiROUND( penDiameter ) );
+ }
+}
+
+
+/* Plot round pad or via.
+ */
+void HPGL_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre,
+ EDA_DRAW_MODE_T trace_mode )
+{
+ wxASSERT( outputFile );
+ DPOINT pos_dev = userToDeviceCoordinates( pos );
+
+ int delta = KiROUND( penDiameter - penOverlap );
+ int radius = ( diametre - KiROUND( penDiameter ) ) / 2;
+
+ if( radius < 0 )
+ radius = 0;
+
+ double rsize = userToDeviceSize( radius );
+
+ fprintf( outputFile, "PA %.0f,%.0f;CI %.0f;\n",
+ pos_dev.x, pos_dev.y, rsize );
+
+ if( trace_mode == FILLED ) // Plot in filled mode.
+ {
+ if( delta > 0 )
+ {
+ while( (radius -= delta ) >= 0 )
+ {
+ rsize = userToDeviceSize( radius );
+ fprintf( outputFile, "PA %.0f,%.0f;CI %.0f;\n",
+ pos_dev.x, pos_dev.y, rsize );
+ }
+ }
+ }
+
+ PenFinish();
+}
+
+
+void HPGL_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize,
+ double orient, EDA_DRAW_MODE_T trace_mode )
+{
+ wxASSERT( outputFile );
+ wxSize size;
+ int delta;
+ int ox, oy, fx, fy;
+
+ size.x = padsize.x / 2;
+ size.y = padsize.y / 2;
+
+ size.x = (padsize.x - (int) penDiameter) / 2;
+ size.y = (padsize.y - (int) penDiameter) / 2;
+
+ if( size.x < 0 )
+ size.x = 0;
+
+ if( size.y < 0 )
+ size.y = 0;
+
+ // If a dimension is zero, the trace is reduced to 1 line.
+ if( size.x == 0 )
+ {
+ ox = pos.x;
+ oy = pos.y - size.y;
+ RotatePoint( &ox, &oy, pos.x, pos.y, orient );
+ fx = pos.x;
+ fy = pos.y + size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ MoveTo( wxPoint( ox, oy ) );
+ FinishTo( wxPoint( fx, fy ) );
+ return;
+ }
+
+ if( size.y == 0 )
+ {
+ ox = pos.x - size.x;
+ oy = pos.y;
+ RotatePoint( &ox, &oy, pos.x, pos.y, orient );
+ fx = pos.x + size.x;
+ fy = pos.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ MoveTo( wxPoint( ox, oy ) );
+ FinishTo( wxPoint( fx, fy ) );
+ return;
+ }
+
+ ox = pos.x - size.x;
+ oy = pos.y - size.y;
+ RotatePoint( &ox, &oy, pos.x, pos.y, orient );
+ MoveTo( wxPoint( ox, oy ) );
+
+ fx = pos.x - size.x;
+ fy = pos.y + size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ LineTo( wxPoint( fx, fy ) );
+
+ fx = pos.x + size.x;
+ fy = pos.y + size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ LineTo( wxPoint( fx, fy ) );
+
+ fx = pos.x + size.x;
+ fy = pos.y - size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ LineTo( wxPoint( fx, fy ) );
+
+ FinishTo( wxPoint( ox, oy ) );
+
+ if( trace_mode == FILLED )
+ {
+ // Plot in filled mode.
+ delta = (int) (penDiameter - penOverlap);
+
+ if( delta > 0 )
+ while( (size.x > 0) && (size.y > 0) )
+ {
+ size.x -= delta;
+ size.y -= delta;
+
+ if( size.x < 0 )
+ size.x = 0;
+
+ if( size.y < 0 )
+ size.y = 0;
+
+ ox = pos.x - size.x;
+ oy = pos.y - size.y;
+ RotatePoint( &ox, &oy, pos.x, pos.y, orient );
+ MoveTo( wxPoint( ox, oy ) );
+
+ fx = pos.x - size.x;
+ fy = pos.y + size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ LineTo( wxPoint( fx, fy ) );
+
+ fx = pos.x + size.x;
+ fy = pos.y + size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ LineTo( wxPoint( fx, fy ) );
+
+ fx = pos.x + size.x;
+ fy = pos.y - size.y;
+ RotatePoint( &fx, &fy, pos.x, pos.y, orient );
+ LineTo( wxPoint( fx, fy ) );
+
+ FinishTo( wxPoint( ox, oy ) );
+ }
+
+
+ }
+}
+
+
+void HPGL_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
+ double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode )
+{
+ wxPoint polygone[4]; // coordinates of corners relatives to the pad
+ wxPoint coord[4]; // absolute coordinates of corners (coordinates in plotter space)
+ int move;
+
+ move = KiROUND( penDiameter );
+
+ for( int ii = 0; ii < 4; ii++ )
+ polygone[ii] = aCorners[ii];
+
+ // polygone[0] is assumed the lower left
+ // polygone[1] is assumed the upper left
+ // polygone[2] is assumed the upper right
+ // polygone[3] is assumed the lower right
+
+ // Plot the outline:
+ for( int ii = 0; ii < 4; ii++ )
+ {
+ coord[ii] = polygone[ii];
+ RotatePoint( &coord[ii], aPadOrient );
+ coord[ii] += aPadPos;
+ }
+
+ MoveTo( coord[0] );
+ LineTo( coord[1] );
+ LineTo( coord[2] );
+ LineTo( coord[3] );
+ FinishTo( coord[0] );
+
+ // Fill shape:
+ if( aTrace_Mode == FILLED )
+ {
+ // TODO: replace this par the HPGL plot polygon.
+ int jj;
+ // Fill the shape
+ move = KiROUND( penDiameter - penOverlap );
+ // Calculate fill height.
+
+ if( polygone[0].y == polygone[3].y ) // Horizontal
+ {
+ jj = polygone[3].y - (int) ( penDiameter + ( 2 * penOverlap ) );
+ }
+ else // vertical
+ {
+ jj = polygone[3].x - (int) ( penDiameter + ( 2 * penOverlap ) );
+ }
+
+ // Calculation of dd = number of segments was traced to fill.
+ int delta = (int) ( penDiameter - penOverlap );
+
+ if( delta )
+ jj = jj / delta;
+ else
+ jj = 0;
+
+ // Trace the outline.
+ for( ; jj > 0; jj-- )
+ {
+ polygone[0].x += move;
+ polygone[0].y -= move;
+ polygone[1].x += move;
+ polygone[1].y += move;
+ polygone[2].x -= move;
+ polygone[2].y += move;
+ polygone[3].x -= move;
+ polygone[3].y -= move;
+
+ // Test for crossed vertexes.
+ if( polygone[0].x > polygone[3].x ) /* X axis intersection on
+ * vertexes 0 and 3 */
+ {
+ polygone[0].x = polygone[3].x = 0;
+ }
+
+ if( polygone[1].x > polygone[2].x ) /* X axis intersection on
+ * vertexes 1 and 2 */
+ {
+ polygone[1].x = polygone[2].x = 0;
+ }
+
+ if( polygone[1].y > polygone[0].y ) /* Y axis intersection on
+ * vertexes 0 and 1 */
+ {
+ polygone[0].y = polygone[1].y = 0;
+ }
+
+ if( polygone[2].y > polygone[3].y ) /* Y axis intersection on
+ * vertexes 2 and 3 */
+ {
+ polygone[2].y = polygone[3].y = 0;
+ }
+
+ for( int ii = 0; ii < 4; ii++ )
+ {
+ coord[ii] = polygone[ii];
+ RotatePoint( &coord[ii], aPadOrient );
+ coord[ii] += aPadPos;
+ }
+
+ MoveTo( coord[0] );
+ LineTo( coord[1] );
+ LineTo( coord[2] );
+ LineTo( coord[3] );
+ FinishTo( coord[0] );
+ }
+ }
+}