构造Delaunay三角形网格(代码整理)

原文链接

package cma.common.isoline;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
 
   public abstract class   Coordinate   {
 
         //投影方式
         public static   int     LAMBERT     =   1 ;
         public static   int     MERCATOR    =   2 ;
         public static   int     BBQ         =   3 ;
         public static   int     NBQ         =   4 ;
         public static   int     LINEAR      =   5 ;
         public static   int     POLAR       =   6 ;
 
         //静态常量,地球半径,来源:《大气科学常用公式》,P601,附录
         public static   double   RADIUS         =   6371.004 ; //地球平均半径,单位:公里(Km)。
         public static   double   RADIUS_POLAR   =   6356.755 ; //地球两极半径,单位:公里(Km)。
         public static   double   RADIUS_EQUATOR =   6373.140 ; //地球赤道半径,单位:公里(Km)。
 
         //标准经纬度(0.0<=longitude<=360, -90.0<=latitude<=90.0)
         protected   Point2D.Double    standard; //兰勃特、极射赤面投影用到
 
         //中心经纬度(0.0<=longitude<=360, -90.0<=latitude<=90.0),用于定位,不一定是中心
         protected   Point2D.Double    center;
 
         //中心经纬度对应的屏幕坐标
         protected   Point             place;
 
         //偏移
         protected   Point             offset;
 
         //缩放比例(非0正值,经向纬向可以不同)
         protected   Point2D.Double    scaleXY;
         //缩放系数
         protected   double              scale;
         protected   double              scaleOriginal;
 
         public    int   type    = - 1 ;
 
   /**
      * 功能:
      *      获得标准经纬度
      * 参数:
      *      无
      * 返回值:
      *      标准经纬度
      */
         public   Point2D.Double getStandard () {
             return ( standard ) ;
         }
 
   /**
      * 功能:
      *      获得中心经纬度
      * 参数:
      *      无
      * 返回值:
      *      中心经纬度
      */
         public   Point2D.Double getCenter () {
             return ( center ) ;
         }
 
   /**
      * 功能:
      *      获得中心经纬度对应的屏幕坐标
      * 参数:
      *      无
      * 返回值:
      *      中心经纬度对应的屏幕坐标
      */
         public   Point getPlace () {
             return ( place ) ;
         }
 
   /**
      * 功能:
      *      获得缩放系数
      * 参数:
      *      无
      * 返回值:
      *      缩放系数
      */
         public   double   getScale () {
             return ( scale ) ;
         }
 
   /**
      * 功能:
      *      获得缩放比例
      * 参数:
      *      无
      * 返回值:
      *      缩放比例
      */
         public   Point2D.Double getScaleXY () {
             return ( scaleXY ) ;
         }
 
   /**
      * 功能:
      *      放大
      * 参数:
      *      无
      * 返回值:
      *      无
      */
         public   void   zoomIn () {
             scale   = scale *   2.0 ;
         }
 
   /**
      * 功能:
      *      缩小
      * 参数:
      *      无
      * 返回值:
      *      无
      */
         public   void   zoomOut () {
             scale   = scale /   2.0 ;
         }
 
   /**
      * 功能:
      *      复原
      * 参数:
      *      无
      * 返回值:
      *      无
      */
         public   void   revert () {
             scale   = scaleOriginal;
         }
 
   /*
   //======================================================================
   //  以下为抽象方法定义,具体实现由继承类来完成。
   //======================================================================
   */
 
   /**
      * 功能:
      *      获得屏幕坐标
      * 参数:
      *      lon     - 经度
      *      lat     - 纬度
      * 返回值:
      *      屏幕坐标
      */
         public abstract   Point getPosition ( double   lon,   double   lat ) ;
 
   /**
      * 功能:
      *      获得屏幕坐标对应的经纬度
      * 参数:
      *      x       - 屏幕水平坐标
      *      y       - 屏幕垂直坐标
      * 返回值:
      *      对应的经纬度
      */
         public abstract   Point2D.Double getCoordinate ( int   x,   int   y ) ;
 
   /**
      * 功能:
      *      获得角度(不同投影含义不同)
      * 参数:
      *      lon - 水平坐标
      *      lat - 垂直坐标
      * 返回值:
      *      角度值
      */
         //Lambert、Stereogram、Polar类需要重载此方法,Linear、Mercator类直接返回0。
         public   double   getAngle ( double   lon,   double   lat ) {
             return ( 0.0 ) ;
         }
 
   /**
      * 功能:
      *      画经线、纬线
      * 参数:
      *      g       - 图形设备
      *      f       - 字体
      *      c       - 画线颜色
      *      inc_lon - 经线间隔
      *      inc_lat - 纬线间隔
      * 返回值:
      *      无
      */
         public abstract   void   drawGridLine ( Graphics2D g, Font f, Color c,   int   inc_lon,   int   inc_lat ) ;
 
   }
/**
 * 
 */
package cma.common.isoline;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import  java.text.DecimalFormat;

public class Linear extends  Coordinate{
 /**
  * 功能:
  *      重置参数
  * 参数:
  *      lon,lat     - 中心经纬度,
  *      px,py       - 中心经纬度对应的屏幕坐标
  *      sx,sy       - 缩放系数
  * 返回值:
  *      无
  */
   public  void  reset ( double  lon,  double  lat,  int  px,  int  py,  double  sc,  double  sx,  double  sy ) {
        type            = Coordinate.LINEAR;
        center          =  new  Point2D.Double (
                            lon <    0.0  ?    0.0  : lon >  360.0  ?  360.0  : lon,
                            lat < - 90.0  ? - 90.0  : lat >   90.0  ?   90.0  : lat
                        ) ;
        place           =  new  Point ( px, py ) ;
        scaleXY         =  new  Point2D.Double ( sx== 0.0 ? 1.0 :Math.abs ( sx ) , sy== 0.0 ? 1.0 :Math.abs ( sy )) ;
        scale           = Math.abs ( sc ) ;
        scaleOriginal   = scale;
        offset          =  new  Point ( 0 ,  0 ) ; //未用
    }

 /**
  * 功能:
  *      构造函数
  * 参数:
  *      无(使用缺省值)
  * 返回值:
  *      无
  */
    public  Linear () {
        reset ( 109.40 ,  24.35 ,  640 ,  480 ,  1.0 ,  10.0 ,  10.0 ) ;
    }

 /**
  * 功能:
  *      构造函数
  * 参数:
  *      lon,lat     - 中心经纬度,
  *      px,py       - 中心经纬度对应的屏幕坐标
  *      sc          - 缩放系数
  * 返回值:
  *      无
  */
    public  Linear ( double  lon,  double  lat,  int  px,  int  py,  double  sc ) {
        reset ( lon, lat, px, py, sc,  10.0 *sc,  10.0 *sc ) ;
    }

 /**
  * 功能:
  *      构造函数
  * 参数:
  *      lon,lat     - 中心经纬度,
  *      px,py       - 中心经纬度对应的屏幕坐标
  *      sx,sy       - 缩放比例
  * 返回值:
  *      无
  */
    public  Linear ( double  lon,  double  lat,  int  px,  int  py,  double  sx,  double  sy ) {
        reset ( lon, lat, px, py,  1.0 , sx, sy ) ;
    }

 /**
  * 功能:
  *      重置参数
  * 参数:
  *      lon,lat     - 中心经纬度,
  *      px,py       - 中心经纬度对应的屏幕坐标
  *      sx,sy       - 缩放系数
  * 返回值:
  *      无
  */
    public  void  reset ( double  lon,  double  lat,  int  px,  int  py,  double  sx,  double  sy ) {
        reset ( lon, lat, px, py,  1.0 , sx, sy ) ;
    }

 /**
  * 功能:
  *      获得屏幕坐标
  * 参数:
  *      lon     - 经度
  *      lat     - 纬度
  * 返回值:
  *      屏幕坐标
  */
    public  Point getPosition ( double  lon,  double  lat ) {
        return (
            new  Point (
                place.x +  ( int )( 0.5  +  ( lon - center.x )  * scale * scaleXY.x ) ,
                place.y +  ( int )( 0.5  +  ( center.y - lat )  * scale * scaleXY.y )
            )
        ) ;
    }

 /**
  * 功能:
  *      获得屏幕坐标对应的经纬度
  * 参数:
  *      x       - 屏幕水平坐标
  *      y       - 屏幕垂直坐标
  * 返回值:
  *      对应的经纬度
  */
    public  Point2D.Double getCoordinate ( int  x,  int  y ) {
        return (
            new  Point2D.Double (
                center.x +  ( x - place.x )  / scale / scaleXY.x,
                center.y +  ( place.y - y )  / scale / scaleXY.y
            )
        ) ;
    }

 /**
  * 功能:
  *      画经线、纬线
  * 参数:
  *      g       - 图形设备
  *      f       - 字体
  *      c       - 画线颜色
  *      inc_lon - 经线间隔
  *      inc_lat - 纬线间隔
  * 返回值:
  *      无
  */
    public  void  drawGridLine ( Graphics2D g, Font f, Color c,  int  inc_lon,  int  inc_lat ) {

        DecimalFormat   df  =  new  DecimalFormat ( "0.#" ) ;
        Color   saveColor   = g.getColor () ;
        Font    saveFont    = g.getFont () ;
        g.setColor ( c ) ;
        g.setFont ( null ==f?f: new  Font ( "Times New Roman" , Font.PLAIN,  12 )) ;
        FontMetrics fm  = g.getFontMetrics () ;
        String      text;
        byte         tmpByte [] ;
        int          bytesWidth, bytesHeight = fm.getHeight () ;;
        Point       pos1, pos2;
        if (  inc_lon >  0  ) {
            for ( double  lon= 0.0 ;lon<= 360.0 ;lon=lon+inc_lon ) {
                if (  180.0  == lon  ) {
                    for ( double  lat=- 90.0 ;lat<= 90.0 ;lat=lat+inc_lat ) {
                        text        = df.format ( lat ) ;
                        tmpByte     = text.getBytes () ;
                        bytesWidth  = fm.bytesWidth ( tmpByte,  0 , tmpByte.length ) ;
                        pos1    =  this .getPosition ( lon, lat ) ;
                        g.drawString ( text, pos1.x-bytesWidth/ 2 , pos1.y+bytesHeight/ 3 ) ;
                    }
                }
                pos1    =  this .getPosition ( lon, - 90.0 ) ;
                pos2    =  this .getPosition ( lon,   90.0 ) ;
                g.drawLine ( pos1.x, pos1.y, pos2.x, pos2.y ) ;
            }
        }
        if (  inc_lat >  0  ) {
            for ( double  lat=- 90.0 ;lat<= 90.0 ;lat=lat+inc_lat ) {
                if (  0.0  == lat  ) {
                    for ( double  lon= 0.0 ;lon<= 360.0 ;lon=lon+inc_lon ) {
                        text        = df.format ( lon ) ;
                        tmpByte     = text.getBytes () ;
                        bytesWidth  = fm.bytesWidth ( tmpByte,  0 , tmpByte.length ) ;
                        pos1    =  this .getPosition ( lon, lat ) ;
                        g.drawString ( text, pos1.x-bytesWidth/ 2 , pos1.y+bytesHeight/ 3 ) ;
                    }
                }
                pos1    =  this .getPosition (   0.0 , lat ) ;
                pos2    =  this .getPosition ( 360.0 , lat ) ;
                g.drawLine ( pos1.x, pos1.y, pos2.x, pos2.y ) ;
            }
        }
        g.setFont ( saveFont ) ;
        g.setColor ( saveColor ) ;
    }
}

 package cma.common.isoline;

public class TriangleVertex {

  //以从小到大的顺序存放A<B<C
  public int A; //顶点A的在离散点序列中的索引
  public int B; //顶点B的在离散点序列中的索引
  public int C; //顶点C的在离散点序列中的索引

 /**
  *功能:
  *重新设定
  *参数:
  *i,j,k-三顶点在离散点序列中的索引
  *返回值:
  *无
  */
  public boolean reset ( int i, int j, int k ){
  if ( i< 0 ||j< 0 ||k< 0 || //顶点必须是正确的索引值
  i==j||j==k||k==i ){ //顶点不能相同
  A=- 1 ;
  B=- 1 ;
  C=- 1 ;
  return ( false ) ;
  }
  else {
  A=Math.min ( Math.min ( i,j ) ,k ) ;
  C=Math.max ( Math.max ( i,j ) ,k ) ;
  B=
  i!=A&&i!=C?i:
  j!=A&&j!=C?j:
  k; //k!=A&&k!=C?k:-1;
 /*if(B==-1){
  A=-1;
  C=-1;
  }
 */
  return ( true ) ;
  }
  }

 /**
  *功能:
  *构造函数
  *参数:
  *无
  *返回值:
  *无
  */
  public TriangleVertex (){
  A=- 1 ;
  B=- 1 ;
  C=- 1 ;
  }

 /**
  *功能:
  *构造函数
  *参数:
  *i,j,k-三顶点的索引
  *返回值:
  *无
  */
  public TriangleVertex ( int i, int j, int k ){
  reset ( i,j,k ) ;
  }

 /**
  *功能:
  *判断三角形是否等价于指定的三角形
  *备注:
  *两个均由(-1,-1,-1)构成的三角形是不相等的,因为不是一个有效的三角形
  *参数:
  *a,b-两个顶点的索引值
  *返回值:
  *true-存在
  *false-不存在
  */
  public boolean equals ( int a, int b, int c ){
  return ( exists ( a,b,c )) ;
  }

 /**
  *功能:
  *判断三角形是否有三个顶点与指定的参数相同,即两个三角形相等
  *参数:
  *a,b,c-三个顶点的索引值
  *返回值:
  *true-存在
  *false-不存在
  */
  public boolean exists ( int a, int b, int c ){
  return (
  a!=b&&b!=c&&c!=a&&
  exists ( a ) &&
  exists ( b ) &&
  exists ( c )
  ) ;
  }

 /**
  *功能:
  *判断三角形是否有两个顶点与指定的参数相同
  *参数:
  *a,b-两个顶点的索引值
  *返回值:
  *true-存在
  *false-不存在
  */
  public boolean exists ( int a, int b ){
  return ( a!=b&&exists ( a ) &&exists ( b )) ;
  }

 /**
  *功能:
  *判断三角形是否存在指定的顶点
  *参数:
  *a-顶点的索引值
  *返回值:
  *true-存在
  *false-不存在
  */
  public boolean exists ( int a ){
  return (
  a>= 0 && ( a==A||a==B||a==C )
  ) ;
  }
}
package cma.common.isoline;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import java.util.Vector;

import javax.imageio.ImageIO;

/**
 * @ClassName: Delaunary
 * @date: 2021年6月15日 下午4:36:23
 * 
 * @Copyright: 2021 www.ankept.com Inc. All rights reserved.
 */
public class Delaunay {

// 由于研究不透其Triangulate方法,故按以下条件重写:
// 1、搜索满足外接圆包含新点的三角形序列;
// 2、在此序列中搜索非共有的边,即此三角形序列构成的边界;
// 3、判断新点与某三角形中作为边界的边是否可以建立新三角形,条件是:新点与该边对应的顶点同处该边界边的同侧。
	public Point2D.Double[] points; // 离散点序列
	// trianglecircleradius的长度一致
	public Vector triangle; // 三角形序列,存储TriangleVertex对象(各顶点在points中的索引)
	public Vector circle; // 三角形的外接圆圆心,存储Point2D.Double对象
	public Vector radius; // 三角形的外接圆半径,存储Double对象
	private int nt; // 三角形总数
	private int np; // 离散点总数

	/**
	 * 功能: 构造函数 参数: pts-离散点序列,一般为经纬度值,长度必须>=3 返回值: 是否成功
	 */
	public Delaunay(Point2D.Double[] pts) {
		np = pts.length;
		points = new Point2D.Double[np + 3]; // 最后三个数据存放超大三角形的三个顶点
		for (int i = 0; i < np; i++) {
			points[i] = new Point2D.Double(pts[i].x, pts[i].y);
		}
		triangle = new Vector();
		circle = new Vector();
		radius = new Vector();
		nt = 0; //
	}

	/**
	 * 功能: 构造一个SuperTriangle,使得所有离散点均落在该三角形内 参数: 无 返回值: 无
	 */
	private boolean createEdge() {
		double dmax, xmin, ymin, xmax, ymax, xmid, ymid;
		xmin = points[0].x;
		ymin = points[0].y;
		xmax = points[0].x;
		ymax = points[0].y;
		for (int i = 1; i < np; i++) {
			xmin = Math.min(xmin, points[i].x);
			ymin = Math.min(ymin, points[i].y);
			xmax = Math.max(xmax, points[i].x);
			ymax = Math.max(ymax, points[i].y);
		}
		xmin = Math.floor(xmin);
		xmax = Math.ceil(xmax);
		ymin = Math.floor(ymin);
		ymax = Math.ceil(ymax);

		// 源数据区域的中心
		xmid = (xmin + xmax) / 2.0;
		ymid = (ymin + ymax) / 2.0;

		// 经向和纬向最大距离的极值dmax,以中心为(xmid,ymid)、边长为dmax的正方形作为源数据的范围
		dmax = Math.max(xmax - xmin, ymax - ymin);
		dmax = Math.ceil(1.1 * dmax); // 增加0.1*dmax可确保离散点不在超大三角形的边上

		// 超大三角形的顶点
		points[np + 0] = new Point2D.Double(xmid - dmax, ymid - dmax / 2);
		points[np + 1] = new Point2D.Double(xmid, ymid + dmax / 2 + dmax);
		points[np + 2] = new Point2D.Double(xmid + dmax, ymid - dmax / 2);

		// 创建超大三角形
		boolean enabled = add(np + 0, np + 1, np + 2);
		return (enabled);
	}

	/**
	 * 功能: 删除与SupperTriangle各顶点相关的三角形,包括SupperTriangle 参数: 无 返回值: 无
	 */
	private void deleteEdge() {
		int sA = np + 0;
		int sB = np + 1;
		int sC = np + 2;
		TriangleVertex tv;
		for (int i = nt - 1; i >= 0; i--) {
			tv = (TriangleVertex) triangle.get(i);
			if (tv.exists(sA) || tv.exists(sB) || tv.exists(sC)) { // 某个顶点与超大三角形相同
				this.delete(i);
			}
		}
	}

	/**
	 * 功能:求三角形的外接圆圆心位置 参数: x1,y1-顶点A的位置 x2,y2-顶点B的位置 x3,y3-顶点C的位置 返回值: 外接圆的圆心位置 公式:
	 * 根据“外接圆心到三顶点的距离均相等”推导得到
	 * (1)r*r=(x-x1)*(x-x1)+(y-y1)*(y-y1)=x*x-2*x*x1+x1*x1+y*y-x*y*y1+y1*y1
	 * (2)r*r=(x-x2)*(x-x2)+(y-y2)*(y-y2)=x*x-2*x*x2+x2*x2+y*y-x*y*y2+y2*y2
	 * (3)r*r=(x-x3)*(x-x3)+(y-y3)*(y-y3)=x*x-2*x*x3+x3*x3+y*y-x*y*y3+y3*y3
	 * 分别相减,略掉x*x和y*y
	 * x=[(y2-y1)*(y3*y3-y1*y1+x3*x3-x1*x1)-(y3-y1)*(y2*y2-y1*y1+x2*x2-x1*x1)]/{2*(x3-x1)*(y2-y1)-2*((x2-x1)*(y3-y1)]}
	 * y=[(x2-x1)*(x3*x3-x1*x1+y3*y3-y1*y1)-(x3-x1)*(x2*x2-x1*x1+y2*y2-y1*y1)]/{2*(y3-y1)*(x2-x1)-2*((y2-y1)*(x3-x1)]}
	 */
	public Point2D.Double circumcircle(double x1, double y1, double x2, double y2, double x3, double y3) {
		double x1x1 = x1 * x1;
		double x2x2 = x2 * x2;
		double x3x3 = x3 * x3;
		double y1y1 = y1 * y1;
		double y2y2 = y2 * y2;
		double y3y3 = y3 * y3;
		double x2_x1 = x2 - x1;
		double x3_x1 = x3 - x1;
		double y2_y1 = y2 - y1;
		double y3_y1 = y3 - y1;
		double x = ((y2_y1) * (y3y3 - y1y1 + x3x3 - x1x1) - (y3_y1) * (y2y2 - y1y1 + x2x2 - x1x1))
				/ (2 * (x3_x1) * (y2_y1) - 2 * (x2_x1) * (y3_y1));
		double y = ((x2_x1) * (x3x3 - x1x1 + y3y3 - y1y1) - (x3_x1) * (x2x2 - x1x1 + y2y2 - y1y1))
				/ (2 * (y3_y1) * (x2_x1) - 2 * (y2_y1) * (x3_x1));
		return (new Point2D.Double(x, y));
	}

	/**
	 * 功能: 判断点相对于矢线a->b的位置 参数: x0,y0-要判断的点 x1,y1-线段端点a的位置 x2,y2-线段端点b的位置 返回值:
	 * -1-在矢线a->b的左侧 0-在矢线a->b上(或延长线上) 1-在矢线a->b的右侧
	 */
	public int position(double x0, double y0, double x1, double y1, double x2, double y2) {
		double xab = (x0 - x1) * (y2 - y1) - (y0 - y1) * (x2 - x1);
		return (xab < 0.0 ? -1 : xab == 0.0 ? 0 : 1);
	}

	/**
	 * 功能: 判断AB与CD是否相交 参数: xa,ya-线段AB的端点a的位置 xb,yb-线段AB的端点b的位置 xc,yc-线段CD的端点c的位置
	 * xd,yd-线段CD的端点d的位置 返回值: true-相交 false-相交
	 */
	public boolean cross(double xa, double ya, double xb, double yb, double xc, double yc, double xd, double yd) {
		int a_cd = this.position(xa, ya, xc, yc, xd, yd);
		int b_cd = this.position(xb, yb, xc, yc, xd, yd);
		return (1 != a_cd * b_cd); // A与B在CD的同侧,则a_cd与b_cd同号,异侧则异号,某点在CD加上,则乘积为0
	}

	/**
	 * 功能: 获得外接圆包含指定点的三角形序列中的索引值列表 参数: p-待检查的点 返回值: 序号列表
	 */
	private int[] contains(Point2D.Double p) {
		if (nt == 0) {
			return (null);
		}
		Vector lists = new Vector();
		Point2D.Double pc; // 三角形的外接圆圆心位置
		double r; // 三角形的外接圆半径
		double pr2;
		int[] res = new int[nt];
		int count = 0;
		Arrays.fill(res, -1);
		for (int i = 1; i < nt; i++) { // [0]是超大三角形,
			r = ((Double) radius.get(i)).doubleValue();
			pc = (Point2D.Double) circle.get(i);
			pr2 = (p.x - pc.x) * (p.x - pc.x) + (p.y - pc.y) * (p.y - pc.y);
			// 与外接圆圆心的距离不超过外接圆的半径
			if (pr2 <= r * r) {
				res[count++] = i;
			}
		}
		if (count == 0) {
			return (null);
		}
		int[] results = new int[count];
		for (int i = 0; i < count; i++) {
			results[i] = res[i];
		}
		return (results);
	}

	/**
	 * 功能: 清空创建的三角形 参数: 无 返回值: 无
	 */
	public void clear() {
		triangle.clear();
		circle.clear();
		radius.clear();
		nt = 0;
	}

	/**
	 * 功能: 删除指定的三角形 参数: index-三角形索引值 返回值: 无
	 */
	public void remove(int index) {
		if (index > 0 && index < nt) { // 不允许从类外删除SuperTriangle
			delete(index);
		}
	}

	/**
	 * 功能: 删除指定的三角形 参数: index-三角形索引值 返回值: 无
	 */
	private void delete(int index) {
		if (index >= 0 && index < nt) {
			TriangleVertex tv = (TriangleVertex) triangle.get(index);
			// System.out.println("Delaunay.java#234:removeT"+String.valueOf(index)+"="+String.valueOf(tv.A)+","+String.valueOf(tv.B)+","+String.valueOf(tv.C));
			triangle.remove(index);
			circle.remove(index);
			radius.remove(index);
			nt--;
		}
	}

	/**
	 * 功能: 删除指定的三角形 参数: index-三角形索引值 返回值: 无
	 */
	public void remove(int[] index) {
		if (null == index || index.length == 0) {
			return;
		}
		int[] idx = new int[index.length];
		for (int i = 0; i < index.length; i++) {
			idx[i] = index[i];
		}
		Arrays.sort(idx);
		for (int i = idx.length - 1; i >= 0; i--) {
			this.remove(idx[i]);
		}
	}

	/**
	 * 注:此方法已废弃 功能: 根据index点与△abc的位置关系确定增加的三角形() 参数: index-新点的索引 abc-已知的三角形 返回值:
	 * 能否成功创建
	 */
	public boolean add(int index, TriangleVertex abc) {
		// index点相对于△abc三边的位置
		int posAB = this.position(points[index].x, points[index].y, points[abc.A].x, points[abc.A].y, points[abc.B].x,
				points[abc.B].y);
		int posBC = this.position(points[index].x, points[index].y, points[abc.B].x, points[abc.B].y, points[abc.C].x,
				points[abc.C].y);
		int posCA = this.position(points[index].x, points[index].y, points[abc.C].x, points[abc.C].y, points[abc.A].x,
				points[abc.A].y);
		boolean inTriangle = // 全部在A->B->C->A的同一侧,说明在三角形内
				(posAB == -1 && posBC == -1 && posCA == -1) || (posAB == 1 && posBC == 1 && posCA == 1);
		boolean enabled = false;
		if (inTriangle) { // 在△abc内
			// System.out.println("Delaunay.java#295:P"+String.valueOf(index)+"inT("+String.valueOf(abc.A)+","+String.valueOf(abc.B)+","+String.valueOf(abc.C)+")");
			enabled = this.add(index, abc.A, abc.B) && this.add(index, abc.B, abc.C) && this.add(index, abc.C, abc.A);
		} else if (posBC == posCA) { // 在AB边上或AB边外
			// System.out.println("Delaunay.java#302:"+String.valueOf(nt)+"="+String.valueOf(index)+","+String.valueOf(abc.A)+","+String.valueOf(abc.B));
			enabled = this.add(index, abc.A, abc.B);
		} else if (posAB == posCA) { // 在BC边上或BC边外
			// System.out.println("Delaunay.java#306:"+String.valueOf(nt)+"="+String.valueOf(index)+","+String.valueOf(abc.B)+","+String.valueOf(abc.C));
			enabled = this.add(index, abc.B, abc.C);
		} else if (posAB == posBC) { // 在CA边上或CA边外
			// System.out.println("Delaunay.java#310:T"+String.valueOf(nt)+"="+String.valueOf(index)+","+String.valueOf(abc.C)+","+String.valueOf(abc.A));
			enabled = this.add(index, abc.C, abc.A);
		} else {
			// System.out.println("Delaunay.java#314:none");
			enabled = false;
		}
		return (enabled);
	}

	/**
	 * 功能: 增加一个三角形,并计算其外接圆圆心和半径 参数: a,b,c-顶点的索引(必须保证是一个有效的三角形) 返回值: 能否成功创建
	 */
	public boolean add(int a, int b, int c) {
		boolean enabled = false;
		// 创建一个三角形
		TriangleVertex tv = new TriangleVertex();
		enabled = tv.reset(a, b, c);
		if (!enabled) {
			return (false);
		}
		int ntSave = nt; // 记录现有的项数
		try {
			triangle.add(tv);

			// 三角形的外接圆圆心
			Point2D.Double p = this.circumcircle(points[a].x, points[a].y, points[b].x, points[b].y, points[c].x,
					points[c].y);
			circle.add(p);

			// 三角形的外接圆半径
			Double d = new Double(
					Math.sqrt((points[a].x - p.x) * (points[a].x - p.x) + (points[a].y - p.y) * (points[a].y - p.y)));
			radius.add(d);
			// System.out.println("Delaunay.java#355:T"+String.valueOf(nt)+"="+String.valueOf(tv.A)+","+String.valueOf(tv.B)+","+String.valueOf(tv.C));
			nt++;
			return (true);
		} catch (Exception ex) { // 出错时删除已经增加的项
			for (int i = triangle.size() - 1; i > ntSave; i--) {
				triangle.remove(i);
			}
			for (int i = circle.size() - 1; i > ntSave; i--) {
				circle.remove(i);
			}
			for (int i = radius.size() - 1; i > ntSave; i--) {
				radius.remove(i);
			}
			nt = ntSave;
			System.out.println(ex.getMessage());
			ex.printStackTrace();
			return (false);
		}
	}

	/**
	 * 功能: 增加一系列三角形,并计算外接圆圆心和半径 参数: index-离散点索引 lists-外接圆包含该离散点的三角形序列 返回值: 无
	 */
	public void add(int index, int[] lists) {
		if (null == lists) {
			return;
		}
		/*
		 * if(lists.length==1){ this.add(index,(TriangleVertex)triangle.get(lists[0]));
		 * return; }
		 */ int[] idx = new int[lists.length];
		for (int i = 0; i < lists.length; i++) {
			idx[i] = lists[i];
		}
		Arrays.sort(idx);
		TriangleVertex[] tvs = new TriangleVertex[idx.length];
		boolean[][] existsSize = new boolean[3][idx.length]; // 判断是否为共有边
		Arrays.fill(existsSize[0], false); // AB边列表
		Arrays.fill(existsSize[1], false); // BC边列表
		Arrays.fill(existsSize[2], false); // CA边列表
		for (int i = 0; i < idx.length; i++) {
			tvs[i] = (TriangleVertex) triangle.get(idx[i]);
		}
		for (int i = 0; i < tvs.length; i++) { // 在三角形序列中查找非共有的边,即序列构成的区域的边界
			for (int j = 0; j < tvs.length; j++) {
				if (i != j) {
					existsSize[0][i] = existsSize[0][i] || tvs[j].exists(tvs[i].A, tvs[i].B); // 其它三角形是否也存在AB边
					existsSize[1][i] = existsSize[1][i] || tvs[j].exists(tvs[i].B, tvs[i].C); // 其它三角形是否也存在BC边
					existsSize[2][i] = existsSize[2][i] || tvs[j].exists(tvs[i].C, tvs[i].A); // 其它三角形是否也存在CA边
				}
			}
		}
		int pos1, pos2;
		for (int i = 0; i < tvs.length; i++) { // 非共有的边,与P(index)点构成新三角形
			if (!existsSize[0][i] && // 非共有的边
					!this.cross( // PC与AB不相交
							points[index].x, points[index].y, points[tvs[i].C].x, points[tvs[i].C].y,
							points[tvs[i].A].x, points[tvs[i].A].y, points[tvs[i].B].x, points[tvs[i].B].y)) {
				this.add(index, tvs[i].A, tvs[i].B); // 构成△PAB
			}
			if (!existsSize[1][i] && // 非共有的边
					!this.cross( // PA与BC不相交
							points[index].x, points[index].y, points[tvs[i].A].x, points[tvs[i].A].y,
							points[tvs[i].B].x, points[tvs[i].B].y, points[tvs[i].C].x, points[tvs[i].C].y)) {
				this.add(index, tvs[i].B, tvs[i].C); // 构成△PBC
			}
			if (!existsSize[2][i] && // 非共有的边
					!this.cross( // PB与CA不相交
							points[index].x, points[index].y, points[tvs[i].B].x, points[tvs[i].B].y,
							points[tvs[i].C].x, points[tvs[i].C].y, points[tvs[i].A].x, points[tvs[i].A].y)) {
				this.add(index, tvs[i].C, tvs[i].A); // 构成△PCA
			}
		}
	}

	/**
	 * 功能: 建立三角形网格 参数: 无 返回值: 成功创建的三角形个数
	 */
	public int build() {
		// 清空已经存在的三角形网格
		this.clear();

		if (!createEdge() || // 创建超大三角形
				!this.add(0, np + 0, np + 1) || // 第一个离散点与超大三角形构成三个小三角形
				!this.add(0, np + 1, np + 2) || !this.add(0, np + 0, np + 2)) {
			return (0);
		}
		for (int i = 1; i < np; i++) { // 对所有离散点循环([0]已经使用)
			int[] lists = this.contains(points[i]); // 获得外接圆包含该点的三角形序列
			this.add(i, lists); // 添加三角形
			this.remove(lists);
		}
		deleteEdge();
		return (nt);
	}

	/**
	 * 功能: 显示三角形网格 参数: g-图形设备 c-颜色 crd-坐标投影对象 返回值: 无
	 */
	public void draw(Graphics2D g, Color c, Coordinate crd) {
		Color saveColor = g.getColor();
		g.setColor(c);
		TriangleVertex tv;
		Point p1, p2, p3 /* ,p0 */ ;
		int r;
		for (int i = 0; i < nt; i++) {
			tv = (TriangleVertex) triangle.get(i);
			p1 = crd.getPosition(points[tv.A].x, points[tv.A].y);
			p2 = crd.getPosition(points[tv.B].x, points[tv.B].y);
			p3 = crd.getPosition(points[tv.C].x, points[tv.C].y);
			g.drawLine(p1.x, p1.y, p2.x, p2.y);
			g.drawLine(p2.x, p2.y, p3.x, p3.y);
			g.drawLine(p3.x, p3.y, p1.x, p1.y);
		}
		for (int i = 0; i < np; i++) {
			p1 = crd.getPosition(points[i].x, points[i].y);
			g.setColor(Color.red);
			g.fillRect(p1.x - 1, p1.y - 1, 3, 3);
			g.setColor(Color.blue);
			g.drawString(String.valueOf(i), p1.x, p1.y);
		}
		g.setColor(saveColor);
	}
	public static void main(String[] args) {
		int   points = 10;
		Point2D.Double[]  pts = new Point2D.Double[points];
		for(int i=0;i<points;i++) {
		   pts[i] = new Point2D.Double(//在中国区域(70-140E, 10-60N)产生随机点
		           70.0 + 69.0 * Math.random() + Math.random(),
		           10.0 + 49.0 * Math.random() + Math.random());
		}
		
		Delaunay  delaunayT  = new Delaunay(pts);//创建Delaunay对象
		int     nTriangle  = delaunayT.build();//创建Delaunay网络
		Vector triangle2 = delaunayT.triangle;
		int       width  = 1600;
		int       height = 1024;
		BufferedImage  image  = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D   g    = image.createGraphics();
		g.setColor(new Color(192,240,255));//Micaps1.0的背景色
		g.fillRect(0, 0, width, height);//背景色填充
		
		Linear   coordinate = new Linear(110.0, 35.0,width/2, height/2, 15, 15);//线性投影
		//Diamond09.drawBorderline(g, Color.gray, coordinate, "/path/to/ProvinceMap.dat");//画省界、国界、洲界
		coordinate.drawGridLine(g, null, Color.green, 10, 10);//画经纬线
		
		delaunayT.draw(g, Color.blue, coordinate);//显示Delaunay网格
		File file = new File("D:/pic.jpg");
		try {
			ImageIO.write(image, "jpg", file);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NewTech精选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值