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