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 + -
显示快捷键?