package net.sourceforge.fidocadj.primitives; import java.io.*; import java.util.*; import net.sourceforge.fidocadj.dialogs.*; import net.sourceforge.fidocadj.export.*; import net.sourceforge.fidocadj.geom.*; import net.sourceforge.fidocadj.globals.*; import net.sourceforge.fidocadj.graphic.*; /** Class to handle the PCB pad primitive. <pre> This file is part of FidoCadJ. FidoCadJ 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 3 of the License, or (at your option) any later version. FidoCadJ 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 FidoCadJ. If not, @see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>. Copyright 2007-2014 by Davide Bucci </pre> @author Davide Bucci */ public final class PrimitivePCBPad extends GraphicPrimitive { private int rx; private int ry; private int sty; private int ri; // The radius of the rounded corner in logical units. This is hardcoded // here as it has been done for FidoCadJ, but one may consider let this // value to be changed by the user interactively private static final int CORNER_DIAMETER = 5; private boolean drawOnlyPads; // A PCB pad is defined by one points, plus text tags static final int N_POINTS=3; // Those are data which are kept for the fast redraw of this primitive. // Basically, they are calculated once and then used as much as possible // without having to calculate everything from scratch. private int x1, y1, rrx, rry, xa,ya, rox, roy, rix, riy; private int rrx2, rry2, rix2, riy2; /** Gets the number of control points used. @return the number of points used by the primitive */ public int getControlPointNumber() { return N_POINTS; } /** Create a PCB pad @param x1 the x coordinate (logical unit). @param y1 the y coordinate (logical unit). @param wx the width of the pad @param wy the height of the pad @param radi the internal radius. @param st the style of the pad @param layer the layer to be used. @param f the name of the font for attached text. @param size the size of the font for attached text. */ public PrimitivePCBPad(int x1, int y1, int wx, int wy, int radi, int st, int layer, String f, int size) { super(); initPrimitive(-1, f, size); virtualPoint[0].x=x1; virtualPoint[0].y=y1; virtualPoint[getNameVirtualPointNumber()].x=x1+5; virtualPoint[getNameVirtualPointNumber()].y=y1+5; virtualPoint[getValueVirtualPointNumber()].x=x1+5; virtualPoint[getValueVirtualPointNumber()].y=y1+10; rx=wx; ry=wy; ri=radi; sty=st; setLayer(layer); } /** Constructor. Create an empty PCBPad object with all the sizes and dimensions set to zero. @param f the name of the font for attached text. @param size the size of the font for attached text. */ public PrimitivePCBPad(String f, int size) { super(); rx=0; ry=0; sty=0; ri=0; initPrimitive(-1, f, size); } /** Check if the holes should be drawn. @return true for a PCBpad. */ @Override public boolean needsHoles() { return true; } /** Draw the graphic primitive on the given graphic context. @param g the graphic context in which the primitive should be drawn. @param coordSys the graphic coordinates system to be applied. @param layerV the layer description. */ public void draw(GraphicsInterface g, MapCoordinates coordSys, Vector layerV) { if(!selectLayer(g,layerV)) return; drawText(g, coordSys, layerV, -1); if(changed) { changed=false; /* in the PCB pad primitive, the the virtual points represent the position of the pad to be drawn. */ x1=virtualPoint[0].x; y1=virtualPoint[0].y; xa=coordSys.mapXi(x1,y1,false); ya=coordSys.mapYi(x1,y1,false); rrx=Math.abs(xa-coordSys.mapXi(x1+rx,y1+ry, false)); rry=Math.abs(ya-coordSys.mapYi(x1+rx,y1+ry, false)); rrx2 = rrx/2; rry2 = rry/2; coordSys.trackPoint(xa-rrx2,ya-rry2); coordSys.trackPoint(xa+rrx2,ya+rry2); rox=Math.abs(xa-coordSys.mapXi(x1+CORNER_DIAMETER, y1+CORNER_DIAMETER, false)); roy=Math.abs(ya-coordSys.mapYi(x1+CORNER_DIAMETER, y1+CORNER_DIAMETER, false)); rix=Math.abs(xa-coordSys.mapXi(x1+ri,y1+ri, false)); riy=Math.abs(ya-coordSys.mapYi(x1+ri,y1+ri, false)); rix2 = rix/2; riy2 = riy/2; } // Exit if the primitive is offscreen. This is a simplification, but // ensures that the primitive is correctly drawn when it is // partially visible. if(!g.hitClip(xa-rrx2,ya-rry2, rrx, rry)) return; g.applyStroke(1, 0); if (drawOnlyPads) { g.setColor(g.getColor().white()); // Drill the hole g.fillOval(xa-rix2, ya-riy2,rix,riy); } else { switch(sty) { case 1: // Rectangular pad g.fillRect(xa-rrx2, ya-rry2,rrx,rry); break; case 2: // Rounded corner rectangular pad g.fillRoundRect(xa-rrx2, ya-rry2,rrx,rry,rox,roy); break; case 0: //NOPMD default: // Oval Pad g.fillOval(xa-rrx2, ya-rry2,rrx,rry); break; } } } /** Parse a token array and store the graphic data for a given primitive Obviously, that routine should be called *after* having recognized that the called primitive is correct. That routine also sets the current layer. @param tokens the tokens to be processed. tokens[0] should be the command of the actual primitive. @param N the number of tokens present in the array. @throws IOException if the arguments are incorrect or the primitive is invalid. */ public void parseTokens(String[] tokens, int N) throws IOException { changed=true; // assert it is the correct primitive if (tokens[0].equals("PA")) { // PCB Area pad /* Example PA 752 50 15 15 4 1 1 */ if (N<7) { IOException E=new IOException("bad arguments on PA"); throw E; } // Load the points in the virtual points associated to the // current primitive. int x1 = virtualPoint[0].x=Integer.parseInt(tokens[1]); int y1 = virtualPoint[0].y=Integer.parseInt(tokens[2]); virtualPoint[getNameVirtualPointNumber()].x=x1+5; virtualPoint[getNameVirtualPointNumber()].y=y1+5; virtualPoint[getValueVirtualPointNumber()].x=x1+5; virtualPoint[getValueVirtualPointNumber()].y=y1+10; rx=Integer.parseInt(tokens[3]); ry=Integer.parseInt(tokens[4]); ri=Integer.parseInt(tokens[5]); sty=Integer.parseInt(tokens[6]); if(N>7) parseLayer(tokens[7]); } else { IOException E=new IOException("PA: Invalid primitive:"+tokens[0]+ " programming error?"); throw E; } } /** Set the draw only pads flag. @param pd the value of the flag. */ public void setDrawOnlyPads(boolean pd) { drawOnlyPads=pd; } /** Get the control parameters of the given primitive. @return a vector of ParameterDescription containing each control parameter. The first parameters should always be the virtual points. */ public Vector<ParameterDescription> getControls() { Vector<ParameterDescription> v=super.getControls(); ParameterDescription pd = new ParameterDescription(); pd.parameter= Integer.valueOf(rx); pd.description=Globals.messages.getString("ctrl_x_radius"); v.add(pd); pd = new ParameterDescription(); pd.parameter= Integer.valueOf(ry); pd.description=Globals.messages.getString("ctrl_y_radius"); v.add(pd); pd = new ParameterDescription(); pd.parameter= Integer.valueOf(ri); pd.description=Globals.messages.getString("ctrl_internal_radius"); v.add(pd); pd = new ParameterDescription(); pd.parameter= Integer.valueOf(sty); // A list should be better pd.description=Globals.messages.getString("ctrl_pad_style"); v.add(pd); return v; } /** Set the control parameters of the given primitive. This method is specular to getControls(). @param v a vector of ParameterDescription containing each control parameter. The first parameters should always be the virtual points. @return the next index in v to be scanned (if needed) after the execution of this function. */ public int setControls(Vector<ParameterDescription> v) { int i=super.setControls(v); ParameterDescription pd; pd=(ParameterDescription)v.get(i); ++i; // Check, just for sure... if (pd.parameter instanceof Integer) rx=((Integer)pd.parameter).intValue(); else System.out.println("Warning: unexpected parameter!"+pd); pd=(ParameterDescription)v.get(i); ++i; // Check, just for sure... if (pd.parameter instanceof Integer) ry=((Integer)pd.parameter).intValue(); else System.out.println("Warning: unexpected parameter!"+pd); pd=(ParameterDescription)v.get(i); ++i; // Check, just for sure... if (pd.parameter instanceof Integer) ri=((Integer)pd.parameter).intValue(); else System.out.println("Warning: unexpected parameter!"+pd); pd=(ParameterDescription)v.get(i); ++i; // Check, just for sure... if (pd.parameter instanceof Integer) sty=((Integer)pd.parameter).intValue(); else System.out.println("Warning: unexpected parameter!"+pd); return i; } /** Rotate the primitive. Here we just rotate 90° by 90° by swapping the x and y size of the pad @param bCounterClockWise specify if the rotation should be done counterclockwise. @param ix the x coordinate of the rotation point. @param iy the y coordinate of the rotation point. */ public void rotatePrimitive(boolean bCounterClockWise, int ix, int iy) { super.rotatePrimitive(bCounterClockWise, ix, iy); int swap=rx; rx=ry; ry=swap; } /** Gets the distance (in primitive's coordinates space) between a given point and the primitive. When it is reasonable, the behaviour can be binary (polygons, ovals...). In other cases (lines, points), it can be proportional. @param px the x coordinate of the given point. @param py the y coordinate of the given point. @return the distance in logical units. */ public int getDistanceToPoint(int px, int py) { // Here we check if the given point lies inside the text areas if(checkText(px, py)) return 0; int distance=GeometricDistances.pointToPoint( virtualPoint[0].x,virtualPoint[0].y, px,py)-Math.min(rx,ry)/2; return distance>0?distance:0; } /** Obtain a string command descripion of the primitive. @param extensions true if FidoCadJ extensions to the old FidoCAD format should be active. @return the FIDOCAD command line. */ public String toString(boolean extensions) { String s = "PA "+virtualPoint[0].x+" "+virtualPoint[0].y+" "+ rx+" "+ry+" "+ri+" "+sty+" "+getLayer()+"\n"; s+=saveText(extensions); return s; } /** Export the primitive on a vector graphic format. @param exp the export interface to employ. @param cs the coordinate mapping to employ. @throws IOException if a problem occurs, such as it is impossible to write on the output file. */ public void export(ExportInterface exp, MapCoordinates cs) throws IOException { exportText(exp, cs, -1); exp.exportPCBPad(cs.mapX(virtualPoint[0].x,virtualPoint[0].y), cs.mapY(virtualPoint[0].x,virtualPoint[0].y), sty, Math.abs(cs.mapX(virtualPoint[0].x+rx, virtualPoint[0].y+ry)- cs.mapX(virtualPoint[0].x,virtualPoint[0].y)), Math.abs(cs.mapY(virtualPoint[0].x+rx, virtualPoint[0].y+ry)- cs.mapY(virtualPoint[0].x,virtualPoint[0].y)), (int)(ri*cs.getXMagnitude()), getLayer(),drawOnlyPads); } /** Get the number of the virtual point associated to the Name property @return the number of the virtual point associated to the Name property */ public int getNameVirtualPointNumber() { return 1; } /** Get the number of the virtual point associated to the Value property @return the number of the virtual point associated to the Value property */ public int getValueVirtualPointNumber() { return 2; } }