nchu-software-oop-2022-5
前言!!!!
仅提供思路,希望对你有所帮助
前置准备
Graphical(图形类)父类的抽离
三角形,四边形,五边形…随着图形的增加,比起一个个在不同的类里面写相同的东西,比如,构成图形的点,线,图形面积周长…不妨想想抽离出一个父类,统一写这些重复的玩意。那么现在就好好考虑哪些属性和方法可以被抽离出来呢?
展示我抽离的一些属性和方法作为参考:(省略类型,参数,具体请参考附录,这里做大概了解和说明思路)
- 属性:
构成图形的点 points;
构成图形的线 lines;
面积 area;
周长 sidelength;
凹凸性 isConvexGraphical; - 方法:
计算多边形面积 area;
计算多边形长度 sidelength;
判断凹凸性 checkConvex;
打印图形信息 print;
判断图形是否包含某个点 isContainPoint;
以上还只是抽离出来的属性和方法,接下来是自身的属性和方法:
- 属性:
具体几边形 len (也就是points数组和lines数组的长度);
图形状态 status(标记是否构成了多边形);
状态信息 message(不构成图形的原因); - 方法
判断两个图形之间的关系 relationshipWith;
判断两个图形是否完全分离 isSeparatedFrom;
判断是否完全包含另一个图形 isContainGra;
判断两个图形是否一模一样 isSameTo;
判断两个图形的重叠面积 overlappingArea;
去除重复点 removeMulti;
判断不相邻边是否有交点 checkEdge;
以上方法的实现:
析构函数
析构函数接受一个Points数组参数
- 对points数组进行出重操作,如果去重之后点数不足2,则结束构造,设置status = -1 设置 message = “Not enough points”
- 对于points数组中的连续三个元素P1P2P3,如果向量P1P2 和向量P2P3的夹角为0度则去除P2点,如果为180度则结束构造,设置status = -1 设置message = “lines reverse coincidence”
- 开始赋值lines属性,并判断不相邻的line是否有交点,如果有交点则设置 status = -1 设置message = “Non adjacent edges have intersections”
- 设置属性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, },并按照一定的顺序排序,使得该数组按顺序可以构成多边形,那么求出这个多边形的面积则是两个图形的重叠面积
-
图形的交点:
暴力枚举两图形的lines求解 -
部分顶点
同样可以由上图观察出,这些顶点必然会出现在另一个图形的内部 -
排序:
提供两种思路- 求出这些点集intersection的重心,任选一个点集中的点与重心构成参考向量(OP1):
则不同向量OPx和参考向量OP1的夹角不相同,如果根据这些角度的大小对这些点集进行排序,最终会得到以P1开始,逆时针遍历的集合{ P1, P2, P3, P4, P5 };
根据这个集合就可以构成一个多边形了- 首先会有两个集合,一个集合为上诉点集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集合则一定可以构成多边形
前一个思路凹凸多边形都适用,后者只适用于凸多边形
- 求出这些点集intersection的重心,任选一个点集中的点与重心构成参考向量(OP1):
点线形系列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
思路:
- cmd == 1
使用Graphical构造实例,直接判断实例属性[ len ] 是否等于5即可 - cmd ==2
同上,判断[ isConvexGraphical ] 是否为真即可,为真则输出[ sidelength ] [ area ] - cmd == 3
同上构造出实例,判断[ len ] 的值,等于3构造Triangle实例 等于4则构造Quadrilateral实例,等于5则构造Pentagon实例。
前两者上次作业都有,如果是构造了五边形-
特判line与边重合
-
特判某点在line上面的情况,分别有:
只交这个点
和两个对角(任一)相交
和两个对边邻边(任一)相交
和对边相交
-
任意相交与两个边
-
实现:
: 仅提供思路
点线形系列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
思路:
- cmd == 4
实例化两个Graphical对象,如果[ status ] 为-1则输出异常信息
调用relationshipWith函数 - cmd == 5
实例化同上
调用overlappingArea函数 - 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");
}
}
}
感谢看到最后 😃