nchu-software-oop-2022-5

前言!!!!

仅提供思路,希望对你有所帮助

前置准备

Graphical(图形类)父类的抽离

三角形,四边形,五边形…随着图形的增加,比起一个个在不同的类里面写相同的东西,比如,构成图形的点,线,图形面积周长…不妨想想抽离出一个父类,统一写这些重复的玩意。那么现在就好好考虑哪些属性和方法可以被抽离出来呢?
展示我抽离的一些属性和方法作为参考:(省略类型,参数,具体请参考附录,这里做大概了解和说明思路)

  1. 属性:
    构成图形的点 points;
    构成图形的线 lines;
    面积 area;
    周长 sidelength;
    凹凸性 isConvexGraphical;
  2. 方法:
    计算多边形面积 area;
    计算多边形长度 sidelength;
    判断凹凸性 checkConvex;
    打印图形信息 print;
    判断图形是否包含某个点 isContainPoint;

以上还只是抽离出来的属性和方法,接下来是自身的属性和方法:

  1. 属性:
    具体几边形 len (也就是points数组和lines数组的长度);
    图形状态 status(标记是否构成了多边形);
    状态信息 message(不构成图形的原因);
  2. 方法
    判断两个图形之间的关系 relationshipWith;
    判断两个图形是否完全分离 isSeparatedFrom;
    判断是否完全包含另一个图形 isContainGra;
    判断两个图形是否一模一样 isSameTo;
    判断两个图形的重叠面积 overlappingArea;
    去除重复点 removeMulti;
    判断不相邻边是否有交点 checkEdge;

以上方法的实现:

析构函数
析构函数接受一个Points数组参数

  1. 对points数组进行出重操作,如果去重之后点数不足2,则结束构造,设置status = -1 设置 message = “Not enough points”
  2. 对于points数组中的连续三个元素P1P2P3,如果向量P1P2 和向量P2P3的夹角为0度则去除P2点,如果为180度则结束构造,设置status = -1 设置message = “lines reverse coincidence”
  3. 开始赋值lines属性,并判断不相邻的line是否有交点,如果有交点则设置 status = -1 设置message = “Non adjacent edges have intersections”
  4. 设置属性area sidelength isConvexGraphical 的值,正常结束构造

面积:
对于求任意多边形的面积,同样是分割成不同的三角形然后求和,但是考虑到凹多边形特殊性求三角形面积采用向量的叉乘。
以原点为参考点O,相邻顶点P1,P2,求:
在这里插入图片描述
按{P1, P2, P3, P4, P5}顺序求出OP1×OP2 ,OP2×OP3… 求和即可
相关证明查看 参考文章

周长
对边长求和即可

多边形的凹凸性
我们知道向量叉乘是有正负的,对于下面的向量,我们求OP1 × OP2,结果是正,而反过来求叉乘则是负,直观来看就是:
P1在向量OP2的左侧则叉乘为正,P2在向量OP1的右侧则叉乘为负。
在这里插入图片描述

假设当前连续的三个顶点分别是P1,P2,P3。计算向量(P1,P2),(P1,P3)的叉乘,也就是计算三角形P1P2P3的面积,得到的结果如果大于0,则表示P2点在线段P1和P3的右侧,多边形的顶点是逆时针序列。然后依次计算下一个前后所组成向量的叉乘,如果在计算时,出现负值,则此多边形时凹多边形,如果所有顶点计算完毕,其结果都是大于0,则多边形时凸多边形。

判断图形是否包含某一个点
首先特判在边上的情况!

假设点位于多边形内部:
在这里插入图片描述
显然以P为顶点为顶点的三角形面积和等于多边形面积

假设点位于多边形外部:
在这里插入图片描述
显然以P为顶点的三角形面积和不等于多边形面积

判断两个图形是否完全分离
两个图形重叠面积为0 + 点都不在对方的边上

判断一个图形this是否完全包含另一个图形g
g图形的所有点都位于this图形内部或者边上

判断两个图形是否一模一样(this,g)
首先len属性要相等,其次g图形的每个点都可以在this图形中对应找到

判断两图形之间的关系
判断优先级:分离,完全重合,包含,被包含,连接,交错
其中前四者都有对应函数实现
判断连接还是交错,只需看有没有重叠面积就行

判断两个图形的重叠面积
请先观察下面的图形:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

不知道是否观察出,重叠部分的多边形,他的顶点由,两个图形边的交点和部分顶点构成
假设我们求出了这些交点和顶点构成数组intersection = { P1, I2, P2, P3, },并按照一定的顺序排序,使得该数组按顺序可以构成多边形,那么求出这个多边形的面积则是两个图形的重叠面积

  1. 图形的交点:
    暴力枚举两图形的lines求解

  2. 部分顶点
    同样可以由上图观察出,这些顶点必然会出现在另一个图形的内部

  3. 排序:
    提供两种思路

    1. 求出这些点集intersection的重心,任选一个点集中的点与重心构成参考向量(OP1):
      在这里插入图片描述

    则不同向量OPx和参考向量OP1的夹角不相同,如果根据这些角度的大小对这些点集进行排序,最终会得到以P1开始,逆时针遍历的集合{ P1, P2, P3, P4, P5 };
    根据这个集合就可以构成一个多边形了

    1. 首先会有两个集合,一个集合为上诉点集A = { P1, P5, P2, P3, P4 },一空集B = { };
      同样选择一个参考点P1,之后A集合去除P1,B集合加入P1 变成:
      A = { P5, P2, P3, P4 } B = { P1 }
      {
      遍历A集合,求出与B集合末尾元素(P1)最近的点Px;
      A = A - Px ; B = B + Px
      }
      重复上诉大括号内的步骤,直到A集合为空
      得到的B集合则一定可以构成多边形

    前一个思路凹凸多边形都适用,后者只适用于凸多边形

点线形系列5-凸五边形的计算-1

用户输入一组选项和数据,进行与五边形有关的计算。
以下五边形顶点的坐标要求按顺序依次输入,连续输入的两个顶点是相邻顶点,第一个和最后一个输入的顶点相邻。
选项包括:
1:输入五个点坐标,判断是否是五边形,判断结果输出true/false。
2:输入五个点坐标,判断是凹五边形(false)还是凸五边形(true),如果是凸五边形,则再输出五边形周长、面积,结果之间以一个英文空格符分隔。 若五个点坐标无法构成五边形,输出"not a pentagon"
3:输入七个点坐标,前两个点构成一条直线,后五个点构成一个凸五边形、凸四边形或凸三角形,输出直线与五边形、四边形或三角形相交的交点数量。如果交点有两个,再按面积从小到大输出被直线分割成两部分的面积(不换行)。若直线与多边形形的一条边线重合,输出"The line is coincide with one of the lines"。若后五个点不符合五边形输入,若前两点重合,输出"points coincide"。
以上3选项中,若输入的点无法构成多边形,则输出"not a polygon"。输入的五个点坐标可能存在冗余,假设多边形一条边上两个端点分别是x、y,边线中间有一点z,另一顶点s:
1)符合要求的输入:顶点重复或者z与xy都相邻,如:x x y s、x z y s、x y x s、s x y y。此时去除冗余点,保留一个x、一个y。2) 不符合要求的输入:z不与xy都相邻,如:z x y s、x z s y、x s z y

输入格式:
基本格式:选项+“:”+坐标x+“,”+坐标y+" “+坐标x+”,“+坐标y。点的x、y坐标之间以英文”,"分隔,点与点之间以一个英文空格分隔。

输出格式:
基本输出格式见每种选项的描述。
异常情况输出:
如果不符合基本格式,输出"Wrong Format"。
如果符合基本格式,但输入点的数量不符合要求,输出"wrong number of points"。
注意:输出的数据若小数点后超过3位,只保留小数点后3位,多余部分采用四舍五入规则进到最低位。小数点后若不足3位,按原始位数显示,不必补齐。例如:1/3的结果按格式输出为 0.333,1.0按格式输出为1.0

思路:

  1. cmd == 1
    使用Graphical构造实例,直接判断实例属性[ len ] 是否等于5即可
  2. cmd ==2
    同上,判断[ isConvexGraphical ] 是否为真即可,为真则输出[ sidelength ] [ area ]
  3. cmd == 3
    同上构造出实例,判断[ len ] 的值,等于3构造Triangle实例 等于4则构造Quadrilateral实例,等于5则构造Pentagon实例。
    前两者上次作业都有,如果是构造了五边形
    1. 特判line与边重合
      在这里插入图片描述

    2. 特判某点在line上面的情况,分别有:
      只交这个点
      在这里插入图片描述

      和两个对角(任一)相交
      在这里插入图片描述

      和两个对边邻边(任一)相交
      在这里插入图片描述

      和对边相交
      在这里插入图片描述

    3. 任意相交与两个边
      在这里插入图片描述

实现:

: 仅提供思路

点线形系列5-凸五边形的计算-2

用户输入一组选项和数据,进行与五边形有关的计算。
以下五边形顶点的坐标要求按顺序依次输入,连续输入的两个顶点是相邻顶点,第一个和最后一个输入的顶点相邻。
选项包括:
4:输入十个点坐标,前、后五个点分别构成一个凸多边形(三角形、四边形、五边形),判断它们两个之间是否存在包含关系(一个多边形有一条或多条边与另一个多边形重合,其他部分都包含在另一个多边形内部,也算包含)。
两者存在六种关系:1、分离(完全无重合点) 2、连接(只有一个点或一条边重合) 3、完全重合 4、被包含(前一个多边形在后一个多边形的内部)5、交错 6、包含(后一个多边形在前一个多边形的内部)。
各种关系的输出格式如下:
1、no overlapping area between the previous triangle/quadrilateral/ pentagon and the following triangle/quadrilateral/ pentagon
2、the previous triangle/quadrilateral/ pentagon is connected to the following triangle/quadrilateral/ pentagon
3、the previous triangle/quadrilateral/ pentagon coincides with the following triangle/quadrilateral/ pentagon
4、the previous triangle/quadrilateral/ pentagon is inside the following triangle/quadrilateral/ pentagon
5、the previous triangle/quadrilateral/ pentagon is interlaced with the following triangle/quadrilateral/ pentagon
6、the previous triangle/quadrilateral/ pentagon contains the following triangle/quadrilateral/ pentagon
5:输入十个点坐标,前、后五个点分别构成一个凸多边形(三角形、四边形、五边形),输出两个多边形公共区域的面积。注:只考虑每个多边形被另一个多边形分割成最多两个部分的情况,不考虑一个多边形将另一个分割成超过两个区域的情况。
6:输入六个点坐标,输出第一个是否在后五个点所构成的多边形(限定为凸多边形,不考虑凹多边形),的内部(若是五边形输出in the pentagon/outof the pentagon,若是四边形输出in the quadrilateral/outof the quadrilateral,若是三角形输出in the triangle/outof the triangle)。输入入错存在冗余点要排除,冗余点的判定方法见选项5。如果点在多边形的某条边上,输出"on the triangle/on the quadrilateral/on the pentagon"。
以上4、5、6选项输入的五个点坐标可能存在冗余,假设多边形一条边上两个端点分别是x、y,边线中间有一点z,另一顶点s:
1)符合要求的输入:顶点重复或者z与xy都相邻,如:x x y s、x z y s、x y x s、s x y y。此时去除冗余点,保留一个x、一个y。2) 不符合要求的输入:z不与xy都相邻,如:z x y s、x z s y、x s z y

输入格式:
基本格式:选项+“:”+坐标x+“,”+坐标y+" “+坐标x+”,“+坐标y。点的x、y坐标之间以英文”,"分隔,点与点之间以一个英文空格分隔。

输出格式:
输出的数据若小数点后超过3位,只保留小数点后3位,多余部分采用四舍五入规则进到最低位。小数点后若不足3位,按原始位数显示,不必补齐。例如:1/3的结果按格式输出为 0.333,1.0按格式输出为1.0

思路:

  1. cmd == 4
    实例化两个Graphical对象,如果[ status ] 为-1则输出异常信息
    调用relationshipWith函数
  2. cmd == 5
    实例化同上
    调用overlappingArea函数
  3. cmd == 6
    实例化一个Graphical对象
    调用isContainPoint函数

实现:

: 仅提供思路

附录

Point 类

package Points;

public class Point{
    public double x, y;

    public Point(){
        this.x = 0;
        this.y = 0;
    }
    public Point(double a,double b){
        this.x = a;
        this.y = b;
    }

    public void print(){
        String x = String.format("%.6f",this.x);
        String y = String.format("%.6f",this.y);
        x = x.replaceAll("0+?$", "");
        y = y.replaceAll("0+?$", "");
        System.out.printf("(%s , %s)\n",this.x,this.y);
    }

    //两点坐标相同
    public boolean isSameTo(Point a){
        return (this.x == a.x)&&(this.y == a.y);
    }
    //两点距离
    public double disToPoint(Point another){
        return Math.sqrt(Math.pow(this.x-another.x,2) + Math.pow(this.y-another.y,2));
    }
    //点到直线的垂直距离
    public double disToLine(Line l){
        return Math.abs(l.a*this.x+l.b*this.y+l.c) / Math.sqrt(Math.pow(l.a,2)+Math.pow(l.b,2));
    }
    //判断是否在直线之上
    public boolean inLine(Line l){
        return Math.abs(l.a*this.x + l.b*this.y + l.c) < 0.000001;
    }
    //判断是否在线段之内(包括端点)
    public boolean inLineSegment_close(Line l){
        if(!this.inLine(l)) return false;
        double res = this.disToPoint(l.sta) + this.disToPoint(l.ed) - l.length();
        return Math.abs(res) < 0.000001;
    }
    //判断是否在线段之内(不包括端点)
    public boolean inLineSegment(Line l){
        return this.inLineSegment_close(l) &&
                (!this.isSameTo(l.sta)) &&
                (!this.isSameTo(l.ed));
    }
    public Point deepCopy(){
        Point res = new Point();
        res.x = this.x;
        res.y = this.y;
        return res;
    }

    public Point add(Point another){
        Point res = this.deepCopy();
        res.x += another.x;
        res.y += another.y;
        return res;
    }

    public Point sub(Point another){
        Point res = this.deepCopy();
        res.x -= another.x;
        res.y -= another.y;
        return res;
    }


    //求点集重心
    public static Point focusPoint(Point[] points ){
        Point res = new Point(0,0);
        for(Point item:points){
            res = res.add(item);
        }
        res.x /= points.length;
        res.y /= points.length;
        return res;
    }

}

Line 类

package Points;

public class Line{
    public Point sta, ed;
    public double a,b,c;
    private final Point vector;

    public Line(Point a,Point b)throws Exception{
        if(a.isSameTo(b)){
            throw new Exception("points coincide");
        }
        this.sta = a;
        this.ed = b;
        this.a = (-(a.y-b.y));
        this.b = (a.x-b.x);
        this.c = (-this.a*this.sta.x-this.b*this.sta.y);
        this.vector = ed.sub(sta);
    }

    public void print(){
        System.out.printf("%fX + %fY + %f = 0\n",this.a,this.b,this.c);
    }

    //求线段长度
    public double length(){
        return this.sta.disToPoint(this.ed);
    }

    //求线段斜率
    public double slope(){
        if(this.b == 0){
            return 2^48;
        }
        return -this.a / this.b;
    }

    //判断是否平行
    public boolean isParallelTo(Line another){
        if(this.b==0 || another.b==0){
            return (this.b == 0 && another.b == 0);
        }
        return ((this.a / this.b) == (another.a / another.b));
    }

    //判断是否重合
    public boolean isSameTo(Line another){
        return this.isParallelTo(another) && (this.sta.inLine(another));
    }

    //判断是否垂直
    public boolean isVerticalTo(Line another){
         return this.a * another.a + this.b * another.b == 0;
    }
    //求两条直线交点
    public Point getIntersection(Line another){
        if(this.isParallelTo(another)) return null;
        Point res = new Point();
        res.y = (another.a*this.c-this.a*another.c) / (this.a*another.b-another.a*this.b);
        res.x = (this.b*another.c-another.b*this.c) / (this.a*another.b-another.a*this.b);
        return res;
    }

    //LineSegmentIntersection 获取线段交点,返回值可能为null
    public Point lsi(Line another){
        Point res = this.getIntersection(another);
        if(res == null) return res;
        boolean ok = (res.inLineSegment_close(this) && res.inLineSegment_close(another));
        return ok ? res:null;
    }

    /*---------------------------------------------分割线-------------------------------------------------------------*/
    /*---------------------------------------------分割线-------------------------------------------------------------*/
    /*---------------------------------------------分割线-------------------------------------------------------------*/
    //求向量模
    public double vectorLength(){
        return Math.sqrt( Math.pow(this.vector.x,2) + Math.pow(this.vector.y,2) );
    }
    //求向量点积
    public double vectorMul(Line another){
        return (this.vector.x * another.vector.x) + (this.vector.y * another.vector.y);
    }

    //求向量叉积
    public double vectorCrossMul(Line another){
        return (this.vector.x * another.vector.y) - (another.vector.x * this.vector.y);
    }
    //求两向量夹角(非0向量)
    public double vectorAngle(Line another){
        double cos_angle = this.vectorMul(another) / (this.vectorLength() * another.vectorLength());
        return Math.acos(cos_angle);
    }

}

Graphical 类

package Points;

import java.util.Arrays;
import java.util.Comparator;

public class Graphical {
    public int len=0,status=1;    //多边形边数,状态
    public Point[] points;
    public Line[] lines;
    public double sideLength = 0,area = 0;   //边长,面积
    public boolean isConvexGraphical = true;
    public  String message = "init"; //信息

    public Graphical(Point[] points){
      	//去除重复点

        //判断相邻边夹角  0则去除中间点,夹角180则 status = -1, message = "lines reverse coincidence"

        //初始化边

        //判断任意不相邻边(线段交点)是否有交点


        //初始化这些属性
    }
    public void print(){
        //根据需要打印一些信息,用于debug
    }
    //判断图形是否包含某个点返回值-1,0,1 (内部,边缘,外部)
    public int isContainPoint(Point p){
        //面积法
    }
    //判断两个图形类之间的关系()
    public String relationshipWith(Graphical g){
        //分离

        //完全重合

        //包含

        //被包含

        //连接

        //交错

    }

    //判断和另一个图形完全分离(重叠面积为0,并且任意点都在this之外)
    public boolean isSeparatedFrom(Graphical g){
        //重叠面积为0 + 任意点都不在对方的边上
    }
    //判断完全包含另一个图形(任意点都在this之内)
    public boolean isContainGra(Graphical g){
        //重叠面积 = 自身面积
        //或者g的点都在this之内
    }
    //判断两个图形是否一模一样(点完全重合)
    public boolean isSameTo(Graphical g){
        //g中的任意点都可以在this中找到对应
    }

    //计算两个图形的重叠面积(交点加内部顶点构成重叠多边形)
    public double overlappingArea(Graphical g){

        //求出两多边形的交点 

        //intersection数组继续加入数据,加入"特殊"顶点 

        //去除intersections的重复点

        /*排序交点数组*/


    }

    //去除所有重复点
    private Point[] removeMulti(Point[] points){
    }
    //判断不相邻边是否有交点
    private void checkEdge(){
    }
    //多边形面积
    private static void area(Graphical e){
    }
    //多边形周长
    private static void sideLength(Graphical e){
    }
    //多边形凹凸性
    private static void checkConvex(Graphical e){
    }


}


Triangle 类

package Points;

import java.util.Arrays;
import java.util.Comparator;

public class Triangle extends Graphical{

    public Triangle(Point[] points)throws Exception{
        super(points);
        if(this.status == -1 || this.len != 3){
            throw new Exception("not a triangle");
        }
    }


    //判断是否为等腰三角形
    public boolean isIsoscelesTriangle(){
        return this.lines[0].length() == this.lines[1].length() ||
                this.lines[1].length() == this.lines[2].length() ||
                this.lines[2].length() == this.lines[0].length();
    }

    //判断是否为等边三角形
    public boolean isEquilateralTriangle(){
        return this.lines[0].length() == this.lines[1].length() &&
                this.lines[1].length() == this.lines[2].length();
    }

    //求三个点围成的图形面积(三点可能共线,面积为0)
    public static double area(Point a,Point b,Point c){
        double len1 = a.disToPoint(b);
        double len2 = b.disToPoint(c);
        double len3 = c.disToPoint(a);
        double p = (len1+len2+len3) / 2;
        return Math.sqrt(p*(p-len1)*(p-len2)*(p-len3));
    }


    //求三角形类型,(锐角0,直角1,钝角2)
    public int type(){
        double[] num = new double[3];
        num[0] = this.lines[0].length();num[1] = this.lines[1].length();num[2] = this.lines[2].length();
        Arrays.sort(num);
        double tmp = Math.pow(num[0],2) + Math.pow(num[1],2) - Math.pow(num[2],2);
        if(Math.abs(tmp) < 0.0000001) return 1;
        if(tmp < 0) return 2;
        return 0;
    }

}

Quadrilateral 类

package Points;

public class Quadrilateral extends Graphical{

    public Quadrilateral(Point[] points)throws Exception{
        super(points);
        if(this.status == -1 || this.len != 4){
            throw new Exception("not a quadrilateral");
        }
    }

    //判断是否为平行四边形(对边分别平行)
    public boolean isParallelQuadrilateral(){
        return this.lines[0].isParallelTo(this.lines[2]) &&
                this.lines[1].isParallelTo(this.lines[3]);
    }

    //判断是否为菱形(平行四边形 + 四边长度相等)
    public boolean isDiamond(){
        boolean v = this.lines[0].length() == this.lines[1].length() &&
                    this.lines[1].length() == this.lines[2].length() &&
                    this.lines[2].length() == this.lines[3].length();
        return this.isParallelQuadrilateral() && v;
    }

    //判断是否为矩形(对角线相等)
    public boolean isRectangle(){
        return  this.points[0].disToPoint(this.points[2]) == this.points[1].disToPoint(this.points[3]);
    }

    //判断是否为正方形(菱形 + 矩形)
    public boolean isSquare(){
        return this.isDiamond() && this.isRectangle();
    }


}

Pentagon 类

package Points;

public class Pentagon extends Graphical {

    public Pentagon(Point[] points)throws Exception{
        super(points);
        if(this.status == -1 || this.len != 5){
            throw new Exception("not a pentagon");
        }
    }


}


感谢看到最后 😃

参考文章

判断多边形凹凸性
计算任意多边形的面积
计算多边形的重叠面积
多边形点集排序

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值