arc.java
来自「用Java开发的、实现类似Visio功能的应用程序源码」· Java 代码 · 共 1,554 行 · 第 1/3 页
JAVA
1,554 行
/**
* $Id:Arc.java $
*
* Copyright 2004 ~ 2005 JingFei International Cooperation LTD. All rights reserved. *
*/
package com.jfimagine.jfgraph.geom;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import com.jfimagine.jfgraph.geom.LineSeg;
import com.jfimagine.jfgraph.geom.Ellipse;
import com.jfimagine.jfgraph.geom.JFPoint;
import com.jfimagine.jfgraph.geom.Rect;
import com.jfimagine.jfgraph.geom.Angle;
/**
* Arc class. A class used to represent an arc shape.
*
* <p> Attention: Here we used a clockwise quadrant system.
* And the first quadrant is under right bottom corner.
*
* @author CookieMaker
*
* @version $Revision: 1.00 $
*/
public class Arc implements Cloneable {
/**
* The closure type for an open arc with no path segments
* connecting the two ends of the arc segment.
*/
public final static int OPEN = 0;
/**
* The closure type for an arc closed by drawing a straight
* line segment from the start of the arc segment to the end of the
* arc segment.
*/
public final static int CHORD = 1;
/**
* The closure type for an arc closed by drawing straight line
* segments from the start of the arc segment to the center
* of the full ellipse and from that point to the end of the arc segment.
*/
public final static int PIE = 2;
/**
* start point of arc.
*/
private JFPoint m_startPoint =new JFPoint();
/**
* end point of arc.
*/
private JFPoint m_endPoint =new JFPoint();
/**
* control point of arc.
*/
private JFPoint m_controlPoint =new JFPoint();
/**
* closure type of arc, OPEN,CHORD or PIE.
*/
private int m_arcType =PIE;
/**
* Get arc type of this arc. OPEN,CHORD or PIE.
*
* @return The arc type.
*
*/
public int getArcType(){
return m_arcType;
}
/**
* Set arc type of this arc. OPEN,CHORD or PIE.
*
* @param arcType The arc type.
*
*/
public void setArcType(int arcType){
m_arcType =arcType;
}
/**
* Get center position of this arc.
*
* @return The center position.
*
*/
public JFPoint getCenter(){
/*
* Consider the three points, start point, end point and control point,
* Connect the points by lines and erect perpendicular bisectors.
* The point of intersection of the perpendicular bisectors is
* the center of the circle or arc passing through all three points.
*/
//middle point between start point and control point
JFPoint mid1 =m_startPoint.midPoint(m_controlPoint);
//slope of the line from start point to control point
double slope1 =m_startPoint.getSlope(m_controlPoint);
//slope of the erect perpendicular bisector of the line from startpoint to control point
if (slope1==GeomConst.LARGE_VALUE)
slope1 =0;
else if (slope1==0)
slope1 =GeomConst.LARGE_VALUE;
else
slope1 =-1/slope1;
//middle point between end point and control point
JFPoint mid2 =m_endPoint.midPoint(m_controlPoint);
//slope of the line from end point to control point
double slope2 =m_endPoint.getSlope(m_controlPoint);
//slope of the erect perpendicular bisector of the line from endpoint to control point
if (slope2==GeomConst.LARGE_VALUE)
slope2 =0;
else if (slope2==0)
slope2 =GeomConst.LARGE_VALUE;
else
slope2 =-1/slope2;
/**
* get the point intersected by the two erect perpendicular bisectors.
*
* Consider the following two equations of the two bisectors.
* y=k1 * x+b1
* y=k2 * x+b2
*
* we get,
* y= slope1 * (x-mid1.x) + mid1.y;
* y= slope2 * (x-mid2.x) + mid2.y;
*/
double x=0, y=0;
if ((float)slope1==(float)slope2){
//this will never happen.
return m_startPoint.midPoint(m_endPoint);
}else if (slope1==0){
if (slope2==GeomConst.LARGE_VALUE){
x =mid2.getX();
y =mid1.getY();
}else{
y =mid1.getY();
x =(y + slope2 * mid2.getX() - mid2.getY())/slope2;
}
}else if (slope1==GeomConst.LARGE_VALUE){
if (slope2==0){
x =mid1.getX();
y =mid2.getY();
}else{
x =mid1.getX();
y =slope2 * (x-mid2.getX()) + mid2.getY();
}
}else if (slope2==0){
if (slope1==GeomConst.LARGE_VALUE){
x =mid1.getX();
y =mid2.getY();
}else{
y =mid2.getY();
x =(y + slope1 * mid1.getX() - mid1.getY())/slope1;
}
}else if (slope2==GeomConst.LARGE_VALUE){
if (slope1==0){
x =mid2.getX();
y =mid1.getY();
}else{
x =mid2.getX();
y =slope1 * (x-mid1.getX()) + mid1.getY();
}
}else{
/* From the two equations,
* y= slope1 * (x-mid1.x) + mid1.y;
* y= slope2 * (x-mid2.x) + mid2.y;
*
* we get,
* x =(slope1 * mid1.x + mid1.y - slope2 * mid2.x +mid2.y)/(slope1-slope2)
*/
x =(slope1 * mid1.getX() - mid1.getY() - slope2 * mid2.getX() +mid2.getY())/(slope1-slope2);
y =slope1 * (x-mid1.getX()) + mid1.getY();
}
return new JFPoint(x,y);
}
/**
* Get start point of this arc.
*
* @return the start point.
*
*/
public JFPoint getStartPoint(){
return m_startPoint;
}
/**
* Set start point of this arc.
*
* @param startPoint The start point.
*
*/
public void setStartPoint(JFPoint startPoint){
setStartPoint(startPoint,false);
}
/**
* Set start point of this arc.
*
* @param startPoint The start point.
* @param adjustControlPoint True if need to adjust the control point to a new position.
*
*/
public void setStartPoint(JFPoint startPoint, boolean adjustControlPoint){
if (!adjustControlPoint){
//set new start point
m_startPoint.setValue(startPoint);
return;
}
if (m_endPoint.distance(startPoint)<GeomConst.MIN_LINELENGTH)
return;
//middle point from start point to end point.
JFPoint middlePoint =m_startPoint.midPoint(m_endPoint);
//we'll keep control height while moving any end point of arc.
double controlHeight =m_controlPoint.distance(middlePoint);
//if control point is under clockwise side of vector from startpoint to endpoint.
boolean clockwise =JFVector.underClockwiseSide(m_startPoint,m_controlPoint,m_startPoint,m_endPoint);
//new control point
m_controlPoint=JFPoint.getMiddleUprightPoint(startPoint,m_endPoint,controlHeight,clockwise);
//set new start point
m_startPoint.setValue(startPoint);
}
/**
* Get end point of this arc.
*
* @return the end point.
*
*/
public JFPoint getEndPoint(){
return m_endPoint;
}
/**
* set end point of this arc.
*
* @param endPoint The end point.
*
*/
public void setEndPoint(JFPoint endPoint){
setEndPoint(endPoint,false);
}
/**
* set end point of this arc.
*
* @param endPoint The end point.
* @param adjustControlPoint True if need to adjust the control point to a new position.
*
*/
public void setEndPoint(JFPoint endPoint, boolean adjustControlPoint){
if (!adjustControlPoint){
//set new end point
m_endPoint.setValue(endPoint);
return;
}
if (m_startPoint.distance(endPoint)<GeomConst.MIN_LINELENGTH)
return;
//middle point from start point to end point.
JFPoint middlePoint =m_startPoint.midPoint(m_endPoint);
//we'll keep control height while moving any end point of arc.
double controlHeight =m_controlPoint.distance(middlePoint);
//if control point is under clockwise side of vector from startpoint to endpoint.
boolean clockwise =JFVector.underClockwiseSide(m_startPoint,m_controlPoint,m_startPoint,m_endPoint);
//new control point
m_controlPoint=JFPoint.getMiddleUprightPoint(m_startPoint,endPoint,controlHeight,clockwise);
//set new end point
m_endPoint.setValue(endPoint);
}
/**
* Get control point of this arc.
* A control point is on the middle of the curve of this arc.
*
* @return the control point.
*
*/
public JFPoint getControlPoint(){
return m_controlPoint;
}
/**
* Set control point of this arc.
* A control point is on the middle of the curve of this arc.
*
* @param controlPoint The control point.
*
*/
public void setControlPoint(JFPoint controlPoint){
setControlPoint(controlPoint,false);
}
/**
* Set control point of this arc.
* A control point is on the middle of the curve of this arc.
*
* @param controlPoint The control point.
* @param adjustControlPoint True if need to adjust the control point to a new position.
*
*/
public void setControlPoint(JFPoint controlPoint, boolean adjustControlPoint){
if (!adjustControlPoint){
m_controlPoint.setValue(controlPoint);
return;
}
//middle point from start point to end point.
JFPoint middlePoint =m_startPoint.midPoint(m_endPoint);
//default control point of arc.
JFPoint defaultControlPoint =getDefaultControlPoint(m_startPoint,m_endPoint);
//line from center point to default control point.
LineSeg line = new LineSeg(middlePoint,defaultControlPoint);
//projective upright foot on line above by 'control point'.
JFPoint newControlPoint =line.uprightFoot(controlPoint.getX(), controlPoint.getY());
if (newControlPoint==null || newControlPoint.distance(middlePoint)<GeomConst.MIN_LINELENGTH)
return;
//set new control point
m_controlPoint.setValue(newControlPoint);
}
/**
* Get the angle of this arc.
*
* the angle of arc. positive value is under counter-clockwise direction.
* @return the angle.
*
*/
public double getAngle(){
JFPoint center =getCenter();
double startAngle =Angle.getAngle(center,m_startPoint);
double endAngle =Angle.getAngle(center,m_endPoint);
//if control point is under clockwise side of vector from startpoint to endpoint.
boolean clockwise =JFVector.underClockwiseSide(m_startPoint,m_controlPoint,m_startPoint,m_endPoint);
if (startAngle>endAngle){
if (clockwise){
return 2 * Angle.PI - (startAngle-endAngle);
}else{
return -1 * (startAngle - endAngle);
}
}else{
if (clockwise){
return endAngle - startAngle;
}else{
return -1 * (2 * Angle.PI - (endAngle-startAngle));
}
}
}
/**
* Set the angle of this arc.
* We keep the sign(positive or negative) of angle here. Adjust the end point actually.
*
* @param angle The angle.
* @return True if successfully set the angle, false otherwise.
*
*/
public boolean setAngle(double angle){
return setAngle(angle,m_startPoint,getCenter());
}
/**
* Set the angle of this arc.
* We keep the sign(positive or negative) of angle here. Adjust the end point and control point actually.
*
* @param angle The angle.
* @param startPoint The start point.
* @param center The center of this arc.
*
*/
public boolean setAngle(double angle,JFPoint startPoint, JFPoint center){
//operation below will keep the sign of angle,
//so it's quite different as Angle.getValidAngle
while (angle< -2 * Angle.PI){
angle += 2 * Angle.PI;
}
while (angle> 2 * Angle.PI){
angle -= 2 * Angle.PI;
}
//don't process too small angle.
if (Math.abs(angle)/Angle.PI*180<1)
return false;
m_startPoint.setValue(startPoint);
double radius =center.distance(m_startPoint);
double startAngle =Angle.getValidAngle(Angle.getAngle(center,m_startPoint));
double endAngle =0;
double endX=0, endY=0;
//if control point is under clockwise side of vector from startpoint to endpoint.
boolean clockwise =false;
if (angle>0){
clockwise =true;
}else{
clockwise =false;
}
endAngle =startAngle + angle;
//consider a counter-clockwise quadrant.
endX =center.getX()+radius * Math.cos(endAngle);
endY =center.getY()-radius * Math.sin(endAngle);
//new end point.
m_endPoint.setValue(endX,endY);
//slope of the line from start point to end point
double slope =m_startPoint.getSlope(m_endPoint);
//new slope of the erect perpendicular bisectors of the line from start point to end point.
if (slope==0)
slope =GeomConst.LARGE_VALUE;
else if (slope==GeomConst.LARGE_VALUE)
slope =0;
else
slope =-1/slope;
//new control point.
m_controlPoint =center.nearPoint(slope,radius,m_startPoint,!clockwise);
return true;
}
/**
* get radius of current arc.
*
* @return the radius.
*
*/
public double getRadius(){
JFPoint center =getCenter();
return center.distance(m_startPoint);
}
/**
* set radius of current arc. Setting radius is actually same as move startpoint to a new position.
*
* @param the radius.
*
*/
public void setRadius(double radius){
radius =Math.abs(radius);
if (radius<GeomConst.MIN_LINELENGTH)
return;
JFPoint center =getCenter();
if (center.distance(m_startPoint)==radius)
return;
m_startPoint =center.nearPoint(m_startPoint,radius);
m_endPoint =center.nearPoint(m_endPoint,radius);
m_controlPoint =center.nearPoint(m_controlPoint,radius);
}
/**
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?