import java.awt.*; import java.util.LinkedList; public class BezierCurve extends Widget{ protected Point[] vertices; protected Point[] vertices_before_drag; Handle[] handles; int number_of_vertices = 0; Color vertex_color = Color.BLACK; Color connector_color = new Color(100,100,255); Color curve_color = Color.BLACK; Color convex_hull_color = new Color(255,100,100); int drag_offset_x; int drag_offset_y; int max_recursion_depth; boolean show_handles; boolean show_connectors; boolean show_points; boolean show_lines; boolean show_convex_hull; boolean is_current_curve; public BezierCurve(){ vertices = new Point[0]; handles = new Handle[0]; } public void expand_arrays(){ Point[] new_vertices = new Point[Math.max(vertices.length*2,1)]; for(int i=0;iindex){ new_vertices[i-1] = vertices[i]; new_handles[i-1] = handles[i]; } } handles = new_handles; vertices = new_vertices; return number_of_vertices--; } public int elevate_degree(){ // don't bother with zero or one if(number_of_vertices<2) return number_of_vertices; //for now just dicconect this curve from spline for(int index=0;indexvertices.length) expand_arrays(); vertices[number_of_vertices] = new Point(x,y); handles[number_of_vertices] = new Handle((int)x,(int)y,this); return number_of_vertices++; } public void deCasteljau_recursive( Point[] control_points, double t_before, double t_after, Point p_before, Point p_after, int depth, boolean force ){ p_before.t =t_before; p_after.t =t_after; // BASE CASES if( !force && depth >= max_recursion_depth) return; if( !force && LOW_DRAW_DISTANCE > Point.distance(p_before,p_after)) return; double t = (t_before+t_after)/2.0; Point[] first_half = new Point[control_points.length]; Point[] second_half = new Point[control_points.length]; subdivide(control_points,number_of_vertices,first_half,second_half,t); Point p_middle = second_half[0]; p_before.next = p_middle; p_after.previous = p_middle; p_middle.next = p_after; p_middle.previous = p_before; // before deCasteljau_recursive( control_points, t_before, t, p_before, p_middle, depth +1, false); // after deCasteljau_recursive( control_points, t, t_after, p_middle, p_after, depth+1, false); } public Point deCasteljau(Point[] points, double t){ // find max distance between any two points double max_distance = 0; for(int i=0;i0) max_distance = Math.max(max_distance, (points[i-1].x-points[i].x)*(points[i-1].x-points[i].x)+ (points[i-1].y-points[i].y)*(points[i-1].y-points[i].y)); // BASECASE // will appear as a line on the screen anyway if(max_distance<1.0) return points[points.length-1]; Point[] first_half = new Point[points.length]; Point[] second_half = new Point[points.length]; subdivide(points,number_of_vertices,first_half,second_half,t); return second_half[0]; } public void select(int mx, int my){ select(mx,my,true); } public void select(int mx, int my, boolean affect_twins){ if(affect_twins){ LinkedList twin_list = new LinkedList(); twin_list(twin_list); for(BezierCurve curve: twin_list){ curve.select(mx,my,false); } } drag_offset_x = mx; drag_offset_y = my; vertices_before_drag = new Point[number_of_vertices]; for(int i=0;i list_so_far){ for(int i=0;i twin_list = new LinkedList(); twin_list(twin_list); for(BezierCurve curve: twin_list){ curve.is_down = true; curve.drag(mx,my,false); } } for(int i=0;i0) distance += (int) Point.distance(vertices[i-1],vertices[i]); } int resolution = Math.max(1,(distance)); double t_step = 1.0/(double)resolution; double t = 0.0; for(int i=0;i= (point.x-mx)*(point.x-mx)+(point.y-my)*(point.y-my)) return true; t+=t_step; } }else{ if(parameter_of_point_at(mx,my)>0) return true; if(show_connectors){ for(int i = 0; i0) if(inside_line( vertices[i-1].x,vertices[i-1].y, vertices[i].x,vertices[i].y, mx,my)) return true; } if(show_convex_hull){ Point[] convex_hull = graham_scan(vertices,number_of_vertices); for(int i = 0; i= (p.x-x)*(p.x-x)+(p.y-y)*(p.y-y)) return p.t ; if(null!=p.next) if(inside_line(p.next.x,p.next.y,p.x,p.y,x,y)){ return (p.t+p.next.t)*0.5;// this could be even better... } p = p.next; } return -1; } public BezierCurve cut_at(int x, int y){ double t = parameter_of_point_at(x,y); if(t<0) return null; Point[] first_half = new Point[number_of_vertices]; Point[] second_half = new Point[number_of_vertices]; subdivide(vertices,number_of_vertices,first_half,second_half,t); vertices = first_half; for(int i=0;ix&&bx&&a= 0 && cw(sorted[index-1],sorted[index],sorted[i])>=0) { index--; } index++; swap(sorted, index, i); } Point[] convex_hull = new Point[index]; for(int i=0;icotangent_right ){ result[li+ri] = right[ri]; ri++; }else{ // break tie with distance if(Point.distance(left[li],p)p.x) return Double.MAX_VALUE; else return -Double.MAX_VALUE; } return (a.x-p.x)/(a.y-p.y); } public Point find_lowest_point(Point[] points){ Point p = null; for(int i=0;ipoints[i].y || (p.y==points[i].y && p.x> points[i].x)) p = points[i]; } return p; } public void draw(Graphics2D g2){ // Draw Handle Connectors if(show_connectors){ g2.setColor(!is_current_curve ? Color.GRAY: connector_color); for(int i = 0; i0) g2.drawLine( (int)vertices[i-1].x, (int)vertices[i-1].y, (int)vertices[i].x, (int)vertices[i].y); } if(show_convex_hull){ Point[] convex_hull = graham_scan(vertices,number_of_vertices); g2.setColor(!is_current_curve ? Color.GRAY: convex_hull_color); /* if(number_of_vertices>=3) for(int i = 0; i1){ // make a copy to save memory and protect original Point[] copy = new Point[number_of_vertices]; int distance = 1; for(int i=0;i0) distance += (int) Point.distance(vertices[i-1],vertices[i]); } //int resolution = Math.max(1,(int)Math.sqrt(distance)); if(false){ int resolution = Math.max(1,(distance)); double t_step = 1.0/(double)resolution; Point[] draw_points = new Point[resolution]; double t = 0.0; for(int i=0;i0) g2.drawLine( (int)draw_points[i-1].x, (int)draw_points[i-1].y, (int)draw_points[i].x, (int)draw_points[i].y); t += t_step; } }else{ Point p0 = new Point(vertices[0].x,vertices[0].y); Point p1 = new Point( vertices[number_of_vertices-1].x, vertices[number_of_vertices-1].y); p0.next = p1; p1.previous = p0; deCasteljau_recursive(vertices,0.0,1.0,p0,p1,0,true); Point p = p0; while(null != p){ if(show_points){ g2.setColor(!is_current_curve ? Color.GRAY: vertex_color); g2.fillOval( (int)p.x-1, (int)p.y-1, 2, 2); } if(show_lines){ g2.setColor(!is_current_curve ? Color.GRAY: curve_color); if(null!=p.next) g2.drawLine( (int)p.next.x, (int)p.next.y, (int)p.x, (int)p.y); } p = p.next; } } } //Draw handles if(show_handles){ for(int i = 0; i