/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shape;

import java.awt.*;
import java.awt.geom.*;
import java.text.*;
import geomExtension.*;
import java.util.Vector;
import javax.swing.JOptionPane;
import DrawTop.*;
import util.*;

public class SegmentModifier{
    int type=0;
    int subPathIndex=0;
    int segmentPid=0;
    public int segmentId1=0;
    public int segmentId2=0;
    public String segmentType1="";
    public String segmentType2="";
    Point2D p=null;
    Segment2D tangentIn=null;
    Segment2D tangentOut=null;
    int jointType=0;
    int hitType=0;
  //Modifier type
    public static final int SegmentP=1;
    public static final int SegmentMP=2;
    public static final int Tangent=3;
  //Joint type  
    public static final int Smooth=4;
    public static final int Cusp=5;
  //Hit information
    public static final int TinP1=6;
    public static final int TinP2=7;
    public static final int ToutP1=8;
    public static final int ToutP2=9;
  //code string   
    public static String[] codeStr ={"Undef","SegmentP","SegmentMP",
        "Tangent","Smooth","Cusp","TinP1","TinP2","ToutP1","ToutP2"};
    final static int Mark_NormalSize=6;
    static Vector workVector= new Vector();
    static int debug=0;
    
    public SegmentModifier(int type, int subPathIndex, int segmentPid, int segmentId1, int segmentId2, 
            String segmentType1, String segmentType2, Point2D p, 
            int jointType, Segment2D tangentIn, Segment2D tangentOut){
        this.type=type;
        this.subPathIndex=subPathIndex;
        this.segmentPid=segmentPid;
        this.segmentId1=segmentId1;
        this.segmentId2=segmentId2;
        this.segmentType1=segmentType1;
        this.segmentType2=segmentType2;
        this.p=p;
        this.jointType=jointType;
        this.tangentIn=tangentIn;
        this.tangentOut=tangentOut;
        
    }
    public int getType(){
        return this.type;
    }
    
    public int getSubPathIndex(){
        return this.subPathIndex;
    }
    public int getSegmentPid(){
        return this.segmentPid;
    }
    
    public int getSegmentId1(){
        return this.segmentId1;
    }
    
    public int getSegmentId2(){
        return this.segmentId2;
    }
    
    public Point2D getP(){
        return this.p;
    }
    
    public int getJointType(){
        return this.jointType;
    }
    
    public void setJointType(int jointType){
        this.jointType=jointType;
    }

    public Segment2D getTangentIn(){
        return this.tangentIn;
    }
    
    public void setTangentIn(Segment2D tangentIn){
        this.tangentIn=tangentIn;
    }

    public Segment2D getTangentOut(){
        return this.tangentOut;
    }
    
    public void setTangentOut(Segment2D tangentOut){
        this.tangentOut=tangentOut;
    }
    
    public Point2D getTinP1(){
        Point2D pt=null;
        if(this.tangentIn!=null){
            Line2D line=(Line2D)this.tangentIn.getShape();
            pt=line.getP1();
        }
        return pt;
    }
    public Point2D getTinP2(){
        Point2D pt=null;
        if(this.tangentIn!=null){
            Line2D line=(Line2D)this.tangentIn.getShape();
            pt=line.getP2();
        }
        return pt;
    }
    public Point2D getToutP1(){
        Point2D pt=null;
        if(this.tangentOut!=null){
            Line2D line=(Line2D)this.tangentOut.getShape();
            pt=line.getP1();
        }
        return pt;
    }
    public Point2D getToutP2(){
        Point2D pt=null;
        if(this.tangentOut!=null){
            Line2D line=(Line2D)this.tangentOut.getShape();
            pt=line.getP2();
        }
        return pt;
    }
    
    public int getHitType(){
        return this.hitType;
    }
    
    protected void setHitType(int type){
        this.hitType=type;
    }
    
    public String toString(){
        NumberFormat nf1 = NumberFormat.getNumberInstance();
        nf1.setMaximumIntegerDigits(4);
        nf1.setMinimumFractionDigits(2);
        NumberFormat nf2 = NumberFormat.getNumberInstance();
        nf2.setMaximumIntegerDigits(5);
        nf2.setMinimumFractionDigits(2);
        String str="  SegmentModifier ";
        String tangentInStr="null";
        if(this.tangentIn!=null) tangentInStr=this.tangentIn.toString();
        String tangentOutStr="null";
        if(this.tangentOut!=null) tangentOutStr=this.tangentOut.toString();
        str+="type="+codeStr[this.type]+", hitType="+codeStr[this.hitType]
                +", subPath="+this.subPathIndex+", segmentPid="+this.segmentPid
                +", segmentId1="+this.segmentId1+" ("+this.segmentType1+")"
                +", segmentId2="+this.segmentId2+" ("+this.segmentType2+")"
                +", point="+Util.Pt(this.p)
                +", jointType="+codeStr[this.jointType]+", tangentIn="+tangentInStr
                +", tangentOut="+tangentOutStr;
        return str;
    }
    //public SegmentModifier(int type, int subPathIndex, int segment1, int segment2, 
    //        String segmentType1, String segmentType2, Point2D p, 
    //        int jointType, Segment2D tangentIn, Segment2D tangentOut
    public Object clone(){
        SegmentModifier ctrlP=new SegmentModifier(this.type,this.subPathIndex, this.segmentPid,
                this.segmentId1, this.segmentId1, this.segmentType1, this.segmentType2,
                this.p, this.jointType, this.tangentIn, this.tangentOut);

        this.setHitType(this.getHitType());
        return ctrlP;
    }
    
//GeneralCurveElement proper
    public static SegmentModifier[] getSegmentModifiers(Curve2D curve2D){
        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;
        int numSubPath=gcurve.getNumOfSubPaths();
        Curve2D[] subCurves=gcurve.getSubPaths();
        workVector.clear();
        for(int k=0;k<numSubPath;k++){
            Curve2D subCurve=subCurves[k];
            int numseg=subCurve.getNumOfSegments();
            boolean closed=subCurve.isClosed();
           //Segment (junction) Point Modifier
            for(int i=0;i<=numseg;i++){
                SegmentModifier modifier=getSegmentModifier(curve2D, SegmentModifier.SegmentP, k, i);
                workVector.add(modifier);
            }
            //MiddlePoint Modifier
            for(int i=0;i<numseg;i++){
                int segType=subCurve.getSegment2D(i).getType();
                if(segType==Segment2D.LINE||segType==Segment2D.ARC){
                    SegmentModifier modifier=getSegmentModifier(curve2D, SegmentModifier.SegmentMP, k, i);
                    workVector.add(modifier);
                }
            }
            //Tangent Modifier
            for(int i=0;i<=numseg;i++){
                int segmentId1=i-1;
                int segmentId2=i;
                if(i==numseg) segmentId2=-1;
                if(closed){
                    if(segmentId1<0) segmentId1=numseg-1;
                    if(segmentId2<0) segmentId2=0;
                }
                int segType1=-1;
                int segType2=-1;
                if(segmentId1>=0) segType1=subCurve.getSegment2D(segmentId1).getType();
                if(segmentId2>=0) segType2=subCurve.getSegment2D(segmentId2).getType();
                if(segType1==Segment2D.CUBIC||segType2==Segment2D.CUBIC){
                    SegmentModifier modifier=getSegmentModifier(curve2D, SegmentModifier.Tangent, k, i);
                    if(modifier!=null) workVector.add(modifier);
                }
            }
        }
        SegmentModifier[] Modifiers=new SegmentModifier[workVector.size()];
        for(int i=0;i<workVector.size();i++) Modifiers[i]=(SegmentModifier)workVector.get(i);
            String str="GeneralCurveElement.getSegmentModifiers";
            for(int i=0;i<Modifiers.length;i++){
                str+="\n"+Modifiers[i].toString();
            }
        if(debug>0) System.out.println(str);
        return Modifiers;
    }
    
//GeneralCurveElement proper
    public static SegmentModifier getSegmentModifier(Curve2D curve2D, int type, 
            int subPath, int segmentPtIndex){
        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;
        Curve2D[] subCurves=curve2D.getSubPaths();
        Curve2D subCurve=subCurves[subPath];
        int numseg=subCurve.getNumOfSegments();
        int index=segmentPtIndex;
        boolean closed=subCurve.isClosed();
        SegmentModifier modifier=null;
      //-----------------------------------------------------------------------------------//
      // SegmentModifier constructor                                                       //
      // public SegmentModifier(int type, int subPathIndex, int segmentPid,                // 
      //       int segmentId1, int segmentId2, String segmentType1, String segmentType2,   //
      //       Point2D p, int jointType, Segment2D tangentIn, Segment2D tangentOut         //
      //-----------------------------------------------------------------------------------// 
        if(type==SegmentModifier.SegmentP){
            int segmentId1=index-1;
            int segmentId2=index;
            if(index==numseg) segmentId2=-1;
            if(closed){
                if(segmentId1<0) segmentId1=numseg-1;
                if(segmentId2<0) segmentId2=0;
            }
            String segTypeStr1="";
            String segTypeStr2="";
            if(segmentId1<0) segTypeStr1="null";
            else {
                segTypeStr1=Segment2D.codeStr[subCurve.getSegment2D(segmentId1).getType()];
            }
            if(segmentId2<0) segTypeStr2="null";
            else {
                segTypeStr2=Segment2D.codeStr[subCurve.getSegment2D(segmentId2).getType()];
            }
          //segment startPoint Modifier  
            modifier=new SegmentModifier(SegmentModifier.SegmentP, 
                   subPath, index, segmentId1, segmentId2, segTypeStr1, segTypeStr2, subCurve.getP(index),
                   0, null, null);
        }
        
        if(type==SegmentModifier.SegmentMP&&index<numseg){
            int segType=subCurve.getSegment2D(index).getType();
            if(segType==Segment2D.LINE||segType==Segment2D.ARC){
                String segTypeStr=Segment2D.codeStr[segType];
                modifier=new SegmentModifier(SegmentModifier.SegmentMP,
                    subPath, index, index, index, segTypeStr, segTypeStr, 
                        subCurve.getP(index+0.5d), 0, null, null);
            }
        }
       //Tangent Modifier
        if(type==SegmentModifier.Tangent){
            int segmentId1=index-1;
            int segmentId2=index;
            if(index==numseg) segmentId2=-1;
            if(closed){
                if(segmentId1<0) segmentId1=numseg-1;
                if(segmentId2<0) segmentId2=0;
            }
            int segType1=-1;
            int segType2=-1;
            if(segmentId1>=0) segType1=subCurve.getSegment2D(segmentId1).getType();
            if(segmentId2>=0) segType2=subCurve.getSegment2D(segmentId2).getType();
                String segTypeStr1="";
                String segTypeStr2="";
                if(segmentId1<0) segTypeStr1="null";
                else {
                    segTypeStr1=Segment2D.codeStr[subCurve.getSegment2D(segmentId1).getType()];
                }
                if(segmentId2<0) segTypeStr2="null";
                else {
                    segTypeStr2=Segment2D.codeStr[subCurve.getSegment2D(segmentId2).getType()];
                }
                modifier=new SegmentModifier(SegmentModifier.Tangent, 
                        subPath, index, segmentId1, segmentId2, segTypeStr1, segTypeStr2, subCurve.getP(index),
                        0, null, null);
             //set tangent
                Vector2D Tin=null;
                Vector2D Tout=null;
                if(segmentId1>=0&&segType1==Segment2D.CUBIC) 
                    Tin=subCurve.getSegment2D(segmentId1).getTangent(1);
                if(segmentId2>=0&&segType2==Segment2D.CUBIC) 
                    Tout=subCurve.getSegment2D(segmentId2).getTangent(0);
                modifier.setJointType(SegmentModifier.Cusp);
                if(Tin!=null&&Tout!=null&&Vector2D.isSameDirection(Tin, Tout, 3d)) {
                    modifier.setJointType(SegmentModifier.Smooth);
                }
                Segment2D TinSeg=null;
                Segment2D ToutSeg=null;
                if(Tin!=null) TinSeg=getTangentLine(subCurve.getP(index), Tin);
                if(Tout!=null) ToutSeg=getTangentLine(subCurve.getP(index), Tout);
                modifier.setTangentIn(TinSeg);
                modifier.setTangentOut(ToutSeg);
        }
        return modifier;
    }

    private static Segment2D getTangentLine(Point2D point, Vector2D Tvec){
        double lineLength = DrawParameters.TempLineLength/DrawParameters.Scale;
        Vector2D PT=new Vector2D(point);
        Tvec=Vector2D.unitVector(Tvec);
        Tvec=Vector2D.multiply(lineLength, Tvec);
        Vector2D P1=Vector2D.sub(PT,Tvec);
        Vector2D P2=Vector2D.add(PT,Tvec);
        Line2D line=new Line2D.Double(P1.getX(), P1.getY(), P2.getX(), P2.getY());
        Segment2D segment=new Segment2D(Segment2D.LINE,line);
        return segment;
    }
    
//GeneralCurveElement proper
    public static SegmentModifier getHitSegmentModifier(SegmentModifier[] modifiers, Point2D point){
        int size=0;
        if(modifiers!=null) size=modifiers.length;
        int hit=0;
        int hitType=0;
        double distMin=1e+5;
        for(int i=0;i<size;i++){
            double dist=Vector2D.dist(modifiers[i].getP(), point);
            if(dist<distMin){
                distMin=dist;
                hit=i;
                hitType=modifiers[i].getType();
            }
            Point2D tp=modifiers[i].getTinP1();
            if(tp!=null) {
                dist=Vector2D.dist(tp, point);
                if(dist<distMin){
                    distMin=dist;
                    hit=i;
                    hitType=SegmentModifier.TinP1;
                }
            }
            tp=modifiers[i].getTinP2();
            if(tp!=null) {
                dist=Vector2D.dist(tp, point);
                if(dist<distMin){
                    distMin=dist;
                    hit=i;
                    hitType=SegmentModifier.TinP2;
                }
            }
            tp=modifiers[i].getToutP1();
            if(tp!=null) {
                dist=Vector2D.dist(tp, point);
                if(dist<distMin){
                    distMin=dist;
                    hit=i;
                    hitType=SegmentModifier.ToutP1;
                }
            }
            tp=modifiers[i].getToutP2();
            if(tp!=null) {
                dist=Vector2D.dist(tp, point);
                if(dist<distMin){
                    distMin=dist;
                    hit=i;
                    hitType=SegmentModifier.ToutP2;
                }
            }
        }
        if(distMin<SegmentModifier.Mark_NormalSize){
            modifiers[hit].setHitType(hitType);
            //System.out.println("getHitSegmentModifier: "+ctrlSegments[hit].toString());
            return modifiers[hit];
        } else {
            return null;
        }
    }//end ofgetHitSegmentModifier
    
//GeneralCurveElement proper
    public static boolean modify(Curve2D curve2D, SegmentModifier hitSegmentModifier, 
            int ctrl, Point2D startPoint, Point2D oldPoint, Point2D currentPoint){
        boolean topologyChanged=false;
        if(hitSegmentModifier==null) return topologyChanged;
        int type2DE=curve2D.getType2DE();
        if(type2DE!=Command.GENERAL_CURVE) return topologyChanged;
        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;
        int subPath=hitSegmentModifier.getSubPathIndex();
        int index=hitSegmentModifier.getSegmentPid();
        int index1=hitSegmentModifier.getSegmentId1();
        int index2=hitSegmentModifier.getSegmentId2();
        int hitType=hitSegmentModifier.getHitType();
        Curve2D[] subCurves=curve2D.getSubPaths();
        Point2D[][] endPs=new Point2D[subCurves.length][2];
        for(int i=0;i<subCurves.length;i++){
            if(i!=subPath&&!subCurves[i].isClosed()) {
                endPs[i]=subCurves[i].getEndPoints();
            }
        }
        Curve2D subCurve=subCurves[subPath];
        boolean closed=subCurve.isClosed();
        int numseg=subCurve.getNumOfSegments(); 
        Segment2D[] segments=subCurve.getSegment2Ds();
        Point2D newP=currentPoint;
        Point2D oldP=oldPoint;
        if(debug>0) System.out.println("GeneralCurveElement.modify hitType="
                +SegmentModifier.codeStr[hitType]
                + ", subPath="+subPath+", numseg="+numseg+", closed="+closed+", index="+index);
        if(ctrl>0) {
            newP=DiscreteAngledLine.getControlledPT(0d, 90d, startPoint, newP);
            oldP=DiscreteAngledLine.getControlledPT(0d, 90d, startPoint, oldP);
        }
        if(hitType==SegmentModifier.SegmentP){
            if(ctrl>0) currentPoint=DiscreteAngledLine.getControlledPT(0d, 90d, startPoint, newP);
            if(index>=0&&index<=numseg){
                if(index1>=0) segments[index1]=segments[index1].moveSegmentEndPT(1, newP);
                if(index2>=0) segments[index2]=segments[index2].moveSegmentEndPT(0, newP);
                subCurve.setData(segments);
                gcurve.setSubPaths(subCurves);
            }
        // closed check
            if(index==0||index==numseg){
                Point2D P0=subCurve.getP(0);
                Point2D P1=subCurve.getP(numseg);
                double dist=Vector2D.dist(P0, P1);
                if(debug>0)System.out.println("closing check subPath="+subPath+", index="+index+", dist="+Util.Num(dist)
                        +", P0="+Util.Pt(P0)+", P1="+Util.Pt(P1));
                if(!closed&&dist<DrawParameters.ClosedTolerance){
                    topologyChanged=true;
                    clearMessage("modify_close");
                    if(index==0) segments[0]=segments[0].moveSegmentEndPT(0, P1);
                    if(index==numseg) segments[numseg-1]=segments[numseg-1].moveSegmentEndPT(1, P0);
                    drawMessage("modify_close", "closing", P1);
                    subCurve.setData(segments);
                    gcurve.setSubPaths(subCurves);
                    ObjectTable.getDrawPanel().repaint();
                }
             }//End of closing check
           //connecting check 
             if(index==0||index==numseg){
                Point2D P0=subCurve.getP(index);
                int[] inf=checkDistance(P0, endPs);
                int isave=inf[0];
                int jsave=inf[1];
                double dist=1.0e+5;
                if(isave>=0&&jsave>=0) dist=Vector2D.dist(P0, endPs[isave][jsave]);
                if(dist<DrawParameters.ClosedTolerance){
                    topologyChanged=true;
                    int path=isave;
                    int ptIndex=0;
                    if(jsave>0) ptIndex=numseg;
                    System.out.println("isConnecting path="+path+", ptIndex="+ptIndex);
                    Point2D targetP=subCurves[path].getP(ptIndex);
                    clearMessage("modify_connect");
                    if(index==0) segments[0]=segments[0].moveSegmentEndPT(0, targetP);
                    if(index==numseg) segments[numseg-1]=segments[numseg-1].moveSegmentEndPT(1, targetP);
                    drawMessage("modify_connect", "connecting", targetP);
                    subCurve.setData(segments);
                    gcurve.setSubPaths(subCurves);
                    ObjectTable.getDrawPanel().repaint();
                }
            }//End of connecting check
        }//End of SegmentP
        if(hitType==SegmentModifier.SegmentMP){
            double moveX=newP.getX()-oldP.getX();
            double moveY=newP.getY()-oldP.getY();
            Point2D p1=segments[index].getP(0);
            Point2D p2=segments[index].getP(1);
            if(ctrl>0) p2=DiscreteAngledLine.getControlledPT(0d, 90d, p1, p2);
            p1.setLocation(p1.getX()+moveX, p1.getY()+moveY);
            p2.setLocation(p2.getX()+moveX, p2.getY()+moveY);
            if(index>=0&&index<=numseg-1){
                segments[index]=segments[index].moveSegmentEndPT(0, p1);
                segments[index]=segments[index].moveSegmentEndPT(1, p2);
            }
            if(index>=1) segments[index-1]=segments[index-1].moveSegmentEndPT(1, p1);
            if(index<=numseg-2) segments[index+1]=segments[index+1].moveSegmentEndPT(0, p2);
            if(index==0&&closed) segments[numseg-1]=segments[numseg-1].moveSegmentEndPT(1, p1);
            if(index==numseg-1&&closed) segments[0]=segments[0].moveSegmentEndPT(0, p2);
            subCurve.setData(segments);
            gcurve.setSubPaths(subCurves);
        }//End of SegmentMP
        
        int junctionType=hitSegmentModifier.getJointType();
        if(hitType==SegmentModifier.TinP1||hitType==SegmentModifier.TinP2||
           hitType==SegmentModifier.ToutP1||hitType==SegmentModifier.ToutP2){
            Vector2D tan=null;
            if(index==numseg) tan=segments[numseg-1].getTangent(1);
            else tan=segments[index].getTangent(0);
            double len=Vector2D.length(tan);
            Vector2D newTangent=Vector2D.sub(currentPoint, subCurve.getP(index));
            if(hitType==SegmentModifier.TinP1||hitType==SegmentModifier.ToutP1) {
                newTangent=Vector2D.multiply(-1.0, newTangent);
            } 
            newTangent=Vector2D.multiply(len, Vector2D.unitVector(newTangent));

            if(index>=0&&index<=numseg){
                if(hitType==SegmentModifier.TinP1||hitType==SegmentModifier.TinP2){
                    segments[index1]=segments[index1].moveSegmentTangent(1, newTangent);
                    if(junctionType==SegmentModifier.Smooth)
                        segments[index2]=segments[index2].moveSegmentTangent(0, newTangent);
                }
                if(hitType==SegmentModifier.ToutP1||hitType==SegmentModifier.ToutP2){
                    segments[index2]=segments[index2].moveSegmentTangent(0, newTangent);
                    if(junctionType==SegmentModifier.Smooth)
                        segments[index1]=segments[index1].moveSegmentTangent(1, newTangent);
                }
                subCurve.setData(segments);
            }
            String str=subCurve.toString();
            if(debug>0) System.out.println("subCurve print:"+str);
            gcurve.setSubPaths(subCurves);
        }
        return topologyChanged;
    }
    
   private static int[] checkDistance(Point2D P, Point2D[][] endPs){
        int[] ptId=new int[2];
        double distMin=1.0e+5;
        int isave=-1;
        int jsave=-1;
        for(int i=0;i<endPs.length;i++){
            for(int j=0;j<endPs[i].length;j++){
                if(endPs[i][j]!=null){
                    double dist=Vector2D.dist(P, endPs[i][j]);
                    if(dist<distMin){
                        distMin=dist;
                        isave=i;
                        jsave=j;
                    }
                }
            }
        }
        ptId[0]=isave;
        ptId[1]=jsave;
        return ptId;
    }
   
//GeneralCurveElement proper
    public static void addPoint(Curve2D curve2D, SegmentModifier hitSegmentModifier, Point2D point){
        CurvePT curvePT=Curve2DUtil.getShortestLine(point, curve2D);
        double t=curvePT.getParameter();
        int it=(int)t;
        if(debug>0) System.out.println("GeneralCurveElement.addPoint t="+t);
        int numseg=curve2D.getNumOfSegments();
        Segment2D segment=curve2D.getSegment2D(it);
        Segment2D trimmedSeg1=segment.trimSegment(0, t-it);
        Segment2D trimmedSeg2=segment.trimSegment(t-it, 1);
        Segment2D[] newSegments=new Segment2D[numseg+1];
        for(int i=0;i<numseg;i++){
            segment=curve2D.getSegment2D(i);
            if(i<it) newSegments[i]=segment;
            if(i==it) {
                newSegments[i]=trimmedSeg1;
                newSegments[i+1]=trimmedSeg2;
            }
            if(i>it) newSegments[i+1]=segment;
        }
        GeneralCurve2DE generalCurve=(GeneralCurve2DE)curve2D;
        generalCurve.setData(newSegments);
    }

//GeneralCurveElement proper
    public static void deletePoint(Curve2D curve2D, SegmentModifier hitSegmentModifier){
        if(hitSegmentModifier==null) return;
        System.out.println("GeneralCurveElement.deletePoint hitSegmentModifier"
                +hitSegmentModifier.toString());
        int type2DE=curve2D.getType2DE();
        if(type2DE!=Command.GENERAL_CURVE) return;

        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;

        int subPath=hitSegmentModifier.getSubPathIndex();
        int index=hitSegmentModifier.getSegmentPid();
        int index1=hitSegmentModifier.getSegmentId1();
        int index2=hitSegmentModifier.getSegmentId2();
        int hitType=hitSegmentModifier.getHitType();
        if(hitType!=SegmentModifier.SegmentP) return;
        
        if(curve2D.getNumOfSegments()==1) {
            JOptionPane.showMessageDialog(ObjectTable.getDrawMain(), 
                    " Warning: GeneralCurve has only two points", "", JOptionPane.ERROR_MESSAGE);
            return;
        }
        Curve2D[] curves=gcurve.getSubPaths();
        Curve2D curve=curves[subPath];
        int numseg=curve.getNumOfSegments();
        boolean closed=curve.isClosed();
        Segment2D[] segments=curve.getSegment2Ds();
        Segment2D[] newSegments=new Segment2D[numseg-1];
        if(index>0&&index<numseg){
            Segment2D newSegment=connectSegments(segments[index1], segments[index2]);
            for(int i=0;i<numseg;i++){
                if(i<index-1) newSegments[i]=(Segment2D)segments[i].clone();
                if(i==index-1) newSegments[i]=newSegment;
                if(i==index) continue;
                if(i>index) newSegments[i-1]=(Segment2D)segments[i].clone();
            }
        }
        if(index==0){
            for(int i=1;i<numseg;i++) newSegments[i-1]=segments[i];
            if(closed){
                Segment2D newSegment=connectSegments(segments[numseg-1], segments[0]);
                newSegments[numseg-2]=newSegment;
            }
        }
        if(index==numseg){
            for(int i=0;i<numseg-1;i++) newSegments[i]=segments[i];
            if(closed){
                Segment2D newSegment=connectSegments(segments[numseg-1], segments[0]);
                newSegments[0]=newSegment;
            }
        }
        curve.setData(newSegments);
        gcurve.setSubPaths(curves);

        String str="";
        str+="\n** SegmentModifier GeneralCurveElement.deletePoint deletePoint segmentPtIndex="+index
                +", target subPath="+subPath+", localSegmentPtIndex="+index
                + "\n"+gcurve.toString();
        if(debug>0) System.out.println(str);
    }
   
    private static Segment2D connectSegments(Segment2D segment1, Segment2D segment2){
        Point2D[] P=new Point2D[2];
        Vector2D[] Tin=new Vector2D[2];
        Vector2D[] Tout=new Vector2D[2];
        P[0]=segment1.getP(0);
        P[1]=segment2.getP(1);
        Tout[0]=segment1.getTangent(0);
        Tin[0]=(Vector2D)Tout[0].clone();
        Tin[1]=segment2.getTangent(1);
        Tout[1]=(Vector2D)Tin[1].clone();
        FergusonCurve2D fergusonCurve=new FergusonCurve2D(P, Tin, Tout);
        Vector2D[] newTangents=fergusonCurve.getOptimizedTangents(P[0], P[1], Tout[0], Tin[1]);
        fergusonCurve.setTout(newTangents[0], 0);
        fergusonCurve.setTin(newTangents[1], 1);
        CubicCurve2D cubic=fergusonCurve.getCubicCurve2D(0);
        Segment2D newSegment=new Segment2D(Segment2D.CUBIC, cubic);
        String str="";
        str+="\n -- connectSegment"
             +"\n  -- segment1="+segment1.toString()+"\n  -- segment2="+segment2.toString()
             +"\n  -- newSegment="+newSegment.toString();
        if(debug>0) System.out.println(str);
        return newSegment;
    }
    
//GeneralCurveElement proper
    public static void smoothPoint(Curve2D curve2D, SegmentModifier hitSegmentModifier){
        if(hitSegmentModifier==null) return;
        System.out.println("GeneralCurveElement.smoothPoint hitSegmentModifier"
                +hitSegmentModifier.toString());
        int type2DE=curve2D.getType2DE();
        if(type2DE!=Command.GENERAL_CURVE) return;
        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;
        
        int subPath=hitSegmentModifier.getSubPathIndex();
        int index=hitSegmentModifier.getSegmentPid();
        int index1=hitSegmentModifier.getSegmentId1();
        int index2=hitSegmentModifier.getSegmentId2();
        int hitType=hitSegmentModifier.getHitType();
        if(hitType!=SegmentModifier.SegmentP) return;
        
        Curve2D[] curves=gcurve.getSubPaths();
        Curve2D curve=curves[subPath];
        int numseg=curve.getNumOfSegments();
        Segment2D[] segments=curve.getSegment2Ds();
        if(index<0||index>numseg) return;
        if(index1<0||index2<0) return; //unavailable segment or segment point index
        if(segments[index1].getType()!=Segment2D.CUBIC
                &&segments[index2].getType()!=Segment2D.CUBIC) return;
       //Hereafter, index doesn't represent an end point of an open curve.
        Vector2D Tin=curve.getTangent(index-Curve2D.eps);
        Vector2D Tout=curve.getTangent(index+Curve2D.eps);
        Vector2D T=Vector2D.add(Tin, Tout);
        if(segments[index1].getType()==Segment2D.CUBIC
                &&segments[index2].getType()==Segment2D.CUBIC){
            segments[index1]=segments[index1].moveSegmentTangent(1, T);
            segments[index2]=segments[index2].moveSegmentTangent(0, T);
        }
        if(segments[index1].getType()==Segment2D.CUBIC
                &&segments[index2].getType()!=Segment2D.CUBIC){
            segments[index1]=segments[index1].moveSegmentTangent(1, Tout);
        }
        if(segments[index1].getType()!=Segment2D.CUBIC
                &&segments[index2].getType()==Segment2D.CUBIC){
            segments[index2]=segments[index2].moveSegmentTangent(0, Tin);
        }
        curve.setData(segments);
        gcurve.setSubPaths(curves);
    }
    
//GeneralCurveElement proper
    public static void cuspPoint(Curve2D curve2D, SegmentModifier hitSegmentModifier){
        if(hitSegmentModifier==null) return;
        System.out.println("GeneralCurveElement.smoothPoint hitSegmentModifier"
                +hitSegmentModifier.toString());
        int type2DE=curve2D.getType2DE();
        if(type2DE!=Command.GENERAL_CURVE) return;
        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;
        int subPath=hitSegmentModifier.getSubPathIndex();
        int index=hitSegmentModifier.getSegmentPid();
        int index1=hitSegmentModifier.getSegmentId1();
        int index2=hitSegmentModifier.getSegmentId2();
        int jointType=hitSegmentModifier.getJointType();
        if(jointType==SegmentModifier.Cusp) return;
        int hitType=hitSegmentModifier.getHitType();
        if(hitType!=SegmentModifier.SegmentP) return;
        
        Curve2D[] curves=gcurve.getSubPaths();
        Curve2D curve=curves[subPath];
        int numseg=curve.getNumOfSegments();
        Segment2D[] segments=curve.getSegment2Ds();
        if(index<0||index>numseg) return;
        if(index1<0||index2<0) return; //unavailable segment or segment point index
        if(segments[index1].getType()!=Segment2D.CUBIC
                &&segments[index2].getType()!=Segment2D.CUBIC) return;
       //Hereafter, index doesn't represent an end point of an open curve.
        Point2D P0=segments[index1].getP(0);
        Point2D P1=segments[index2].getP(0);
        Point2D P2=segments[index2].getP(1);
        Vector2D Tin=curve.getTangent(index-Curve2D.eps);
        Vector2D Tout=curve.getTangent(index+Curve2D.eps);
        Vector2D newTin=divertTangent(P0, P1, P2, Tin, 1);
        Vector2D newTout=divertTangent(P0, P1, P2, Tout, -1);
        if(segments[index1].getType()==Segment2D.CUBIC
                &&segments[index2].getType()==Segment2D.CUBIC){
            segments[index1]=segments[index1].moveSegmentTangent(1, newTin);
            segments[index2]=segments[index2].moveSegmentTangent(0, newTout);
        }
        if(segments[index1].getType()==Segment2D.CUBIC
                &&segments[index2].getType()!=Segment2D.CUBIC){
            segments[index1]=segments[index1].moveSegmentTangent(1, newTin);
        }
        if(segments[index1].getType()!=Segment2D.CUBIC
                &&segments[index2].getType()==Segment2D.CUBIC){
            segments[index2]=segments[index2].moveSegmentTangent(0, newTout);
        }
        curve.setData(segments);
        gcurve.setSubPaths(curves);
    }
    
    private static Vector2D divertTangent(Point2D P0, Point2D P1, Point2D P2, Vector2D tangent, int dir){
        double divertAngle=5;
        Vector2D v0=Vector2D.sub(P2,P0);
        Vector2D v1=Vector2D.sub(P1,P0);
        Vector2D normal=Vector2D.unitNormalVector(v0);
        if(Vector2D.sproduct(normal, v1)<0) normal=Vector2D.multiply(-1d, normal);
        double targetLen=Vector2D.length(tangent);
        Vector2D unitTarget=Vector2D.multiply(1.0/targetLen, tangent);
        double divert=divertAngle/180*Math.PI;
        if(dir<0) divert=-divert;
        Vector2D divertVec=Vector2D.multiply(divert, normal);
        Vector2D newTarget=Vector2D.multiply(targetLen, Vector2D.add(unitTarget, divertVec));
        return newTarget;
    }
    
//GeneralCurveElement proper
    public static void disconnectPoint(Curve2D curve2D, SegmentModifier hitSegmentModifier){
        double trimLength=4; //pixels
        if(hitSegmentModifier==null) return;
        if(debug>=0) System.out.println("\nhitSegmentModifier"
                +hitSegmentModifier.toString());
        int type2DE=curve2D.getType2DE();
        if(type2DE!=Command.GENERAL_CURVE) return;

        GeneralCurve2DE gcurve=(GeneralCurve2DE)curve2D;
        if(debug>=0) {
            String str="\n** SegmentModifier disconnectPoint gcurve:"+gcurve.toString();
            System.out.println(str);
        }
        int subPath=hitSegmentModifier.getSubPathIndex();
        int index=hitSegmentModifier.getSegmentPid();
        int index1=hitSegmentModifier.getSegmentId1();
        int index2=hitSegmentModifier.getSegmentId2();
        int hitType=hitSegmentModifier.getHitType();
        if(hitType!=SegmentModifier.SegmentP) return;

        Curve2D[] curves=gcurve.getSubPaths();
        Curve2D curve=curves[subPath];
        boolean closed=curve.isClosed();
        int numseg=curve.getNumOfSegments();
        if((index==0||index==numseg)&&!closed){
            System.err.println("** SegmentModifier disconnectPoint "
                    + "start or end point specified for unclosed curve");
            return;
        }
        Segment2D[] segments=curve.getSegment2Ds();
        if(index<0||index>numseg) return;
        if(index1<0||index2<0) return; //unavailable segment or segment point index
        Vector2D Tin=curve.getTangent(index-Curve2D.eps);
        Vector2D Tout=curve.getTangent(index+Curve2D.eps);
        double TinLen=Vector2D.length(Tin);
        double ToutLen=Vector2D.length(Tout);
        double t1=trimLength/TinLen;
        double t2=trimLength/ToutLen;
        if(index1>=0) segments[index1]=segments[index1].trimSegment(0, 1-t1);
        if(index2>=0) segments[index2]=segments[index2].trimSegment(t2, 1);
        int pathCount=0;
        GeneralCurve2DE[] newSubCurves=new GeneralCurve2DE[2];
        if(index==0||index==numseg){
            Segment2D[] newSegments=new Segment2D[numseg];
            for(int i=0;i<numseg;i++){
                newSegments[i]=(Segment2D)segments[i].clone();
            }
            newSubCurves[pathCount]=new GeneralCurve2DE(newSegments);
            pathCount++;
        } else {
            if(index1>=0){
                Segment2D[] newSegments=new Segment2D[index1+1];
                for(int i=0;i<=index1;i++){
                    newSegments[i]=(Segment2D)segments[i].clone();
                }
                newSubCurves[pathCount]=new GeneralCurve2DE(newSegments);
                pathCount++;
            }
            if(index2>=0){
                Segment2D[] newSegments=new Segment2D[numseg-index2];
                for(int i=0;i<numseg-index2;i++){
                    newSegments[i]=(Segment2D)segments[i+index2].clone();
                }
                newSubCurves[pathCount]=new GeneralCurve2DE(newSegments);
                pathCount++;
            }
        }
        Curve2D[] newCurves=new Curve2D[curves.length+pathCount-1];
        int ind=0;
        for(int i=0;i<subPath;i++){
            newCurves[i]=curves[i];
            ind++;
        }
        for(int i=0;i<pathCount;i++){
            newCurves[ind]=newSubCurves[i];
            ind++;
        }
        for(int i=subPath+1;i<curves.length;i++){
            newCurves[ind]=curves[i];
            ind++;
        }
        gcurve.setSubPaths(newCurves);
        
        if(debug>=0) {
            String str="** SegmentModifier result gcurve:"+curve2D.toString();
            System.out.println(str);
        }
    }//End of disconnectPoint

    private static void drawMessage(String id, String message, Point2D pos){
        TempShapeManager tempShapeManager=ObjectTable.getTempShapeManager();
        TempShape tempShape=new TempShape(id, message, pos, Color.RED, 
                DrawParameters.DefaultFont);
        tempShapeManager.addTempShape(tempShape);
    }
    private static void clearMessage(String id){
        TempShapeManager tempShapeManager=ObjectTable.getTempShapeManager();
        tempShapeManager.clearTempShape(id);
    }
}