JAVA实现扫描线算法(超详细)

首先说一下,教科书上的扫描线算法确实是用c++很好实现,而且网上有很多源码,而java实现的基本没有(可能是我没看到),所以肖先生还是打算自己码(实验作业写这个而自己又个是写java的猿0.0)。

对于扫描线的实现过程,我只在这里大概讲下书本上的内容(自己去看),主要还是讲一下自己实现时算法的改动和实现方法。

扫描线算法:顾名思义,就是从Ymin开始扫描,然后构建出NET,之后根据NET建立AET。

贴个图:

实现的时候首先是构造NET,因为对于java来说不能像c++一样直接用指针所以我用对象数组和Node类(如下代码)构造了类似数组+指针的数据结构。在实现了NET后开始通过NET实现AET,在这里我改变了一种实现方式,教科书上是一次次遍历扫描线,然后将NET插入AET后进行排序等一系列操作,而我因为是自己写的数据结构,如果说再建个表按书上的方式来最后还得自己实现链表排序等一系列操作。所以我这里直接用一个包含Arraylist的对象数组代替了。我的方法是:直接从NET开始遍历每个节点,得到节点后将它以及它自己之后会引申出的插入AET的节点(比如当前扫描线y=0 节点  X:1 dx:-1  Ymax:3 那之后会插入AET的就是 0 -1 1  和   -1  -1  2 )将这些节点不论顺序的先插入AET对应扫描线位置的对象数组的list中,将NET中节点全部遍历完之后再最后对AET中每个对象数组的list进行排序。最后得到了NET在进行打印。

贴个代码(仅供参考学习交流):

package PolygonScanningAndFilling;
public class Node {    //新编表记录x,dx,yMax
  public int x;
  public float dx;
  public int yMax;
  public Node next;
  public int ymin;
  public Node(int x, int dx, int yMax){
    this.x=x;
    this.dx=dx;
    this.yMax=yMax;
  }
  public void getYmin(int Ymin){
    this.ymin=Ymin;
  }
}
package PolygonScanningAndFilling;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class classAndArray {
  public List<Integer> list = new ArrayList<Integer>();
  public classAndArray(){
  }
  public void listSort() {
    Collections.sort(list);
  }
}
package PolygonScanningAndFilling;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
public class PolygonScanning extends JPanel {
  static int X0;
  static int Y0;
  static int X1;
  static int Y1;
  static int a[]=new int [10];    //保存点击的10个x坐标
  static int b[]=new int [10];    //保存点击的10个y坐标
  static int index=0;
  static int time=0;
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    this.addMouseListener(new MouseAdapter() {
      public void mouseExited(MouseEvent e) {
          time++;
          repaint();
      }
  });
    Graphics2D g2d = (Graphics2D)g;
    int Ymax=0;
    for(int i=0;i<b.length;i++)
    {
      if(Ymax<b[i])
        Ymax=b[i];
    }
    // System.out.println("Ymax"+Ymax);
    /*
     * 画出多边形
     */
       int Sum=0;
       for(;Sum<=index;Sum++) {
         if(Sum==index-1)
          {
             g2d.drawLine(a[Sum], b[Sum], a[0],b[0]);
             break;
          }
         else
           {
           g2d.drawLine(a[Sum], b[Sum], a[Sum+1],b[Sum+1]);
           }
       }
      if(time!=0) {
       Node [] head =new Node [Ymax];    //建立对应扫描边数的链表长度
       for(int i=0;i<index-1;i++)
       {
         if(b[i]<b[i+1])          //从第一个点开始若前一个点小于后一个点
         {
           if(head[b[i]]==null)
             head[b[i]]=new Node(0,0,0);
             head[b[i]].ymin=b[i];
             if(head[b[i]].next==null)    //该点是第一个插入的节点
               {
                 head[b[i]].next=new Node(0,0,0);
                 head[b[i]].next.x=a[i];
                 head[b[i]].next.dx=(float)(a[i]-a[i+1])/(b[i]-b[i+1]);
                 head[b[i]].next.yMax=b[i+1];    //ymax为后一点的y
               }
             else {                //该点不是第一个插入的节点
                   if(head[b[i]].next.next==null)
                   head[b[i]].next.next=new Node(0,0,0);
                   if((float)(a[i]-a[i+1])/(b[i]-b[i+1])<head[b[i]].next.dx)  //当前插入x比之前存在的节点x小
                   {
                     head[b[i]].next.next.x=head[b[i]].next.x;
                     head[b[i]].next.next.dx=head[b[i]].next.dx;
                     head[b[i]].next.next.yMax=head[b[i]].next.yMax;
                     head[b[i]].next.x=a[i];
                     head[b[i]].next.dx=(float)(a[i]-a[i+1])/(b[i]-b[i+1]);
                     head[b[i]].next.yMax=b[i+1];
                   }
                   else
                   {
                     head[b[i]].next.next.x=a[i];
                     head[b[i]].next.next.dx=(float)(a[i]-a[i+1])/(b[i]-b[i+1]);
                     head[b[i]].next.next.yMax=b[i+1];
                   }
               }
         }
         else
         {
           if(head[b[i+1]]==null)
           head[b[i+1]]=new Node(0,0,0);
           head[b[i+1]].ymin=b[i+1];
           if(head[b[i+1]].next==null)    //该点是第一个插入的节点
           {
             head[b[i+1]].next=new Node(0,0,0);
             head[b[i+1]].next.x=a[i+1];
             head[b[i+1]].next.dx=(float)(a[i]-a[i+1])/(b[i]-b[i+1]);
             head[b[i+1]].next.yMax=b[i];    //ymax为后一点的y
           }
           else {                //该点不是第一个插入的节点
               if(head[b[i+1]].next.next==null)
                 head[b[i+1]].next.next=new Node(0,0,0);
               if((float)(a[i]-a[i+1])/(b[i]-b[i+1])<head[b[i+1]].next.dx)  //当前插入x比之前存在的节点x小
               {
                 head[b[i+1]].next.next.x=head[b[i+1]].next.x;
                 head[b[i+1]].next.next.dx=(float)head[b[i+1]].next.dx;
                 head[b[i+1]].next.next.yMax=head[b[i+1]].next.yMax;
                 head[b[i+1]].next.x=a[i+1];
                 head[b[i+1]].next.dx=(float)(a[i]-a[i+1])/(b[i]-b[i+1]);
                 head[b[i+1]].next.yMax=b[i];
               }
               else
               {
                 head[b[i+1]].next.next.x=a[i+1];
                 head[b[i+1]].next.next.dx=(float)(a[i]-a[i+1])/(b[i]-b[i+1]);
                 head[b[i+1]].next.next.yMax=b[i];
               }
           }
         }
       }
       if(index>0)
       {  if(b[0]<b[index-1])          //从第一个点到最后一个点
         {
           if(head[b[0]]==null)
             head[b[0]]=new Node(0,0,0);
             head[b[0]].ymin=b[0];
             if(head[b[0]].next==null)    //该点是第一个插入的节点
               {
                 head[b[0]].next=new Node(0,0,0);
                 head[b[0]].next.x=a[0];
                 head[b[0]].next.dx=(float)(a[0]-a[index-1])/(b[0]-b[index-1]);
                 head[b[0]].next.yMax=b[index-1];    //ymax为后一点的y
               }
             else {                //该点不是第一个插入的节点
                 if(head[b[0]].next.next==null)
                   head[b[0]].next.next=new Node(0,0,0);
                   if((float)(a[0]-a[index-1])/(b[0]-b[index-1])<head[b[0]].next.dx)  //当前插入x比之前存在的节点x小
                   {
                     head[b[0]].next.next.x=head[b[0]].next.x;
                     head[b[0]].next.next.dx=head[b[0]].next.dx;
                     head[b[0]].next.next.yMax=head[b[0]].next.yMax;
                     head[b[0]].next.x=a[0];
                     head[b[0]].next.dx=(float)(a[0]-a[index-1])/(b[0]-b[index-1]);
                     head[b[0]].next.yMax=b[index-1];
                   }
                   else
                   {
                     head[b[0]].next.next.x=a[0];
                     head[b[0]].next.next.dx=(float)(a[0]-a[index-1])/(b[0]-b[index-1]);
                     head[b[0]].next.next.yMax=b[index-1];
                   }
               }
         }
         else
         {
           if(head[b[index-1]]==null)
           head[b[index-1]]=new Node(0,0,0);
           head[b[index-1]].ymin=b[index-1];
           if(head[b[index-1]].next==null)    //该点是第一个插入的节点
           {
             head[b[index-1]].next=new Node(0,0,0);
             head[b[index-1]].next.x=a[index-1];
             head[b[index-1]].next.dx=(float)(a[0]-a[index-1])/(b[0]-b[index-1]);
             head[b[index-1]].next.yMax=b[0];    //ymax为后一点的y
           }
           else {                //该点不是第一个插入的节点
               if(head[b[index-1]].next.next==null)
               head[b[index-1]].next.next=new Node(0,0,0);
               if((float)(a[0]-a[index-1])/(b[0]-b[index-1])<head[b[index-1]].next.dx)  //当前插入x比之前存在的节点x小
               {
                 head[b[index-1]].next.next.x=head[b[index-1]].next.x;
                 head[b[index-1]].next.next.dx=head[b[index-1]].next.dx;
                 head[b[index-1]].next.next.yMax=head[b[index-1]].next.yMax;
                 head[b[index-1]].next.x=a[index-1];
                 head[b[index-1]].next.dx=(float)(a[0]-a[index-1])/(b[0]-b[index-1]);
                 head[b[index-1]].next.yMax=b[0];
               }
               else
               {
                 head[b[index-1]].next.next.x=a[index-1];
                 head[b[index-1]].next.next.dx=(float)(a[0]-a[index-1])/(b[0]-b[index-1]);
                 head[b[index-1]].next.next.yMax=b[0];
               }
           }
         }
       }
     for(int i=0;i<Ymax;i++)
       if(head[i]!=null)
         while(head[i].next!=null)
         {  System.out.println("新编表y"+head[i].ymin+"新编表x"+head[i].next.x+"新编表dx"+head[i].next.dx+"新编表yMax"+head[i].next.yMax);
           if(head[i].next.next!=null)
           {
             System.out.println("多的"+"新编表y"+head[i].ymin+"新编表x"+head[i].next.next.x+"新编表dx"+head[i].next.next.dx+"新编表yMax"+head[i].next.next.yMax);
           }
           break;
         }
     int YMIN=b[0];
    for(int i=0;i<b.length;i++)
    {
      if(YMIN>b[i]&&b[i]!=0)
        YMIN=b[i];
    }
    classAndArray [] ca=new classAndArray [Ymax];
    for(int i=YMIN;i<Ymax;i++)
      ca[i]=new classAndArray();
    //一个点一个点的全装入ca中再排序打印出点
      for(int i=0;i<Ymax;i++)
       {
          if(head[i]!=null)
           if(head[i].next!=null)
           {
             //System.out.println("新编表y"+head[i].ymin+"新编表x"+head[i].next.x+"新编表dx"+head[i].next.dx+"新编表yMax"+head[i].next.yMax);
               for(int j=head[i].ymin;j<head[i].next.yMax;j++)
               {
                 ca[i+j-head[i].ymin].list.add(head[i].next.x+(int)(0.5+((j-head[i].ymin)*head[i].next.dx)));
                 //System.out.print("ca[i+j-head[i].ymin]为"+(i+j-head[i].ymin)+"值为"+ca[i+j-head[i].ymin].list.toString());
                 //System.out.println("Ymin为"+i+" x为"+(head[i].next.x+(j-head[i].ymin)*head[i].next.dx));
               }
             if(head[i].next.next!=null)
             {
               for(int j=head[i].ymin;j<head[i].next.next.yMax;j++)
               {
                 ca[i+j-head[i].ymin].list.add(head[i].next.next.x+(int)(0.5+(j-head[i].ymin)*head[i].next.next.dx));
                 //System.out.print("next中ca[i+j-head[i].ymin]为"+(i+j-head[i].ymin)+"值为"+ca[i+j-head[i].ymin].list.toString());
                 //System.out.println("Ymin为"+i+" x为"+head[i].next.next.x+(j-head[i].ymin)*head[i].next.next.dx);
               }
               //System.out.println("多的"+"新编表y"+head[i].ymin+"新编表x"+head[i].next.next.x+"新编表dx"+head[i].next.next.dx+"新编表yMax"+head[i].next.next.yMax);
             }
           }
       }
//
      for(int i=YMIN;i<Ymax;i++)
      {
        ca[i].listSort();
       for (int j = 0; j < ca[i].list.size(); j++) {
             if(j%2==0||(j==0))
             {
               g2d.drawLine(ca[i].list.get(j), i, ca[i].list.get(j+1), i);
             }
           }
        System.out.println(ca[i].list.toString());
      }
   }
  }
  private static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setLocationRelativeTo(null);
    frame.setLayout(null);
    JPanel jp=new JPanel();
    frame.setContentPane(jp);
    frame.setVisible(true);
     frame.addMouseListener(new MouseAdapter() {
        });
    jp.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent e) {
          if(e.getButton() == e.BUTTON1)
          {a[index]=e.getX();
          b[index]=e.getY();
          System.out.println("坐标为("+a[index]+","+b[index]+")");
          index++;
          frame.setVisible(true);
          }
          if(e.getButton() == e.BUTTON3)
          {
            frame.setContentPane(new PolygonScanning());
            frame.setVisible(true);
          }
        }
    }
        );
   frame.setSize(600, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
  public static void main(String[] args) throws IOException {
    createAndShowGUI();
  }
}

效果截图(先在面板里点击点,右键出现所需填充的轮廓,鼠标移出面板填充)

 总结

以上所述是小编给大家介绍的JAVA实现扫描线算法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • Java求质数的几种常用算法分析

    本文实例讲述了Java求质数的几种常用算法.分享给大家供大家参考,具体如下: 1.根据质数的定义求 质数定义:只能被1或者自身整除的自然数(不包括1),称为质数. 利用它的定义可以循环判断该数除以比它小的每个自然数(大于1),如果有能被它整除的,则它就不是质数. 对应代码是: void printPrime(int n){//判断n是否是质数 boolean isPrime=true;//是否是质数的标志 for(int i=n-1;i>1;i-){//n除以每个比n小比1大的自然数 if(n%

  • Java生成随机时间的简单随机算法

    根据起始时间生成随机事件,代码如下: public static Date randomDate(Date beginDate,Date endDate ){ if(beginDate.getTime() >= endDate.getTime()){ return new Date(); } long date = random(beginDate.getTime(),endDate.getTime()); return new Date(date); } public static long

  • java实现Dijkstra最短路径算法

    任务描述:在一个无向图中,获取起始节点到所有其他节点的最短路径描述 Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表方式 用OPEN,CLOSE表的方式,其采用的是贪心法的算法策略,大概过程如下: 1.声明两个集合,open和close,open用于存储未遍历的节点,close用来存储

  • JAVA实现扫描线算法(超详细)

    首先说一下,教科书上的扫描线算法确实是用c++很好实现,而且网上有很多源码,而java实现的基本没有(可能是我没看到),所以肖先生还是打算自己码(实验作业写这个而自己又个是写java的猿0.0). 对于扫描线的实现过程,我只在这里大概讲下书本上的内容(自己去看),主要还是讲一下自己实现时算法的改动和实现方法. 扫描线算法:顾名思义,就是从Ymin开始扫描,然后构建出NET,之后根据NET建立AET. 贴个图: 实现的时候首先是构造NET,因为对于java来说不能像c++一样直接用指针所以我用对象

  • OneinStack一键安装PHP/JAVA/HHVM和超详细的VPS手动安装LNMP的方法

    继著名的LAMP Stack(Linux + Apache + MySQL/MariaDB + PHP)网站环境之后,LNMP Stack(Linux + Nginx + MySQL/MariaDB + PHP)以其负载小.静态文件处理能力强的优势,在Linux平台上开始流行,尤其是在配置不太高的VPS上应用广泛. 说起LNMP,多数人应该知道lnmp.org站长开发的LNMP一键安装包,该脚本虚拟主机管理.FTP用户管理.Nginx.MySQL/MariaDB.PHP的升级.常用缓存组件的安装

  • Java集合框架超详细小结

    目录 一:Collection集合 1.1集合概述: 1.2集合架构 1.3Collection集合常用方法 二:迭代器Iterator 2.1Iterator接口 2.2Iterator的实现原理: 2.3增强for() 2.4迭代器注意事项 三:泛型 3.1泛型概述 3.2泛型的优缺点 3.3泛型的定义与使用 泛型方法 泛型接口 3.4泛型的通配符 通配符高级使用-----受限泛型 四:Java常见数据结构 4.1栈 4.2队列 4.3数组 4.4链表 4.5红黑树 五:List集合体系 5

  • 非常适合新手学生的Java线程池超详细分析

    目录 线程池的好处 创建线程池的五种方式 缓存线程池CachedThreadPool 固定容量线程池FixedThreadPool 单个线程池SingleThreadExecutor 定时任务线程池ScheduledThreadPool ThreadPoolExecutor创建线程池(十分推荐) ThreadPoolExecutor的七个参数详解 workQueue handler 如何触发拒绝策略和线程池扩容? 线程池的好处 可以实现线程的复用,避免重新创建线程和销毁线程.创建线程和销毁线程对

  • Java DefaultListableBeanFactory接口超详细介绍

    目录 前言 AliasRegistry SimpleAliasRegistry SingletonBeanRegistry DefaultSingletonBeanRegistry FactoryBeanRegistrySupport AbstractBeanFactory AbstractAutowireCapableBeanFactory BeanDefinitionRegistry ConfigurableListableBeanFactory 前言 本文,对bean工厂的接口做分析梳理具

  • Java 垃圾回收超详细讲解记忆集和卡表

    目录 那么如何才能解决跨代引用呢? 记忆集 卡表 在说记忆集和卡表之前,先给大家介绍一下跨代引用的问题. 假如要现在进行一次只局限于新生代区域内的收集(Minor GC),但新生代的实例对象1在老年代中被引用,为了找出该区域(新生代)中所有的存活对象,不得不在固定的GC Roots之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反过来也是一样.遍历整个老年代所有对象的方案虽然理论上可行,但无疑会为内存回收带来很大的性能负担. 事实上并不只是新生代.老年代之间才有跨代引用的问题,

  • Java 垃圾回收超详细讲解记忆集和卡表

    目录 跨代引用 解决跨代引用 记忆集 卡表 跨代引用 在说记忆集和卡表之前,先给大家介绍一下跨代引用的问题. 假如要现在进行一次只局限于新生代区域内的收集(Minor GC),但新生代的实例对象1在老年代中被引用,为了找出该区域(新生代)中所有的存活对象,不得不在固定的GC Roots之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反过来也是一样.遍历整个老年代所有对象的方案虽然理论上可行,但无疑会为内存回收带来很大的性能负担. 事实上并不只是新生代.老年代之间才有跨代引用的问

  • Java Lambda表达式超详细介绍

    目录 一.背景 1.Lambda表达式的语法 2.函数式接口 二.Lambda表达式的基本使用 三.语法精简 四.变量捕获 五.Lambda在集合当中的使用 1.Collection接口 六.List接口 1.sort()方法的演示 七.Map接口 一.背景 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块). L

  • Java中关于内存泄漏出现的原因汇总及如何避免内存泄漏(超详细版)

    Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题.内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收.最近自己阅读了大量相关的文档资料,打算做个 总结 沉淀下来跟大家一起分享和学习,也给自己一个警示,以后 coding 时怎么避免这些情况,提高应用的体验和质量. 我会从 java 内存泄漏的基础知识开始,并通过具体例子来说明 Android 引起内存泄漏的各种原因,以

  • 超详细的Java 问题排查工具单

    前言 平时的工作中经常碰到很多疑难问题的处理,在解决问题的同时,有一些工具起到了相当大的作用,在此书写下来,一是作为笔记,可以让自己后续忘记了可快速翻阅,二是分享,希望看到此文的同学们可以拿出自己日常觉得帮助很大的工具,大家一起进步. 闲话不多说,开搞. Linux命令类 tail 最常用的tail -f tail -300f shopbase.log #倒数300行并进入实时监听文件写入模式 grep grep forest f.txt #文件查找 grep forest f.txt cpf.

随机推荐