构造Delaunay三角形网格

来源:互联网 发布:微信小程序游戏源码 编辑:程序博客网 时间:2024/05/17 08:22
Delaunay是一种在离散点序列中快速构造三角形网格的方法,本代码依据的Delaunay三角形的性质:在已知的Dalaunay三角化的网格上加入一点P,只需要删除所有外接圆包含此点的三角形,并连接P与所有可见的点(即连接后不会与其他边相交),则形成的网格仍然满足Delaunay三角剖分的条件。

方法:
1、构造超大三角形,使得所有离散点均落在该三角形的内部;
2、以该超大三角形作为Delaunay三角形集D的首个成员;
3、对所有离散点集里的每个点,搜索D中满足外接圆包含该点的三角形集T;
4、新点与T构成三角形集N,在D中删除T,并加入N;
5、重复第3、4步;
6、删除D中所有与超大三角形有关的三角形。

 


Delaunay.java
001 /**
002   Delaunay 方法构造三角形网格
003 
004       PACKAGE: cma.common.isoline
005      FILENAME: Delaunay.java
006      LANGUAGE: Java2 v1.4
007      ORIGINAL: none
008   DESCRIPTION:
009        CREATE: 2007-07-06 17:43:49
010         UPDATE 2007-07-08
011      MODIFIER: 刘泽军 (BJ0773@gmail.com)
012                广西气象减灾研究所
013                Guangxi Institude of Meteorology and Disaster-reducing Research(GIMDR)
014 
015     REFERENCE: 参考了 <b>Delaunay.pas</b>
016 
017       COMPILE: javac TriangleVertex.java Delaunay.java
018 
019   由于研究不透其Triangulate方法,故按以下条件重写:
020   1、搜索满足外接圆包含新点的三角形序列;
021   2、在此序列中搜索非共有的边,即此三角形序列构成的边界;
022   3、判断新点与某三角形中作为边界的边是否可以建立新三角形,条件是:新点与该边对应的顶点同处该边界边的同侧。
023 
024 */
025 
026 /*
027 用法:
028     int     points  = 10000;
029     Point2D.Double[]    pts = new Point2D.Double[points];
030     for(int i=0;i<points;i++) {
031         pts[i]  = new Point2D.Double(//在中国区域(70-140E, 10-60N)产生随机点
032                         70.0 + 69.0 * Math.random() + Math.random(),
033                         10.0 + 49.0 * Math.random() + Math.random());
034     }
035 
036     Delaunay    delaunayT   = new Delaunay(pts);//创建Delaunay对象
037     int         nTriangle   = delaunayT.build();//创建Delaunay网络
038 
039     int             width   = 1600;
040     int             height  = 1024;
041     BufferedImage   image   = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
042     Graphics2D      g       = image.createGraphics();
043     g.setColor(new Color(192,240,255));//Micaps1.0的背景色
044     g.fillRect(0, 0, width, height);//背景色填充
045 
046     Linear      coordinate  = new Linear(110.0, 35.0,width/2, height/2, 15, 15);//线性投影
047     Diamond09.drawBorderline(g, Color.gray, coordinate, "/path/to/ProvinceMap.dat");//画省界、国界、洲界
048     coordinate.drawGridLine(g, null, Color.green, 10, 10);//画经纬线
049 
050     delaunayT.draw(g, Color.blue, coordinate);//显示Delaunay网格
051 
052     //输出到浏览器
053     ServletOutputStream sos     = response.getOutputStream();
054     JPEGImageEncoder    encoder = JPEGCodec.createJPEGEncoder(sos);
055 
056     //以下三行改进图片质量
057     JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
058     param.setQuality(1.0f, false);
059     encoder.setJPEGEncodeParam(param);
060 
061     encoder.encode(image);
062 */
063 
064 //Credit to Paul Bourke (pbourke@swin.edu.au) for the original Fortran 77 Program :))
065 //Conversion to Visual Basic by EluZioN (EluZioN@casesladder.com)
066 //Conversion from VB to Delphi6 by Dr Steve Evans (steve@lociuk.com)
067 //08Jul2007 Conversion from Delphi6 to Java 1.4 by LIU Zejun (BJ0773@gmail.com)
068 ///////////////////////////////////////////////////////////////////////////////
069 //June 2002 Update by Dr Steve Evans (steve@lociuk.com): Heap memory allocation
070 //added to prevent stack overflow when MaxVertices and MaxTriangles are very large.
071 //Additional Updates in June 2002:
072 //Bug in InCircle function fixed. Radius r := Sqrt(rsqr).
073 //Check for duplicate points added when inserting new point.
074 //For speed, all points pre-sorted in x direction using quicksort algorithm and
075 //triangles flagged when no longer needed. The circumcircle centre and radius of
076 //the triangles are now stored to improve calculation time.
077 ///////////////////////////////////////////////////////////////////////////////
078 //08 Jul 2007 Update by LIU Zejun (BJ0773@gmail.com)
079 //Rewrite the FUNCTION InCircle(rename to circumcircle) to improve calculation time.
080 //Rewrite the FUNCTION Triangulate(rename to build) by used another algorithm.
081 //Convert all data dimension to DYNAMIC(used java.util.Vector).
082 ///////////////////////////////////////////////////////////////////////////////
083 //You can use this code however you like providing the above credits remain in tact
084 
085 
086 package cma.common.isoline;//要用到cma.common.isoline.TriangleVertex
087 
088 import java.io.*;
089 import java.lang.*;
090 import java.util.*;
091 import java.awt.*;
092 import java.awt.geom.*;
093 import java.text.DecimalFormat;
094 import java.awt.image.*;
095 import com.sun.image.codec.jpeg.*;
096 
097 import cma.common.projection.*;//仅在draw函数中用到
098 
099 public class Delaunay {
100     public      Point2D.Double[]    points;     //离散点序列
101     //triangle circle radius 的长度一致
102     public      Vector              triangle;   //三角形序列,存储TriangleVertex对象(各顶点在points中的索引)
103     public      Vector              circle;     //三角形的外接圆圆心,存储Point2D.Double对象
104     public      Vector              radius;     //三角形的外接圆半径,存储Double对象
105     private     int                 nt;         //三角形总数
106     private     int                 np;         //离散点总数
107 
108 /**
109  * 功能:
110  *      构造函数
111  * 参数:
112  *      pts     - 离散点序列,一般为经纬度值,长度必须>=3
113  * 返回值:
114  *      是否成功
115  */
116     public Delaunay(Point2D.Double[] pts) {
117         np      = pts.length;
118         points  = new Point2D.Double[np+3];//最后三个数据存放超大三角形的三个顶点
119         for(int i=0;i<np;i++) {
120             points[i]   new Point2D.Double(pts[i].x, pts[i].y);
121         }
122         triangle    = new Vector();
123         circle      = new Vector();
124         radius      = new Vector();
125         nt          = 0;//
126     }
127 
128 /*
129 超大三角形(SupperTriangle)示意图
130 假设为经纬度坐标(若为屏幕坐标,则为倒三角形,不影响计算)
131                 E
132                 ^
133                /|/
134               / | /
135              /  |  /
136             /30 | 30/
137            /    |    /
138           /     |     /
139          /      |H     /
140        D+-------+-------+C
141        /|       |       |/
142       / |       |       | /
143      /  |       |       |  /
144     /   |  xmid o ymid  |   /
145    /    |       |       |    /
146   /     |       |       |     /
147  / 60   |       |       |   60 /
148 /-------+-------+-------+-------/
149 F      A               B       G
150 等腰三角形△EFG,o(xmid,ymid)为源数据的中心
151 xmid=(xmin+xmax)/2
152 ymid=(ymin+ymax)/2
153 
154 AB=BC=CD=DA=EH=dmax=Math.max(xmax-xmin, ymax-ymin)
155 FA=DH=CH=BG=AB/2
156 ∠EFG=60度
157 
158 F=(xmid-dmax, ymid-dmax/2)
159 G=(xmid+dmax, ymid-dmax/2)
160 E=(xmid, ymid+dmax/2+dmax)
161 */
162 
163 /**
164  * 功能:
165  *      构造一个SuperTriangle,使得所有离散点均落在该三角形内
166  * 参数:
167  *      无
168  * 返回值:
169  *      无
170  */
171     private boolean createEdge() {
172         double      dmax, xmin, ymin, xmax, ymax, xmid, ymid;
173         xmin        = points[0].x;
174         ymin        = points[0].y;
175         xmax        = points[0].x;
176         ymax        = points[0].y;
177         for(int i=1;i<np;i++) {
178             xmin    = Math.min(xmin, points[i].x);
179             ymin    = Math.min(ymin, points[i].y);
180             xmax    = Math.max(xmax, points[i].x);
181             ymax    = Math.max(ymax, points[i].y);
182         }
183         xmin    = Math.floor(xmin);
184         xmax    = Math.ceil(xmax);
185         ymin    = Math.floor(ymin);
186         ymax    = Math.ceil(ymax);
187 
188         //源数据区域的中心
189         xmid    = (xmin+xmax)/2.0;
190         ymid    = (ymin+ymax)/2.0;
191 
192         //经向和纬向最大距离的极值dmax,以中心为(xmid,ymid)、边长为dmax的正方形作为源数据的范围
193         dmax    = Math.max(xmax-xmin, ymax-ymin);
194         dmax    = Math.ceil(1.1*dmax);//增加0.1*dmax可确保离散点不在超大三角形的边上
195 
196         //超大三角形的顶点
197         points[np+0]    new Point2D.Double(xmid-dmax, ymid-dmax/2);
198         points[np+1]    new Point2D.Double(xmid, ymid+dmax/2+dmax);
199         points[np+2]    new Point2D.Double(xmid+dmax, ymid-dmax/2);
200 
201         //创建超大三角形
202         boolean enabled = add(np+0, np+1, np+2);
203         return(enabled);
204     }
205 
206 /**
207  * 功能:
208  *      删除与SupperTriangle各顶点相关的三角形,包括SupperTriangle
209  * 参数:
210  *      无
211  * 返回值:
212  *      无
213  */
214     private void deleteEdge() {
215         int sA  = np + 0;
216         int sB  = np + 1;
217         int sC  = np + 2;
218         TriangleVertex  tv;
219         for(int i=nt-1;i>=0;i--) {
220             tv  = (TriangleVertex)triangle.get(i);
221             iftv.exists(sA|| tv.exists(sB|| tv.exists(sC) ) {//某个顶点与超大三角形相同
222                 this.delete(i);
223             }
224         }
225     }
226 
227 /**
228  * 功能:求三角形的外接圆圆心位置
229  * 参数:
230  *      x1,y1   - 顶点A的位置
231  *      x2,y2   - 顶点B的位置
232  *      x3,y3   - 顶点C的位置
233  * 返回值:
234  *      外接圆的圆心位置
235  * 公式:
236  *      根据“外接圆心到三顶点的距离均相等”推导得到
237  * (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
238  * (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
239  * (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
240  * 分别相减,略掉x*x和y*y
241  * 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)]}
242  * 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)]}
243  */
244     public Point2D.Double circumcircle(
245             double x1, double y1, double x2, double y2, double x3, double y3) {
246         double  x1x1    = x1 * x1;
247         double  x2x2    = x2 * x2;
248         double  x3x3    = x3 * x3;
249         double  y1y1    = y1 * y1;
250         double  y2y2    = y2 * y2;
251         double  y3y3    = y3 * y3;
252         double  x2_x1   = x2 - x1;
253         double  x3_x1   = x3 - x1;
254         double  y2_y1   = y2 - y1;
255         double  y3_y1   = y3 - y1;
256         double  x =((y2_y1)*(y3y3 -y1y1 +x3x3 -x1x1-(y3_y1)*(y2y2 -y1y1 +x2x2 -x1x1 )) (2*(x3_x1)*(y2_y1)-2*(x2_x1)*(y3_y1));
257         double  y =((x2_x1)*(x3x3 -x1x1 +y3y3 -y1y1-(x3_x1)*(x2x2 -x1x1 +y2y2 -y1y1 )) (2*(y3_y1)*(x2_x1)-2*(y2_y1)*(x3_x1));
258         return(
259             new Point2D.Double(x, y)
260         );
261     }
262 
263 /**
264  * 功能:
265  *      判断点相对于矢线a->b的位置
266  * 参数:
267  *      x0,y0   - 要判断的点
268  *      x1,y1   - 线段端点a的位置
269  *      x2,y2   - 线段端点b的位置
270  * 返回值:
271  *      -1  - 在矢线a->b的左侧
272  *      0   - 在矢线a->b上(或延长线上)
273  *      1   - 在矢线a->b的右侧
274  */
275     public int position(
276             double x0, double y0,
277             double x1, double y1,
278             double x2, double y2) {
279         double  xab   =   (x0-x1)*(y2-y1)-(y0-y1)*(x2-x1);
280         return(xab < 0.0 ? -: xab == 0.0 1);
281     }
282 
283 /**
284  * 功能:
285  *      判断AB与CD是否相交
286  * 参数:
287  *      xa,ya   - 线段AB的端点a的位置
288  *      xb,yb   - 线段AB的端点b的位置
289  *      xc,yc   - 线段CD的端点c的位置
290  *      xd,yd   - 线段CD的端点d的位置
291  * 返回值:
292  *      true    - 相交
293  *      false   - 相交
294  */
295     public boolean cross(
296             double xa, double ya,
297             double xb, double yb,
298             double xc, double yc,
299             double xd, double yd) {
300         int a_cd    = this.position(xa, ya, xc, yc, xd, yd);
301         int b_cd    = this.position(xb, yb, xc, yc, xd, yd);
302         return(1!=a_cd*b_cd);//A与B在CD的同侧,则a_cd与b_cd同号,异侧则异号,某点在CD加上,则乘积为0
303     }
304 
305 /**
306  * 功能:
307  *      获得外接圆包含指定点的三角形序列中的索引值列表
308  * 参数:
309  *      p       - 待检查的点
310  * 返回值:
311  *      序号列表
312  */
313     private int[] contains(Point2D.Double p) {
314         ifnt == ) {
315             return(null);
316         }
317         Vector          lists   = new Vector();
318         Point2D.Double  pc; //三角形的外接圆圆心位置
319         double          r;  //三角形的外接圆半径
320         double          pr2;
321         int[]           res     = new int[nt];
322         int             count   = 0;
323         Arrays.fill(res, -1);
324         for(int i=1;i<nt;i++) {//[0]是超大三角形,
325             r   = ((Double)radius.get(i)).doubleValue();
326             pc  = (Point2D.Double)circle.get(i);
327             pr2 = (p.x - pc.x)*(p.x - pc.x)+(p.y - pc.y)*(p.y - pc.y);
328             //与外接圆圆心的距离不超过外接圆的半径
329             ifpr2 <= r*r ) {
330                 res[count++]    = i;
331             }
332         }
333         ifcount == ) {
334             return(null);
335         }
336         int[]   results = new int[count];
337         for(int i=0;i<count;i++) {
338             results[i]  = res[i];
339         }
340         return(results);
341     }
342 
343 /**
344  * 功能:
345  *      清空创建的三角形
346  * 参数:
347  *      无
348  * 返回值:
349  *      无
350  */
351     public void clear() {
352         triangle.clear();
353         circle.clear();
354         radius.clear();
355         nt  = 0;
356     }
357 
358 /**
359  * 功能:
360  *      删除指定的三角形
361  * 参数:
362  *      index   - 三角形索引值
363  * 返回值:
364  *      无
365  */
366     public void remove(int index) {
367         ifindex > && index < nt ) {//不允许从类外删除SuperTriangle
368             delete(index);
369         }
370     }
371 
372 /**
373  * 功能:
374  *      删除指定的三角形
375  * 参数:
376  *      index   - 三角形索引值
377  * 返回值:
378  *      无
379  */
380     private void delete(int index) {
381         ifindex >= && index < nt ) {
382             TriangleVertex  tv  = (TriangleVertex)triangle.get(index);
383 //          System.out.println("Delaunay.java # 234 : remove T" + String.valueOf(index) + "=" + String.valueOf(tv.A) + "," + String.valueOf(tv.B) + "," + String.valueOf(tv.C));
384             triangle.remove(index);
385             circle.remove(index);
386             radius.remove(index);
387             nt--;
388         }
389     }
390 
391 /**
392  * 功能:
393  *      删除指定的三角形
394  * 参数:
395  *      index   - 三角形索引值
396  * 返回值:
397  *      无
398  */
399     public void remove(int[] index) {
400         ifnull == index || index.length == ) {
401             return;
402         }
403         int[]   idx = new int[index.length];
404         for(int i=0;i<index.length;i++) {
405             idx[i]  = index[i];
406         }
407         Arrays.sort(idx);
408         for(int i=idx.length-1;i>=0;i--) {
409             this.remove(idx[i]);
410         }
411     }
412 
413 /** 注:此方法已废弃
414  * 功能:
415  *      根据index点与△abc的位置关系确定增加的三角形()
416  * 参数:
417  *      index   - 新点的索引
418  *      abc     - 已知的三角形
419  * 返回值:
420  *      能否成功创建
421  */
422     public boolean add(int index, TriangleVertex abc) {
423         //index点相对于△abc三边的位置
424         int posAB   = this.position(
425                         points[index].x, points[index].y,
426                         points[abc.A].x, points[abc.A].y,
427                         points[abc.B].x, points[abc.B].y
428                     );
429         int posBC   = this.position(
430                         points[index].x, points[index].y,
431                         points[abc.B].x, points[abc.B].y,
432                         points[abc.C].x, points[abc.C].y
433                     );
434         int posCA   = this.position(
435                         points[index].x, points[index].y,
436                         points[abc.C].x, points[abc.C].y,
437                         points[abc.A].x, points[abc.A].y
438                     );
439         boolean inTriangle  =//全部在A->B->C->A的同一侧,说明在三角形内
440                             (posAB == -&& posBC == -&& posCA == -1||
441                             (posAB ==  && posBC ==  && posCA ==  1);
442         boolean enabled     = false;
443         ifinTriangle ) {//在△abc内
444 //          System.out.println("Delaunay.java # 295 : P" + String.valueOf(index) + " in T(" + String.valueOf(abc.A) + "," + String.valueOf(abc.B) + "," + String.valueOf(abc.C) + ")");
445             enabled =
446                         this.add(index, abc.A, abc.B&&
447                         this.add(index, abc.B, abc.C&&
448                         this.add(index, abc.C, abc.A);
449         }
450         else ifposBC == posCA ) {//在AB边上或AB边外
451 //          System.out.println("Delaunay.java # 302 : " + String.valueOf(nt) + "=" + String.valueOf(index) + "," + String.valueOf(abc.A) + "," + String.valueOf(abc.B));
452             enabled = this.add(index, abc.A, abc.B);
453         }
454         else ifposAB == posCA ) {//在BC边上或BC边外
455 //          System.out.println("Delaunay.java # 306 : " + String.valueOf(nt) + "=" + String.valueOf(index) + "," + String.valueOf(abc.B) + "," + String.valueOf(abc.C));
456             enabled = this.add(index, abc.B, abc.C);
457         }
458         else ifposAB == posBC ) {//在CA边上或CA边外
459 //          System.out.println("Delaunay.java # 310 : T" + String.valueOf(nt) + "=" + String.valueOf(index) + "," + String.valueOf(abc.C) + "," + String.valueOf(abc.A));
460             enabled = this.add(index, abc.C, abc.A);
461         }
462         else {
463 //          System.out.println("Delaunay.java # 314 : none");
464             enabled = false;
465         }
466         return(enabled);
467     }
468 
469 /**
470  * 功能:
471  *      增加一个三角形,并计算其外接圆圆心和半径
472  * 参数:
473  *      a,b,c   - 顶点的索引(必须保证是一个有效的三角形)
474  * 返回值:
475  *      能否成功创建
476  */
477     public boolean add(int a, int b, int c) {
478         boolean enabled     = false;
479         //创建一个三角形
480         TriangleVertex  tv  = new TriangleVertex();
481         enabled = tv.reset(a, b, c);
482         if!enabled ) {
483             return(false);
484         }
485         int ntSave  = nt;//记录现有的项数
486         try {
487             triangle.add(tv);
488 
489             //三角形的外接圆圆心
490             Point2D.Double  p   = this.circumcircle(
491                                         points[a].x, points[a].y,
492                                         points[b].x, points[b].y,
493                                         points[c].x, points[c].y);
494             circle.add(p);
495 
496             //三角形的外接圆半径
497             Double          d   = new Double(
498                                     Math.sqrt(
499                                         (points[a].x-p.x)*(points[a].x-p.x+
500                                         (points[a].y-p.y)*(points[a].y-p.y)
501                                     )
502                                 );
503             radius.add(d);
504 //          System.out.println("Delaunay.java # 355 : T" + String.valueOf(nt) + "=" + String.valueOf(tv.A) + "," + String.valueOf(tv.B) + "," + String.valueOf(tv.C));
505             nt++;
506             return(true);
507         }
508         catch(Exception ex) {//出错时删除已经增加的项
509             for(int i=triangle.size()-1;i>ntSave;i--) {
510                 triangle.remove(i);
511             }
512             for(int i=circle.size()-1;i>ntSave;i--) {
513                 circle.remove(i);
514             }
515             for(int i=radius.size()-1;i>ntSave;i--) {
516                 radius.remove(i);
517             }
518             nt  = ntSave;
519             System.out.println(ex.getMessage());
520             ex.printStackTrace();
521             return(false);
522         }
523     }
524 
525 /**
526  * 功能:
527  *      增加一系列三角形,并计算外接圆圆心和半径
528  * 参数:
529  *      index   - 离散点索引
530  *      lists   - 外接圆包含该离散点的三角形序列
531  * 返回值:
532  *      无
533  */
534     public void add(int index, int[] lists) {
535         ifnull == lists ) {
536             return;
537         }
538 /*      if( lists.length == 1 ) {
539             this.add(index, (TriangleVertex)triangle.get(lists[0]));
540             return;
541         }
542 */      int[]   idx     = new int[lists.length];
543         for(int i=0;i<lists.length;i++) {
544             idx[i]  = lists[i];
545         }
546         Arrays.sort(idx);
547         TriangleVertex[]    tvs         = new TriangleVertex[idx.length];
548         boolean[][]         existsSize  = new boolean[3][idx.length];//判断是否为共有边
549         Arrays.fill(existsSize[0]false);//AB边列表
550         Arrays.fill(existsSize[1]false);//BC边列表
551         Arrays.fill(existsSize[2]false);//CA边列表
552         for(int i=0;i<idx.length;i++) {
553             tvs[i]  (TriangleVertex)triangle.get(idx[i]);
554         }
555         for(int i=0;i<tvs.length;i++) {//在三角形序列中查找非共有的边,即序列构成的区域的边界
556             for(int j=0;j<tvs.length;j++) {
557                 ifi != j ) {
558                     existsSize[0][i]    = existsSize[0][i|| tvs[j].exists(tvs[i].A, tvs[i].B);//其它三角形是否也存在AB边
559                     existsSize[1][i]    = existsSize[1][i|| tvs[j].exists(tvs[i].B, tvs[i].C);//其它三角形是否也存在BC边
560                     existsSize[2][i]    = existsSize[2][i|| tvs[j].exists(tvs[i].C, tvs[i].A);//其它三角形是否也存在CA边
561                 }
562             }
563         }
564         int pos1, pos2;
565         for(int i=0;i<tvs.length;i++) {//非共有的边,与P(index)点构成新三角形
566             if(     !existsSize[0][i&&//非共有的边
567                     !this.cross(//PC与AB不相交
568                         points[index].x, points[index].y,
569                         points[tvs[i].C].x, points[tvs[i].C].y,
570                         points[tvs[i].A].x, points[tvs[i].A].y,
571                         points[tvs[i].B].x, points[tvs[i].B].y
572                     )
573                 ) {
574                 this.add(index, tvs[i].A, tvs[i].B);//构成△PAB
575             }
576             if!existsSize[1][i]  &&//非共有的边
577                     !this.cross(//PA与BC不相交
578                         points[index].x, points[index].y,
579                         points[tvs[i].A].x, points[tvs[i].A].y,
580                         points[tvs[i].B].x, points[tvs[i].B].y,
581                         points[tvs[i].C].x, points[tvs[i].C].y
582                     )
583                 ) {
584                 this.add(index, tvs[i].B, tvs[i].C);//构成△PBC
585             }
586             if!existsSize[2][i]  &&//非共有的边
587                     !this.cross(//PB与CA不相交
588                         points[index].x, points[index].y,
589                         points[tvs[i].B].x, points[tvs[i].B].y,
590                         points[tvs[i].C].x, points[tvs[i].C].y,
591                         points[tvs[i].A].x, points[tvs[i].A].y
592                     )
593                 ) {
594                 this.add(index, tvs[i].C, tvs[i].A);//构成△PCA
595             }
596         }
597     }
598 
599 /**
600  * 功能:
601  *      建立三角形网格
602  * 参数:
603  *      无
604  * 返回值:
605  *      成功创建的三角形个数
606  */
607     public int build() {
608         //清空已经存在的三角形网格
609         this.clear();
610 
611         if!createEdge()   ||//创建超大三角形
612             !this.add(0, np+0, np+1)    ||//第一个离散点与超大三角形构成三个小三角形
613             !this.add(0, np+1, np+2)    ||
614             !this.add(0, np+0, np+2)    ) {
615             return(0);
616         }
617         for(int i=1;i<np;i++) {//对所有离散点循环([0]已经使用)
618             int[]   lists   = this.contains(points[i]);//获得外接圆包含该点的三角形序列
619             this.add(i, lists);//添加三角形
620             this.remove(lists);
621         }
622         deleteEdge();
623         return(nt);
624     }
625 
626 /**
627  * 功能:
628  *      显示三角形网格
629  * 参数:
630  *      g       - 图形设备
631  *      c       - 颜色
632  *      crd     - 坐标投影对象
633  * 返回值:
634  *      无
635  */
636     public void draw(Graphics2D g, Color c, Coordinate crd) {
637         Color   saveColor   = g.getColor();
638         g.setColor(c);
639         TriangleVertex  tv;
640         Point   p1, p2, p3/*, p0*/;
641         int     r;
642         for(int i=0;i<nt;i++) {
643             tv  = (TriangleVertex)triangle.get(i);
644             p1  = crd.getPosition(points[tv.A].x, points[tv.A].y);
645             p2  = crd.getPosition(points[tv.B].x, points[tv.B].y);
646             p3  = crd.getPosition(points[tv.C].x, points[tv.C].y);
647             g.drawLine(p1.x, p1.y, p2.x, p2.y);
648             g.drawLine(p2.x, p2.y, p3.x, p3.y);
649             g.drawLine(p3.x, p3.y, p1.x, p1.y);
650         }
651         for(int i=0;i<np;i++) {
652             p1  = crd.getPosition(points[i].x, points[i].y);
653             g.setColor(Color.red);
654             g.fillRect(p1.x-1, p1.y-133);
655             g.setColor(Color.blue);
656             g.drawString(String.valueOf(i), p1.x, p1.y);
657         }
658         g.setColor(saveColor);
659     }
660 
661 }

 


TriangleVertex.java
001 /*
002 
003   三角形顶点
004 
005       PACKAGE: cma.common.isoline
006      FILENAME: TriangleVertex.java
007      LANGUAGE: Java2 v1.4
008      ORIGINAL: none
009   DESCRIPTION: Triangle Vertex Index
010        CREATE: 2007-07-06 23:09:14
011        UPDATE: 2007-07-06
012        AUTHOR: 刘泽军 (BJ0773@gmail.com)
013                广西气象减灾研究所
014                Guangxi Institude of Meteorology and Disaster-reducing Research(GIMDR)
015 
016       COMPILE: javac TriangleVertex.java
017 
018 */
019 
020 package cma.common.isoline;
021 
022 import java.lang.Math.*;
023 
024 public class TriangleVertex {
025 
026     //以从小到大的顺序存放 A < B < C
027     public int      A;      //顶点A的在离散点序列中的索引
028     public int      B;      //顶点B的在离散点序列中的索引
029     public int      C;      //顶点C的在离散点序列中的索引
030 
031 /**
032  * 功能:
033  *      重新设定
034  * 参数:
035  *      i,j,k   - 三顶点在离散点序列中的索引
036  * 返回值:
037  *      无
038  */
039     public boolean reset(int i, int j, int k) {
040         if(     i <  || j <  || k <  ||//顶点必须是正确的索引值
041                 i == j || j == k || k == i ) {//顶点不能相同
042             A   = -1;
043             B   = -1;
044             C   = -1;
045             return(false);
046         }
047         else {
048             A   = Math.min(Math.min(i, j), k);
049             C   = Math.max(Math.max(i, j), k);
050             B   =
051                     i != A && i != C ? i :
052                     j != A && j != C ? j :
053                     k;//k != A && k != C ? k : -1;
054 /*          if( B == -1 ) {
055                 A   = -1;
056                 C   = -1;
057             }
058 */
059             return(true);
060         }
061     }
062 
063 /**
064  * 功能:
065  *      构造函数
066  * 参数:
067  *      无
068  * 返回值:
069  *      无
070  */
071     public TriangleVertex() {
072         A   = -1;
073         B   = -1;
074         C   = -1;
075     }
076 
077 /**
078  * 功能:
079  *      构造函数
080  * 参数:
081  *      i,j,k   - 三顶点的索引
082  * 返回值:
083  *      无
084  */
085     public TriangleVertex(int i, int j, int k) {
086         reset(i, j, k);
087     }
088 
089 /**
090  * 功能:
091  *      判断三角形是否等价于指定的三角形
092  * 备注:
093  *      两个均由(-1, -1, -1)构成的三角形是不相等的,因为不是一个有效的三角形
094  * 参数:
095  *      a,b     - 两个顶点的索引值
096  * 返回值:
097  *      true    - 存在
098  *      false   - 不存在
099  */
100     public boolean equals(int a, int b, int c) {
101         return(exists(a, b, c));
102     }
103 
104 /**
105  * 功能:
106  *      判断三角形是否有三个顶点与指定的参数相同,即两个三角形相等
107  * 参数:
108  *      a,b,c   - 三个顶点的索引值
109  * 返回值:
110  *      true    - 存在
111  *      false   - 不存在
112  */
113     public boolean exists(int a, int b, int c) {
114         return(
115             a != b && b != c && c != a &&
116             exists(a&&
117             exists(b&&
118             exists(c)
119         );
120     }
121 
122 /**
123  * 功能:
124  *      判断三角形是否有两个顶点与指定的参数相同
125  * 参数:
126  *      a,b     - 两个顶点的索引值
127  * 返回值:
128  *      true    - 存在
129  *      false   - 不存在
130  */
131     public boolean exists(int a, int b) {
132         return(a != b && exists(a&& exists(b));
133     }
134 
135 /**
136  * 功能:
137  *      判断三角形是否存在指定的顶点
138  * 参数:
139  *      a       - 顶点的索引值
140  * 返回值:
141  *      true    - 存在
142  *      false   - 不存在
143  */
144     public boolean exists(int a) {
145         return(
146             a >= && (a == A || a == B || a == C)
147         );
148     }
149 
150 }