C# カーブエディタ作成 右クリック動作

前の記事

bravememo.hatenablog.com

完成図

GAME DVR で録画してるので右クリックメニューが映りません。


CurveEditor 右クリック動作実装

右クリックでいろいろ

右クリックでコンテキストメニューを表示するにはcontextMenuStripが必要です。 Form上にcontextMenuStripを配置したらcontextMenuStripを選択すると入力項目が出るので、 下記画像のように入力すれば外観の完成です。

f:id:Brave345:20191216171533p:plain

あとは追加したメニューをダブルクリックしてクリックイベントを追加してイベントを実装してあげれば完成です。

点の追加と削除はいままで実装してたやつをそのまま使いました。

線を直線にする機能を開始点と終了点でベクトル取り、その中間点に制御点を置くことで実現しました。

グラフのリセット機能

いよいよいままで飾りだったメニューバーの実装です。 今回は新規作成とエディタの終了部分を実装しました。 項目は先ほどのcontextMenuStripと同じことをMenuStripでもやればできます。

f:id:Brave345:20191216171601p:plain

あとは新規作成の項目を選択して下記画像のようにショートカットキーを設定してあげれば完成です。 f:id:Brave345:20191216171643p:plain

以下参考URL

builder.japan.zdnet.com

ソースコード

いつものように差分だけ

//CurvePointControl.cs
       /// <summary>
        /// グラフの初期化
        /// </summary>
        public void CurveEditorInit()
        {
            m_SelectMode = SelectMode.None;
            CancelMovePoint();
            m_SelectPoint = 0;
            m_list.Clear();
            //初期の曲線設定
            BezierPoint startBezirPoint = new BezierPoint();
            startBezirPoint.startPoint = new Point(ScrrenLeftPosX, ScrrenBottomPosY);
            startBezirPoint.endPoint = new Point(ScrrenRightPosX, ScrrenTopPosY);
            //適当に右にずらした位置に制御点を置く
            startBezirPoint.controlPoint1 = new Point(ScrrenLeftPosX + 30, ScrrenCenterpPosY + 30);
            startBezirPoint.controlPoint2 = new Point(ScrrenLeftPosX + 30, ScrrenCenterpPosY - 30);
            m_list.Add(startBezirPoint);
        }
//Form.cs
    //右クリック動作----------------------------------------------------------------
        private void AddPointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_CurvePointControl.AddPoint(m_MousePos);
            pictureBox1.Refresh();//再描画
        }

        private void DeletePointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_CurvePointControl.DeletePoint();//点削除
            pictureBox1.Refresh();//再描画
        }
        /// <summary>
        /// 線を直線にする
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void StraightLineEditToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_CurvePointControl.StraightLineEdit();
        }
        //  メニューバー--------------------------------------------------------------------------
        private void 新規作成NToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("現在あるグラフをリセットしてよろしいでしょうか?",
    "質問",
    MessageBoxButtons.OKCancel,
    MessageBoxIcon.Exclamation,
    MessageBoxDefaultButton.Button2);

            //何が選択されたか調べる
            if (result == DialogResult.OK)
            {
                //「はい」が選択された時
                m_CurvePointControl.CurveEditorInit();
                  pictureBox1.Refresh();//再描画
            }

        }
        /// <summary>
        /// エディタの終了
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuEnd_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("アプリケーションを終了してもよろしいでしょうか?",
"質問",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Exclamation,
MessageBoxDefaultButton.Button2);

            //何が選択されたか調べる
            if (result == DialogResult.OK)
            {
                //「はい」が選択された時
                Close();
            }

        }

終わりに

次はグラフの保存とエディタでグラフの読み込みを実装していきたい。

次の記事

bravememo.hatenablog.com

C# カーブエディタ作成 入力項目の実装その2

前の記事

bravememo.hatenablog.com

完成図

だいぶそれっぽくなってきた。


CurveEditor ver 0 6 α版 入力項目の同期 その2

グラフ、入力項目の表記変更

・大幅に見た目をかえました。 見た目はUniyのAnimationCurveを参考にしました。 pictureobxのサイズを640×340から540×530にし、中で500×500のグラフを表示するようにしました

横長のグラフだとこのグラフを読み込んだ際に縦に伸びてしまい、思ったような挙動にならないはずなので、正方形にしました。

・グラフの表記は画面の座標から1.0,1.0にし、値の調整をわかりやすくしました。

ChageDecimalPos~とChageNomalPos~で座標を変換しています。 方法としてはシンプルで位置をグラフの大きさである500で割ってあげれば0.002まで表現することができます。m_CurvePointControlに座標を渡す際は numericUpDown.Valueの値(0~1)に500をかければ元の座標が出せるのでそれを渡せば今まで通り曲線を表示できます。

グラフの外側にある数値表示

数値表記はlabelでやっていてBackColorをTransparentにしてLavelInitでやっているように pictureBox1.Controls.Addをlabelに対してしてあげれば背景を透過させた文字が表示できます。

labelの配置位置です。白いやつがlabelです。

f:id:Brave345:20191212145900p:plain

pictureBox1.Controls.Addすると位置がデザイン画面で表示している位置とずれます。 おそらく原因としては実行前の配置はFormの相対座標が保存されているがpictureBox1を親にしても、Formの相対座標をそのまま使われてるからだと思われます。 なので配置位置は実行結果見ながら地道に位置をずらしました。 なんかいいやり方はないのかな・・・

処理負荷軽減

いままでRefresh()で描画の更新をしていましたが、それだとForm全体が描画の再更新がかかるため重かったため、Refresh()を pictureBox1.Refresh()にすることで、pictureBoxだけ描画だけ更新することができ、無駄な描画更新が減りました。

点の描画OnOff機能

checkBoxを使用してチェックがついてないときは点の描画処理を呼ばないことで点の描画のOnOffを実装しています。 注意点としてCheckedChangedイベントでRefresh()を呼ばないとチェックの変更を行った際に描画の更新がされないのでCheckedChangedイベントを設定しましょう。

線の色変更、線の追加

0.2ごとに線を追加してグラフっぽくしました。線の表示方法今までのやり方と同じでGraphicsのDrawLineで描画しています。

曲線はにして見やすくしました。UniyのAnimationcurveぽいでしょ?

ソースコード

全部書くと長くなるので大きく変更した部分だけ掲載します。

//Form.cs
 public partial class Form1 : Form
    {
        /// <summary>
        /// 直線
        /// </summary>
        CenterLine m_CenterLine = new CenterLine();
        TopLine m_TopLine = new TopLine();
        BottomLine m_BottomLine = new BottomLine();
        VerticalEndLine m_VerticalLeftLine = new VerticalEndLine(true);
        VerticalEndLine m_VerticalRightLine = new VerticalEndLine(false);

        const int LineNum = 9;
        VerticalLine[] m_VerticalCenterLines = new VerticalLine[LineNum];
        SideLine[] m_SideLine = new SideLine[LineNum];
        /// <summary>
        /// 曲線
        /// </summary>
        CurvePointControl m_CurvePointControl = new CurvePointControl();

        const int ScrrenCenterpPosY = 162;  //中央
        const int ScrrenTopPosY = 10;      //上端
        const int ScrrenBottomPosY = 510;   //下端
        Point m_MousePos;//一時保存用マウスの座標
        CurvePointControl.BezierPoint bp;//一時保存用

        //中心の線を引くためのポイント初期化
        public void StandartPointInit()
        {
            m_CenterLine.Init();
            m_TopLine.Init();
            m_BottomLine.Init();
            m_VerticalLeftLine.Init();
            m_VerticalRightLine.Init();
            for(int i = 0; i < LineNum; i++)
            {
                //縦線
                m_VerticalCenterLines[i] = new VerticalLine((i + 1) * 50  + 10);
                m_VerticalCenterLines[i].Init();
                //横線
                m_SideLine[i] = new SideLine((i + 1) * 50  + 10);
                m_SideLine[i].Init();
            }
         
        }
        //入力項目の初期化
        public void numericUpDownInit()
        {
            //値が変わった時の呼び出されるメソッド設定
            numericUpDown1.ValueChanged += new EventHandler(ChangeSelectPointX);
            numericUpDown2.ValueChanged += new EventHandler(ChangeControlPoint1X);
            numericUpDown3.ValueChanged += new EventHandler(ChangeControlPoint2X);
            numericUpDown4.ValueChanged += new EventHandler(ChangeSelectPointY);
            numericUpDown5.ValueChanged += new EventHandler(ChangeControlPoint1Y);
            numericUpDown6.ValueChanged += new EventHandler(ChangeControlPoint2Y);
            numericUpDown7.ValueChanged += new EventHandler(ChangeEndPoint);
            numericUpDown7.Value = 1;
            numericUpDown8.ValueChanged += new EventHandler(ChangeFirstStartPoint);
            numericUpDown8.Value = 0;
            ChangeFirstStartPoint(null,null);
            ChangeEndPoint(null, null);
        }

        /// <summary>
        /// 背景を透過させるための初期化
        /// </summary>
        public void LavelInit()
        {
            pictureBox1.Controls.Add(label10);
            pictureBox1.Controls.Add(label11);
            pictureBox1.Controls.Add(label12);
            pictureBox1.Controls.Add(label13);
            pictureBox1.Controls.Add(label14);
            pictureBox1.Controls.Add(label15);
            pictureBox1.Controls.Add(label16);
            pictureBox1.Controls.Add(label17);
            pictureBox1.Controls.Add(label18);
            pictureBox1.Controls.Add(label19);
            pictureBox1.Controls.Add(label20);
            pictureBox1.Controls.Add(label21);
        }
        /// <summary>
        /// 点を動かした際同期を取る
        /// </summary>
        public void numericUpDownSync()
        {
            bp = m_CurvePointControl.GetBezierPoint();
            numericUpDown1.Value = ChageDecimalPosX(bp.startPoint.X);
            numericUpDown2.Value = ChageDecimalPosX(bp.controlPoint1.X);
            numericUpDown3.Value = ChageDecimalPosX(bp.controlPoint2.X);
            numericUpDown4.Value = ChageDecimalPosY(bp.startPoint.Y);
            numericUpDown5.Value = ChageDecimalPosY(bp.controlPoint1.Y);
            numericUpDown6.Value = ChageDecimalPosY(bp.controlPoint2.Y);
            numericUpDown7.Value = ChageDecimalPosY(m_CurvePointControl.GetEndPointY());
            numericUpDown8.Value = ChageDecimalPosY(m_CurvePointControl.GetFirstStartPointY());
        }

        /// <summary>
        /// マウスを動かしてる間の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            m_CurvePointControl.MovePoint(e);
            numericUpDownSync();
          //  Invalidate();重くなるのでいらない
            pictureBox1.Refresh();//再描画
        }  

        /// <summary>
        /// 選択点X
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeSelectPointX(object sender, EventArgs e)
        {
            int num3 = ChageNomalPosX(numericUpDown1.Value);
            int num4 = m_CurvePointControl.SetStartPointX(num3);
            numericUpDown1.Value = ChageDecimalPosX(num4); 
             if (!m_CurvePointControl.isMoveSelectPoint()) pictureBox1.Refresh();//再描画
        }
        /// <summary>
        /// 選択点Y
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeSelectPointY(object sender, EventArgs e)
        {
            int num = ChageNomalPosY(numericUpDown4.Value);
            int num2 = m_CurvePointControl.SetStartPointY(num);
            numericUpDown4.Value = ChageDecimalPosY(num2);
            if (!m_CurvePointControl.isMoveSelectPoint()) pictureBox1.Refresh();//再描画
            //最初の開始点と同期を取る
            numericUpDown8.Value = ChageDecimalPosY(m_CurvePointControl.GetFirstStartPointY());
        }
        /// <summary>
        /// 制御点1X
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint1X(object sender, EventArgs e)
        {
            int num = ChageNomalPosX(numericUpDown2.Value);
            int num2 = m_CurvePointControl.SetControl1PointX(num);
            numericUpDown2.Value =ChageDecimalPosX(num2);
             if (!m_CurvePointControl.isMoveSelectPoint())  pictureBox1.Refresh();//再描画
        }
        /// <summary>
        /// 制御点1Y
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint1Y(object sender, EventArgs e)
        {
            int num = ChageNomalPosY(numericUpDown5.Value);
            int num2 = m_CurvePointControl.SetControl1PointY(num);
            numericUpDown5.Value = ChageDecimalPosY(num2);
            if (!m_CurvePointControl.isMoveSelectPoint())  pictureBox1.Refresh();//再描画
        }
        /// <summary>
        /// 制御点2X
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint2X(object sender, EventArgs e)
        {
            int num = ChageNomalPosX(numericUpDown3.Value);
            int num2 = m_CurvePointControl.SetControl2PointX(num);
            numericUpDown3.Value = ChageDecimalPosX(num2);
            if (!m_CurvePointControl.isMoveSelectPoint())  pictureBox1.Refresh();//再描画
        }
        /// <summary>
        /// 制御点2Y
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint2Y(object sender, EventArgs e)
        {
            int num = ChageNomalPosY(numericUpDown6.Value);
            int num2 = m_CurvePointControl.SetControl2PointY(num);
            numericUpDown6.Value = ChageDecimalPosY(num2);
            if (!m_CurvePointControl.isMoveSelectPoint()) pictureBox1.Refresh();//再描画
        }
        //開始点せってい
        public void ChangeFirstStartPoint(object sender, EventArgs e)
        {
            int num = ChageNomalPosY(numericUpDown8.Value);
            int num2 = m_CurvePointControl.SetFirstStartPoint(num); ;
            numericUpDown8.Value = ChageDecimalPosY(num2);
            if (!m_CurvePointControl.isMoveSelectPoint()) pictureBox1.Refresh();//再描画
            bp = m_CurvePointControl.GetBezierPoint();
            numericUpDown4.Value = ChageDecimalPosY( bp.startPoint.Y);
        }
        /// <summary>
        /// 終了点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeEndPoint(object sender, EventArgs e)
        {

            int num = ChageNomalPosY(numericUpDown7.Value);
            int num2 = m_CurvePointControl.SetEndPoint(num); ;
            numericUpDown7.Value = ChageDecimalPosY(num2);
            if (!m_CurvePointControl.isMoveSelectPoint()) pictureBox1.Refresh();//再描画
            bp = m_CurvePointControl.GetBezierPoint();
        }

  
        //小数から元の座標に変換
        public int ChageNomalPosX(decimal Value)
        {
            decimal num = Value;

            decimal num2 = (num * (decimal)500);
            int num3 = Convert.ToInt32(num2);

            num3 += 10;//10からグラフが始まってるので右に+10
            return num3;
        }
        //元の座標から0~1の間に変換
        public decimal ChageDecimalPosX(int posX)
        {
            posX = Math.Max(0, posX - 10); //10からグラフが始まってるので右に-10
            return (decimal)posX / (decimal)500;
        }
        //小数から元の座標に変換
        public int ChageNomalPosY(decimal Value)
        {
            decimal num = Value;

            decimal num2 = num * (decimal)500;
            int num3 = Convert.ToInt32(num2);

            num3 = num3 - 500;
            num3 = num3 * -1;
            num3 += 10;//10からグラフが始まってるので右に+10
    
            return num3;
        }
        //元の座標から0~1の間に変換
        public decimal ChageDecimalPosY(int posY)
        {
            //  posY = Math.Min(500, posY - 10);
            //10からグラフが始まってるので右に-10
            posY = Math.Max(0, posY - 10);
            posY = posY - 500;
            posY = posY * -1;
    
            return Math.Max(0,  (decimal)posY / (decimal)500);
        }
    
        //チェックボックスの値が変化したときに呼ばれる
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            pictureBox1.Refresh();//再描画
        }

終わりに

だいぶカーブエディタも完成に近づいてきました。 この調子で頑張ろう。

次の記事

bravememo.hatenablog.com

C# カーブエディタ作成 入力項目の実装その1

前の記事

bravememo.hatenablog.com

完成図


CurveEditor ver 0 5 α版 入力項目の同期

入力項目を反映 同期をとる

numericUpDownのValueChangedイベントで数字の変更時にnumericUpDownのValueに代入と点の座標に直接代入しています。

流れとしては 点を動かす→MouseMove内 のnumericUpDownSync()でnumericUpDownのValueに代入→Valueが変わるのでValueChangedを呼ぶ→同じ数代入するのでValueChangedを呼ばす終了点項目に入力→ValueChangedで点の座標に代入(値を範囲内に収める)→MouseMove呼んでないので Refreshで再描画する。 といった感じに実装しました。

ダブルクリックで点を追加

pictureboxのDoubleClicイベントを使用して実装しました。 点の位置はマウスの位置と終了点のX座標を終了点より低ければその線と次の線の間に新しい点を挿入させます。

注意点としてDoubleClicイベントではマウスの座標が取得できません。(引数がMouseEventArgsでないため)

マウスの座標の取得はFormのMouseDownの際にで一時的に保存しておくことで解決させました。

ダブルクリックを実装する時ダブルクリックイベントとクリックイベントが両方呼ばれる問題があるのですが、このアプリではそれを利用した感じになります。

ソースコード

全部書くと長くなるので追加した部分だけ掲載します。

//Formcs
 public partial class Form1 : Form
    {
        int m_MinNum = -150;//最小値
        int m_MaxNum = 150;  //最大値

        const int ScrrenCenterpPosY = 160;  //中央
        const int ScrrenTopPosY = 10;      //上端
        Point m_MousePos;//一時保存用変数
        public Form1()
        {
            InitializeComponent();
            Text = "CurveEditor ver:0.5 α版";
            //ちらつき防止
            SetStyle(
    ControlStyles.DoubleBuffer |
    ControlStyles.UserPaint |
    ControlStyles.AllPaintingInWmPaint, true);
             
            StandartPointInit();
            KeyPreview = true;//キー入力有効化
            //入力装置の初期化
            numericUpDownInit();
        }

        //入力項目の初期化
        public void numericUpDownInit()
        {
            //値が変わった時の呼び出されるメソッド設定
            numericUpDown1.ValueChanged += new EventHandler(ChangeSelectPointX);
            numericUpDown2.ValueChanged += new EventHandler(ChangeControlPoint1X);
            numericUpDown3.ValueChanged += new EventHandler(ChangeControlPoint2X);
            numericUpDown4.ValueChanged += new EventHandler(ChangeSelectPointY);
            numericUpDown5.ValueChanged += new EventHandler(ChangeControlPoint1Y);
            numericUpDown6.ValueChanged += new EventHandler(ChangeControlPoint2Y);
            numericUpDown7.ValueChanged += new EventHandler(ChangeEndPoint);
            numericUpDown7.Value = ScrrenTopPosY;
            numericUpDown8.ValueChanged += new EventHandler(ChangeFirstStartPoint);
            numericUpDown8.Value = ScrrenCenterpPosY;
            numericUpDown9.ValueChanged += new EventHandler(ChangeMaxValue);
            numericUpDown9.Value = m_MaxNum;
            numericUpDown10.ValueChanged += new EventHandler(ChangeMinValue);
            numericUpDown10.Value = m_MinNum;
            ChangeFirstStartPoint(null,null);
            ChangeEndPoint(null, null);
        }
        CurvePointControl.BezierPoint bp;//一時保存用
        /// <summary>
        /// 点を動かした際同期を取る
        /// </summary>
        public void numericUpDownSync()
        {
            bp = m_CurvePointControl.GetBezierPoint();
            numericUpDown1.Value = bp.startPoint.X;
            numericUpDown2.Value = bp.controlPoint1.X;
            numericUpDown3.Value = bp.controlPoint2.X;
            numericUpDown4.Value = bp.startPoint.Y;
            numericUpDown5.Value = bp.controlPoint1.Y;
            numericUpDown6.Value = bp.controlPoint2.Y;
            numericUpDown7.Value = m_CurvePointControl.GetEndPointY();
            numericUpDown8.Value = m_CurvePointControl.GetFirstStartPointY();
        }
        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Left:
                    m_CurvePointControl.SearchSelectPoint(e);
                    SaveMousePos(e);
                    break;
                case MouseButtons.Middle:
                    break;
                case MouseButtons.Right:
                    break;
            }
        }
        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Left:
               
                      m_CurvePointControl.CancelMovePoint();
                    if (!m_CurvePointControl.isSelectPoint())
                    {
                        //入力項目のリセット
                        ResetCurvePointValue();
                    }
                    break;
                case MouseButtons.Middle:
                    break;
                case MouseButtons.Right:
                    break;
            }
        }
        //マウスを動かしてる間
        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            m_CurvePointControl.MovePoint(e);
            numericUpDownSync();
          //  Invalidate();重くなるのでいらない
            Refresh();//再描画
        }  
   
        //点追加ダブルクリック時呼びだす
        private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {
            m_CurvePointControl.AddPoint(m_MousePos);
            Refresh();//再描画
        }

        /// <summary>
        ///  picture内のマウス座標を保存
        /// </summary>
        public void SaveMousePos(MouseEventArgs e)
        {    
            Point p = new Point();
            p.X = e.X;
            p.Y = e.Y;
            m_MousePos = p;
        }

        /// <summary>
        /// 選択点X
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeSelectPointX(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown1.Value);
            numericUpDown1.Value = m_CurvePointControl.SetStartPointX(num);
            if (!m_CurvePointControl.isMoveSelectPoint()) Refresh();//再描画
        }
        /// <summary>
        /// 選択点Y
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeSelectPointY(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown4.Value);
            numericUpDown4.Value = m_CurvePointControl.SetStartPointY(num);
            if (!m_CurvePointControl.isMoveSelectPoint()) Refresh();//再描画
            //最初の開始点と同期を取る
            numericUpDown8.Value = m_CurvePointControl.GetFirstStartPointY();
        }
        /// <summary>
        /// 制御0点1X
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint1X(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown2.Value);
            numericUpDown2.Value = m_CurvePointControl.SetControl1PointX(num);
            if (!m_CurvePointControl.isMoveSelectPoint())  Refresh();//再描画
        }
        /// <summary>
        /// 制御0点1Y
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint1Y(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown5.Value);
            numericUpDown5.Value = m_CurvePointControl.SetControl1PointY(num);
            if (!m_CurvePointControl.isMoveSelectPoint())  Refresh();//再描画
        }
        /// <summary>
        /// 制御0点2X
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint2X(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown3.Value);
            numericUpDown3.Value = m_CurvePointControl.SetControl2PointX(num);
            if (!m_CurvePointControl.isMoveSelectPoint())  Refresh();//再描画
        }
        /// <summary>
        /// 制御0点2Y
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeControlPoint2Y(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown6.Value);
            numericUpDown6.Value = m_CurvePointControl.SetControl2PointY(num);
            if (!m_CurvePointControl.isMoveSelectPoint()) Refresh();//再描画
        }
        //開始点せってい
        public void ChangeFirstStartPoint(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown8.Value);
            numericUpDown8.Value = m_CurvePointControl.SetFirstStartPoint(num);
            if (!m_CurvePointControl.isMoveSelectPoint()) Refresh();//再描画

            bp = m_CurvePointControl.GetBezierPoint();
            numericUpDown4.Value = bp.startPoint.Y;
        }
        /// <summary>
        /// 終了点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeEndPoint(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown7.Value);
            numericUpDown7.Value = m_CurvePointControl.SetEndPoint(num);
            if (!m_CurvePointControl.isMoveSelectPoint()) Refresh();//再描画
            bp = m_CurvePointControl.GetBezierPoint();
        }
        public void ChangeMaxValue(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown9.Value);
            m_MaxNum = num;
            if (!m_CurvePointControl.isMoveSelectPoint())  Refresh();//再描画
        }

        public void ChangeMinValue(object sender, EventArgs e)
        {
            int num = Convert.ToInt32(numericUpDown8.Value);
            m_MinNum = num;
            if (!m_CurvePointControl.isMoveSelectPoint()) Refresh();//再描画
        }
        //カーブポイント入力項目のリセット
        public void ResetCurvePointValue()
        {
              numericUpDown1.Value = 0;
              numericUpDown2.Value = 0;
              numericUpDown3.Value = 0;
              numericUpDown4.Value = 0;
              numericUpDown5.Value = 0;
              numericUpDown6.Value = 0;
        }

    }
//CurvePointControl.cs
  class CurvePointControl
 {
  /// <summary>
        /// 点の追加(ダブルクリックVer)
        /// </summary>
        /// <param name="p"></param>
        public void AddPoint(Point p)
        {
            var SelectNum = SearchSelectStartPoint(p);//何番目の点になるか
            BezierPoint startBezirPoint = new BezierPoint();
            const int ofsetY = 30;

            startBezirPoint.startPoint = new Point(Math.Min(p.X, ScrrenRightPosX - 1), Clamp(p.Y, ScrrenTopPosY, ScrrenBottomPosY));
            startBezirPoint.endPoint = new Point(m_list[SelectNum].endPoint.X, m_list[SelectNum].endPoint.Y);
            //制御点は追加される開始点の少し右に生成させる
            var cpointX = Clamp(startBezirPoint.startPoint.X + 10, ScrrenLeftPosX, ScrrenRightPosX - 1);
            startBezirPoint.controlPoint1 = new Point(cpointX, Clamp( startBezirPoint.startPoint.Y + ofsetY, ScrrenTopPosY, ScrrenBottomPosY));
            startBezirPoint.controlPoint2 = new Point(cpointX, Clamp(startBezirPoint.startPoint.Y - ofsetY, ScrrenTopPosY, ScrrenBottomPosY));

            m_list.Insert(SelectNum + 1, startBezirPoint);//新しい点追加

            //追加前の最後の終了点を追加した最後の開始点とつなげる ここ大事!
            BezierPoint BezirPoint = new BezierPoint();
            BezirPoint = m_list[SelectNum];
            BezirPoint.endPoint = m_list[SelectNum + 1].startPoint;//繋ぎなおす
            m_list[SelectNum] = BezirPoint;

            //ダブルクリックで選択モードが解除されてるので選択モードに
            m_SelectMode = SelectMode.SelectStart;
            //増やした点を選択状態に
            m_SelectPoint = SelectNum + 1;
        }

        /// <summary>
        /// どれかの動いてる点を選択してるか
        /// </summary>
        /// <returns></returns>
        public bool isMoveSelectPoint()
        {
            if (m_isMoveStartPoint) return true;//選択した開始点
            if (m_isMoveControl1Point) return true;//選択した制御点1
            if (m_isMoveControl2Point) return true;//選択した制御点2
            if (m_isMoveEndPoint) return true;//選択した最後の点
            return false;
        }
        /// <summary>
        /// どれかの点を選択してるか
        /// </summary>
        /// <returns></returns>
        public bool isSelectPoint()
        {
            if (m_SelectMode == SelectMode.None) return false;
            return true;
        }
    
        /// <summary>
        ///    開始点セット
        /// </summary>
        /// <param name="y"></param>
        public int SetStartPointX(int x)
        {
            BezierPoint sp = m_list[m_SelectPoint]; //最初の点

            if (isSelectFirstStartPoint()) return 0;
            if (m_SelectMode == SelectMode.None) return 0;

            var BeforeSelectPoint = m_SelectPoint - 1;
            //X軸の移動 intervalPointPosを加算減算すること隣の点と同じ座標にならないようにする
            int minpx = m_list[BeforeSelectPoint].startPoint.X + intervalPointPos;
            int maxpx = sp.endPoint.X - intervalPointPos;
            sp.startPoint.X = Clamp(x, minpx, maxpx);
            //ひとつ前の終了点の移動
            BezierPoint sp2 = m_list[BeforeSelectPoint];
            sp2.endPoint = sp.startPoint;
            m_list[BeforeSelectPoint] = sp2;

            m_list[m_SelectPoint] = sp;
            return x;
        }
        /// <summary>
        ///    開始点セット
        /// </summary>
        /// <param name="y"></param>
        public int SetStartPointY(int y)
        {
            if (m_SelectMode == SelectMode.None) return 0;

            BezierPoint sp = m_list[m_SelectPoint]; //最初の点のY

            sp.startPoint.Y = Clamp(y, ScrrenTopPosY, ScrrenBottomPosY);
            m_list[m_SelectPoint] = sp;

            //最初の開始点を選択してないなら前の終了点も移動させる
            if (!isSelectFirstStartPoint())
            {
                var BeforeSelectPoint = m_SelectPoint - 1;
                BezierPoint sp2 = m_list[BeforeSelectPoint];
                sp2.endPoint = sp.startPoint;
                m_list[BeforeSelectPoint] = sp2;
            }
            return sp.startPoint.Y;
        }
        /// <summary>
        ///    制御点1Xセット
        /// </summary>
        /// <param name="y"></param>
        public int SetControl1PointX(int x)
        {
            if (m_SelectMode == SelectMode.None) return 0;

            BezierPoint sp = m_list[m_SelectPoint]; //選択している点
            sp.controlPoint1.X = Clamp(x, ScrrenLeftPosX, ScrrenRightPosX);

            //一番最初の開始点以外を選択しているなら  +1 -1することで隣の点と同じ座標にならないようにする
            if (!isSelectFirstStartPoint())
            {
                int minpx = m_list[m_SelectPoint - 1].endPoint.X + intervalPointPos;
                sp.controlPoint1.X = Clamp(sp.controlPoint1.X, minpx, ScrrenRightPosX - 1);
            }

            //一番最後の終了点以外を選択しているなら  +1 -1することで隣の点と同じ座標にならないようにする
            if (!isSelectLastEndPoint())
            {
                int maxpx = m_list[m_SelectPoint + 1].startPoint.X - intervalPointPos;
                sp.controlPoint1.X = Clamp(sp.controlPoint1.X, ScrrenLeftPosX + 1, maxpx);
            }
            m_list[m_SelectPoint] = sp;

            return sp.controlPoint1.X;
        }
        /// <summary>
        ///    制御点2Xセット
        /// </summary>
        /// <param name="y"></param>
        public int SetControl2PointX(int x)
        {
            if (m_SelectMode == SelectMode.None) return 0;

            BezierPoint sp = m_list[m_SelectPoint]; //選択している点
            sp.controlPoint2.X = Clamp(x, ScrrenLeftPosX, ScrrenRightPosX);

            //一番最初の開始点以外を選択しているなら  +1 -1することで隣の点と同じ座標にならないようにする
            if (!isSelectFirstStartPoint())
            {
                int minpx = m_list[m_SelectPoint - 1].endPoint.X + intervalPointPos;
                sp.controlPoint2.X = Clamp(sp.controlPoint2.X, minpx, ScrrenRightPosX - 1);
            }

            //一番最後の終了点以外を選択しているなら  +1 -1することで隣の点と同じ座標にならないようにする
            if (!isSelectLastEndPoint())
            {
                int maxpx = m_list[m_SelectPoint + 1].startPoint.X - intervalPointPos;
                sp.controlPoint2.X = Clamp(sp.controlPoint2.X, ScrrenLeftPosX + 1, maxpx);
            }
            m_list[m_SelectPoint] = sp;

            return sp.controlPoint2.X;
        }
        /// <summary>
        ///    制御点1Yセット
        /// </summary>
        /// <param name="y"></param>
        public int SetControl1PointY(int y)
        {
            if (m_SelectMode == SelectMode.None) return 0;

            BezierPoint sp = m_list[m_SelectPoint];

            sp.controlPoint1.Y = Clamp(y, ScrrenTopPosY, ScrrenBottomPosY);
            m_list[m_SelectPoint] = sp;
            return sp.controlPoint1.Y;
        }
        /// <summary>
        ///    制御点2Yセット
        /// </summary>
        /// <param name="y"></param>
        public int SetControl2PointY(int y)
        {
            if (m_SelectMode == SelectMode.None) return 0;

            BezierPoint sp = m_list[m_SelectPoint];

            sp.controlPoint2.Y = Clamp(y, ScrrenTopPosY, ScrrenBottomPosY);
            m_list[m_SelectPoint] = sp;
            return sp.controlPoint2.Y;
        }
        /// <summary>
        /// 最初の開始点セット
        /// </summary>
        /// <param name="y"></param>
        public int SetFirstStartPoint(int y)
        {
            BezierPoint sp = m_list[0]; //最初の点
            sp.startPoint.Y = Clamp(y, ScrrenTopPosY, ScrrenBottomPosY);
            m_list[0] = sp;
            return sp.startPoint.Y;
        }
        /// <summary>
        /// 最後の終了点セット
        /// </summary>
        /// <param name="y"></param>
         public int  SetEndPoint(int y)
        {
            var LastCnt = m_list.Count() - 1;

            BezierPoint sp = m_list[LastCnt]; //最後の点
            sp.endPoint.Y = Clamp(y, ScrrenTopPosY, ScrrenBottomPosY);
            m_list[LastCnt] = sp;
            return sp.endPoint.Y;
        }
        public BezierPoint GetBezierPoint()
        {
            return m_list[m_SelectPoint];
        }
        public int GetFirstStartPointY()
        {
            return m_list[0].startPoint.Y;
        }
        public int GetEndPointY()
        {
            var LastCnt = m_list.Count() - 1;
            return m_list[LastCnt].endPoint.Y;
        }
    }
}

終わりに

この段階でやけに重いのでどうにかしたい

次の記事

bravememo.hatenablog.com

C# カーブエディタ作成 点の削除機能

前回の記事

bravememo.hatenablog.com

完成画面


CurveEditor ver 0 5 α版

点の削除機能

点の削除はボタンとDeleteキーからできるようにしています。 Deleteキーの入力についてですが、キー入力を受け付けられるようにするにはFormのKeyPreivewをTrueにさせる必要があります。

f:id:Brave345:20191211101840p:plain

削除方法はlist.RemoveAt()を使用して削除しています。 ただし追加の時と同じで削除する際も選択している前後の点を結びなおさないといけないです。

開始点の移動制限

前回の制御点と同じような感じで前の開始点と現在の終了点の間に収まるようにClampさせるだけです。

ソースコード

全部乗せると長いので前回の差分だけ載せます

//CurvePointControl.cs
 class CurvePointControl
    {
     /// <summary>
        /// 選択している点の削除(ボタンver)
        /// </summary>
        public void DeletePoint()
        {
            //削除できないなら
            if(!isDeletePoint())
            {
                DeleteErrorMessage();//エラーメッセージ
                return;
            }
            var LastCnt = m_list.Count() - 1;

            //最後の開始点を選択してるなら
            if (m_SelectPoint == LastCnt)
            {
                var BeforeSelectPoint = m_SelectPoint - 1;  //ひとつ前の終了点の移動
                //一つ前の終了点と最後の終了点を結ぶ
                BezierPoint sp2 = m_list[BeforeSelectPoint];

                sp2.endPoint.X = ScrrenRightPosX;//終了点は一番右端
                sp2.endPoint.Y = m_list[LastCnt].endPoint.Y;
                m_list[BeforeSelectPoint] = sp2;
            }
            else
            {         
                var NextSelectPoint = m_SelectPoint + 1;  //選択している次のポイント
                var BeforeSelectPoint = m_SelectPoint - 1;  //ひとつ前の終了点の移動
                //一つ前の終了点と一つ先の開始点をつなぐ
                BezierPoint sp2 = m_list[BeforeSelectPoint];

                sp2.endPoint = m_list[NextSelectPoint].startPoint;
                m_list[BeforeSelectPoint] = sp2;
            }
            //削除
            m_list.RemoveAt(m_SelectPoint);
            //削除したときは選択モード解除 でないとエラーが出る
            m_SelectMode = SelectMode.None;
        }
        /// <summary>
        /// 点を削除できる状態か
        /// </summary>
        /// <returns></returns>
        bool isDeletePoint()
        {
            //最初の開始点は削除できない
            if (m_SelectPoint == 0) return false;
            //開始点をしてる時のみ削除できる
            if (m_SelectMode != SelectMode.SelectStart) return false;

            return true;
        }
        /// <summary>
        /// 削除出来ない点ならエラーメッセージを出す
        /// </summary>
        public void DeleteErrorMessage()
        {
            //点を選択してないときは削除できない
            if (m_SelectMode == SelectMode.None)
            {
                MessageBox.Show("点を選択していません");
                return;
            }
            //終了点は削除できない
            if (m_SelectMode == SelectMode.SelectEnd)
            {
                MessageBox.Show("最後の点は削除できません。");
                return;
            }
            //最初の開始点は削除できない
            if (m_SelectPoint == 0)
            {
                MessageBox.Show("最初の点は削除できません。");
                return;
            }
        }

   /// <summary>
        /// 開始点の移動
        /// </summary>
        /// <param name="mouse"></param>
        public void MoveStartPoint(MouseEventArgs mouse)
        {
            BezierPoint sp = m_list[m_SelectPoint]; //選択している点

            sp.startPoint.Y = Clamp(mouse.Y, ScrrenTopPosY, ScrrenBottomPosY);
            // 一番最初の開始点だけY軸にしか動かせないように
            if (!isSelectFirstStartPoint())
            {
            
                var BeforeSelectPoint = m_SelectPoint - 1;
                //X軸の移動 intervalPointPosを加算減算すること隣の点と同じ座標にならないようにする
                int minpx = m_list[BeforeSelectPoint].startPoint.X + intervalPointPos;
                int maxpx = sp.endPoint.X - intervalPointPos;
                sp.startPoint.X = Clamp(mouse.X, minpx, maxpx);
                //ひとつ前の終了点の移動
                BezierPoint sp2 = m_list[BeforeSelectPoint];
                sp2.endPoint = sp.startPoint;
                m_list[BeforeSelectPoint] = sp2;
            }
       
            m_list[m_SelectPoint] = sp;
        }

};
//Formcs
        //点削除ボタンクリック
        private void button1_Click(object sender, EventArgs e)
        {
            m_CurvePointControl.DeletePoint();
            Refresh();//再描画
        }
        //キーを押した際のイベント
        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            //デリートキー押したら
             if (e.KeyCode == Keys.Delete)
             {
                 m_CurvePointControl.DeletePoint();//点削除
                 Refresh();//再描画
             }
            //エスケープキーを押したら
            if (e.KeyCode == Keys.Escape)
            {
                Close();
            }
        }

終わりに

これで保存と読み込み機能以外はカーブエディタとしての最低限の機能はできました。 次ずっと飾りだった入力項目の実装をしていこうかなと思います

次の記事

bravememo.hatenablog.com

C# カーブエディタ作成 点の追加機能

前回の記事

bravememo.hatenablog.com

完成図

点の追加ボタンを4回押して2つ目の点を選択した状態です

f:id:Brave345:20191210103108p:plain

点の追加機能

点の追加はAddPoint()で行っています。 点の生成場所は最後の開始点と終了点の間に生成しており、位置は最後の開始点のX+30に位置に生成しています。 さらに点の移動機能も改良しており、いままではY軸の移動だけでしたが、一番最初の開始点以外はX軸の動きを出来るようにしました。 ここでとくに大事になってくるのは追加前の最後の線(開始点と終了点)の扱いについてです。 これをうまくやらないと思ったような線を描画できません。

制御点の移動制限

制御点の位置は線ごとの開始点と終了の間にいないとX軸が同じ線が出てしまうので制御点の位置に制限をかけないといけません OrganizeControlPoint()で生成時の調整MoveControl1Point()で移動時の制限をかけていて単純にClampを使って制限をかけているだけです。

ソースコード

Formクラスまで書くと長くなるので追加した部分だけ掲載します。

//CurvePointControl.cs
 /// <summary>
    /// カーブを表示するための点を制御させるクラス
    /// </summary>
    class CurvePointControl
    {
        //3次ベジェ曲線に必要な点
        struct BezierPoint
        {
            public Point startPoint;     //開始点
            public Point controlPoint1;  //制御点1
            public Point controlPoint2;  //制御点2
            public Point endPoint;       //終了点
        }
        //選択状態
        enum SelectMode
        {
            SelectStart,//開始点選択
            SelectEnd,  //終了点
            None,       //何も選択していない
        }
        List<BezierPoint> m_list = new List<BezierPoint>();//線を引くための点を格納する場所

        //グラフの範囲を示す座標
        const int ScrrenRightPosX = 600;    //右端
        const int ScrrenBottomPosY = 310;   //下端
        const int ScrrenLeftPosX = 0;       //左端
        const int ScrrenTopPosY =  10;      //上端
        const int ScrrenCenterpPosY = 160;  //中央

        int m_SelectPoint = 0;  //左から何番目の点を選択精しているか
        const float m_cpSize = 8; //点のサイズ
        bool m_isMoveStartPoint = false;      //開始点を選択した状態でドラッグできるか
        bool m_isMoveEndPoint = false;       //終了点を選択した状態でドラッグできるか
        bool m_isMoveControl1Point = false;  //制御点1を選択した状態でドラッグできるか
        bool m_isMoveControl2Point = false;  //制御点2を選択した状態でドラッグできるか
        SelectMode m_SelectMode = SelectMode.None;    //点を選択しているか 
        //線を引くためのパス
        GraphicsPath m_path = new GraphicsPath();//曲線を引くためのパス
        GraphicsPath m_path2 = new GraphicsPath();//直線を引くためのパス
        //いろんな色
        Brush m_PointColor = new SolidBrush(Color.FromArgb(255, 255, 0, 0));       //点の色
        Pen m_PointLineColor = new Pen(Color.FromArgb(200, 245, 245, 245),4);     //強調線の色
        Pen m_CPointLineColor = new Pen(Color.FromArgb(125, 245, 245, 245), 4);   //制御点の強調線の色
        Pen m_pen = new Pen(Color.White, 1.5f);                           //曲線の色
        Pen m_pen2 = new Pen(Color.FromArgb(125, 245, 245, 245), 2.5f);   //直線の色
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public  CurvePointControl()
        {
            //初期の曲線設定
            BezierPoint startBezirPoint = new BezierPoint();
            startBezirPoint.startPoint = new Point(ScrrenLeftPosX, ScrrenCenterpPosY);//中央配置
            startBezirPoint.endPoint = new Point(ScrrenRightPosX, ScrrenTopPosY);
            startBezirPoint.controlPoint1 = new Point(ScrrenLeftPosX + 10, ScrrenCenterpPosY + 30);
            startBezirPoint.controlPoint2 = new Point(ScrrenLeftPosX + 10, ScrrenCenterpPosY - 30);
            m_list.Add(startBezirPoint);
        }
      
        /// <summary>
        //3次ベジェ曲線を結ぶすべての点描画
        /// <summary>
        public void PointrPaint(PaintEventArgs e)
        {
            var pontcnt = m_list.Count();
            //開始点すべて描画
            for(int i = 0; i < pontcnt; i++)
            {
                e.Graphics.FillEllipse(m_PointColor, m_list[i].startPoint.X - m_cpSize / 2, m_list[i].startPoint.Y - m_cpSize / 2, m_cpSize, m_cpSize);
            }
            int lastpoint = pontcnt - 1;
            //最後の終了点だけ描画
            e.Graphics.FillEllipse(m_PointColor, m_list[lastpoint].endPoint.X - m_cpSize / 2, m_list[lastpoint].endPoint.Y - m_cpSize / 2, m_cpSize, m_cpSize);

        }
        /// <summary>
        //選択している開始点を強調描画
        /// <summary>
        public void SelectPointrPaint(PaintEventArgs e)
        {
            switch (m_SelectMode)
            {
                case SelectMode.SelectStart:  //開始点を選択しているか
                    e.Graphics.DrawEllipse(m_PointLineColor, m_list[m_SelectPoint].startPoint.X - m_cpSize / 2, m_list[m_SelectPoint].startPoint.Y - m_cpSize / 2, m_cpSize, m_cpSize);
                    e.Graphics.DrawEllipse(m_CPointLineColor, m_list[m_SelectPoint].controlPoint1.X - m_cpSize / 2, m_list[m_SelectPoint].controlPoint1.Y - m_cpSize / 2, m_cpSize, m_cpSize);
                    e.Graphics.DrawEllipse(m_CPointLineColor, m_list[m_SelectPoint].controlPoint2.X - m_cpSize / 2, m_list[m_SelectPoint].controlPoint2.Y - m_cpSize / 2, m_cpSize, m_cpSize);
                    break;
                case SelectMode.SelectEnd://終了点を選択している
                    int LastNum = m_list.Count() - 1;//最後の点
                    e.Graphics.DrawEllipse(m_PointLineColor, m_list[LastNum].endPoint.X - m_cpSize / 2, m_list[LastNum].endPoint.Y - m_cpSize / 2, m_cpSize, m_cpSize);
                    break;
                case SelectMode.None:
                    break;
                default:
                    Debug.Assert(false, "選択状態がおかしいよ!");
                    break;
            }
        }
        /// <summary>
        //選択している点の制御点描画
        /// <summary>
        public void ControlPaint(PaintEventArgs e)
        {
            //開始点を選択してるときのみ制御点の描画をする
            if (m_SelectMode != SelectMode.SelectStart) return;
            e.Graphics.FillEllipse(m_PointColor, m_list[m_SelectPoint].controlPoint1.X - m_cpSize / 2, m_list[m_SelectPoint].controlPoint1.Y - m_cpSize / 2, m_cpSize, m_cpSize);
            e.Graphics.FillEllipse(m_PointColor, m_list[m_SelectPoint].controlPoint2.X - m_cpSize / 2, m_list[m_SelectPoint].controlPoint2.Y - m_cpSize / 2, m_cpSize, m_cpSize);
        }
        /// <summary>
        //3次ベジェ曲線描画
        /// <summary>
        public void BezierPaint(PaintEventArgs e)
        {
            m_path.Reset();
            m_path2.Reset();
            //パス追加
            foreach (BezierPoint item in m_list)
            {
                //曲線パス
                Point[] p = new Point[4];
                p[0] = item.startPoint;
                p[1] = item.controlPoint1;
                p[2] = item.controlPoint2;
                p[3] = item.endPoint;
                m_path.AddBeziers(p);
            }
            //直線パス
            m_path2.AddLine(m_list[m_SelectPoint].startPoint, m_list[m_SelectPoint].controlPoint1);
            m_path2.AddLine(m_list[m_SelectPoint].startPoint, m_list[m_SelectPoint].controlPoint2);
            //描画
            e.Graphics.DrawPath(m_pen, m_path);
            //制御点は開始点を選択しているときだけ描画する
            if (m_SelectMode != SelectMode.SelectStart) return;
            e.Graphics.DrawPath(m_pen2, m_path2);
        }
        /// <summary>
        /// 点の追加(ダブルクリックVer)
        /// </summary>
        /// <param name="p"></param>
        public void AddPoint(Point p)
        {
          
        }
        /// <summary>
        /// 点の追加(ボタンver)
        /// </summary>
        public void AddPoint()
        {
            var LastCnt = m_list.Count() - 1;
            BezierPoint startBezirPoint = new BezierPoint();
            const int ofsetX = 30;
            //
            startBezirPoint.startPoint = new Point(Math.Min( m_list[LastCnt].startPoint.X + ofsetX, ScrrenRightPosX), m_list[LastCnt].startPoint.Y);
            startBezirPoint.endPoint = new Point(m_list[LastCnt].endPoint.X, m_list[LastCnt].endPoint.Y);
            //制御点は追加される開始点の少し右に生成させる
            var cpointX = Clamp(startBezirPoint.startPoint.X + 10, ScrrenLeftPosX, ScrrenRightPosX);
            startBezirPoint.controlPoint1 = new Point(cpointX, m_list[LastCnt].startPoint.Y + 30);
            startBezirPoint.controlPoint2 = new Point(cpointX, m_list[LastCnt].startPoint.Y - 30);
            m_list.Add(startBezirPoint);//新しい点追加

            //追加前の最後の終了点を追加した最後の開始点とつなげる
            BezierPoint BezirPoint = new BezierPoint();
            BezirPoint = m_list[LastCnt];
            BezirPoint.endPoint = m_list[LastCnt + 1].startPoint;//繋ぎなおす
            m_list[LastCnt] = BezirPoint;
        }
  
        /// <summary>
        /// 点の移動
        /// </summary>
        /// <param name="mouse"></param>
        public void MovePoint(MouseEventArgs mouse)
        {
            if (m_isMoveStartPoint)//選択した開始点
            {
                MoveStartPoint(mouse);
            }
            else if (m_isMoveControl1Point)//選択した制御点1
            {
                MoveControl1Point(mouse);
            }
            else if (m_isMoveControl2Point)//選択した制御点2
            {
                MoveControl2Point(mouse);
            }
            else if(m_isMoveEndPoint)//選択した最後の点
            {
                MoveEndPoint(mouse);
             }  
        }
        /// <summary>
        /// 開始点の移動
        /// </summary>
        /// <param name="mouse"></param>
        public void MoveStartPoint(MouseEventArgs mouse)
        {
            BezierPoint sp = m_list[m_SelectPoint]; //選択している点

            sp.startPoint.Y = Clamp(mouse.Y, ScrrenTopPosY, ScrrenBottomPosY);
            // 一番最初の開始点だけY軸にしか動かせないように
            if (!isSelectFirstStartPoint())
            {
                //X軸の移動
                sp.startPoint.X = Clamp(mouse.X, ScrrenLeftPosX, ScrrenRightPosX);
                //ひとつ前の終了点の移動
                var BeforeSelectPoint = m_SelectPoint - 1;
                BezierPoint sp2 = m_list[BeforeSelectPoint];
                sp2.endPoint = sp.startPoint;
                m_list[BeforeSelectPoint] = sp2;
            }

            m_list[m_SelectPoint] = sp;
        }
        /// <summary>
        /// 制御点1の移動
        /// </summary>
        /// <param name="mouse"></param>
        public void MoveControl1Point(MouseEventArgs mouse)
        {
            BezierPoint sp = m_list[m_SelectPoint]; //選択している点
          
            sp.controlPoint1.X = Clamp(mouse.X, ScrrenLeftPosX, ScrrenRightPosX);
            sp.controlPoint1.Y = Clamp(mouse.Y, ScrrenTopPosY, ScrrenBottomPosY);
            //一番最初の開始点以外を選択しているなら
            if (!isSelectFirstStartPoint())
            {
                sp.controlPoint1.X = Clamp(sp.controlPoint1.X, m_list[m_SelectPoint - 1].endPoint.X, ScrrenRightPosX);
            }

            //一番最後の終了点以外を選択しているなら
            if (!(isSelectLastEndPoint()))
            {
                sp.controlPoint1.X = Clamp(sp.controlPoint1.X, ScrrenLeftPosX, m_list[m_SelectPoint + 1].startPoint.X);
            }
            m_list[m_SelectPoint] = sp;
        }
        /// <summary>
        /// 制御点2の移動
        /// </summary>
        /// <param name="mouse"></param>
        public void MoveControl2Point(MouseEventArgs mouse)
        {
            BezierPoint sp = m_list[m_SelectPoint]; //選択している点
            sp.controlPoint2.X = Clamp(mouse.X, ScrrenLeftPosX, ScrrenRightPosX);
            sp.controlPoint2.Y = Clamp(mouse.Y, ScrrenTopPosY, ScrrenBottomPosY);

            //一番最初の開始点以外を選択しているなら
            if (!isSelectFirstStartPoint())
            {
                sp.controlPoint2.X = Clamp(sp.controlPoint2.X, m_list[m_SelectPoint - 1].endPoint.X, ScrrenRightPosX);
            }

            //一番最後の終了点以外を選択しているなら
            if (!isSelectLastEndPoint())
            {
                sp.controlPoint2.X = Clamp(sp.controlPoint2.X, ScrrenLeftPosX, m_list[m_SelectPoint + 1].startPoint.X);
            }
            m_list[m_SelectPoint] = sp;
        }
        /// <summary>
        /// 最後の終了点の移動
        /// </summary>
        /// <param name="mouse"></param>
        public void MoveEndPoint(MouseEventArgs mouse)
        {
            int LastNum = m_list.Count() - 1;
            BezierPoint sp = m_list[LastNum]; //選択している点
            sp.endPoint.Y = Clamp(mouse.Y, ScrrenTopPosY, ScrrenBottomPosY); ;
            m_list[LastNum] = sp;
        }
        /// <summary>
        /// 点を選択してドラック出来る状態か検索する クリックしたときに呼ぶ
        /// </summary>
        ///  /// <param name="mouse"></param>
        public void SearchSelectPoint(MouseEventArgs mouse)
        {
            var pontcnt = m_list.Count();
            //すべての開始点を調べる
            for (int i = 0; i < pontcnt; i++)
            {
                if(isSearchSelectStartPoint(mouse, i))
                {
                    //開始点を選択
                    m_SelectPoint = i;
                    m_isMoveStartPoint = true;

                    m_SelectMode = SelectMode.SelectStart;
                    return;
                }
            }
            //制御点1を選択
            if (isSearchSelectControlPoint(mouse))
            {
                m_isMoveControl1Point = true;
                return;
            }
            //制御点2を選択
            if (isSearchSelectContro2Point(mouse))
            {
                m_isMoveControl2Point = true;
                return;
            }
            //終了点を選択
            if (isSearchSelectEndPoint(mouse))
            {
                m_isMoveEndPoint = true;
                m_SelectMode = SelectMode.SelectEnd;
                return;
            }
            //無選択
            m_isMoveStartPoint = false;
            m_isMoveEndPoint = false;
            m_SelectMode = SelectMode.None;
        }

        /// <summary>
        /// すべての制御点の位置を整理させる
        /// </summary>
        public void OrganizeControlPoint()
        {
            int ListMax = m_list.Count();
            int LastNum = m_list.Count() - 1;
            //制御点がいてはいけない場所なら位置を補正する
            for (int i = 0; i < ListMax; i++)
            {
                BezierPoint sp = m_list[i]; //選択している点
                //左端以外なら
                if (i != 0)
                {
                    sp.controlPoint1.X = Clamp(sp.controlPoint1.X, m_list[i - 1].endPoint.X, ScrrenRightPosX);
                    sp.controlPoint2.X = Clamp(sp.controlPoint2.X, m_list[i - 1].endPoint.X, ScrrenRightPosX);
                }
                //右端以外なら
                if (i != LastNum)
                {
                    sp.controlPoint1.X = Clamp(sp.controlPoint1.X, ScrrenLeftPosX, m_list[i + 1].startPoint.X);
                    sp.controlPoint2.X = Clamp(sp.controlPoint2.X, ScrrenLeftPosX, m_list[i + 1].startPoint.X);
                }
                m_list[i] = sp;
            }
        }
        /// <summary>
        /// 移動可能選択状態を解除 クリックを終えたら呼ぶ
        /// </summary>
        public void CancelMovePoint()
        {
            m_isMoveStartPoint = false;
            m_isMoveEndPoint = false;
            m_isMoveControl1Point = false;
            m_isMoveControl2Point = false;
        }
       /// <summary>
       /// 開始点がマウスカーソルの下にあるか検索
       /// </summary>
       /// <param name="mouse"></param>
       /// <returns></returns>
        public bool isSearchSelectStartPoint(MouseEventArgs mouse,int num)
        {
            float SelectPointSize = m_cpSize + 20;//判定は少し大きめに
            if (mouse.X >= m_list[num].startPoint.X - SelectPointSize / 2 && mouse.X < m_list[num].startPoint.X + SelectPointSize / 2)
            {
                if (mouse.Y >= m_list[num].startPoint.Y - SelectPointSize / 2 && mouse.Y < m_list[num].startPoint.Y + SelectPointSize / 2)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 制御点1がマウスカーソルの下にあるか検索
        /// </summary>
        /// <param name="mouse"></param>
        /// <returns></returns>
        public bool isSearchSelectControlPoint(MouseEventArgs mouse)
        {
            if (m_SelectMode != SelectMode.SelectStart) return false;
            float SelectPointSize = m_cpSize + 20;//判定は少し大きめに
            if (mouse.X >= m_list[m_SelectPoint].controlPoint1.X - SelectPointSize / 2 && mouse.X < m_list[m_SelectPoint].controlPoint1.X + SelectPointSize / 2)
            {
                if (mouse.Y >= m_list[m_SelectPoint].controlPoint1.Y - SelectPointSize / 2 && mouse.Y < m_list[m_SelectPoint].controlPoint1.Y + SelectPointSize / 2)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 制御点2がマウスカーソルの下にあるか検索
        /// </summary>
        /// <param name="mouse"></param>
        /// <param name="num"></param>
        /// <returns></returns>
        public bool isSearchSelectContro2Point(MouseEventArgs mouse)
        {
            if (m_SelectMode != SelectMode.SelectStart) return false;
            float SelectPointSize = m_cpSize + 25;//判定は少し大きめに
            if (mouse.X >= m_list[m_SelectPoint].controlPoint2.X - SelectPointSize / 2 && mouse.X < m_list[m_SelectPoint].controlPoint2.X + SelectPointSize / 2)
            {
                if (mouse.Y >= m_list[m_SelectPoint].controlPoint2.Y - SelectPointSize / 2 && mouse.Y < m_list[m_SelectPoint].controlPoint2.Y + SelectPointSize / 2)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 開始点がマウスカーソルの下にあるか検索
        /// </summary>
        /// <param name="mouse"></param>
        /// <returns></returns>
        public bool isSearchSelectEndPoint(MouseEventArgs mouse)
        {
            float SelectPointSize = m_cpSize + 25;//判定は少し大きめに
            int LastNum = m_list.Count() - 1;//最後の点
            if (mouse.X >= m_list[LastNum].endPoint.X - SelectPointSize / 2 && mouse.X < m_list[LastNum].endPoint.X + SelectPointSize / 2)
            {
                if (mouse.Y >= m_list[LastNum].endPoint.Y - SelectPointSize / 2 && mouse.Y < m_list[LastNum].endPoint.Y + SelectPointSize / 2)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 最初の開始点を選択しているか
        /// </summary>
        /// <returns></returns>
        bool isSelectFirstStartPoint()
        {
            return m_SelectPoint == 0;
        }
        /// <summary>
        /// 最後の終了点を選択しているか
        /// </summary>
        /// <returns></returns>
        bool isSelectLastEndPoint()
        {
            var LastCnt = m_list.Count() - 1;
            return m_SelectPoint == LastCnt;
        }
        /// <summary>
        /// /値を特定の範囲に宣言する
        /// </summary>
        /// <param name="x"></param>
        /// <param name="minVal"></param>
        /// <param name="maxVal"></param>
        /// <returns></returns>
        public int  Clamp(int x, int minVal, int maxVal)
        {
            return Math.Min(Math.Max(minVal, x), maxVal);
        }
    }

終わりに

次は点の削除機能の実装をしたい。 あとは開始点の移動範囲をどうするかきめないといけない。開始点が終了点より右にいってしまうときに点を入れ替えるか、そもそも超えないようにするか。まあとりあえずは開始点の動きに制限書けるほうが楽そう。

次の記事

bravememo.hatenablog.com