/// <summary>
        /// 指定したコントロールカーブにベジエ曲線を追加します。
        /// </summary>
        /// <param name="curve_type"></param>
        /// <param name="chain"></param>
        public void addBezierChain(CurveType curve_type, BezierChain chain, int chain_id)
        {
            BezierChain add = (BezierChain)chain.clone();

            add.id = chain_id;
            this.get(curve_type).Add(add);
        }
        /// <summary>
        /// 与えられたBezierChainがx軸について陰かどうかを判定する
        /// </summary>
        /// <param name="chain"></param>
        /// <returns></returns>
        public static bool isBezierImplicit(BezierChain chain)
        {
            int size = chain.points.Count;

            if (size < 2)
            {
                return(true);
            }
            BezierPoint last_point = chain.points[0];

            for (int i = 1; i < size; i++)
            {
                BezierPoint point = chain.points[i];
                double      pt1   = last_point.getBase().getX();
                double      pt2   = (last_point.getControlRightType() == BezierControlType.None) ? pt1 : last_point.getControlRight().getX();
                double      pt4   = point.getBase().getX();
                double      pt3   = (point.getControlLeftType() == BezierControlType.None) ? pt4 : point.getControlLeft().getX();
                if (!isUnitBezierImplicit(pt1, pt2, pt3, pt4))
                {
                    return(false);
                }
                last_point = point;
            }
            return(true);
        }
        public Object clone()
        {
            BezierChain result = new BezierChain(this.mColor);

            foreach (var bp in points)
            {
                result.points.Add((BezierPoint)bp.clone());
            }
            result.Default = this.Default;
            result.id      = id;
            return(result);
        }
        public void setBezierChain(CurveType curve_type, int chain_id, BezierChain item)
        {
            List <BezierChain> list = this.get(curve_type);
            int count = list.Count;

            for (int i = 0; i < count; i++)
            {
                if (list[i].id == chain_id)
                {
                    list[i] = item;
                    break;
                }
            }
        }
        public void handleMoveButtonClick(bool backward)
        {
            // イベントの送り主によって動作を変える
            int delta = 1;

            if (backward)
            {
                delta = -1;
            }

            // 選択中のデータ点を検索し,次に選択するデータ点を決める
            BezierChain target = AppManager.getVsqFile().AttachedCurves.get(m_track - 1).getBezierChain(m_curve_type, m_chain_id);
            int         index  = -2;
            int         size   = target.size();

            for (int i = 0; i < size; i++)
            {
                if (target.points[i].getID() == m_point_id)
                {
                    index = i + delta;
                    break;
                }
            }

            // 次に選択するデータ点のインデックスが有効範囲なら,選択を実行
            if (0 <= index && index < size)
            {
                // 選択を実行
                m_point_id = target.points[index].getID();
                m_point    = target.points[index];
                updateStatus();
                m_parent.mEditingPointID = m_point_id;
                m_parent.doInvalidate();

                // スクリーン上でデータ点が見えるようにする
                FormMain main = m_parent.getMainForm();
                if (main != null)
                {
                    main.ensureVisible((int)m_point.getBase().getX());
                }
            }
        }
        public BezierChain extractPartialBezier(double t_start, double t_end)
        {
            if (this.size() <= 1)
            {
                throw new Exception("chain must has two or more bezier points");
            }
            double start = this.points[0].getBase().getX();
            double end   = this.points[this.size() - 1].getBase().getX();

            // [from, to]が、このベジエ曲線の範囲内にあるかどうかを検査
            if (start > t_start || t_end > end)
            {
                throw new Exception("no bezier point appeared in the range of \"from\" to \"to\"");
            }

            // t_start, t_endが既存のベジエデータ点位置を被っていないかどうか検査しながらコピー
            bool        t_start_added = false; // 最初の区間が追加された直後だけ立つフラグ
            BezierChain edited        = new BezierChain(mColor);
            int         count         = 0;

            for (int i = 0; i < this.points.Count - 1; i++)
            {
                if (this.points[i].getBase().getX() < t_start && t_start < this.points[i + 1].getBase().getX())
                {
                    if (this.points[i].getBase().getX() < t_end && t_end < this.points[i + 1].getBase().getX())
                    {
#if DEBUG
                        AppManager.debugWriteLine("points[i].Base.X < t_start < t_end < points[i + 1].Base.X");
#endif
                        PointD x0 = this.points[i].getBase();
                        PointD x1 = this.points[i + 1].getBase();
                        PointD c0 = (this.points[i].getControlRightType() == BezierControlType.None) ?
                                    x0 : this.points[i].getControlRight();
                        PointD c1 = (this.points[i + 1].getControlLeftType() == BezierControlType.None) ?
                                    x1 : this.points[i + 1].getControlLeft();
                        PointD[] res = cutUnitBezier(x0, c0, c1, x1, t_start);

                        x0  = res[3];
                        c0  = res[4];
                        c1  = res[5];
                        x1  = res[6];
                        res = cutUnitBezier(x0, c0, c1, x1, t_end);

                        BezierPoint left  = new BezierPoint(res[0]);
                        BezierPoint right = new BezierPoint(res[3]);
                        left.setControlRight(res[1]);
                        right.setControlLeft(res[2]);
                        left.setControlRightType(this.points[i].getControlRightType());
                        right.setControlLeftType(this.points[i + 1].getControlLeftType());
                        edited.add(left);
                        edited.add(right);
                        t_start_added = true;
                        break;
                    }
                    else
                    {
#if DEBUG
                        AppManager.debugWriteLine("points[i].Base.X < t_start < points[i + 1].Base.X");
#endif
                        PointD x0 = this.points[i].getBase();
                        PointD x1 = this.points[i + 1].getBase();
                        PointD c0 = (this.points[i].getControlRightType() == BezierControlType.None) ?
                                    x0 : this.points[i].getControlRight();
                        PointD c1 = (this.points[i + 1].getControlLeftType() == BezierControlType.None) ?
                                    x1 : this.points[i + 1].getControlLeft();
                        PointD[] res = cutUnitBezier(x0, c0, c1, x1, t_start);

                        BezierPoint left  = new BezierPoint(res[3]);
                        BezierPoint right = new BezierPoint(res[6]);

                        left.setControlRight(res[4]);
                        left.setControlRightType(this.points[i].getControlRightType());

                        right.setControlLeft(res[5]);
                        right.setControlRight(this.points[i + 1].getControlRight());
                        right.setControlRightType(this.points[i + 1].getControlRightType());
                        right.setControlLeftType(this.points[i + 1].getControlLeftType());
                        edited.points.Add(left);
                        count++;
                        edited.points.Add(right);
                        count++;
                        t_start_added = true;
                    }
                }
                if (t_start <= this.points[i].getBase().getX() && this.points[i].getBase().getX() <= t_end)
                {
                    if (!t_start_added)
                    {
                        edited.points.Add((BezierPoint)this.points[i].clone());
                        count++;
                    }
                    else
                    {
                        t_start_added = false;
                    }
                }
                if (this.points[i].getBase().getX() < t_end && t_end < this.points[i + 1].getBase().getX())
                {
                    PointD x0 = this.points[i].getBase();
                    PointD x1 = this.points[i + 1].getBase();
                    PointD c0 = (this.points[i].getControlRightType() == BezierControlType.None) ?
                                x0 : this.points[i].getControlRight();
                    PointD c1 = (this.points[i + 1].getControlLeftType() == BezierControlType.None) ?
                                x1 : this.points[i + 1].getControlLeft();
                    PointD[] res = cutUnitBezier(x0, c0, c1, x1, t_end);

                    edited.points[count - 1].setControlRight(res[1]);

                    BezierPoint right = new BezierPoint(res[3]);
                    right.setControlLeft(res[2]);
                    right.setControlLeftType(this.points[i + 1].getControlLeftType());
                    edited.add(right);
                    count++;
                    break;
                }
            }

            if (this.points[this.points.Count - 1].getBase().getX() == t_end && !t_start_added)
            {
                edited.add((BezierPoint)this.points[this.points.Count - 1].clone());
                count++;
            }

            for (int i = 0; i < edited.size(); i++)
            {
                edited.points[i].setID(i);
            }
            return(edited);
        }
        /// <summary>
        /// 指定したカーブ種類のベジエ曲線の,指定した範囲を削除します.
        /// 削除はclock_startからclock_endの範囲について行われ,clock_end以降のシフト操作は行われません.
        /// つまり,操作後にclock_end以降のイベントが(clock_end - clock_start)だけ前方にシフトしてくることはありません.
        /// </summary>
        /// <param name="target_curve"></param>
        /// <param name="clock_start"></param>
        /// <param name="clock_end"></param>
        /// <returns></returns>
        public bool deleteBeziers(
            List <CurveType> target_curve,
            int clock_start,
            int clock_end
            )
        {
            bool edited = false;

            foreach (var curve in target_curve)
            {
                if (curve.isScalar() || curve.isAttachNote())
                {
                    continue;
                }
                List <BezierChain> tmp = new List <BezierChain>();
                foreach (var bc in this.get(curve))
                {
                    int len = bc.points.Count;
                    if (len < 1)
                    {
                        continue;
                    }
                    int chain_start = (int)bc.points[0].getBase().getX();
                    int chain_end;
                    if (len < 2)
                    {
                        chain_end = chain_start;
                    }
                    else
                    {
                        chain_end = (int)bc.points[len - 1].getBase().getX();
                    }
                    if (clock_start < chain_start && chain_start < clock_end && clock_end < chain_end)
                    {
                        // end ~ chain_endを残す
                        try {
                            BezierChain chain = bc.extractPartialBezier(clock_end, chain_end);
                            chain.id = bc.id;
                            tmp.Add(chain);
                            edited = true;
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".deleteBeziers; ex=" + ex + "\n");
                        }
                    }
                    else if (chain_start <= clock_start && clock_end <= chain_end)
                    {
                        // chain_start ~ startとend ~ chain_endを残す
                        try {
                            BezierChain chain1 = bc.extractPartialBezier(chain_start, clock_start);
                            chain1.id = bc.id;
                            BezierChain chain2 = bc.extractPartialBezier(clock_end, chain_end);
                            chain2.id = -1;  // 後で番号をつける
                            tmp.Add(chain1);
                            tmp.Add(chain2);
                            edited = true;
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".deleteBeziers; ex=" + ex + "\n");
                        }
                    }
                    else if (chain_start < clock_start && clock_start < chain_end && chain_end < clock_end)
                    {
                        // chain_start ~ startを残す
                        try {
                            BezierChain chain = bc.extractPartialBezier(chain_start, clock_start);
                            chain.id = bc.id;
                            tmp.Add(chain);
                            edited = true;
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".deleteBeiers; ex=" + ex + "\n");
                        }
                    }
                    else if (clock_start <= chain_start && chain_end <= clock_end)
                    {
                        // 全体を削除
                        edited = true;
                    }
                    else
                    {
                        // 全体を残す
                        tmp.Add((BezierChain)bc.clone());
                    }
                }
                this.get(curve).Clear();
                foreach (var bc in tmp)
                {
                    if (bc.id >= 0)
                    {
                        addBezierChain(curve, bc, bc.id);
                    }
                }
                foreach (var bc in tmp)
                {
                    if (bc.id < 0)
                    {
                        bc.id = this.getNextId(curve);
                        addBezierChain(curve, bc, bc.id);
                    }
                }
            }
            return(edited);
        }
        /// <summary>
        /// 指定した種類のコントロールカーブにベジエ曲線を追加します。
        /// AddBezierChainとの違い、オーバーラップする部分があれば自動的に結合されます。
        /// chainには2個以上のデータ点が含まれている必要がある
        /// </summary>
        /// <param name="curve"></param>
        /// <param name="chain"></param>
        public void mergeBezierChain(CurveType curve, BezierChain chain)
        {
            if (chain.points.Count <= 1)
            {
                return;
            }
            int chain_start = (int)chain.getStart();
            int chain_end   = (int)chain.getEnd();

            // まず、全削除する必要のあるBezierChainを検索
            List <int>         delete_list = new List <int>();
            List <BezierChain> src         = this.get(curve);

            //foreach ( int id in this[curve].Keys ) {
            for (int j = 0; j < src.Count; j++)
            {
                //BezierChain bc = this[curve][id];
                BezierChain bc = src[j];
                if (bc.points.Count <= 0)
                {
                    continue;
                }
                int bc_start = (int)bc.getStart();
                int bc_end   = (int)bc.getEnd();
                if (chain_start <= bc_start && bc_end <= chain_end)
                {
                    delete_list.Add(bc.id);
                }
            }

            // 削除を実行
            foreach (var id in delete_list)
            {
                remove(curve, id);
                //this[curve].Remove( id );
            }

            // マージする必要があるかどうかを検査。
            bool processed = true;

            while (processed)
            {
                processed = false;
                List <BezierChain> list = this.get(curve);
                //foreach ( int id in this[curve].Keys ) {
                for (int j = 0; j < list.Count; j++)
                {
                    //BezierChain bc = this[curve][id];
                    BezierChain bc    = list[j];
                    int         id    = bc.id;
                    int         start = (int)bc.getStart();
                    int         end   = (int)bc.getEnd();

                    // 被っている箇所が2箇所以上ある可能性があるので、ifでヒットしてもbreakしない
                    if (start < chain_start && chain_start <= end && end < chain_end)
                    {
                        // bcのchain_start ~ endを削除し、chain_startで結合
                        BezierChain bc_edit = null;
                        try {
                            bc_edit = bc.extractPartialBezier(start, chain_start);
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".mergeBezierChain; ex=" + ex + "\n");
                            continue;
                        }
                        bc_edit.id = bc.id;
                        int last = bc_edit.size() - 1;

                        // 接合部分では、制御点無しでステップ変化する
                        bc_edit.points[last].setControlRightType(BezierControlType.None);
                        chain.points[0].setControlLeftType(BezierControlType.None);

                        int copy_start = 0;
                        if (bc_edit.points[last].getBase().getY() == chain.points[0].getBase().getY())
                        {
                            // bcの終点とchainの始点の座標が一致している場合
                            if (bc_edit.points[last].getControlLeftType() != BezierControlType.None)
                            {
                                bc_edit.points[last].setControlLeftType(BezierControlType.Master);
                            }
                            bc_edit.points[last].setControlRight(chain.points[0].controlLeft);
                            if (chain.points[0].getControlRightType() != BezierControlType.None)
                            {
                                bc_edit.points[last].setControlLeftType(BezierControlType.Master);
                            }
                            copy_start = 1;
                        }
                        for (int i = copy_start; i < chain.points.Count; i++)
                        {
                            chain.points[i].setID(bc_edit.getNextId());
                            bc_edit.add(chain.points[i]);
                        }
                        //this[curve].Remove( id );
                        remove(curve, id);
                        chain       = bc_edit;
                        chain_start = (int)chain.getStart();
                        chain_end   = (int)chain.getEnd();
                        processed   = true;
                        break;
                    }
                    else if (chain_start < start && start <= chain_end && chain_end < end)
                    {
                        // bcのstart ~ chain_endを削除し、chain_endで結合
                        BezierChain bc_edit = null;
                        try {
                            bc_edit = bc.extractPartialBezier(chain_end, end);
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".mergeBezierChain; ex=" + ex + "\n");
                            continue;
                        }
                        bc_edit.id = bc.id;
                        int last = chain.size() - 1;

                        // 接合部分では、制御点無しでステップ変化する
                        bc_edit.points[0].setControlLeftType(BezierControlType.None);
                        chain.points[last].setControlRightType(BezierControlType.None);

                        int copy_end = last;
                        if (chain.points[last].getBase().getY() == bc_edit.points[0].getBase().getY())
                        {
                            // bcの終点とchainの始点の座標が一致している場合
                            if (chain.points[last].getControlLeftType() != BezierControlType.None)
                            {
                                chain.points[last].setControlLeftType(BezierControlType.Master);
                            }
                            chain.points[last].setControlRight(bc_edit.points[0].controlLeft);
                            if (bc_edit.points[0].getControlRightType() != BezierControlType.None)
                            {
                                chain.points[last].setControlLeftType(BezierControlType.Master);
                            }
                            copy_end = last - 1;
                        }
                        for (int i = 0; i <= copy_end; i++)
                        {
                            chain.points[i].setID(bc_edit.getNextId());
                            bc_edit.add(chain.points[i]);
                        }
                        //this[curve].Remove( id );
                        remove(curve, id);
                        chain       = bc_edit;
                        chain_start = (int)chain.getStart();
                        chain_end   = (int)chain.getEnd();
                        processed   = true;
                        break;
                    }
                    else if (start < chain_start && chain_end < end)
                    {
                        // bcのchain_start ~ chain_endをchainで置き換え
                        // left + chain + right
                        BezierChain left = null;
                        try {
                            left = bc.extractPartialBezier(start, chain_start);
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".mergeBezierChain; ex=" + ex + "\n");
                            continue;
                        }
                        BezierChain right = null;
                        try {
                            right = bc.extractPartialBezier(chain_end, end);
                        } catch (Exception ex) {
                            Logger.write(typeof(BezierCurves) + ".mergeBezierChain; ex=" + ex + "\n");
                            continue;
                        }
                        left.id = bc.id;

                        // 接合部ではステップ変化
                        left.points[left.size() - 1].setControlRightType(BezierControlType.None);
                        chain.points[0].setControlLeftType(BezierControlType.None);
                        chain.points[chain.size() - 1].setControlRightType(BezierControlType.None);
                        right.points[0].setControlLeftType(BezierControlType.None);

                        int copy_start = 0;
                        int copy_end   = chain.size() - 1;

                        if (left.points[left.size() - 1].getBase().getY() == chain.points[0].getBase().getY())
                        {
                            // bcの終点とchainの始点の座標が一致している場合
                            if (left.points[left.size() - 1].getControlLeftType() != BezierControlType.None)
                            {
                                left.points[left.size() - 1].setControlLeftType(BezierControlType.Master);
                            }
                            left.points[left.size() - 1].setControlRight(chain.points[0].controlLeft);
                            if (chain.points[0].getControlRightType() != BezierControlType.None)
                            {
                                left.points[left.size() - 1].setControlLeftType(BezierControlType.Master);
                            }
                            copy_start = 1;
                        }

                        if (chain.points[chain.size() - 1].getBase().getY() == right.points[0].getBase().getY())
                        {
                            // bcの終点とchainの始点の座標が一致している場合
                            if (chain.points[chain.size() - 1].getControlLeftType() != BezierControlType.None)
                            {
                                chain.points[chain.size() - 1].setControlLeftType(BezierControlType.Master);
                            }
                            chain.points[chain.size() - 1].setControlRight(right.points[0].controlLeft);
                            if (right.points[0].getControlRightType() != BezierControlType.None)
                            {
                                chain.points[chain.size() - 1].setControlLeftType(BezierControlType.Master);
                            }
                            copy_end = chain.size() - 2;
                        }

                        // 追加
                        for (int i = copy_start; i <= copy_end; i++)
                        {
                            chain.points[i].setID(left.getNextId());
                            left.add(chain.points[i]);
                        }
                        for (int i = 0; i < right.points.Count; i++)
                        {
                            right.points[i].setID(left.getNextId());
                            left.add(right.points[i]);
                        }
                        //this[curve].Remove( id );
                        remove(curve, id);
                        chain       = left;
                        chain_start = (int)chain.getStart();
                        chain_end   = (int)chain.getEnd();
                        processed   = true;
                        break;
                    }
                }
            }

            if (!processed)
            {
                chain.id = this.getNextId(curve);
            }
            //this[curve].Add( chain.ID, chain );
            addBezierChain(curve, chain, chain.id);
        }