/* Copyright 2006 Rene Grothmann, modified by Eric Hakenholz This file is part of C.a.R. software. C.a.R. is a 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, version 3 of the License. C.a.R. 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, see . */ package rene.zirkel.objects; // file: Circle3Object.java import eric.bar.JPropertiesBar; import java.awt.Checkbox; import java.awt.Color; import java.awt.Frame; import java.awt.TextField; import java.awt.event.FocusEvent; import java.util.ArrayList; import java.util.Enumeration; import java.util.Vector; import eric.JEricPanel; import rene.dialogs.Warning; import rene.gui.Global; import rene.gui.IconBar; import rene.gui.IconBarListener; import rene.gui.MyLabel; import rene.gui.TextFieldAction; import rene.util.xml.XmlWriter; import rene.gui.Global; import rene.zirkel.ZirkelCanvas; import rene.zirkel.construction.Construction; import rene.zirkel.construction.ConstructionException; import rene.zirkel.construction.Count; import rene.zirkel.expression.Expression; import rene.zirkel.expression.ExpressionColor; import rene.zirkel.expression.InvalidException; import rene.zirkel.graphics.MyGraphics; import rene.zirkel.graphics.MainGraphics; import rene.zirkel.graphics.PolygonDrawer; public class AngleObject extends ConstructionObject implements InsideObject { protected PointObject P1, P2, P3; static Count N=new Count(); double A, A1, A2, A3D; // Dibs : A3D est l'angle 3D double X, Y; boolean Fixed; Expression E; boolean Filled=false; final static double LabelScale=0.66; public static final int NORMALSIZE=1, SMALL=0, LARGER=2, LARGE=3, RECT=4; protected int DisplaySize=NORMALSIZE; public AngleObject(final Construction c, final PointObject p1, final PointObject p2, final PointObject p3) { super(c); P1=p1; P2=p2; P3=p3; validate(); setColor(ColorIndex, SpecialColor); updateText(); Unit=Global.getParameter("unit.angle", "°"); double xx=P1.getX()+P3.getX()-2*P2.getX(); double yy=P1.getY()+P3.getY()-2*P2.getY(); final double ll=Math.max(Math.sqrt(xx*xx+yy*yy),0.000001); xx=xx/ll; yy=yy/ll; XcOffset=xx*25/c.getPixel(); YcOffset=yy*25/c.getPixel(); } public AngleObject(final Construction c) { super(c); } @Override public void setDefaults() { setShowName(Global.getParameter("options.angle.shownames", false)); setShowValue(Global.getParameter("options.angle.showvalues", false)); setColor(Global.getParameter("options.angle.color", 0), Global.getParameter("options.angle.pcolor", (ExpressionColor) null, this)); setColorType(Global.getParameter("options.angle.colortype", 0)); setFilled(Global.getParameter("options.angle.filled", false)); setHidden(Cn.Hidden); setObtuse(Global.getParameter("options.angle.obtuse", false)); setSolid(Global.getParameter("options.angle.solid", false)); setLarge(Cn.LargeFont); setBold(Cn.BoldFont); setPartial(Cn.Partial); } @Override public void setTargetDefaults() { setShowName(Global.getParameter("options.angle.shownames", false)); setShowValue(Global.getParameter("options.angle.showvalues", false)); setColor(Global.getParameter("options.angle.color", 0), Global.getParameter("options.angle.pcolor", (ExpressionColor) null, this)); setColorType(Global.getParameter("options.angle.colortype", 0)); setFilled(Global.getParameter("options.angle.filled", false)); setObtuse(Global.getParameter("options.angle.obtuse", false)); setSolid(Global.getParameter("options.angle.solid", false)); } @Override public String getTag() { return "Angle"; } @Override public int getN() { return N.next(); } @Override public void updateText() { if (!Fixed||E==null) { setText(text3(Global.name("text.angle"), P1.getName(), P2.getName(), P3.getName())); } else { setText(text4(Global.name("text.angle.fixed"), P1.getName(), P2.getName(), P3.getName(), "\""+E.toString()+"\"")); } } @Override public String getDisplayValue() { // if (ZirkelCanvas.AnglesFactor<=2) { // return ""+(int) (A/Math.PI*180+0.5); // } else { // return ""+round(A/Math.PI*180, ZirkelCanvas.AnglesFactor); // } if (P1.is3D()&&P2.is3D()&&P3.is3D()) return Global.getLocaleNumber(A3D/Math.PI*180, "angles"); else return Global.getLocaleNumber(A/Math.PI*180, "angles"); } @Override public boolean nearto(final int x, final int y, final ZirkelCanvas zc) { if (!displays(zc)) { return false; } final double dx=zc.x(x)-X, dy=zc.y(y)-Y; final double size=zc.dx(zc.selectionSize()); final double rd=getDisplaySize(zc), r=Math.sqrt(dx*dx+dy*dy); boolean near; Value=Math.abs(r-rd); if (Filled||DisplaySize==RECT) { near=(rA1-c&&aMath.PI) { A1=A2; A=2*Math.PI-A; A2=A1+A; } } } double x[]=new double[4], y[]=new double[4]; double x3D[]= new double[4], y3D[]= new double[4], z3D[]= new double[4]; double xx3D, yy3D, zz3D; double xx, yy, zz; @Override public void paint(final MyGraphics g, final ZirkelCanvas zc) { if (zc.is3D()&&P1.is3D()&&P2.is3D()&&P3.is3D()) { // angle 3D //final double xx1=P1.getX()-P2.getX(); //final double yy1=P1.getY()-P2.getY(); //final double xx2=P3.getX()-P2.getX(); //final double yy2=P3.getY()-P2.getY(); double xx3D1=P1.getX3D()-P2.getX3D(); double yy3D1=P1.getY3D()-P2.getY3D(); double zz3D1=P1.getZ3D()-P2.getZ3D(); double d3D1=Math.sqrt(xx3D1*xx3D1+yy3D1*yy3D1+zz3D1*zz3D1); double xx3D2=P3.getX3D()-P2.getX3D(); double yy3D2=P3.getY3D()-P2.getY3D(); double zz3D2=P3.getZ3D()-P2.getZ3D(); double d3D2=Math.sqrt(xx3D2*xx3D2+yy3D2*yy3D2+zz3D2*zz3D2); xx3D1/=d3D1; // on normalise yy3D1/=d3D1; zz3D1/=d3D1; xx3D2/=d3D2; yy3D2/=d3D2; zz3D2/=d3D2; final double x3DP2=P2.getX3D(); final double y3DP2=P2.getY3D(); final double z3DP2=P2.getZ3D(); final double xO=zc.getConstruction().find("O").getX(); final double yO=zc.getConstruction().find("O").getY(); final double deltaxX=zc.getConstruction().find("X").getX()-xO; final double deltaxY=zc.getConstruction().find("Y").getX()-xO; final double deltaxZ=zc.getConstruction().find("Z").getX()-xO; final double deltayX=zc.getConstruction().find("X").getY()-yO; final double deltayY=zc.getConstruction().find("Y").getY()-yO; final double deltayZ=zc.getConstruction().find("Z").getY()-yO; double R3D=getDisplaySize(zc); if (!Valid||mustHide(zc)) { return; } final double R=zc.col(getDisplaySize(zc))-zc.col(0); final double c1=zc.col(X)-R, r1=zc.row(Y)-R; // paint: double DA=(A2-A1)/Math.PI*180; if (DA<0) { DA+=360; } else if (DA>=360) { DA-=360; } if (visible(zc)) { if (isStrongSelected()&&g instanceof MainGraphics) { ((MainGraphics) g).drawMarkerArc(c1+R, r1+R, R, A1/Math.PI*180, DA); } if (Filled) { if (DisplaySize==RECT) { // Dibs : sert à rien ? final double dx1=Math.cos(A1), dy1=Math.sin(A1), dx2=Math.cos(A1+DA/180*Math.PI), dy2=Math.sin(A1+DA/180*Math.PI); double dx3=dx1+dx2, dy3=dy1+dy2; if (DA>180) { dx3=-dx3; dy3=-dy3; } if (Selected||getColorType()!=THIN) { g.setColor(this); g.drawLine(c1+R+R*dx1, r1+R-R*dy1, c1+R+R*dx3, r1+R-R*dy3); g.drawLine(c1+R+R*dx3, r1+R-R*dy3, c1+R+R*dx2, r1+R-R*dy2); } x[0]=c1+R; y[0]=r1+R; x[1]=c1+R+R*dx1; y[1]=r1+R-R*dy1; x[2]=c1+R+R*dx3; y[2]=r1+R-R*dy3; x[3]=c1+R+R*dx2; y[3]=r1+R-R*dy2; g.fillPolygon(x, y, 4, false, getColorType()!=THICK, this); } else { // angle 3D rempli if (Math.abs(A3D-Math.PI/2)<0.00002) { // angle 3D droit try { R3D/=1.4; x3D[0]=x3DP2; y3D[0]=y3DP2; z3D[0]=z3DP2; x3D[1]=x3DP2+R3D*xx3D1; y3D[1]=y3DP2+R3D*yy3D1; z3D[1]=z3DP2+R3D*zz3D1; x3D[2]=x3DP2+R3D*(xx3D1+xx3D2); y3D[2]=y3DP2+R3D*(yy3D1+yy3D2); z3D[2]=z3DP2+R3D*(zz3D1+zz3D2); x3D[3]=x3DP2+R3D*xx3D2; y3D[3]=y3DP2+R3D*yy3D2; z3D[3]=z3DP2+R3D*zz3D2; x[0]=xO+x3D[0]*deltaxX+y3D[0]*deltaxY+z3D[0]*deltaxZ; y[0]=yO+x3D[0]*deltayX+y3D[0]*deltayY+z3D[0]*deltayZ; x[1]=xO+x3D[1]*deltaxX+y3D[1]*deltaxY+z3D[1]*deltaxZ; y[1]=yO+x3D[1]*deltayX+y3D[1]*deltayY+z3D[1]*deltayZ; x[2]=xO+x3D[2]*deltaxX+y3D[2]*deltaxY+z3D[2]*deltaxZ; y[2]=yO+x3D[2]*deltayX+y3D[2]*deltayY+z3D[2]*deltayZ; x[3]=xO+x3D[3]*deltaxX+y3D[3]*deltaxY+z3D[3]*deltaxZ; y[3]=yO+x3D[3]*deltayX+y3D[3]*deltayY+z3D[3]*deltayZ; x[0]=zc.col(x[0]); y[0]=zc.row(y[0]); x[1]=zc.col(x[1]); y[1]=zc.row(y[1]); x[2]=zc.col(x[2]); y[2]=zc.row(y[2]); x[3]=zc.col(x[3]); y[3]=zc.row(y[3]); } catch (final Exception e) { System.out.println("exception angle"); } g.fillPolygon(x, y, 4, false, getColorType()!=THICK, this); g.setColor(this); g.drawLine(x[1], y[1], x[2], y[2]); g.drawLine(x[2], y[2], x[3], y[3]); } else { // pas droit g.setColor(this); final PolygonDrawer pd=new PolygonDrawer(true,g, this); final double h=zc.dx(zc.getOne()); double pvectx1=yy3D1*zz3D2-zz3D1*yy3D2; // Objectif : obtenir le "bon vecteur normal" à P2P1 double pvecty1=zz3D1*xx3D2-xx3D1*zz3D2; double pvectz1=xx3D1*yy3D2-yy3D1*xx3D2; //final double norme1=Math.sqrt(pvectx1*pvectx1+pvecty1*pvecty1+pvectz1*pvectz1); //pvectx1/=norme1; pvecty1/=norme1; pvectz1/=norme1; double pvectx2=pvecty1*zz3D1-pvectz1*yy3D1; double pvecty2=pvectz1*xx3D1-pvectx1*zz3D1; double pvectz2=pvectx1*yy3D1-pvecty1*xx3D1; final double norme2=Math.sqrt(pvectx2*pvectx2+pvecty2*pvecty2+pvectz2*pvectz2); pvectx2/=norme2; pvecty2/=norme2; pvectz2/=norme2; //final double ux=xx3D1/d3D1, uy=yy3D1/d3D1, uz=zz3D1/d3D1; double alpha=0; boolean valid=false; int compteur=0; ArrayList cx= new ArrayList(), cy=new ArrayList(); cx.add(0.0); cy.add(0.0); //juste pour coordonner des indices while (alpha<=A3D) { try { xx3D=x3DP2+R3D*(Math.cos(alpha)*xx3D1+Math.sin(alpha)*pvectx2); yy3D=y3DP2+R3D*(Math.cos(alpha)*yy3D1+Math.sin(alpha)*pvecty2); zz3D=z3DP2+R3D*(Math.cos(alpha)*zz3D1+Math.sin(alpha)*pvectz2); xx=xO+xx3D*deltaxX+yy3D*deltaxY+zz3D*deltaxZ; yy=yO+xx3D*deltayX+yy3D*deltayY+zz3D*deltayZ; final double c=zc.col(xx), r=zc.row(yy); cx.add(c); cy.add(r); if (valid) { pd.drawTo(c, r); compteur++; } else { pd.startPolygon(c, r); } valid=true; } catch (final RuntimeException e) { System.out.println("runtime exception"); valid=false; } alpha+=h; } pd.finishPolygon(); final double ccx[]= new double[compteur+2], ccy[]= new double[compteur+2]; ccx[0]=zc.col(xO+x3DP2*deltaxX+y3DP2*deltaxY+z3DP2*deltaxZ); ccy[0]=zc.row(yO+x3DP2*deltayX+y3DP2*deltayY+z3DP2*deltayZ); for (int i=1; i=360) { DA-=360; } if (visible(zc)) { if (isStrongSelected()&&g instanceof MainGraphics) { ((MainGraphics) g).drawMarkerArc(c1+R, r1+R, R, A1/Math.PI*180, DA); } if (Filled) { if (DisplaySize==RECT) { final double dx1=Math.cos(A1), dy1=Math.sin(A1), dx2=Math.cos(A1+DA/180*Math.PI), dy2=Math.sin(A1+DA/180*Math.PI); double dx3=dx1+dx2, dy3=dy1+dy2; if (DA>180) { dx3=-dx3; dy3=-dy3; } if (Selected||getColorType()!=THIN) { g.setColor(this); g.drawLine(c1+R+R*dx1, r1+R-R*dy1, c1+R+R*dx3, r1+R-R*dy3); g.drawLine(c1+R+R*dx3, r1+R-R*dy3, c1+R+R*dx2, r1+R-R*dy2); } x[0]=c1+R; y[0]=r1+R; x[1]=c1+R+R*dx1; y[1]=r1+R-R*dy1; x[2]=c1+R+R*dx3; y[2]=r1+R-R*dy3; x[3]=c1+R+R*dx2; y[3]=r1+R-R*dy2; g.fillPolygon(x, y, 4, false, getColorType()!=THICK, this); } else { if (Math.abs(DA-90)<0.0000001) { final double dx1=Math.cos(A1), dy1=Math.sin(A1), dx2=Math.cos(A1+DA/180*Math.PI), dy2=Math.sin(A1+DA/180*Math.PI); final double dx3=dx1+dx2, dy3=dy1+dy2; x[0]=c1; y[0]=r1; x[0]=c1+R; y[0]=r1+R; x[1]=c1+R+R*dx1; y[1]=r1+R-R*dy1; x[2]=c1+R+R*dx3; y[2]=r1+R-R*dy3; x[3]=c1+R+R*dx2; y[3]=r1+R-R*dy2; g.fillPolygon(x, y, 4, false, getColorType()!=THICK, this); g.setColor(this); g.drawLine(x[1], y[1], x[2], y[2]); g.drawLine(x[2], y[2], x[3], y[3]); } else { g.fillArc(c1, r1, 2*R, 2*R, A1/Math.PI*180, DA, Selected||getColorType()!=THIN, getColorType()!=THICK, true, this); } } } else { g.setColor(this); if (DisplaySize==RECT) { final double dx1=Math.cos(A1), dy1=Math.sin(A1), dx2=Math.cos(A1+DA/180*Math.PI), dy2=Math.sin(A1+DA/180*Math.PI); g.drawLine(c1+R+R*dx1, r1+R-R*dy1, c1+R+R*(dx1+dx2), r1+R-R*(dy1+dy2)); g.drawLine(c1+R+R*(dx1+dx2), r1+R-R*(dy1+dy2), c1+R+R*dx2, r1+R-R*dy2); } else { if (Math.abs(DA-90)<0.0000001) { final double dx1=Math.cos(A1), dy1=Math.sin(A1), dx2=Math.cos(A1+DA/180*Math.PI), dy2=Math.sin(A1+DA/180*Math.PI); final double dx3=dx1+dx2, dy3=dy1+dy2; x[1]=c1+R+R*dx1; y[1]=r1+R-R*dy1; x[2]=c1+R+R*dx3; y[2]=r1+R-R*dy3; x[3]=c1+R+R*dx2; y[3]=r1+R-R*dy2; g.setColor(this); g.drawLine(x[1], y[1], x[2], y[2]); g.drawLine(x[2], y[2], x[3], y[3]); } else { g.drawCircleArc(c1+R, r1+R, R, A1/Math.PI*180, DA, this); } } } } final String s=translateToUnicode(getDisplayText()); if (!s.equals("")) { g.setLabelColor(this); setFont(g); DisplaysText=true; final double dx=Math.cos(A1+A/2), dy=Math.sin(A1+A/2); // if (s.equals("90"+getUnit()) || Name.startsWith(".")) // { // // if (KeepClose) // { double d=Math.sqrt(XcOffset*XcOffset+YcOffset*YcOffset); // TX1=zc.col(X+d*dx)-3; // TY1=zc.row(Y+d*dy)-3; // TX2=TX1+9; // TY2=TY1+9; // g.drawRect(zc.col(X+d*dx)-1, // zc.row(Y+d*dy)-1,3,3); // } // else // { TX1=zc.col(X+zc.dx(R*LabelScale)*dx+XcOffset)-3; // TY1=zc.row(Y+zc.dy(R*LabelScale)*dy+YcOffset)-3; // TX2=TX1+9; // TY2=TY1+9; // g.drawRect(zc.col(X+zc.dx(R*LabelScale)*dx+XcOffset)-1, // zc.row(Y+zc.dy(R*LabelScale)*dy+YcOffset)-1,3,3); // } // } // else // { if (KeepClose) { final double d=Math.sqrt(XcOffset*XcOffset+YcOffset*YcOffset); drawCenteredLabel(g, s, zc, X+d*dx, Y+d*dy, 0, 0); } else { drawCenteredLabel(g, s, zc, X+zc.dx(R*LabelScale)*dx, Y+zc.dy(R*LabelScale)*dy, XcOffset, YcOffset); } // } } } } @Override public boolean canKeepClose() { return true; } @Override public void setKeepClose(final double x, final double y) { KeepClose=true; XcOffset=x-X; YcOffset=y-Y; } double getDisplaySize(final ZirkelCanvas zc) { double R=zc.dx(12*zc.pointSize()); if (DisplaySize==SMALL||DisplaySize==RECT) { R/=2; } else if (DisplaySize==LARGER) { R*=2; } else if (DisplaySize==LARGE) { final double dx=P1.getX()-X, dy=P1.getY()-Y; R=Math.sqrt(dx*dx+dy*dy); } return R; } public double getLength() { return A; } @Override public boolean fixed() { return Fixed; } @Override public void setFixed(final boolean flag) { Fixed=flag; updateText(); } @Override public void setFixed(final String s) { Fixed=true; E=new Expression(s, getConstruction(), this); updateText(); } @Override public boolean canFix() { return P3.moveableBy(this); } @Override public void printArgs(final XmlWriter xml) { if (P1!=null) { xml.printArg("first", P1.getName()); xml.printArg("root", P2.getName()); xml.printArg("second", P3.getName()); } if (DisplaySize==SMALL) { xml.printArg("display", "small"); } if (DisplaySize==NORMALSIZE) { xml.printArg("display", "normalsize"); } if (DisplaySize==LARGE) { xml.printArg("display", "large"); } if (DisplaySize==LARGER) { xml.printArg("display", "larger"); } if (DisplaySize==RECT) { xml.printArg("display", "rectangle"); } if (Filled) { xml.printArg("filled", "true"); } if (Fixed&&E!=null) { xml.printArg("fixed", E.toString()); } if (!Obtuse) { xml.printArg("acute", "true"); } super.printArgs(xml); } @Override public void setDisplaySize(final int i) { DisplaySize=i; } @Override public int getDisplaySize() { return DisplaySize; } @Override public Enumeration depending() { super.depending(); if (P1==null) { return DL.elements(); } if (!Fixed) { return depset(P1, P2, P3); } else { depset(P1, P2, P3); final Enumeration e=E.getDepList().elements(); while (e.hasMoreElements()) { DL.add((ConstructionObject) e.nextElement()); } return DL.elements(); } } @Override public boolean equals(final ConstructionObject o) { if (!(o instanceof AngleObject)||!o.valid()) { return false; } final AngleObject l=(AngleObject) o; return equals(X, l.X)&&equals(Y, l.Y)&&equals(A1, l.A1)&&equals(A2, l.A2); } public static char Translation[]={'a', '\u03B1', 'A', '\u0391', 'b', '\u03B2', 'B', '\u0392', 'c', '\u03B3', 'C', '\u0393', 'd', '\u03B4', 'D', '\u0394', 'e', '\u03B5', 'E', '\u0395', 'f', '\u03D5', 'F', '\u03A6', 'g', '\u03B3', 'G', '\u0393', 'h', '\u03B7', 'H', '\u0397', 'i', '\u03B9', 'I', '\u0399', 'k', '\u03BA', 'K', '\u039A', 'l', '\u03BB', 'L', '\u039B', 'm', '\u03BC', 'M', '\u039C', 'n', '\u03BD', 'N', '\u039D', 'o', '\u03BF', 'O', '\u03A9', 'p', '\u03C0', 'P', '\u03A0', 'q', '\u03C7', 'Q', '\u03A7', 'r', '\u03C1', 'R', '\u03A1', 's', '\u03C3', 'S', '\u03A3', 't', '\u03C4', 'T', '\u03A4', 'u', '\u03C5', 'U', '\u03A5', 'v', '\u03C8', 'V', '\u03A8', 'w', '\u03C9', 'W', '\u03A9', 'x', '\u03BE', 'X', '\u039E', 'y', '\u03C7', 'Y', '\u03A7', 'z', '\u03B6', 'Z', '\u0396',}; public static String translateToUnicode(final String s) { if (s.startsWith("$")) { return s; } if (s.indexOf('\\')<0) { return s; } final StringBuffer b=new StringBuffer(); for (int i=0; i='0'&&ch<='9') { n=n*16+(int) (ch-'0'); } else if (ch>='A'&&ch<='F') { n=n*16+(int) (ch-'A'+10); } else { break; } i++; } if (n>0) { c=(char) n; b.append(c); } i--; continue; } int j=0; for (j=0; j=Translation.length) { b.append(c); } } } } return b.toString(); } @Override public void translate() { P1=(PointObject) P1.getTranslation(); P2=(PointObject) P2.getTranslation(); P3=(PointObject) P3.getTranslation(); if (Fixed) { try { setFixed(E.toString()); E.translate(); } catch (final Exception e) { Fixed=false; } } } @Override public String getE() { if (Fixed&&E!=null) { return E.toString(); } else { if (P1.is3D()&&P2.is3D()&&P3.is3D()) return ""+round(A3D/Math.PI*180); else return ""+round(A/Math.PI*180); } } @Override public double getValue() throws ConstructionException { if (!Valid) { throw new InvalidException("exception.invalid"); } else { if (P1.is3D()&&P2.is3D()&&P3.is3D()) return A3D/Math.PI*180; else return A/Math.PI*180; } } @Override public void setFilled(final boolean flag) { Filled=flag; } @Override public boolean isFilled() { return Filled; } @Override public boolean maybeTransparent() { return true; } @Override public boolean isFilledForSelect() { return false; } public double containsInside(final PointObject P) { final double dx=P.getX()-X, dy=P.getY()-Y; double a=Math.atan2(dy, dx); if (a<0) { a+=2*Math.PI; } if (aA1&&aA1-c&&a0) { return true; } final double x=P.getX(), y=P.getY(); final double x1=P2.getX(), y1=P2.getY(); double xmin=x1, ymin=y1, dmin=1e20; double x2=P1.getX(), y2=P1.getY(); double dx=x2-x1, dy=y2-y1; double r=dx*dx+dy*dy; double h=dx*(x-x1)/r+dy*(y-y1)/r; if (h<0) { h=0; } double xh=x1+h*dx, yh=y1+h*dy; double dist=Math.sqrt((x-xh)*(x-xh)+(y-yh)*(y-yh)); if (dist