From 039ac92480a09266146fc5b0c9ec67a32a2565ad Mon Sep 17 00:00:00 2001 From: saurabhb17 Date: Wed, 26 Feb 2020 16:04:40 +0530 Subject: Added secondary files --- common/common_plotSVG_functions.cpp | 623 ++++++++++++++++++++++++++++++++++++ 1 file changed, 623 insertions(+) create mode 100644 common/common_plotSVG_functions.cpp (limited to 'common/common_plotSVG_functions.cpp') diff --git a/common/common_plotSVG_functions.cpp b/common/common_plotSVG_functions.cpp new file mode 100644 index 0000000..09b69d1 --- /dev/null +++ b/common/common_plotSVG_functions.cpp @@ -0,0 +1,623 @@ +/** + * @file common_plotPS_functions.cpp + * @brief Kicad: Common plot SVG functions + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.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 + */ + +/* Some info on basic items SVG format, used here: + * The root element of all SVG files is the element. + * + * The element is used to group SVG shapes together. + * Once grouped you can transform the whole group of shapes as if it was a single shape. + * This is an advantage compared to a nested element + * which cannot be the target of transformation by itself. + * + * The element represents a rectangle. + * Using this element you can draw rectangles of various width, height, + * with different stroke (outline) and fill colors, with sharp or rounded corners etc. + * + * + * + * + * + * + * + * The element is used to draw circles. + * + * + * The element is used to draw ellipses. + * An ellipse is a circle that does not have equal height and width. + * Its radius in the x and y directions are different, in other words. + * + * + * The element is used to draw lines. + * + * + * + * + * The element is used to draw multiple connected lines + * Here is a simple example: + * + * + * + * The element is used to draw with multiple (3 or more) sides / edges. + * Here is a simple example: + * + * + * + * The element is used to draw advanced shapes combined from lines and arcs, + * with or without fill. + * It is probably the most advanced and versatile SVG shape of them all. + * It is probably also the hardest element to master. + * + * + * Draw an elliptic arc: it is one of basic path command: + * + * flag_arc_large: 0 = small arc > 180 deg, 1 = large arc > 180 deg + * flag_sweep : 0 = CCW, 1 = CW + * The center of ellipse is automatically calculated. + */ +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * Function XmlEsc + * translates '<' to "<", '>' to ">" and so on, according to the spec: + * http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping + * May be moved to a library if needed generally, but not expecting that. + */ +static wxString XmlEsc( const wxString& aStr, bool isAttribute = false ) +{ + wxString escaped; + + escaped.reserve( aStr.length() ); + + for( wxString::const_iterator it = aStr.begin(); it != aStr.end(); ++it ) + { + const wxChar c = *it; + + switch( c ) + { + case wxS( '<' ): + escaped.append( wxS( "<" ) ); + break; + case wxS( '>' ): + escaped.append( wxS( ">" ) ); + break; + case wxS( '&' ): + escaped.append( wxS( "&" ) ); + break; + case wxS( '\r' ): + escaped.append( wxS( " " ) ); + break; + default: + if( isAttribute ) + { + switch( c ) + { + case wxS( '"' ): + escaped.append( wxS( """ ) ); + break; + case wxS( '\t' ): + escaped.append( wxS( " " ) ); + break; + case wxS( '\n' ): + escaped.append( wxS( " " )); + break; + default: + escaped.append(c); + } + } + else + escaped.append(c); + } + } + + return escaped; +} + + +SVG_PLOTTER::SVG_PLOTTER() +{ + m_graphics_changed = true; + SetTextMode( PLOTTEXTMODE_STROKE ); + m_fillMode = NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR + m_pen_rgb_color = 0; // current color value (black) + m_brush_rgb_color = 0; // current color value (black) + m_dashed = false; +} + + +void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + m_plotMirror = aMirror; + m_yaxisReversed = true; // unlike other plotters, SVG has Y axis reversed + plotOffset = aOffset; + plotScale = aScale; + m_IUsPerDecimil = aIusPerDecimil; + iuPerDeviceUnit = 1.0 / aIusPerDecimil; + /* Compute the paper size in IUs */ + paperSize = pageInfo.GetSizeMils(); + paperSize.x *= 10.0 * aIusPerDecimil; + paperSize.y *= 10.0 * aIusPerDecimil; + SetDefaultLineWidth( 100 * aIusPerDecimil ); // arbitrary default +} + + +void SVG_PLOTTER::SetColor( EDA_COLOR_T color ) +{ + PSLIKE_PLOTTER::SetColor( color ); + + if( m_graphics_changed ) + setSVGPlotStyle(); +} + + +void SVG_PLOTTER::setFillMode( FILL_T fill ) +{ + if( m_fillMode != fill ) + { + m_graphics_changed = true; + m_fillMode = fill; + } +} + + +void SVG_PLOTTER::setSVGPlotStyle() +{ + fputs( "\n\n", outputFile ); + + m_graphics_changed = false; +} + +/* Set the current line width (in IUs) for the next plot + */ +void SVG_PLOTTER::SetCurrentLineWidth( int width ) +{ + int pen_width; + + if( width >= 0 ) + pen_width = width; + else + pen_width = defaultPenWidth; + + if( pen_width != currentPenWidth ) + { + m_graphics_changed = true; + currentPenWidth = pen_width; + } + + if( m_graphics_changed ) + setSVGPlotStyle(); +} + + +/* initialize m_red, m_green, m_blue ( 0 ... 255) + * from reduced values r, g ,b ( 0.0 to 1.0 ) + */ +void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b ) +{ + int red = (int) ( 255.0 * r ); + int green = (int) ( 255.0 * g ); + int blue = (int) ( 255.0 * b ); + long rgb_color = (red << 16) | (green << 8) | blue; + + if( m_pen_rgb_color != rgb_color ) + { + m_graphics_changed = true; + m_pen_rgb_color = rgb_color; + + // Currently, use the same color for brush and pen + // (i.e. to draw and fill a contour) + m_brush_rgb_color = rgb_color; + } +} + + +/** + * SVG supports dashed lines + */ +void SVG_PLOTTER::SetDash( bool dashed ) +{ + if( m_dashed != dashed ) + { + m_graphics_changed = true; + m_dashed = dashed; + } + + if( m_graphics_changed ) + setSVGPlotStyle(); +} + + +void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) ); + rect.Normalize(); + DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() ); + DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() ); + DSIZE size_dev = end_dev - org_dev; + // Ensure size of rect in device coordinates is > 0 + // I don't know if this is a SVG issue or a Inkscape issue, but + // Inkscape has problems with negative or null values for width and/or height, so avoid them + DBOX rect_dev( org_dev, size_dev); + rect_dev.Normalize(); + + setFillMode( fill ); + SetCurrentLineWidth( width ); + + // Rectangles having a 0 size value for height or width are just not drawn on Inscape, + // so use a line when happens. + if( rect_dev.GetSize().x == 0.0 || rect_dev.GetSize().y == 0.0 ) // Draw a line + fprintf( outputFile, + "\n", + rect_dev.GetPosition().x, rect_dev.GetPosition().y, + rect_dev.GetEnd().x, rect_dev.GetEnd().y + ); + + else + fprintf( outputFile, + "\n", + rect_dev.GetPosition().x, rect_dev.GetPosition().y, + rect_dev.GetSize().x, rect_dev.GetSize().y, + 0.0 // radius of rounded corners + ); +} + + +void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width ) +{ + DPOINT pos_dev = userToDeviceCoordinates( pos ); + double radius = userToDeviceSize( diametre / 2.0 ); + + setFillMode( fill ); + SetCurrentLineWidth( width ); + + fprintf( outputFile, + " \n", + pos_dev.x, pos_dev.y, radius ); +} + + +void SVG_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, + FILL_T fill, int width ) +{ + /* Draws an arc of a circle, centred on (xc,yc), with starting point + * (x1, y1) and ending at (x2, y2). The current pen is used for the outline + * and the current brush for filling the shape. + * + * The arc is drawn in an anticlockwise direction from the start point to + * the end point + */ + + if( radius <= 0 ) + return; + + if( StAngle > EndAngle ) + std::swap( StAngle, EndAngle ); + + setFillMode( fill ); + SetCurrentLineWidth( width ); + + // Calculate start point. + DPOINT centre_dev = userToDeviceCoordinates( centre ); + double radius_dev = userToDeviceSize( radius ); + + if( !m_yaxisReversed ) // Should be never the case + { + double tmp = StAngle; + StAngle = -EndAngle; + EndAngle = -tmp; + } + + if( m_plotMirror ) + { + if( m_mirrorIsHorizontal ) + { + StAngle = 1800.0 -StAngle; + EndAngle = 1800.0 -EndAngle; + std::swap( StAngle, EndAngle ); + } + else + { + StAngle = -StAngle; + EndAngle = -EndAngle; + } + } + + DPOINT start; + start.x = radius_dev; + RotatePoint( &start.x, &start.y, StAngle ); + DPOINT end; + end.x = radius_dev; + RotatePoint( &end.x, &end.y, EndAngle ); + start += centre_dev; + end += centre_dev; + + double theta1 = DECIDEG2RAD( StAngle ); + + if( theta1 < 0 ) + theta1 = theta1 + M_PI * 2; + + double theta2 = DECIDEG2RAD( EndAngle ); + + if( theta2 < 0 ) + theta2 = theta2 + M_PI * 2; + + if( theta2 < theta1 ) + theta2 = theta2 + M_PI * 2; + + int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees + + if( fabs( theta2 - theta1 ) > M_PI ) + flg_arc = 1; + + int flg_sweep = 0; // flag for sweep always 0 + + // Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves) + // params are start point, radius1, radius2, X axe rotation, + // flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg), + // sweep arc ( 0 = CCW, 1 = CW), + // end point + fprintf( outputFile, "\n", + start.x, start.y, radius_dev, radius_dev, + flg_arc, flg_sweep, + end.x, end.y ); +} + + +void SVG_PLOTTER::PlotPoly( const std::vector& aCornerList, + FILL_T aFill, int aWidth ) +{ + if( aCornerList.size() <= 1 ) + return; + + setFillMode( aFill ); + SetCurrentLineWidth( aWidth ); + + switch( aFill ) + { + case NO_FILL: + fprintf( outputFile, " \n" ); +} + + +/** + * Postscript-likes at the moment are the only plot engines supporting bitmaps... + */ +void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos, + double aScaleFactor ) +{ + // in svg file we must insert a link to a png image file to plot an image + // the image itself is not included in the svg file. + // So we prefer skip the image, and just draw a rectangle, + // like other plotters which do not support images + + PLOTTER::PlotImage( aImage, aPos, aScaleFactor ); + +} + + +void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume ) +{ + if( plume == 'Z' ) + { + if( penState != 'Z' ) + { + fputs( "\" />\n", outputFile ); + penState = 'Z'; + penLastpos.x = -1; + penLastpos.y = -1; + } + + return; + } + + if( penState == 'Z' ) // here plume = 'D' or 'U' + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + + // Ensure we do not use a fill mode when moving tne pen, + // in SVG mode (i;e. we are plotting only basic lines, not a filled area + if( m_fillMode != NO_FILL ) + { + setFillMode( NO_FILL ); + setSVGPlotStyle(); + } + + fprintf( outputFile, "\n", + " \n", + "\n", + (double) paperSize.x / m_IUsPerDecimil * 2.54 / 10000, + (double) paperSize.y / m_IUsPerDecimil * 2.54 / 10000, + origin.x, origin.y, + (int) ( paperSize.x / m_IUsPerDecimil ), + (int) ( paperSize.y / m_IUsPerDecimil) ); + + // Write title + char date_buf[250]; + time_t ltime = time( NULL ); + strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S", + localtime( <ime ) ); + + fprintf( outputFile, + "SVG Picture created as %s date %s \n", + TO_UTF8( XmlEsc( wxFileName( filename ).GetFullName() ) ), date_buf ); + // End of header + fprintf( outputFile, " Picture generated by %s \n", + TO_UTF8( XmlEsc( creator ) ) ); + + // output the pen and brush color (RVB values in hex) and opacity + double opacity = 1.0; // 0.0 (transparent to 1.0 (solid) + fprintf( outputFile, + "\n", outputFile ); + return true; +} + + +bool SVG_PLOTTER::EndPlot() +{ + fputs( " \n\n", outputFile ); + fclose( outputFile ); + outputFile = NULL; + + return true; +} + + +void SVG_PLOTTER::Text( const wxPoint& aPos, + enum EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + bool aMultilineAllowed ) +{ + setFillMode( NO_FILL ); + SetColor( aColor ); + SetCurrentLineWidth( aWidth ); + + // TODO: see if the postscript native text code can be used in SVG plotter + + PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, + aWidth, aItalic, aBold, aMultilineAllowed ); +} -- cgit