/* * File : PolygonDrawPanel.java * Created : 17-may-2002 10:20 * By : allastar * * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2005 Francesc Busquets & Departament * d'Educacio de la Generalitat de Catalunya * * 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 (see the LICENSE file). */ package edu.xtec.jclic.shapers; import edu.xtec.util.StrUtils; import java.awt.Cursor; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; import java.awt.event.*; import java.awt.geom.*; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; /** * * @author allastar * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.09.17 */ public class PolygonDrawPanel implements java.awt.event.MouseMotionListener, java.awt.event.MouseListener{ private List<EditableShape> vShapes; //List that contains the polygon currently being modified private static List<EditableShape> vCopied; private List<EditableShape> vRedrawingLines,vRedrawingLinesBeforeModify,vShapeBeforeModify; private List<PointListener> vPointListeners; private double iniX,iniY,finX,finY,lastFinX,lastFinY; private Point2D iniPoint=null,lastPoint=null; private boolean bSelectedPoint=false; private double zoomX=0,zoomY=0,zoomH=-1,zoomW=-1; private boolean creatingRect=false,creatingEllipse=false, creatingPolygon=false, bRedrawingLines=false, bSelectingArea=false; private boolean bResizing=false; private int resizingDirection=NO_RESIZING; private boolean bSpecialLine=false; private boolean bMoving=false; private EditableShape specialLine=null; private int drawingMode=SELECTING; static double defaultSensibility=1.5; private AffineTransform at; private List<Rectangle> vDrawnBorders=null; private Shape current=null; private JComponent container; private short INITIAL=0; private short END=1; public static final int SELECTING=1; public static final int MOVING=2; public static final int NEW_POINT=4; public static final int DRAWING_RECT=5; public static final int DRAWING_ELLIPSE=6; public static final int DRAWING_POLYGON=7; public static final int ZOOM=12; public static final int NO_RESIZING=-1; public static final int EAST=0; public static final int SOUTH=1; public static final int SOUTH_EAST=2; private int backgroundComposite=0; protected HolesEditorPanel hep; protected boolean canResize; protected Rectangle lastPreviewArea; static Cursor[] cursors=null; public static final int PEN_CURSOR=0; public static final int CIRCLE_CURSOR=1; protected Shape esborram=null; /** Creates new PolygonDrawPanel */ public PolygonDrawPanel(int width, int height, HolesEditorPanel hep, boolean canResize) { this.hep=hep; this.canResize=canResize; vShapes=new ArrayList<EditableShape>(); if(vCopied==null) vCopied=new ArrayList<EditableShape>(); vRedrawingLines=new ArrayList<EditableShape>(); vRedrawingLinesBeforeModify=new ArrayList<EditableShape>(); vPointListeners=new ArrayList<PointListener>(); at=new AffineTransform(); initDrawnBorders(); if(cursors==null){ cursors=new Cursor[2]; Toolkit tk=Toolkit.getDefaultToolkit(); cursors[PEN_CURSOR]=tk.createCustomCursor(edu.xtec.util.ResourceManager.getImageIcon("cursors/llapis.gif").getImage(), new Point(12,24), "pen"); cursors[CIRCLE_CURSOR]=tk.createCustomCursor(edu.xtec.util.ResourceManager.getImageIcon("cursors/cercle.gif").getImage(), new Point(16,16), "circle"); } hep.addKeyListener(new PolygonDrawPanel.KeyHandler()); } public void setDrawingMode(int drawingMode){ if(this.drawingMode!=drawingMode){ this.drawingMode=drawingMode; if(creatingPolygon) joinPolygon(); if(drawingMode!=NEW_POINT && drawingMode!=SELECTING) { endPolygon(); } hep.repaint(0); } } public int getVisibleWidth(){ return hep.getPreviewPanel().getWidth(); } public int getVisibleHeight(){ return hep.getPreviewPanel().getHeight(); } public void initDrawnBorders(){ if(vDrawnBorders!=null) vDrawnBorders.clear(); else vDrawnBorders=new ArrayList<Rectangle>(); for(int i=0;i<hep.getNumShapes();i++){ if(i!=hep.currentShape){ Shape s=hep.getHoles().getShape(i,hep.previewArea); if(s!=null) vDrawnBorders.addAll(getBorders(s)); } } } private List<Rectangle> getBorders(Shape s){ // Utility function that returns the points that define the "segments" of the polygon 's'. int xIni=0; int yIni=0; if(s==null) return null; List<Rectangle> vPoints=new ArrayList<Rectangle>(); double x,y; if(s instanceof GeneralPath){ GeneralPath gp=(GeneralPath)s; PathIterator it=gp.getPathIterator(new AffineTransform()); double[] coords=new double[6]; while (!it.isDone()){ int type=it.currentSegment(coords); switch (type){ case PathIterator.SEG_MOVETO: x=coords[0]; y=coords[1]; vPoints.add(new Rectangle((int)(x+xIni)-(EditableShapeConstants.selectLength/2),(int)(y+yIni)-(EditableShapeConstants.selectLength/2),EditableShapeConstants.selectLength,EditableShapeConstants.selectLength)); break; case PathIterator.SEG_LINETO: x=coords[0]; y=coords[1]; vPoints.add(new Rectangle((int)(x+xIni)-(EditableShapeConstants.selectLength/2),(int)(y+yIni)-(EditableShapeConstants.selectLength/2),EditableShapeConstants.selectLength,EditableShapeConstants.selectLength)); break; case PathIterator.SEG_CUBICTO: x=coords[4]; y=coords[5]; vPoints.add(new Rectangle((int)(x+xIni)-(EditableShapeConstants.selectLength/2),(int)(y+yIni)-(EditableShapeConstants.selectLength/2),EditableShapeConstants.selectLength,EditableShapeConstants.selectLength)); break; case PathIterator.SEG_QUADTO: x=coords[2]; y=coords[3]; vPoints.add(new Rectangle((int)(x+xIni)-(EditableShapeConstants.selectLength/2),(int)(y+yIni)-(EditableShapeConstants.selectLength/2),EditableShapeConstants.selectLength,EditableShapeConstants.selectLength)); break; case PathIterator.SEG_CLOSE: break; default: } it.next(); } } return vPoints; } public void paint(java.awt.Graphics2D g){ Graphics2D g2d=(Graphics2D)g; if(EditableShapeConstants.showDrawnPoints) paintDrawnBorders(g); for(EditableShape esh : vShapes){ if(bSpecialLine && esh==specialLine) esh.paintWithColor(g,drawingMode,EditableShapeConstants.CUT_COLOR); else esh.paintWithColor(g,drawingMode,EditableShapeConstants.ACTIVE_COLOR); } if(bMoving) paintMoved(g); if(creatingRect){ g.setColor(EditableShapeConstants.selectedColor); EditableRectangle rect=new EditableRectangle((int)iniX,(int)iniY,(int)(finX-iniX),(int)(finY-iniY)); rect.paintWithColor(g,drawingMode,EditableShapeConstants.selectedColor); } if(creatingEllipse){ g.setColor(EditableShapeConstants.selectedColor); EditableEllipse2D ellipse=new EditableEllipse2D((int)iniX,(int)iniY,(int)(finX-iniX),(int)(finY-iniY)); ellipse.paintWithColor(g,drawingMode,EditableShapeConstants.selectedColor); } if(creatingPolygon){ if(lastPoint!=null){ EditableLine2D el=new EditableLine2D(lastPoint.getX(),lastPoint.getY(),finX,finY); el.paintWithColor(g,drawingMode,EditableShapeConstants.selectedColor); } } } public void drawGrid(java.awt.Graphics g, int gridWidth){ if(gridWidth<=1) return; int width=(int)(hep.previewArea.getWidth()); int height=(int)(hep.previewArea.getHeight()); g.setColor(EditableShapeConstants.gridColor); for (double i=hep.previewArea.x;i<=hep.previewArea.x+width;i+=(gridWidth*hep.xFactor)){ //from 0 in order to avoid changes in the location of the grid when zoomed //vertical g.drawLine((int)i,hep.previewArea.y,(int)i,(int)(hep.previewArea.y+height)); } for (double i=hep.previewArea.y;i<=hep.previewArea.y+height;i+=(gridWidth*hep.yFactor)){ //horitzontal g.drawLine(hep.previewArea.x,(int)i,(int)(hep.previewArea.x+width),(int)i); } } protected void paintDrawnBorders(java.awt.Graphics2D g){ for(Rectangle r : vDrawnBorders){ double x=r.getX(); double y=r.getY(); double w=r.getWidth(); double h=r.getHeight(); x=x+(w/4); y=y+(h/4); w=w/2; h=h/2; g.setColor(EditableShapeConstants.DRAWN_BORDER_COLOR); g.fillRect((int)x,(int)y,(int)w,(int)h); } } private void paintMoved(java.awt.Graphics2D g){ for(EditableShape esh : vCopied){ EditableShape copied=(EditableShape)esh.clone(); copied.transform(AffineTransform.getTranslateInstance(finX-iniX,finY-iniY)); copied.paintWithColor(g,drawingMode,EditableShapeConstants.movingColor); } } public void updateView(){ List v=getGeneralPath(); if(lastPreviewArea==null) lastPreviewArea=hep.previewArea; if(v.size()>0){ move(hep.previewArea.x-lastPreviewArea.x,hep.previewArea.y-lastPreviewArea.y,false,false); } try{ lastPreviewArea=(Rectangle)(hep.previewArea.clone()); } catch(Exception e){ System.err.println("Error updating view:\n"+e); } } public void setShapeData(ShapeData sd, double x, double y, double scaleX, double scaleY){ //x,y indicate the current position clean(); current=(sd!=null)?sd.getShape(hep.previewArea):null; double firstX=-1,firstY=-1; if(sd!=null && sd.primitiveType>=0 && sd.primitivePoints!=null && sd.primitivePoints.length>3){ EditableShape es; double xTr=(sd.primitivePoints[0]*hep.previewArea.getWidth())+hep.previewArea.getX(); double yTr=(sd.primitivePoints[1]*hep.previewArea.getHeight())+hep.previewArea.getY(); double wSc=sd.primitivePoints[2]*hep.previewArea.getWidth(); double hSc=sd.primitivePoints[3]*hep.previewArea.getHeight(); switch (sd.primitiveType){ case ShapeData.RECTANGLE: es=new EditableRectangle((int)xTr,(int)yTr,(int)wSc,(int)hSc); vShapes.add(es); break; case ShapeData.ELLIPSE: es=new EditableEllipse2D((int)xTr,(int)yTr,(int)wSc,(int)hSc); vShapes.add(es); break; } } else if(sd!=null){ Shape s=sd.getShape(hep.previewArea); if(s instanceof GeneralPath){ GeneralPath gp=(GeneralPath)s; PathIterator it=gp.getPathIterator(new AffineTransform()); double[] coords=new double[6]; while (!it.isDone()){ int type=it.currentSegment(coords); switch (type){ case PathIterator.SEG_MOVETO: x=coords[0]; y=coords[1]; if(firstX==-1){ //To close firstX=x; firstY=y; } break; case PathIterator.SEG_LINETO: vShapes.add(new EditableLine2D(x,y,coords[0],coords[1])); x=coords[0]; y=coords[1]; break; case PathIterator.SEG_CUBICTO: vShapes.add(new EditableCubicCurve2D(x,y,coords[0],coords[1],coords[2],coords[3],coords[4],coords[5])); x=coords[4]; y=coords[5]; break; case PathIterator.SEG_QUADTO: vShapes.add(new EditableQuadCurve2D(x,y,coords[0],coords[1],coords[2],coords[3])); x=coords[2]; y=coords[3]; break; case PathIterator.SEG_CLOSE: if(firstX!=-1 && (x!=firstX || y!=firstY)){ vShapes.add(new EditableLine2D(x,y,firstX,firstY)); x=firstX;y=firstY; } break; default: break; } it.next(); } if(firstX!=-1 && (x!=firstX || y!=firstY)){ // That's to be sure that the shape is always closed vShapes.add(new EditableLine2D(x,y,firstX,firstY)); } } } removeDrawnBorders(sd); } public void clean(){ vShapes=new ArrayList<EditableShape>(); vRedrawingLines=new ArrayList<EditableShape>(); vRedrawingLinesBeforeModify=new ArrayList<EditableShape>(); } public boolean selectDrawnShape(Point2D p) { endPolygon(); for (int i=0;i<hep.getNumShapes();i++){ Shape s=hep.getHoles().getShape(i,hep.previewArea); if(s.contains(p) && hep.currentShape!=i){ hep.setCurrentShape(i); setShapeData(hep.getHoles().getShapeData(i),0,0,1,1); return true; } } hep.setCurrentShape(hep.getHoles().getNumCells()+1); clean(); return false; } public void selectShape(int iIndex){ if(iIndex<0) return; ShapeData sd=hep.getHoles().getShapeData(iIndex); if(sd!=null){ setShapeData(sd,0,0,1,1); } } private EditableShape aproximationToLine(double x, double y){ return aproximationToLine(x,y,null); } private EditableShape aproximationToLine(double x, double y, List<EditableShape> vRedrawingLines){ //returns an EditableShape when there is a corner at (x,y) that not belongs to the lines in //the Rectangle List vRedrawingLines. Otherwise returns null if (vRedrawingLines != null) { for (EditableShape esh : vRedrawingLines) { if (!vRedrawingLines.contains(esh)) { if (esh.hasClickedBorder((int) x, (int) y, false)) { return esh; } } } } return null; } private Point2D getTransformedPoint(Point2D p, boolean mustBeOnGrid){ //mustBeOnGrid is used to discard the approach Point2D mousePoint=new Point2D.Double(p.getX(),p.getY()); if(EditableShapeConstants.gridWidth!=-1 && EditableShapeConstants.pointsOnGrid && mustBeOnGrid) moveToGrid(mousePoint); return mousePoint; } private void moveToGrid(Point2D p){ //Moves the supplied point to the closest grid point int x=(int)p.getX(); int y=(int)p.getY(); x-=hep.previewArea.getX(); y-=hep.previewArea.getY(); double wd=EditableShapeConstants.gridWidth*hep.xFactor; int w=(int)wd; if(w==-1) return; int xLeft=(int)(((int)(x/wd))*wd); if((x-xLeft)<(w/2)) x=xLeft; else x=(int)(((int)((x+w-1)/wd))*wd); int yUp=(int)(((int)(y/wd))*wd); if((y-yUp)<(w/2)) y=yUp; else y=(int)(((int)((y+w-1)/wd))*wd); x+=hep.previewArea.getX(); y+=hep.previewArea.getY(); p.setLocation(x,y); } public Point2D aproximationToDrawnBorder(double x, double y){ // Returns true if a rectangle in vDrawnBorders contains the point (x,y) for(Rectangle2D r : vDrawnBorders){ if(r.contains(x,y)) return new Point2D.Double(r.getX()+(r.getWidth()/2),r.getY()+(r.getHeight()/2)); } return null; } protected void redrawingLines(double x, double y){ //moves all the selected shapes containing (x,y) in one of its corners for(EditableShape esh : vRedrawingLines) esh.changeBorder(x,y); } private void cleanZoom(){ zoomX=0; zoomY=0; zoomW=-1; zoomH=-1; at=new AffineTransform(); cancelCurrentOperations(); bSelectingArea=false; hep.repaint(0); } public void cancelCurrentOperations(){ creatingRect=false; creatingEllipse=false; } public void cut(double x, double y){ copy(false); clean(); bMoving=true; iniX=x; iniY=y; finX=iniX; finY=iniY; } public void cut(){ cut(-1,-1); } public void copy(boolean needSelected){ vCopied.clear(); for(EditableShape esh : vShapes){ esh=(EditableShape)esh.clone(); if(!needSelected || esh.isSelected()){ vCopied.add(esh); } } } public void paste(){ bMoving=true; iniX=-1; iniY=-1; finX=-1; finY=-1; paste(5,5); } public void paste(double x, double y){ List<EditableShape> newCopied=new ArrayList<EditableShape>(); deSelectAll(); for(EditableShape esh : vCopied){ EditableShape copied=(EditableShape)esh.clone(); copied.transform(AffineTransform.getTranslateInstance(x,y)); copied.setSelected(true); vShapes.add(copied); newCopied.add(copied); //to avoid overlap of shapes when the user makes "paste" two times } vCopied=newCopied; } public void deSelectAll(){ for(EditableShape esh : vShapes){ esh.setSelected(false); } bSelectedPoint=false; hep.repaint(0); } private EditableShape nearestLine(double x, double y){ EditableShape nearest=null; double distance=0; double currentDistance; for(EditableShape esh : vShapes){ currentDistance=esh.distanceTo(x,y); if(nearest==null || (currentDistance<distance)){ distance=currentDistance; nearest=esh; } } return nearest; } private void clicatISeleccionada(int x, int y, boolean needSelected){ //With needSelected=false it's not necessary to have selected a shape in order to drag it // leaves in vRedrawingLines the selected lines with one point near the supplied co-ordinates (x,y) Point2D redrawingPoint=null; vRedrawingLines.clear(); for(EditableShape esh : vShapes){ if((!needSelected || esh.isSelected()) && esh.hasClickedBorder(x,y,needSelected)){ Point2D p=esh.getNearestBorder(x,y); if(redrawingPoint==null || redrawingPoint.equals(p)){ redrawingPoint=p; vRedrawingLines.add(esh); } } } vRedrawingLinesBeforeModify=cloneVector(vRedrawingLines); } private List<EditableShape> cloneVector(List<EditableShape> v){ List<EditableShape> vClone=new ArrayList<EditableShape>(); if(v!=null){ for(EditableShape es : v){ vClone.add((EditableShape)es.clone()); } } return vClone; } private void divideShape(EditableShape specialLine,double x, double y){ if(specialLine!=null){ EditableShape[] shapes=specialLine.divide(x,y); if(shapes!=null){ // -> The two following lines are necessary in order to grant the connection of the resulting shape // Dividing two times in the same point can create an independent line out of the shape List<EditableShape> vCheckPoint=new ArrayList<EditableShape>(); vCheckPoint.addAll(vShapes); vShapes.remove(specialLine); for (int i=0;i<shapes.length;i++){ if(shapes[i]!=null) vShapes.add(shapes[i]); } boolean bValidate=validateShape(); if(!bValidate) vShapes=vCheckPoint; } hep.updateView(); } hep.repaint(0); } private boolean validateShape(){ return (getGeneralPath().size()==1); } public List<GeneralPath> getGeneralPath(){ List<GeneralPath> vGpaths=new ArrayList<GeneralPath>(); GeneralPath currentPolygon=new GeneralPath(); List<EditableShape> shapes=new ArrayList<EditableShape>(); shapes.addAll(vShapes); if(!(shapes.size()>0)) return vGpaths; EditableShape esh=shapes.get(0); shapes.remove(esh); currentPolygon.append(esh,true); short notUsedPoint=END; //indicates the side of the last shape non-adjacent to anyone while (shapes.size()>0){ EditableShape shape=getAdjacent(shapes,esh,notUsedPoint); if(shape!=null){ currentPolygon.append(shape,true); notUsedPoint=getNotUsed(esh,shape); //returns the point of the shape non-adjacent to "current" shapes.remove(shape); esh=shape; } else{ vGpaths.add(currentPolygon); currentPolygon=new GeneralPath(); notUsedPoint=END; esh=(EditableShape)shapes.get(0); shapes.remove(esh); currentPolygon.append(esh,true); } } vGpaths.add(currentPolygon); return vGpaths; } private short getNotUsed(EditableShape current, EditableShape shape){ //returns the point of the shape non-adjacent to "current" if(shape.getInitialPoint().equals(current.getInitialPoint())||shape.getInitialPoint().equals(current.getEndPoint())) return END; else return INITIAL; } private EditableShape getAdjacent(List<EditableShape> shapes, EditableShape sh, short notUsedPoint){ Point2D p; if(notUsedPoint==INITIAL) p=sh.getInitialPoint(); else p=sh.getEndPoint(); for(EditableShape shape : shapes){ if(shape.isAdjacentTo(p)) return shape; } return null; } public boolean hasSelectedDrawnShape(Point2D p) { for (int i=0;i<hep.getNumShapes();i++){ Shape s=hep.getHoles().getShape(i,hep.previewArea); if(s.contains(p)) { return true; } } return false; } private double distanceToNearest(double x, double y){ EditableShape nearest=nearestLine(x,y); if(nearest!=null) return nearest.distanceTo(x,y); else return -1; } public void deleteSelected(boolean isCut){ if(hasSelectedPoint()){ joinAdjacentsToSelectedPoint(); bSelectedPoint=false; } else{ List<EditableShape> vShapesCopy=new ArrayList<EditableShape>(); boolean allSelected=true, noneSelected=true; vShapesCopy.addAll(vShapes); for(EditableShape esh : vShapesCopy){ if(!esh.isSelected()) allSelected=false; else{ noneSelected=false; if(isCut || vShapes.size()>=4){ //Avoid to delete objects when there are only 3 or less elements, unless is a "cut" vShapes.remove(esh); if(!isCut) joinAdjacentsTo(esh,vShapes); } } } //allSelected indicates if all the object was selected if(allSelected || noneSelected){ vShapes.clear(); this.current=null; hep.getHoles().removeShape(hep.currentShape); hep.setCurrentShape(hep.getHoles().getNumCells()); } } } private void joinAdjacentsTo(EditableShape current, List<EditableShape> vShapes){ //All the shapes in vShapes will converge in one of the "current" points. EditableShape s1=getAdjacent(vShapes,current,INITIAL); if(s1!=null){ //Always s1.hasClickedBorder(current.getInitialPoint().getX(),current.getInitialPoint().getY(),false); //hasClickedBorder marks the shape corner nearest to the supplied point. Calling //changeBorder this corner will be modified to the new point. s1.changeBorder(current.getEndPoint().getX(),current.getEndPoint().getY()); } } private void joinAdjacentsToSelectedPoint(){ if(vShapes.size()!=1 && vShapes.size()<=3) return; EditableShape other=null; int count=0; for(EditableShape esh : vShapes){ if(esh.hasSelectedBorder()){ if(esh instanceof EditableRectangle){ Point2D p=esh.getNotSelectedBorder(); convertToSimpleShapes(); selectBorder(p.getX(),p.getY()); joinAdjacentsToSelectedPoint(); break; } else{ count++; if(count==1) other=esh; else if(other!=null){ Point2D p1=esh.getNotSelectedBorder(); Point2D p2=other.getNotSelectedBorder(); vShapes.add(new EditableLine2D(p1,p2)); vShapes.remove(esh); vShapes.remove(other); } } } } hep.repaint(0); } private void setEndToVector(double finX, double finY, List<EditableShape> vRedrawingLines){ //approach of all the lines of vRedrawingLines to the point finX, finY for(EditableShape esh : vRedrawingLines) esh.aproximateNearestBorder(finX,finY); } public boolean hasSelectedPoint(){ return bSelectedPoint; } public List<EditableShape> getSelectedShapes(){ List<EditableShape> v=new ArrayList<EditableShape>(); for(EditableShape esh : vShapes){ if(esh.isSelected()) v.add(esh); } return v; } public int getNumShapes(){ return vShapes.size(); } public void deleteCurrent(){ clean(); current=null; } public ShapeData getShapeData(){ ShapeData sd=null; AffineTransform aft=AffineTransform.getScaleInstance((1/hep.previewArea.getWidth()),(1/hep.previewArea.getHeight())); aft.concatenate(AffineTransform.getTranslateInstance(-hep.previewArea.x,-hep.previewArea.y)); if(getNumShapes()==1){ //Is a rectangle or a ellipse EditableShape es = (EditableShape)vShapes.get(0).clone(); es.transform(aft); Shape s; if(es instanceof EditableEllipse2D) s=((EditableEllipse2D)es).getEllipse(); else s=es; sd=ShapeData.getShapeData(s, null, false); } else{ List<GeneralPath> v=getGeneralPath(); //Get only the first polygon found (should be unique) if(v.size()>0){ GeneralPath gp=(v.get(0)); Shape s=gp.createTransformedShape(aft); sd=ShapeData.getShapeData(s, null); } } return sd; } public void endPolygon(){ endPolygon(false,true); } public void endPolygon(boolean changeShape, boolean updateList){ endPolygon(changeShape,updateList,-1); } public void endPolygon(boolean changeShape, boolean updateList, int iNextShape){ ShapeData sd=getShapeData(); addCurrentDrawnBorders(sd); endPolygon(sd,changeShape,updateList,iNextShape); if(sd!=null) clean(); bSelectedPoint=false; } private void addCurrentDrawnBorders(ShapeData sd){ if(sd!=null && hep!=null){ Shape s=sd.getShape(hep.previewArea); vDrawnBorders.addAll(getBorders(s)); } } private void removeDrawnBorders(ShapeData sd){ Shape s=sd.getShape(hep.previewArea); vDrawnBorders.removeAll(getBorders(s)); //removeAll removes all the instances of the elements passed over the shapedata (only one instance is needed) } public void endPolygon(ShapeData sd, boolean changeShape, boolean updateList,int iNextShape){ //Save the created/modified polygon. changeShape indicates if we "come" from a tab key. if(sd!=null){ addCurrentDrawnBorders(sd); if(hep.currentShape<hep.getHoles().getNumCells()){ //hep.currentShape has been modified hep.getHoles().modifyShape(hep.currentShape,sd); hep.updateView(); } else{ //A comment ahs been created sd.comment=StrUtils.secureString(sd.comment, ""+hep.currentShape); hep.getHoles().addShape(sd); hep.updateList(); hep.updateView(); } } int iCurrentShape=hep.currentShape+1; if(changeShape){ if(iNextShape>=0) iCurrentShape=iNextShape; else iCurrentShape=iCurrentShape%hep.getHoles().getNumCells(); } else iCurrentShape=hep.getHoles().getNumCells(); //Next one will be new if(hep.currentShape!=iCurrentShape) hep.setCurrentShape(iCurrentShape); } private void aplicateTransformation(AffineTransform aTransf, boolean needSelected){ for(EditableShape esh : vShapes){ if(!needSelected || esh.isSelected()){ esh.transform(aTransf); } } } public void move(int xInc, int yInc, boolean needSelected, boolean moveAll){ //moveAll indicates if we want to move also the inactive objects AffineTransform aTransf=AffineTransform.getTranslateInstance(xInc,yInc); aplicateTransformation(aTransf, needSelected); hep.repaint(0); } public void scale(double xInc, double yInc, boolean needSelected, boolean scaleAll){ Point2D center=getCenter(scaleAll); AffineTransform aTransf=AffineTransform.getTranslateInstance(center.getX(),center.getY()); aTransf.concatenate(AffineTransform.getScaleInstance(xInc,yInc)); aTransf.concatenate(AffineTransform.getTranslateInstance(-center.getX(),-center.getY())); aplicateTransformation(aTransf, needSelected); hep.repaint(0); } public void rotate(double theta, boolean needSelected, boolean rotateAll){ convertToSimpleShapes(); //If it is a triangle, it will be necessary to convert it to lines in order to rotate the shape Point2D center=getCenter(rotateAll); AffineTransform aTransf=AffineTransform.getRotateInstance(theta,center.getX(),center.getY()); aplicateTransformation(aTransf,needSelected); hep.repaint(0); } private Point2D getCenter(boolean cellCenter){ //Returns the central point of the edited shape when cellCenter==false, otherwise, returns the center of the cell if(!cellCenter){ GeneralPath gp=new GeneralPath(); for(EditableShape esh: vShapes) gp.append(esh,false); Rectangle2D r=gp.getBounds(); //to calculate the central point return new Point2D.Double(r.getCenterX(),r.getCenterY()); } else return new Point2D.Double(hep.getPreviewPanel().getX(),hep.getPreviewPanel().getY()); } private void convertToSimpleShapes(){ //If the edited shape is a rectangle or a ellipse, transform it to a set of segments or cubic lines for(EditableShape esh: vShapes){ if(esh instanceof EditableRectangle){ //Rectangular shapes must be converted to simple shapes in order to rotate it vShapes.remove(esh); EditableShape[] lines=((EditableRectangle)esh).divide(-1,-1,false); //Do no add any point for (int i=0;i<lines.length;i++) if(lines[i]!=null) vShapes.add(lines[i]); } } } private EditableShape getSelectedShape(boolean hasToBeALine){ //Returns the selected line when there is only one EditableShape selected=null; int i=0; for(EditableShape esh : vShapes){ if(esh.isSelected()){ if(!hasToBeALine || esh instanceof EditableLine2D){ selected=esh; i++; } else{ i=2; //Do nothing } } if(i>=2) break; } if(i==1) return selected; else return null; } public void convertToBezier(){ EditableShape selected=getSelectedShape(false); if(selected!=null){ double x1=selected.getInitialPoint().getX(); double y1=selected.getInitialPoint().getY(); double x2=selected.getEndPoint().getX(); double y2=selected.getEndPoint().getY(); double ctrl1x=x1+((x2-x1)/3); double ctrl2x=x1+(2*((x2-x1)/3)); double ctrl1y=y1+((y2-y1)/3); double ctrl2y=y1+(2*((y2-y1)/3)); EditableCubicCurve2D bez=new EditableCubicCurve2D(x1,y1,ctrl1x,ctrl1y,ctrl2x,ctrl2y,x2,y2); bez.setSelected(true); vShapes.remove(selected); vShapes.add(bez); } } public void convertToQuad(){ EditableShape selected=getSelectedShape(false); if(selected!=null){ double x1=selected.getInitialPoint().getX(); double y1=selected.getInitialPoint().getY(); double x2=selected.getEndPoint().getX(); double y2=selected.getEndPoint().getY(); double ctrlx=x1+((x2-x1)/2); double ctrly=y1+((y2-y1)/2); EditableQuadCurve2D quad=new EditableQuadCurve2D(x1,y1,ctrlx,ctrly,x2,y2); quad.setSelected(true); vShapes.remove(selected); vShapes.add(quad); } } public void convertToLine(){ EditableShape selected=getSelectedShape(false); if(selected!=null && !(selected instanceof EditableRectangle)){ EditableLine2D line=new EditableLine2D(selected.getInitialPoint(),selected.getEndPoint()); line.setSelected(true); vShapes.remove(selected); vShapes.add(line); } else if(selected!=null && selected instanceof EditableRectangle){ //Convert a rectangle into four lines vShapes.remove(selected); EditableShape[] lines=((EditableRectangle)selected).divide(-1,-1,false); //Do not add any point for (int i=0;i<lines.length;i++) if (lines[i]!=null) vShapes.add(lines[i]); } } public void notifyShapeChanged(){ for(PointListener pl : vPointListeners){ pl.shapeChanged(); } } public void addPointListener(PointListener listener){ vPointListeners.add(listener); } public void undoLastMove(List<EditableShape> vRedrawingLines, List<EditableShape> vRedrawingLinesBeforeModify){ vShapes.removeAll(vRedrawingLines); vShapes.addAll(vRedrawingLinesBeforeModify); vRedrawingLines.clear(); } private boolean isIntoArea(List<EditableShape> vShapes, boolean move){ boolean isInto=true; Rectangle2D r=new Rectangle2D.Double(hep.previewArea.getX()-1,hep.previewArea.getY()-1,hep.previewArea.getWidth()+2,hep.previewArea.getHeight()+2); Point2D[] borders; for(EditableShape esh : vShapes){ if(!isInto) break; EditableShape es; if(!move) es=esh; else{ es=(EditableShape)esh.clone(); es.transform(AffineTransform.getTranslateInstance(finX-iniX,finY-iniY)); } borders=es.getBorders(); if(borders==null) continue; for(int j=0;j<borders.length && isInto ;j++) isInto=r.contains(borders[j]); } return isInto; } private void joinPolygon(){ if(vShapes.size()>=2){ vShapes.add(new EditableLine2D(lastPoint.getX(),lastPoint.getY(),iniPoint.getX(),iniPoint.getY())); } else vShapes.clear(); creatingPolygon=false; lastPoint=null; iniPoint=null; if(bSelectedPoint) deselectBorder(); bSelectedPoint=false; hep.setDrawingMode(SELECTING); } public void mouseDragged(java.awt.event.MouseEvent mouseEvent) { if((mouseEvent.getModifiers()&java.awt.event.MouseEvent.BUTTON1_MASK)==0) return; //Button 1 was not pressed Point2D mousePoint=getTransformedPoint(mouseEvent.getPoint(),true); if(bMoving) hep.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); if(mousePoint.getX()<hep.previewArea.x || mousePoint.getY()<hep.previewArea.y || mousePoint.getX()>hep.previewArea.x+hep.previewArea.getWidth() || mousePoint.getY()>hep.previewArea.y+hep.previewArea.getHeight()){ return; } vShapeBeforeModify=(drawingMode==SELECTING && !bMoving)?cloneVector(vRedrawingLines):cloneVector(vCopied); EditableShape near=null; Point2D nearDrawn=aproximationToDrawnBorder(mouseEvent.getX(),mouseEvent.getY()); if(nearDrawn!=null && EditableShapeConstants.pointsOnGrid){ finX=nearDrawn.getX(); finY=nearDrawn.getY(); } else{ finX=mousePoint.getX(); finY=mousePoint.getY(); } if(creatingRect||creatingEllipse){ near=aproximationToLine(finX,finY); hep.getPreviewPanel().repaint(0); } else if(bRedrawingLines){ redrawingLines(finX,finY); near=aproximationToLine(finX,finY,vRedrawingLines); //we are over a shape corner not selected near (at less)... hep.repaint(0); } else if(bMoving || esInterior(finX,finY)){ near=nearestLine(finX,finY); if(near!=null){ double d=near.distanceTo(finX,finY); if(!bMoving && d>(EditableShapeConstants.selectLength/2)) cut(finX,finY); } hep.repaint(0); } if(creatingRect||creatingEllipse||bRedrawingLines){ if((near!=null || nearDrawn!=null) && EditableShapeConstants.pointsOnGrid) hep.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); else hep.setCursor(cursors[PEN_CURSOR]); } boolean b=isIntoArea((drawingMode==SELECTING && !bMoving)?vRedrawingLines:vCopied,(bMoving)); if(!b){ if(drawingMode==SELECTING && !bMoving){ undoLastMove(vRedrawingLines,vShapeBeforeModify); finX=lastFinX; finY=lastFinY; } if(bMoving){ finX=lastFinX; finY=lastFinY; } } else { lastFinX=finX; lastFinY=finY; } if(bResizing) setResizingCursor(resizingDirection); } protected boolean esCantonada(double x, double y){ EditableShape near=aproximationToLine(x,y); return (near!=null || aproximationToDrawnBorder(x,y)!=null); } protected boolean esSobreFigura(double x, double y){ int minimumDistance=Math.max(Math.max(2,EditableShapeConstants.selectLength/2),EditableShapeConstants.gridWidth); double dist=distanceToNearest(x,y); return (dist>=0 && dist<minimumDistance); } protected boolean esInterior(double x, double y){ return (current!=null && current.contains(x,y)); } public void mouseMoved(java.awt.event.MouseEvent mouseEvent) { double x,y; Point2D mousePoint=mouseEvent.getPoint(); boolean esCantonada=esCantonada(mouseEvent.getPoint().getX(),mouseEvent.getPoint().getY()); if(drawingMode==NEW_POINT || (esCantonada && EditableShapeConstants.pointsOnGrid)){ x=mousePoint.getX(); y=mousePoint.getY(); } else{ x=mousePoint.getX(); y=mousePoint.getY(); } if(x<hep.previewArea.x || y<hep.previewArea.y || x>hep.previewArea.x+hep.previewArea.getWidth() || y>hep.previewArea.y+hep.previewArea.getHeight()) return; if(drawingMode!=NEW_POINT){ if(esCantonada && (!creatingPolygon || EditableShapeConstants.pointsOnGrid)) hep.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); else if(creatingPolygon) hep.setCursor(cursors[PEN_CURSOR]); else if(!bMoving && esSobreFigura(x,y)) hep.setCursor(cursors[CIRCLE_CURSOR]); else if(esInterior(x,y)) hep.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); else hep.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } if(drawingMode==NEW_POINT){ if(esSobreFigura(x,y)){ bSpecialLine=true; //temporally paint it in another color specialLine=nearestLine(x,y); hep.setCursor(cursors[PEN_CURSOR]); hep.repaint(0); } else{ boolean willRepaint=bSpecialLine; bSpecialLine=false; hep.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); if(willRepaint) hep.repaint(0); } } if(bMoving){ if(iniX==-1){ deleteSelected(true); iniX=x; iniY=y; } finX=x; finY=y; hep.repaint(0); } if(creatingPolygon) { finX=x; finY=y; hep.repaint(0); } if(canResize){ if(!bResizing){ int resizing=getResizing(mousePoint); if(resizing!=NO_RESIZING) setResizingCursor(resizing); } else setResizingCursor(resizingDirection); } } protected int getResizing(Point2D mousePoint){ if(!canResize) return NO_RESIZING; ShapeData sd=hep.getHoles().getEnclosingShapeData(); Rectangle r=hep.getPreviewArea(); double width=r.getWidth(); double height=r.getHeight(); AffineTransform aft=AffineTransform.getTranslateInstance(-hep.previewArea.x,-hep.previewArea.y); aft.transform(mousePoint,mousePoint); if(mousePoint.getX()==(width-1) && mousePoint.getY()==(height-1)) return SOUTH_EAST; else if(mousePoint.getX()==(width-1)) return EAST; else if (mousePoint.getY()==(height-1)) return SOUTH; else return NO_RESIZING; } protected void setResizingCursor(int resizing){ if(resizing==EAST) hep.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR),false); else if(resizing==SOUTH) hep.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR),false); else if(resizing==SOUTH_EAST) hep.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR),false); } protected void selectBorder(double x, double y){ for(EditableShape s : vShapes) s.selectBorder(x,y); } protected void deselectBorder(){ for(EditableShape s : vShapes) s.deselectBorder(); } protected boolean removeNullLines(List<EditableShape> vRedrawingLines){ //Removes from vRedrawingLines all the lines having the two points in (approximately) the same co-ordinates boolean canRemove=false; for(EditableShape s : vRedrawingLines){ if(canRemove) break; if(s instanceof EditableLine2D){ Point2D [] p=s.getBorders(); if(p.length>1){ Rectangle r=new Rectangle((int)(p[0].getX())-(EditableShapeConstants.selectLength/2),(int)(p[0].getY())-(EditableShapeConstants.selectLength/2),EditableShapeConstants.selectLength,EditableShapeConstants.selectLength); if(r.contains(p[1].getX(),p[1].getY())){ //This line is prescindible if(vShapes.size()>=4){ //Do not delete any element when there are only 3 or les (except if it's a cut) canRemove=true; vShapes.remove(s); joinAdjacentsTo(s,vShapes); } } } } } return canRemove; } public void mouseClicked(java.awt.event.MouseEvent mouseEvent) { Point2D mousePoint=mouseEvent.getPoint(); boolean bSobreFigura=esSobreFigura(mousePoint.getX(),mousePoint.getY()); if(drawingMode!=NEW_POINT && drawingMode!=DRAWING_POLYGON && !bSobreFigura && selectDrawnShape(mousePoint) && !creatingPolygon){ notifyShapeChanged(); hep.repaint(0); } else if(drawingMode!=NEW_POINT && bSobreFigura && !creatingPolygon){ EditableShape line=nearestLine(mousePoint.getX(),mousePoint.getY()); if(line!=null){ //The caller wants to select a fragment of the polygon if(esCantonada(mousePoint.getX(),mousePoint.getY())) { Point2D p=line.getNearestBorder(mousePoint.getX(),mousePoint.getY()); deSelectAll(); bSelectedPoint=true; selectBorder(p.getX(),p.getY()); hep.repaint(0); } else{ if(bSelectedPoint) deselectBorder(); bSelectedPoint=false; if(line.isSelected()) line.setSelected(false); else{ if((mouseEvent.getModifiers()&java.awt.event.MouseEvent.SHIFT_MASK)==0) deSelectAll(); line.setSelected(true); } notifyShapeChanged(); hep.repaint(0); } } } if(creatingPolygon){ if(mouseEvent.getClickCount()==2) joinPolygon(); else{ mousePoint=mouseEvent.getPoint(); EditableShape near=aproximationToLine(mousePoint.getX(),mousePoint.getY(),vRedrawingLines); Point2D nearDrawn=null; Point2D nearDrawnOther=aproximationToDrawnBorder(mousePoint.getX(),mousePoint.getY()); if(near!=null){ // if the ending point is near the mouse, approximate to it nearDrawn=near.getNearestBorder(mousePoint.getX(),mousePoint.getY()); } if(nearDrawnOther!=null && EditableShapeConstants.pointsOnGrid){ finX=nearDrawnOther.getX(); finY=nearDrawnOther.getY(); } else{ mousePoint=getTransformedPoint(mouseEvent.getPoint(),true); finX=mousePoint.getX(); finY=mousePoint.getY(); } if(lastPoint!=null){ // isn't the first point if(nearDrawn!=null && iniPoint.getX()==nearDrawn.getX() && iniPoint.getY()==nearDrawn.getY()){//Has clicked over the starting point of the polygon if(vShapes.size()>=2) joinPolygon(); } else{ if(nearDrawn==null){ //Points cannot be repeated vShapes.add(new EditableLine2D(lastPoint.getX(),lastPoint.getY(),finX,finY)); lastPoint=new Point2D.Double(finX,finY); } } } else { //it's the first point iniPoint=new Point2D.Double(finX,finY); lastPoint=iniPoint; } } } } public void mouseEntered(java.awt.event.MouseEvent mouseEvent) { } public void mouseExited(java.awt.event.MouseEvent mouseEvent) { } public void mousePressed(java.awt.event.MouseEvent mouseEvent) { Point2D mousePoint=getTransformedPoint(mouseEvent.getPoint(),drawingMode!=SELECTING); //when selecting, the point doesn't must be in the grid int x=(int)mousePoint.getX(); int y=(int)mousePoint.getY(); if(canResize){ int resizing=getResizing(mousePoint); if(resizing!=NO_RESIZING){ if(drawingMode!=SELECTING) hep.setDrawingMode(SELECTING); bResizing=true; resizingDirection=resizing; } } if(x<hep.previewArea.x || y<hep.previewArea.y || x>hep.previewArea.x+hep.previewArea.getWidth() || y>hep.previewArea.y+hep.previewArea.getHeight()) return; if(bMoving){ //CTRL+X has been pressed while moving a shape paste(finX-iniX,finY-iniY); bMoving=false; } iniX=x; iniY=y; if(drawingMode==SELECTING && !bMoving){ clicatISeleccionada(x,y,false); //false: drag is possible despite of having the shape selected or unselected if(vRedrawingLines.size()>0) bRedrawingLines=true; //Redraw lines when clicking on a corner } else if((drawingMode==DRAWING_RECT || drawingMode==DRAWING_ELLIPSE || drawingMode==DRAWING_POLYGON)&&!hasSelectedDrawnShape(mouseEvent.getPoint())){ if(drawingMode==DRAWING_RECT) creatingRect=true; else if(drawingMode==DRAWING_ELLIPSE) creatingEllipse=true; else creatingPolygon=true; EditableShape near=aproximationToLine(x,y); Point2D pNear=aproximationToDrawnBorder(x,y); if (near!=null){ pNear=near.getNearestBorder(x,y); } if (pNear!=null){ iniX=pNear.getX(); iniY=pNear.getY(); } else{ iniX=x; iniY=y; } finX=iniX; finY=iniY; hep.repaint(0); } else if(drawingMode==NEW_POINT){ EditableShape lineToDivide=specialLine; EditableShape near=aproximationToLine(x,y,null); Point2D nearDrawn=null; boolean isSelect=false; if(near!=null) nearDrawn=near.getNearestBorder(x,y); if(drawingMode==NEW_POINT && (lineToDivide!=null && bSpecialLine && (nearDrawn==null || lineToDivide instanceof EditableEllipse2D || lineToDivide instanceof EditableCubicCurve2D || lineToDivide instanceof EditableQuadCurve2D))){ //A point is added only when the current point is not over another one divideShape(lineToDivide,x,y); } else{ isSelect=selectDrawnShape(mouseEvent.getPoint()); } if(!isSelect){ //A point has been added, or a click has been done out of any shape hep.setDrawingMode(SELECTING); } } } public void mouseReleased(java.awt.event.MouseEvent mouseEvent) { if((mouseEvent.getModifiers()&java.awt.event.MouseEvent.BUTTON1_MASK)==0){//The button 1 was not pressed return; } if(bMoving){ paste(finX-iniX,finY-iniY); bMoving=false; deSelectAll(); } Point2D mousePoint=mouseEvent.getPoint(); EditableShape near=aproximationToLine(mousePoint.getX(),mousePoint.getY(),vRedrawingLines); Point2D nearDrawnPropi=null; Point2D nearDrawn=aproximationToDrawnBorder(mousePoint.getX(),mousePoint.getY()); //Corner of a non-active polygon if(near!=null || nearDrawn!=null) hep.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); else hep.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); if(near!=null){ // if there is any nearest point, approximate the ending point to it nearDrawnPropi=near.getNearestBorder(mousePoint.getX(),mousePoint.getY()); //Corner of the active polygon } mousePoint=getTransformedPoint(mouseEvent.getPoint(),true); if(!(mousePoint.getX()<hep.previewArea.x || mousePoint.getY()<hep.previewArea.y || mousePoint.getX()>hep.previewArea.x+hep.previewArea.getWidth() || mousePoint.getY()>hep.previewArea.y+hep.previewArea.getHeight())){ if(nearDrawn!=null && EditableShapeConstants.pointsOnGrid){ //Only when approaching finX=nearDrawn.getX(); finY=nearDrawn.getY(); } else{ finX=mousePoint.getX(); //This point is maintained as long as the pointer remains into the drawing area finY=mousePoint.getY(); } } if((drawingMode==SELECTING && !bMoving && nearDrawnPropi!=null)|| (!creatingPolygon && vShapes.size()>1 && !validateShape())){ boolean canRemove=removeNullLines(vRedrawingLines); if(!canRemove){ undoLastMove(vRedrawingLines,vRedrawingLinesBeforeModify); finX=iniX; finY=iniY; } } else if(bRedrawingLines){ setEndToVector(finX,finY,vRedrawingLines); bRedrawingLines=false; vRedrawingLines.clear(); vRedrawingLinesBeforeModify.clear(); } if(creatingRect){ creatingRect=false; vShapes.add(new EditableRectangle((int)iniX,(int)iniY,(int)(finX-iniX),(int)(finY-iniY))); if(hep.currentShape>=hep.getHoles().getNumCells()){ //Reserve space for the new rectangle when confirmed(in order to give it a name) ShapeData sd=new ShapeData(); sd.comment = ""+hep.currentShape; hep.getHoles().addShape(sd); hep.updateList(); } } if(creatingEllipse){ creatingEllipse=false; vShapes.add(new EditableEllipse2D((int)iniX,(int)iniY,(int)(finX-iniX),(int)(finY-iniY))); if(hep.currentShape>=hep.getHoles().getNumCells()){ //Reserve space for the new rectangle when confirmed(in order to give it a name) ShapeData sd=new ShapeData(); sd.comment = ""+hep.currentShape; hep.getHoles().addShape(sd); hep.updateList(); } } if(bResizing){ if(resizingDirection!=NO_RESIZING){ double x=mousePoint.getX(); double y=mousePoint.getY(); double xInc=x-iniX; double yInc=y-iniY; if(resizingDirection==EAST) yInc=0; else if(resizingDirection==SOUTH) xInc=0; hep.incDrawingArea(xInc,yInc); } bResizing=false; } ShapeData sd=getShapeData(); current=(sd!=null)?sd.getShape(hep.previewArea):null; //Update the modifications to the shape bSelectingArea=false; hep.repaint(0); if(!creatingPolygon) notifyShapeChanged(); } public class KeyHandler extends KeyAdapter{ @Override public void keyPressed(KeyEvent e){ if(e.getKeyCode()==KeyEvent.VK_DELETE){ deleteSelected(false); hep.shapeChanged(); } } } private double viewIniX=-1; private double viewIniY=-1; }