1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【試作】グラフコンポーネント.DLL の覚書き

Last updated at Posted at 2025-03-26

標準のグラフコンポ―ネントはショボいし、市販のグラフコンポ―ネントもこちらの要求を満たしていなかったのでグラフコンポーネントを自作した。
その時の覚え書き↓
グラフ.png

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


using System.Drawing.Drawing2D; //PixelOffsetModeにAntiAliasにする為。

namespace WindowsFormsControlLibrary_GraphAAA
{
    public partial class GraphAAA: UserControl
    {

        //[Property]===============================================================================

        //グラフ用基本設定値-----------------------------------------------------------------------
        //タイトル
        string title;
        [Description("[GraphAAA追加]グラフのタイトル(題名)"), Category("Appearance")]
        public string _Title
        {
            get { return title; }
            set { title = value; }
        }


        //Marginの設定
        int marginTop;
        [Description("[GraphAAA追加]グラフエリアの上端マージンの設定値"), Category("Appearance")]
        public int _MarginTop
        {
            get { return marginTop; }
            set { marginTop = value; }
        }

        int marginBottom;
        [Description("[GraphAAA追加]グラフエリアの下端マージンの設定値"), Category("Appearance")]
        public int _MarginBottom
        {
            get { return marginBottom; }
            set { marginBottom = value; }
        }

        int marginRight;
        [Description("[GraphAAA追加]グラフエリアの右端マージンの設定値"), Category("Appearance")]
        public int _MarginRight
        {
            get { return marginRight; }
            set { marginRight = value; }
        }

        int marginLeft;
        [Description("[GraphAAA追加]グラフエリアの左端マージンの設定値"), Category("Appearance")]
        public int _MarginLeft
        {
            get { return marginLeft; }
            set { marginLeft = value; }
        }


        //Y軸関連
        int yValueMax;
        [Description("[GraphAAA追加]Y軸の最大値 例) 100000"), Category("Appearance")]
        public int _YValueMax
        {
            get { return yValueMax; }
            set { yValueMax = value; }
        }

        int yValueMin;
        [Description("[GraphAAA追加]Y軸の最小値 例) 0"), Category("Appearance")]
        public int _YValueMin
        {
            get { return yValueMin; }
            set { yValueMin = value; }
        }

        int yDivideNum;
        [Description("[GraphAAA追加]Y軸の補助線用分割数 例) 10"), Category("Appearance")]
        public int _YDivideNum
        {
            get { return yDivideNum; }
            set { yDivideNum = value; }
        }


        //X軸関連 
        DateTime xStartDate;  //【注意】クラス内ではDateTime!
        [Description("[GraphAAA追加]X軸の最小値 例) 2022/04/01"), Category("Appearance")]
        public string _XStartDate
        {
            get { return xStartDate.ToString("yyyy/MM/dd"); }
            set { xStartDate = DateTime.Parse(value); }
        }

        DateTime xEndDate;  //【注意】クラス内ではDateTime!
        [Description("[GraphAAA追加]X軸の最大値 例) 2023/04/01"), Category("Appearance")]
        public string _XEndDate
        {
            get { return xEndDate.ToString("yyyy/MM/dd"); }
            set { xEndDate = DateTime.Parse(value); }
        }

        Boolean xScaleAll;     //グラフのサイズが小さい場合、x軸のラベルを一つ置きに表示する。
                                // true ⇒ すべて表示する。(デフォルト)
                                // false ⇒ 一つ飛ばしに表示
        [Description("[グラフの表示サイズが小さい場合、x軸のラベルを一つ飛ばしで表示する。"), Category("Appearance")]
        public Boolean _xScaleAll
        {
            get { return xScaleAll; }
            set { xScaleAll = value; }
        }



        //グラフ線描画用データ---------------------------------------------------------------------
        //売上実績(出荷実績) 
        //【注意】2次元配列で受け取る
        //  1次元:1,2,3,…
        // 2次元:年月日, 数量, ツールチップコメント
        string[,] salseQty;
        public string[,] _SalseQty
        {
            get { return salseQty; }
            set { salseQty = value; }
        }


        //月末在庫 (上に同じ)
        string[,] monthEndStock;
        public string[,] _MonthEndStock
        {
            get { return monthEndStock; }
            set { monthEndStock = value; }
        }


        //入荷実績(生産実績) (上に同じ)
        string[,] prodQty;
        public string[,] _ProdQty
        {
            get { return prodQty; }
            set { prodQty = value; }
        }

        //期初生産計画生産計画 (上に同じ)
        string[,] initialProdPlan;
        public string[,] _InitialProdPlan
        {
            get { return initialProdPlan; }
            set { initialProdPlan = value; }
        }

        //修正生産計画 (上に同じ)
        string[,] prodPlan;
        public string[,] _ProdPlan
        {
            get { return prodPlan; }
            set { prodPlan = value; }
        }

        //修正生産計画のライン終点
        //2年分を表示するグラフに対し、12ヶ月分のグラフ線を表示する為、
        //修正生産計画線の終点を明示する必要があった。
        DateTime prodPlan_EndDate;
        public DateTime _ProdPlan_EndDate
        {
            get { return prodPlan_EndDate; }
            set {prodPlan_EndDate = value; }
        }


        //期初売上計画
        string[,] initialSalsePlan;
        public string[,] _InitialSalsePlan
        {
            get { return initialSalsePlan; }
            set { initialSalsePlan = value; }
        }

        //修正売上計画(通称:ローリング)
        string[,] salsePlan;
        public string[,] _SalsePlan
        {
            get { return salsePlan; }
            set { salsePlan = value; }
        }


        //期限切れ廃棄予測(英語:expired disposal)
        string[,] expiredDisposal;
        public string[,] _ExpiredDisposal
        {
            get { return expiredDisposal; }
            set { expiredDisposal = value; }
        }



        //単発数量関連-----------------------------------------------------------------------------
        int annualPrdPlan;
        [Description("[GraphAAA追加]年間生産計画数 例) 250000"), Category("Appearance")]
        public int _AnnualPrdPlan
        {
            get { return annualPrdPlan; }
            set { annualPrdPlan = value; }
        }

        int initalQty;
        [Description("[GraphAAA追加]期初在庫 例) 6000"), Category("Appearance")]
        public int _InitalQty
        {
            get { return initalQty; }
            set { initalQty = value; }
        }


        
        //在庫月数関連-----------------------------------------------------------------------------
        //販売数の直近3ヶ月平均
        int salseQty_Ave3M;
        public int _SalseQty_Ave3M
        {
            get { return salseQty_Ave3M; }
            set { salseQty_Ave3M = value; }
        }

        //安全在庫月数(在庫月数ライン(横線)を描画する為の値) 
        Double seftyInventoryMonths;
        public Double _SeftyInventoryMonths
        {
            get { return seftyInventoryMonths; }
            set { seftyInventoryMonths = value; }
        }



        //グラフ描画後の結果出力-------------------------------------------------------------------
        //売上実績の回帰直線(regressionLine)の傾きと切片(緑直線)
        Double salesLine_A;
        public Double _SalesLine_A   //傾き
        {
            get { return salesLine_A; }
        }

        Double salesLine_B;
        public Double _SalesLine_B   //切片
        {
            get { return salesLine_B; }
        }

        //修正生産計画の直線の傾きと切片(赤直線)
        Double prodPlanLine_A;
        public Double _ProdPlanLine_A   //傾き
        {
            get { return prodPlanLine_A; }
        }

        Double prodPlanLine_B;
        public Double _ProdPlanLine_B   //切片
        {
            get { return prodPlanLine_B; }
        }


        //グラフ描画後の最終累積数(12ヶ月後の推定値)
        //売上計画の最終累積数(ピンク線の最後の値)
        int salesPlan_FinalQty;
        public int _SalesPlan_FinalQty  
        {
            get { return salesPlan_FinalQty; }
        }

        //生産計画の最終累積数(赤階段の最後の値)
        int prodPlan_FinalQty;
        public int _ProdPlan_FinalQty   
        {
            get { return prodPlan_FinalQty; }
        }

        //生産計画の廃棄有り最終累積数(廃棄有り赤階段の最後の値)
        int prodPlan_FinalQtyWithDisposal;
        public int _ProdPlan_FinalQtyWithDisposal
        {
            get { return prodPlan_FinalQtyWithDisposal; }
        }



        //開発用-----------------------------------------------------------------------------------
        //デバッグボタンの状態
        Boolean testButton_VisibleStatus;
        public Boolean _TestButton_VisibleStatus
        {
            get { return testButton_VisibleStatus; }
            set { }
        }



        //[Method]==================================================================================
        //描画指示
        public string _DrawGraph()
        {
            return 描画();
        }

        //デバッグボタン表示状態指定
        public void _TestButton_Visible(Boolean cmd)
        {
            //デバッグ用に表示できる様にしておく
            if (cmd == true)
            {
                buttonデバッグ.Visible = true;
                testButton_VisibleStatus = true;
            }
            else
            {
                buttonデバッグ.Visible = false;
                testButton_VisibleStatus = false;
            }
        }



        //[注意]バグ対策プロパティ&メソッド=======================================================

        /*-------------------------------------------------------------------------------
         * 当ユーザーコントロールをフォーム上に配置した際は正常動作するが、
         * タブコントロール上に配置すると、
         * タブコントロールが、当ユーザーコントロールのpictureBoxのLocationとSizeを
         * 勝手に変更してしまう。(MSのバグ?)
         * 
         * その対策として、外部から上記pictureBoxのLocationとSizeを
         * 閲覧、変更できる様にした。
         ------------------------------------------------------------------------------*/

        //Location.Xを戻す
        [Description("pictureBox.Location.Xを戻す"), Category("Appearance")]
        public int _LocX
        {
            get { return pictureBox.Location.Y; }
        }

        //Location.Yを戻す
        [Description("pictureBox.Location.Yを戻す"), Category("Appearance")]
        public int _LocY
        {
            get { return pictureBox.Location.Y; }
        }

        //Size.Widthを戻す
        [Description("pictureBox.Size.Widthを戻す"), Category("Appearance")]
        public int _SizeW
        {
            get { return pictureBox.Size.Width; }
        }

        //Size.Heightを戻す
        [Description("pictureBox.Size.Heightを戻す"), Category("Appearance")]
        public int _SizeH
        {
            get { return pictureBox.Size.Height; }
        }

        //pictureBoxのロケーションとサイズを変更
        public void _ChangePicBoxPosition(int locX, int locY, int W, int H)
        {
            //pictureBoxのロケーションとサイズを変更する。
            Point loc = new Point(locX, locY);
            pictureBox.Location = loc;

            Size s = new Size(W, H);
            pictureBox.Size = s;
        }




        //privateフィールド==============================================================================

        Point p原点 = new Point();    //グラフの原点座標 (Point()=構造体)
        Point p最遠点 = new Point();  //   

        Point pXmax = new Point();      //X軸の最大座標
        Point pYmax = new Point();      //Y軸の最大座標




        private struct ToolTipErea  //ツールチップ風を表示するエリアとメッセージ
        {
            public int x1;      //publicにしないと同じクラスからアクセスできない?
            public int x2;
            public int y1;
            public int y2;
            public string text; //表示する文字列

            public ToolTipErea(int v1, int v2, int v3, int v4, string str1)  //コンストラクタ
            {
                this.x1 = v1;
                this.x2 = v2;
                this.y1 = v3;
                this.y2 = v4;
                this.text = str1;
            }
        }

        private List<ToolTipErea> toolTipErea = new List<ToolTipErea>();



        //初期処理==================================================================================

        public GraphAAA()
        {
            InitializeComponent();
        }


        private void GraphAAA_Load(object sender, EventArgs e)
        {
            
        }



        private void buttonデバッグ_Click(object sender, EventArgs e)
        {
            /*---------------------------------------------------------- 
             *  
             *  デバッグ用にパラメータを設定する。
             * 
             -----------------------------------------------------------*/

            _Title = "1234567 / 健康AAA [単位: 袋]";

            //マージン関連
            _MarginTop = 40;
            _MarginBottom = 40;
            _MarginRight = 30;
            _MarginLeft = 80;

            //期初在庫・年間生産計画
            _InitalQty = 370368;
            _AnnualPrdPlan = 1570368;
            //_AnnualPrdPlan = 0; //<----------------------!!!nullテスト!

            //Y軸関連
            //最大目盛の決定
            // [ロジック] 一旦10^dの逆数で数を小数化してMath.Ceilingで切り上げたのち、10^dの倍数で元に戻す
            int 桁数 = (_InitalQty + _AnnualPrdPlan).ToString().Length;
            int 切り上げ桁 = 桁数 - 1;
            Decimal tmp = (Decimal)(_InitalQty + _AnnualPrdPlan);

            Decimal s = (Decimal)Math.Pow(10.0, 切り上げ桁);
            _YValueMax = (int)(Math.Ceiling(tmp / s) * s);   //最大値決定!

            _YValueMax = 5000000;  //手動で上書き

            _YValueMin = 0;
            _YDivideNum = 10;
        

            //X軸関連
            _XStartDate = "2022/04/01";
            _XEndDate = "2024/04/01";

            //_xScaleAll = true; //X軸の目盛りを全部表示する。
            _xScaleAll = false; //X軸の目盛りを全部表示しない。→ 一個飛ばし。

            //在庫月数関連
            //出荷実績の直近3ヶ月平均
            _SalseQty_Ave3M = 190000;
            //安全在庫月数
            _SeftyInventoryMonths = 1.5;


            //売上実績(出荷実績)
            string[,] Qty1 =
            {
                { "2022/04/01", "0" , "コメント0" },    //原点スタートの場合は、明示的に指定
                { "2022/04/30", "123456", "コメント1"  },
                { "2022/05/31", "125456", "コメント2"  },

                { "2022/06/30", "124456", "コメント2"  },
                { "2022/07/31", "126456", "コメント2"  },
                { "2022/08/31", "124456", "コメント2"  },
                { "2022/09/30", "128456", "コメント2"  },
                { "2022/10/31", "122456", "コメント2"  },
                { "2022/11/30", "125456", "コメント2"  },
                { "2022/12/31", "126456", "コメント2"  },

                { "2023/01/31", "127456", "コメント9"  },
                { "2023/02/28", "123456", "コメント9"  },
                { "2023/03/08", "128456", "コメント当月"  }    //当月の当日分
            };


            _SalseQty = Qty1; //セット!


            //入荷実績 例) { "2022/04/13", "2500", "K1234"  },
            string[,] Qty2 =
            {
                { "2022/04/10","370000","A1234" },
                { "2022/04/15","2000","K1237" },
                { "2022/07/01","400000","K1240" },
                { "2022/10/03","410000","Q1241" },
                { "2022/10/03","2000","J1245" },
                { "2023/01/24","390000","K2111" },
                { "2023/01/25","5000","P2111" }
            };

            _ProdQty = Qty2; //セット!


            //年度生産計画(注意:当年度のみ)
            string[,] Qty3 =
            {
                { "2022/04/15", "400000", ""  },
                { "2022/07/15", "400000", ""  },
                { "2022/10/15", "400000", ""  },
                { "2023/02/15", "400000", ""  },
            };


            _InitialProdPlan = Qty3; //セット!
            //_InitialProdPlan = null; //<----------------------!!!nullテスト!

            //修正生産計画
            string[,] Qty4 =
            {
                { "2023/03/15", "420000", "TypeA"  },
                { "2023/07/15", "490000", "TypeA"  },
                { "2024/02/15", "420000", "TypeA"  }
            };

            _ProdPlan = Qty4; //セット!


            //3月1日トラブル調査**************************
            //_ProdPlan = null;
            //string[,] Qty4a =
            //{
            //    { "2024/03/31", "0", "計画完了or未設定"  }
            //};
            //_ProdPlan = Qty4a;

            //修正生産計画ライン描画の終点をセットする。
            _ProdPlan_EndDate = DateTime.Parse("2024/02/28");


            //月末在庫
            string[,] Qty5 =
            {
                { "2022/04/30", "200000", "4月末"  },
                { "2022/05/31", "200000", "5月末"  },
                { "2022/06/30", "200000", "6月末"  },
                { "2022/07/31", "200000", "7月末"  },
                { "2022/08/31", "200000", "8月末"  },
                { "2022/09/30", "200000", "9月末"  },
                { "2022/10/31", "200000", "10月末"  },
                { "2022/11/30", "200000", "11月末"  },

                { "2022/12/31", "200000", "12月末"  },
                { "2023/01/31", "200000", "1月末"  },
                { "2023/02/28", "200000", "2月末"  },
                { "2023/03/10", "200000", "本日在庫"  }  //本日の在庫
            };

            _MonthEndStock = Qty5; //セット!


            //修正売上計画
            string[,] Qty6 =
            {
                { "2023/03/31", "120000", ""  },
                { "2023/04/30", "120000", ""  },
                { "2023/05/31", "120000", ""  },
                { "2023/06/30", "120000", ""  },

                { "2023/07/31", "120000", ""  },
                { "2023/08/31", "200000", ""  },
                { "2023/09/30", "253000", ""  },
                { "2023/10/31", "220000", ""  },
                { "2023/11/30", "290000", ""  },
                { "2023/12/31", "300000", ""  },
                { "2024/01/31", "310000", ""  },
                { "2024/02/29", "290000", ""  }

            };

            _SalsePlan = Qty6; //セット!


            //期初売上計画
            string[,] Qty7 =
            {
                { "2022/04/01", "0" , "" },    //原点スタートの場合は、明示的に指定
                { "2022/04/30", "120000", ""  },
                { "2022/05/31", "120000", ""  },
                { "2022/06/30", "120000", ""  },
                { "2022/07/31", "120000", ""  },
                { "2022/08/31", "120000", ""  },
                { "2022/09/30", "120000", ""  },
                { "2022/10/31", "120000", ""  },
                { "2022/11/30", "120000", ""  },
                { "2022/12/31", "120000", ""  },
                { "2023/01/31", "120000", ""  },
                { "2023/02/28", "120000", ""  },
                { "2023/03/31", "120000", ""  }   
            };

            _InitialSalsePlan = Qty7; //セット!


            //期限切れ廃棄予測(注:日付は各月20日固定)
            string[,] Qty8 =
            {
                { "2023/08/20", "20000", "1000"  },
                { "2024/01/20", "30000", "250"  }
            };

            _ExpiredDisposal = Qty8; //セット!

            描画();

        }



        private string 描画()
        {
            try
            {
      
                /*-------------------------------------------------------------------------------------
                 *  【重要】座標の指定はすべて仮想座標を指定し、描画直前に実座標に変換する。
                -------------------------------------------------------------------------------------*/

                //エラーチェック ⇒ pictureBox.Width, pictureBox.Height
                if (pictureBox.Width <= 0 || pictureBox.Height <= 0)
                {
                    throw new Exception("ピクチャボックスサイズ異常");
                }


                //初期設定*********************************************************************************************

                //変数のリセット
                toolTipErea.Clear();       //ツールチップ(List)の情報が堆積する問題が発生! クリアするのを忘れてた!

                salesLine_A = 0;
                salesLine_B = 0;
                prodPlanLine_A = 0;
                prodPlanLine_B = 0;

                salesPlan_FinalQty = 0;
                prodPlan_FinalQty = 0;
                prodPlan_FinalQtyWithDisposal = 0;

                //グラフのXY座標の描画エリア(目盛表示を除く部分)を確保する。
                //値はPix数で管理
                p原点 = new Point(marginLeft, marginBottom);     //グラフのXY座標原点!
                pYmax = new Point(marginLeft, pictureBox.Height - marginTop);
                p最遠点 = new Point(pictureBox.Width - marginRight, pictureBox.Height - marginTop);
                pXmax = new Point(pictureBox.Width - marginRight, marginBottom);

                //描画先とするImageオブジェクトを作成する
                Bitmap canvas = new Bitmap(pictureBox.Width, pictureBox.Height);
                //ImageオブジェクトのGraphicsオブジェクトを作成する
                Graphics g = Graphics.FromImage(canvas);

                //PixelOffsetModeにAntiAliasを指定する(画像のギザギザ感の緩和の為)
                g.SmoothingMode = SmoothingMode.AntiAlias;


                //Penオブジェクトの作成
                Pen penBlack = new Pen(Color.Black, 1);     //(幅1の黒色)
                Pen penRed = new Pen(Color.Red, 1);
                Pen penPink = new Pen(Color.Pink, 1);
                Pen penBlue = new Pen(Color.Blue, 1);
                Pen penDarkOrange = new Pen(Color.DarkOrange, 1);
                Pen penLemonChiffon = new Pen(Color.LemonChiffon, 1);
                Pen penGray = new Pen(Color.LightGray, 1);
                Pen penDarkGreen = new Pen(Color.DarkGreen, 1);

                Pen penBlackDash = new Pen(Color.Black, 1);
                penBlackDash.DashPattern = new float[] { 3.0F, 3.0F }; //ダッシュ

                Pen penPinkDash = new Pen(Color.HotPink, 1);
                penPinkDash.DashPattern = new float[] { 3.0F, 3.0F }; //ダッシュ

                Pen penBlackDashDot = new Pen(Color.Black, 1);
                //penBlackDashDot.DashStyle = DashStyle.DashDot;
                penBlackDashDot.DashPattern = new float[] { 3.0F, 3.0F, 10.0F, 3.0F };

                Pen pen出荷実績 = new Pen(Color.DarkGreen, 2);
                Pen pen売上計画 = new Pen(Color.Magenta, 3);

                Pen pen初期在庫 = new Pen(Color.DarkOrange, 2);
                Pen pen製造実績 = new Pen(Color.Red, 2);

                Pen pen初期生産予定 = new Pen(Color.Black, 1);
                //ダッシュ
                //pen初期入荷予定.DashStyle = DashStyle.Dash; //ダッシュの感覚が狭すぎて直線に見えるのでボツ
                //次の破線の長さは線の太さの3倍で、間隔を3倍にする。 ht tps://dobon.net/vb/dotnet/graphics/drawdash.html
                pen初期生産予定.DashPattern = new float[] { 3.0F, 3.0F };

                Pen pen生産予定 = new Pen(Color.Red, 3);

                Pen pen廃棄有り生産予定 = new Pen(Color.Blue , 1);
                //pen廃棄有り生産予定.DashPattern = new float[] { 3.0F, 3.0F };

                Pen pen補助線 = new Pen(Color.Silver, 1);
                Pen pen補助線濃 = new Pen(Color.DimGray, 1);

                //フォントオブジェクトの作成
                Font fntBig = new Font("MS UI Gothic", 12, FontStyle.Bold);
                Font fnt = new Font("MS UI Gothic", 10);
                Font fntSmall = new Font("MS UI Gothic", 9);

                //日付関連の変数定義
                Size s = new Size();
                TimeSpan ts;
                int 日差 = 0;         //期初日~の日差
                int today日差 = 0;    //期初日~当日までの日差
                int width日差 = 0;    //四角形を表示する為の日差

                //先月末年月日
                DateTime 当月1 = DateTime.Parse(DateTime.Now.ToString("yyyy/MM/01"));
                DateTime 先月末年月日 = 当月1.AddDays(-1);

                //XY軸関連の値仮置き用変数定義
                int x = 0;
                int y = 0;
                int w = 0;  //Width
                int h = 0;  //Height

                string y目盛 = "";
                int y軸長 = yValueMax - yValueMin;
                int yPix = pYmax.Y - p原点.Y;

                ts = xEndDate - xStartDate;
                int x軸長 = ts.Days;         //例) 1年間なら365
                int xPix = pXmax.X - p原点.X;

                //Console.WriteLine("x軸長   : " + x軸長.ToString());


                //Console.WriteLine("「補助情報を描画する」を実行します。");
                //補助情報を描画する***********************************************************************************
                // 【注意】レイヤーの下になる画像から順番に書く

                //位置(10, 10)に20x20の長方形を描く(サンプル)
                //g.DrawRectangle(penBlack, 10, 10, 20, 20);

                //描画範囲を四角で囲む---------------------------------------------
                // デバッグ用
                Point[] ps = {
                 p原点,
                 pYmax,
                 p最遠点,
                 pXmax,
                 p原点
                };
                g.DrawLines(penGray, Line_実座標変換(ps));

                //タイトルを書込む-------------------------------------------------
                x = p原点.X;
                y = pYmax.Y + 30;
                g.DrawString(title, fntBig, Brushes.Black, x, Y座標_実座標変換(y));


                //Console.WriteLine("「入荷実績を描画する」を実行します。");
                //入荷実績(生産実績)のを描画する*********************************************************************************
                //【注意1】入荷系は面で表示するので、一番最初に書込む

                //【重要】累積入荷数の先月末までの最終値をここで確保し、(修正)入荷計画の始点として利用する。
                //        先月末年月日確保する。

                int 先月末累積入荷数 = 0;


                //期初在庫(塗りつぶし四角で表示する。)-----------------------------
                ts = DateTime.Now - xStartDate;
                日差 = ts.Days;
                x = (int)((Double)xPix * (Double)日差 / (Double)x軸長);

                int y期初在庫 = (int)(((Double)pYmax.Y - (Double)p原点.Y) * (Double)initalQty / ((Double)yValueMax - (Double)yValueMin));
                int y期初在庫左上座標 = p原点.Y + y期初在庫;  //座標に変換する

                g.FillRectangle(Brushes.LemonChiffon, p原点.X, Y座標_実座標変換(y期初在庫左上座標), x, y期初在庫);



                //入荷実績(生産実績)を描画する---------------------------------------------
                if (prodQty == null)
                {
                    //入荷実績(生産実績)がセットされていないのでスキップ
                }
                else
                {
                    //入荷実績(生産実績)を塗りつぶし四角で表示する。

                    int 単体製造 = 0;
                    int 累積製造 = 0;
                    DateTime 年月日;
                    Point[] ps累積製造 = new Point[0];  //空配列を作って、後で要素を追加する。

                    string コメント = "";   //ツールチップ用コメント

                    //Console.WriteLine("prodQty.GetLength(0): " + prodQty.GetLength(0).ToString());

                    for (int d1 = 0; d1 < prodQty.GetLength(0); d1++)   //0は1次元のこと
                    {
                        年月日 = DateTime.Parse(prodQty[d1, 0]);   //データの年月日の確保
                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;           //期初日~データの年月日までの日数

                        単体製造 = int.Parse(prodQty[d1, 1]);  //単独データの製造数
                        累積製造 += 単体製造;                   //製造数を累積していく

                        //先月末日以前の入荷を累積する ⇒入荷計画の始点として利用する。
                        if (年月日 <= 先月末年月日)
                        {
                            先月末累積入荷数 += 単体製造;
                        }

                        コメント = "";
                        コメント = prodQty[d1, 2];

                        //四角形を表示するLocation(左上の座標)決定
                        // [注意]yは初期在庫に累積在庫を足していく
                        x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)累積製造) / (Double)y軸長);


                        //四角形の高さ・幅を決定。幅はTodayのラインまで引く
                        // wは、Todayとデータ年月日との差
                        //  hは、initalQtyからの差 (←四角の間に隙間を生まない様にするため)
                        ts = DateTime.Now - DateTime.Parse(prodQty[d1, 0]);
                        width日差 = ts.Days;           //期初日~データの年月日までの日数

                        w = (int)((Double)xPix * (Double)width日差 / (Double)x軸長);
                        h = (int)((Double)yPix * (Double)累積製造 / (Double)y軸長);


                        //四角描画
                        g.FillRectangle(Brushes.Gold, x, Y座標_実座標変換(y), w, h);

                        Array.Resize(ref ps累積製造, d1 + 1);  //配列リサイズ
                        ps累積製造[d1] = new Point(x, y);


                        //ドット描画(□4ピクセル)
                        int dotx = x - 2;
                        int doty = y + 2;
                        g.FillEllipse(Brushes.Red, dotx, Y座標_実座標変換(doty), 4, 4);


                        //ツールチップの情報(ポップアップエリア&表示内容)を保存する。
                        string 表示内容 = "入荷実績\n" +
                                            コメント + "\n" +
                                            " 製造数: " + 桁区切り挿入(単体製造.ToString()) + "\n" +
                                            " X座標 : " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                                            " Y座標 : " + 桁区切り挿入((initalQty + 累積製造).ToString());

                        toolTipErea.Add(new ToolTipErea(dotx, dotx + 4, Y座標_実座標変換(doty), Y座標_実座標変換(doty) + 4, 表示内容));

                    }
                }


                //Console.WriteLine("月末在庫の棒グラフをを描画する」を実行します。");
                //月末在庫の棒グラフをを描画する***********************************************************************
                /*
                 * 【月末在庫の棒グラフ表示について】
                 *       x軸:棒の幅:月末日-2day ~ 月末日+1day (3日分) 
                 *     ToolTipの表示位置:月末日を中心
                 *      とする。
                */

                if (monthEndStock == null)
                {
                    //月末在庫がセットされていないのでスキップ
                }
                else
                {
                    for (int d1 = 0; d1 < monthEndStock.GetLength(0); d1++)//1次元の操作
                    {
                        DateTime 年月日 = DateTime.Parse(monthEndStock[d1, 0]);   //データの年月日の確保

                        年月日 = 年月日.AddDays(-2);  //棒の右上の座標を指定する為-2日する。

                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;           //期初日~データの年月日までの日数

                        int 月末在庫 = int.Parse(monthEndStock[d1, 1]);  //月末在庫の確保

                        //四角形(棒)を表示するLocation(左上の座標)決定
                        x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)yPix * (Double)月末在庫 / (Double)y軸長);

                        //四角形(棒)の高さ・幅を決定。
                        // wは、2日分
                        //  hは、initalQtyからの差 (←四角の間に隙間を生まない様にするため)

                        width日差 = 3;           //月末日-1day ~ 月末日+1dayなので2日分

                        w = (int)((Double)xPix * (Double)width日差 / (Double)x軸長);
                        h = (int)((Double)yPix * (Double)月末在庫 / (Double)y軸長);

                        //四角(棒)描画
                        if (d1 == monthEndStock.GetLength(0) - 1)
                        {
                            //最後の棒のみ明るい緑色にする。
                            g.FillRectangle(Brushes.Lime, x, Y座標_実座標変換(y), w, h);
                        }
                        else
                        {
                            g.FillRectangle(Brushes.ForestGreen, x, Y座標_実座標変換(y), w, h);
                        }

                        string コメント = "";
                        コメント = monthEndStock[d1, 2];


                        //ツールチップの設定
                        //ポップアップエリアの左上座標
                        int x1 = x;
                        int y1 = y + 2;
                        //ポップアップエリアの右下座標
                        int x2 = x + 3;
                        int y2 = y - 2;

                        //ツールチップの情報(ポップアップエリア&表示内容)を保存する。
                        string 表示内容 = "在庫\n" +
                                            コメント + "\n" +
                                            " 在庫数: " + 桁区切り挿入(月末在庫.ToString());

                        //座標は絶対座標指定
                        toolTipErea.Add(new ToolTipErea(x1, x2 + 4, Y座標_実座標変換(y1), Y座標_実座標変換(y2), 表示内容));
                    }
                }


                //Console.WriteLine("「Y軸軸目盛を描画する」を実行します。");
                //Y軸目盛を描画する**********************************************************************************
                for (int i = 0; i <= yDivideNum; i++)
                {
                    //Y軸目盛
                    y目盛 = 桁区切り挿入((y軸長 / yDivideNum * i).ToString()); //軸目盛として書込む文字

                    //Console.WriteLine("目盛: " + 目盛);

                    s = 文字幅高サイズチェック(y目盛, fnt);

                    //文字サイズから目盛の表示位置計算する
                    x = (int)((Double)p原点.X - 8 - (Double)s.Width);
                    y = (int)((Double)p原点.Y + (Double)s.Height / 2 + (Double)yPix / (Double)yDivideNum * (Double)i);

                    //目盛書込み 
                    g.DrawString(y目盛, fnt, Brushes.Black, x, Y座標_実座標変換(y));

                    //Y軸の補助線の書込み
                    int y補助線 = (int)((Double)p原点.Y + (Double)yPix / (Double)yDivideNum * (Double)i);

                    Point[] ps補助線 = {
                 new Point(p原点.X, y補助線),
                 new Point(pXmax.X, y補助線)
                };
                    g.DrawLines(pen補助線, Line_実座標変換(ps補助線));
                }


                //Console.WriteLine("「X軸目盛を描画する」を実行します。");
                //X軸目盛を描画する************************************************************************************
                //xStartDate~xEndDateの期間の1日に補助線を引く(厳密には等間隔にならない)

                DateTime targetDate = xStartDate;  //初期値
                string x目盛 = "";              //軸目盛として書込む文字

                int m = 0;
                while (targetDate <= xEndDate)
                {
                    targetDate = xStartDate.AddMonths(m);

                    if (targetDate > xEndDate) { break; }  // 強制的終了


                    if (targetDate.ToString("MM") == "04" || targetDate.ToString("MM") == "01")
                    {
                        //x目盛 = targetDate.ToString("yyyy年\n  M/d");  //1月、4月は年を入れる
                        x目盛 = targetDate.ToString("  M/d\nyyyy年");  //1月、4月は年を入れる
                    }
                    else
                    {
                        x目盛 = targetDate.ToString("M/d");
                    }

                    s = 文字幅高サイズチェック(x目盛, fntSmall);

                    TimeSpan ts日数 = targetDate - xStartDate;
                    日差 = ts日数.Days;

                    //文字サイズから目盛の表示位置計算する
                    x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長 - (Double)s.Width / 2);
                    y = p原点.Y - 8;

                    //目盛書込み 
                    if (xScaleAll == true)
                    {
                        //全てのメモリを書き込む
                        g.DrawString(x目盛, fntSmall, Brushes.Black, x, Y座標_実座標変換(y)); 
                    }
                    else
                    {
                        //1つ置きに表示する。
                        if (m % 2 == 0)
                        {
                            g.DrawString(x目盛, fntSmall, Brushes.Black, x, Y座標_実座標変換(y));
                        }      

                    }
                    

                    //X軸補助線の座標計算
                    x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);


                    //X軸補助線の書込み
                    //但し、4月は濃いめの線にする
                    if (x目盛.Contains("4/1"))
                    {
                        Point[] ps補助線 = {
                             new Point(x, p原点.Y),
                             new Point(x, pYmax.Y)
                            };
                        g.DrawLines(pen補助線濃, Line_実座標変換(ps補助線));
                    }
                    else
                    {
                        Point[] ps補助線 = {
                             new Point(x, p原点.Y),
                             new Point(x, pYmax.Y)
                            };
                        g.DrawLines(pen補助線, Line_実座標変換(ps補助線));
                    }

                    m += 1;
                }


                //Console.WriteLine("「期初生産計画」を実行します。");
                //期初生産計画を描画する*******************************************************************************
                //黒破線の階段ラインにする。

                /*【黒破線の階段ラインの引き方】
                 *    
                 *    1つのデータ(予定)に対し、  
                 *      起点   :一番最初の点
                 *      中間点 :該当データのx値、前のデータy値
                 *      終点   :該当データのx値、該当データy値
                 *  の連続をセットにしてラインを引く
                 * 
                */

                if (initialProdPlan == null)
                {
                    //初期入荷予定がセットされていないのでスキップ
                }
                else
                {
                    //ライン描画
                    int 初期生産予定数_累積 = 0;

                    DateTime 年月日;
                    Point[] ps初期生産予定 = new Point[0];  //空配列を作って、後で要素を追加する。

                    //x=初日、y=初期在庫数を初期値として入力しておく
                    日差 = 0;
                    初期生産予定数_累積 = 0;
                    int x起点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                    int y起点 = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)初期生産予定数_累積) / (Double)y軸長);

                    int  = 0;  //階段の折れたポイントの数

                    //起点を代入しておく
                    Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                    ps初期生産予定[] = new Point(x起点, y起点);
                     += 1;

                    int x中間点 = 0;
                    int y中間点 = 0;

                    int x終点 = 0;
                    int y終点 = 0;

                    for (int d1 = 0; d1 < initialProdPlan.GetLength(0); d1++) //1次元の操作
                    {
                        //中間点
                        年月日 = DateTime.Parse(initialProdPlan[d1, 0]);   //年月日の確保
                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;

                        x中間点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y中間点 = y起点;


                        //終点
                        初期生産予定数_累積 += int.Parse(initialProdPlan[d1, 1]);     //売上数を累積していく

                        x終点 = x中間点;
                        y終点 = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)初期生産予定数_累積) / (Double)y軸長);

                        //Console.WriteLine("年月日  : " + 年月日.ToString());
                        //Console.WriteLine("累積売上: " + 初期入荷予定数_累積.ToString());
                        //Console.WriteLine("");


                        //中間点を代入
                        Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                        ps初期生産予定[] = new Point(x中間点, y中間点);
                         += 1;

                        //終点を代入
                        Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                        ps初期生産予定[] = new Point(x終点, y終点);
                         += 1;

                        y起点 = y終点;  //次回の為に確保


                        //ドット描画(□4ピクセル)
                        //int dotx = x - 2;
                        //int doty = y + 2;
                        //g.FillEllipse(Brushes.DarkGreen, dotx, Y座標_実座標変換(doty), 4, 4);


                        //ツールチップの情報を保存する。
                        //【注意】Y座標の値はここで反転させておく。
                        // 代入例) toolTipErea.Add(new ToolTipErea(10, 30, 10, 30, "構造体の\nテストだよ"));

                        //string 表示内容 = "出荷実績\n" +
                        //                    年月日.ToString("M月度:") + 桁区切り挿入(salseQty[d1, 1]) + "\n" +
                        //                    " X座標: " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                        //                    " Y座標: (累積)" + 桁区切り挿入(累積売上.ToString());

                        //toolTipErea.Add(new ToolTipErea(dotx, dotx + 4, Y座標_実座標変換(doty), Y座標_実座標変換(doty) + 4, 表示内容));


                    }
                    g.DrawLines(pen初期生産予定, Line_実座標変換(ps初期生産予定));
                }


                //Console.WriteLine("「期初売上計画を描画する」を実行します。");
                //期初売上計画を描画する*******************************************************************************
                //黒破線の折れ線ブラフにする。
                if (initialSalsePlan == null)
                {
                    //初期売上計画がセットされていないのでスキップ
                }
                else
                {
                    //ライン描画
                    int 累積計画 = 0;
                    DateTime 年月日;
                    Point[] ps累積計画 = new Point[0];  //空配列を作って、後で要素を追加する。

                    for (int d1 = 0; d1 < initialSalsePlan.GetLength(0); d1++) //1次元の操作
                    {
                        年月日 = DateTime.Parse(initialSalsePlan[d1, 0]);   //年月日の確保
                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;

                        累積計画 += int.Parse(initialSalsePlan[d1, 1]);     //売上計画数を累積していく

                        //Console.WriteLine("年月日  : " + 年月日.ToString());
                        //Console.WriteLine("累積売上: " + 累積売上.ToString());
                        //Console.WriteLine("");

                        x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)yPix * (Double)累積計画 / (Double)y軸長);

                        Array.Resize(ref ps累積計画, d1 + 1);  //配列リサイズ
                        ps累積計画[d1] = new Point(x, y);

                    }
                    g.DrawLines(penPinkDash, Line_実座標変換(ps累積計画));

                }





                //Console.WriteLine("「年度生産計画ライン(横線)を描画する」を実行します。");
                //年度生産計画ライン(横線)を描画する*******************************************************************
                int y年間生産計画 = 0;   //下段で利用するので定義を外出し

                if (annualPrdPlan > 0)  //期初生産計画が無い場合は書かない!!!
                {
                    //【注意】ラインは期初在庫に年間計画を上積みすること!
                    y年間生産計画 = (int)(((Double)pYmax.Y - (Double)p原点.Y) * (Double)(annualPrdPlan + initalQty) / ((Double)yValueMax - (Double)yValueMin));

                    y年間生産計画 = p原点.Y + y年間生産計画;  //座標に変換する

                    Point[] ps年間生産計画 = {
                     new Point(p原点.X, y年間生産計画),
                     new Point(pXmax.X, y年間生産計画)
                    };
                    g.DrawLines(penBlackDash, Line_実座標変換(ps年間生産計画));

                    //年間生産計画数書込み
                    //座標は「年間生産計画ライン書込み」から流用
                    string 年間生産予定 = "期初年間生産計画: " + 桁区切り挿入(annualPrdPlan.ToString());
                    x = marginLeft + 10;
                    y = y年間生産計画 + 12;
                    g.DrawString(年間生産予定, fntSmall, Brushes.Black, x, Y座標_実座標変換(y));
                }



                //Console.WriteLine("「期初在庫ライン(横線)を描画する」を実行します。");
                //期初在庫ライン(横線)を描画する***********************************************************************
                //  注)y期初在庫は先に計算済
                //期初在庫数書込み
                y期初在庫 = (int)(((Double)pYmax.Y - (Double)p原点.Y) * (Double)initalQty / ((Double)yValueMax - (Double)yValueMin));
                int y期初在庫座標 = p原点.Y + y期初在庫;  //座標に変換する

                Point[] ps期初在庫 = {
                 new Point(p原点.X, y期初在庫座標),
                 new Point(pXmax.X, y期初在庫座標)
                };
                g.DrawLines(penDarkOrange, Line_実座標変換(ps期初在庫));

                string 期初在庫 = "期初在庫: " + 桁区切り挿入(initalQty.ToString());

                s = 文字幅高サイズチェック(期初在庫, fnt);

                x = pXmax.X - s.Width - 5;
                y = y期初在庫座標 + 12;
                g.DrawString(期初在庫, fntSmall, Brushes.Red, x, Y座標_実座標変換(y));


                //Console.WriteLine("「在庫月数ライン(横線)を描画する」を実行します。");
                //在庫月数ライン(横線)を描画する******************************************************************
                //期初在庫数書込み
                Double 安全在庫数 = (Double)salseQty_Ave3M * seftyInventoryMonths;
                int int平均3 = (int)(((Double)pYmax.Y - (Double)p原点.Y) * 安全在庫数 / ((Double)yValueMax - (Double)yValueMin));
                int y平均3座標 = p原点.Y + int平均3;  //座標に変換する

                Point[] ps平均3 = {
                 new Point(p原点.X, y平均3座標),
                 new Point(pXmax.X, y平均3座標)
                };
                g.DrawLines(penBlue, Line_実座標変換(ps平均3));

                string str平均3 = "在庫月数" + seftyInventoryMonths.ToString() + ":\n" + 桁区切り挿入(salseQty_Ave3M.ToString());

                s = 文字幅高サイズチェック(str平均3, fnt);

                //xは今年度末の4/1辺りに表示する。
                //x = pXmax.X - s.Width - 5;
                x = (int)((p原点.X + pXmax.X) / 2);
                y = y平均3座標 + 12;
                g.DrawString(str平均3, fntSmall, Brushes.Blue, x, Y座標_実座標変換(y));



                //Console.WriteLine("「理想生産線(斜め線)を描画する」を実行します。");
                //理想生産線(斜め線)を描画する*************************************************************************
                if (annualPrdPlan > 0)  //期初生産計画が無い場合は書かない!!!
                {
                    //終点は、当年度末に変更する
                    ts =  xStartDate.AddYears(1) - xStartDate;
                    日差 = ts.Days;
                    int 当年度末X = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                    Point[] ps理想生産線 = {
                     new Point(p原点.X, y期初在庫座標),
                     new Point(当年度末X, y年間生産計画)
                    };
                    g.DrawLines(penBlack, Line_実座標変換(ps理想生産線));
                }


                //Console.WriteLine("「Todayライン(縦線)を描画する」を実行します。");
                //Todayライン(縦線)を描画する**************************************************************************
                ts = DateTime.Now - xStartDate;
                today日差 = ts.Days;
                x = (int)((Double)p原点.X + (Double)xPix * (Double)today日差 / (Double)x軸長);
                Point[] psTodayLine = {
                 new Point(x, p原点.Y),
                 new Point(x, pYmax.Y)
                };
                g.DrawLines(penBlue, Line_実座標変換(psTodayLine));

                //Todayの文字書込み
                s = 文字幅高サイズチェック("Today", fnt);
                x = x - s.Width / 2;
                y = pYmax.Y + s.Height;
                g.DrawString("Today", fnt, Brushes.Blue, x, Y座標_実座標変換(y));


                //Console.WriteLine("「12ヶ月後ライン(縦線)を描画する」を実行します。");
                //12ヶ月後ライン(縦線)を描画する***********************************************************************
                //12ヶ月後の末日確定
                DateTime 末日12M = DateTime.Parse(DateTime.Now.AddMonths(12).ToString("yyyy/MM/01"));
                末日12M = 末日12M.AddDays(-1); //さらに1日引く

                ts = 末日12M - xStartDate;
                today日差 = ts.Days;
                x = (int)((Double)p原点.X + (Double)xPix * (Double)today日差 / (Double)x軸長);
                Point[] psAfter12MLine = {
                 new Point(x, p原点.Y),
                 new Point(x, pYmax.Y)
                };
                g.DrawLines(penBlackDashDot, Line_実座標変換(psAfter12MLine));

                //Todayの文字書込み
                s = 文字幅高サイズチェック("12ヶ月後", fnt);
                x = x - s.Width / 2;
                y = pYmax.Y + s.Height;
                g.DrawString("12ヶ月後", fnt, Brushes.Black, x, Y座標_実座標変換(y));



                //Console.WriteLine("「累積出荷実績ライン(緑線)を描画する」を実行します。");
                //累積出荷実績ライン(緑線)を描画する*******************************************************************
                double[] xD = new double[0];   //回帰直線計算用の空の配列を作る。
                double[] yD = new double[0];   //回帰直線計算用の空の配列を作る。

                //【重要】ここで、先月までの累積出荷数を確保し、この点から修正売上計画の起点とする。
                int 先月末累積出荷数 = 0;


                if (salseQty == null)
                {
                    //出荷実績がセットされていないのでスキップ
                }
                else
                {
                    //ライン描画
                    int 累積売上 = 0;
                    DateTime 年月日;
                    Point[] ps累積出荷 = new Point[0];  //空配列を作って、後で要素を追加する。

                    for (int d1 = 0; d1 < salseQty.GetLength(0); d1++) //1次元の操作
                    {
                        年月日 = DateTime.Parse(salseQty[d1, 0]);   //年月日の確保
                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;

                        累積売上 += int.Parse(salseQty[d1, 1]);     //売上数を累積していく


                        //【重要】先月までの累積出荷数を確保
                        if (d1 == salseQty.GetLength(0) - 2)
                        {
                            先月末累積出荷数 = 累積売上;
                            //Console.WriteLine("先月末累積出荷数: " + 先月末累積出荷数);
                        }


                        //Console.WriteLine("年月日  : " + 年月日.ToString());
                        //Console.WriteLine("累積売上: " + 累積売上.ToString());
                        //Console.WriteLine("");

                        x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)yPix * (Double)累積売上 / (Double)y軸長);

                        Array.Resize(ref ps累積出荷, d1 + 1);  //配列リサイズ
                        ps累積出荷[d1] = new Point(x, y);


                        //ドット描画(□4ピクセル)
                        int dotx = x - 2;
                        int doty = y + 2;
                        g.FillEllipse(Brushes.DarkGreen, dotx, Y座標_実座標変換(doty), 4, 4);


                        //ツールチップの情報を保存する。
                        //【注意】Y座標の値はここで反転させておく。
                        // 代入例) toolTipErea.Add(new ToolTipErea(10, 30, 10, 30, "構造体の\nテストだよ"));

                        string 表示内容 = "売上実績\n" +
                                            年月日.ToString("M月度:") + 桁区切り挿入(salseQty[d1, 1]) + "\n" +
                                            " X座標: " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                                            " Y座標: (累積)" + 桁区切り挿入(累積売上.ToString());

                        toolTipErea.Add(new ToolTipErea(dotx, dotx + 4, Y座標_実座標変換(doty), Y座標_実座標変換(doty) + 4, 表示内容));


                        //回帰直線計算用の空の配列に値をセットする。
                        Array.Resize(ref xD, d1 + 1);  //配列のリサイズ。既存値はそのまま。
                        xD[d1] = (Double)日差; //x軸は日差を格納して、表示前にピクセルに変換する。
                                             //Console.WriteLine(xD[d1]);

                        Array.Resize(ref yD, d1 + 1);  //配列のリサイズ。既存値はそのまま。
                        yD[d1] = (Double)累積売上;
                        //Console.WriteLine(yD[d1]);
                    }
                    g.DrawLines(pen出荷実績, Line_実座標変換(ps累積出荷));
                }


                //Console.WriteLine("「累積出荷実績の回帰直線(Regression Line)を描画する」を実行します。");
                //累積出荷実績の回帰直線(Regression Line)を描画する****************************************************
                //ht tps://garakutatech.blogspot.com/2020/12/blog-post.html

                //傾き・切片の変数定義
                double b = 0;
                double a = 0;

                //累積出荷実績に2点以上ある場合にのみ近似直線を描画する。
                //近似計算用のポイント数0で、近似直線の傾き&切片を計算する関数を起動させエラーが発生
                //  処置→2点以上がある場合のみ近似直線を引く。
                //但し、原点(0,0)が最初にあるので、5月1日以降表示であれば、
                // if (xD.Length >= 3 && yD.Length >= 3)
                //すること。


                ////どんな値がセットされてるのか?
                //Console.WriteLine("---------累積出荷実績の回帰直線-----------");
                //for (int i = 0; i < xD.Length; i++)
                //{
                //    //Console.WriteLine("i   : " + i.ToString());
                //    //Console.WriteLine("xD  : " + xD[i].ToString());
                //    //Console.WriteLine("yD  : " + yD[i].ToString());
                //}
                //Console.WriteLine("---------ここまで-------------------------");

                if (salseQty == null)
                {
                    //スキップ!
                    //Console.WriteLine("「累積出荷実績の回帰直線(Regression Line)を描画する」を実行します。⇒スキップしました!");
                }
                else
                {
                    //Console.WriteLine("「累積出荷実績の回帰直線(Regression Line)を描画する」を実行します。⇒処理に入りました。!");

                    //【注意】近似直線を調査すメソッド(calculateA)内でdenominatorがゼロになるとゼロ除算エラーが発生する。
                    //そのエラーを回避する為にここでdenominator計算してみて、ゼロならその後の段でスキップする。
                    double denominatorTest = 0;
                    foreach (double xi in xD)
                    {
                        denominatorTest += Math.Pow(xi - xD.Average(), 2);
                    }

                    if (xD.Length >= 4 && yD.Length >= 4 && denominatorTest !=0)
                    //  [条件式説明] 原点、4月末、5月末、6月当日、の4つのデータなので、6/1以降に表示する。
                    //              と同時にdenominatorTestが0で無いこと
                    {
                        //Console.WriteLine("「累積出荷実績の回帰直線(Regression Line)を描画する」を実行します。⇒描画します。!");

                        double average_x = xD.Average();
                        double average_y = yD.Average();

                        a = calculateA(xD, yD, average_x, average_y);
                        b = calculateB(a, average_x, average_y);

                        //プロパティ出力
                        salesLine_A = a;
                        salesLine_B = b;

                        //Console.WriteLine(String.Format("a = {0}, b = {1}", a, b));

                        //回帰直線の始点・終点を指定しを描画する。
                        日差 = 0;
                        int xRgr0 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        int yRgr0 = (int)((Double)p原点.Y + (Double)yPix * (a * (Double)日差 + b) / (Double)y軸長);

                        ts = xEndDate - xStartDate; //x軸の右端を指定
                        日差 = ts.Days;
                        //Console.WriteLine("end側日差: " + 日差);

                        int xRgr1 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        int yRgr1 = (int)((Double)p原点.Y + (Double)yPix * (a * (Double)日差 + b) / (Double)y軸長);

                        //Console.WriteLine(String.Format("xRgr1 = {0}, yRgr1 = {1}", xRgr1, yRgr1));

                        Point[] ps回帰直線 = {
                        new Point(xRgr0, yRgr0),
                        new Point(xRgr1, yRgr1)
                        };
                        g.DrawLines(penDarkGreen, Line_実座標変換(ps回帰直線));
                    }
                    else
                    {
                        //Console.WriteLine("「累積出荷実績の回帰直線(Regression Line)を描画する」を実行します。⇒描画はスキップされました!");
                    }

                    //Console.WriteLine("if文抜けました!!!");

                }


                //Console.WriteLine("「修正売上計画線(通称:ローリング)を描画する」を実行します。");
                //修正売上計画線(通称:ローリング)を描画する***********************************************************
                // 【ポイント】
                //  この折れ線の始点は、先月末の累積販売とする。

                if (salsePlan == null)
                {
                    //修正売上計画線がセットされていないのでスキップ
                }
                else
                {
                    //ライン描画
                    int 累積売上計画 = 0;
                    DateTime 年月日;
                    Point[] ps累積売上計画 = new Point[0];  //空配列を作って、後で要素を追加する。

                    //ラインの始点の確保---------------------------------------------------------------
                    //始点となる先月末日の累積出荷数を配列の先頭に配置する。

                    //配列の先頭に始点の値を入れ、その後のに既存の配列を入れる。
                    string[,] newSalsePlan = new string[salsePlan.GetLength(0) + 1, 3];

                    //始点の値を配列の先頭に代入
                    newSalsePlan[0, 0] = 先月末年月日.ToString("yyyy/MM/dd");
                    newSalsePlan[0, 1] = 先月末累積出荷数.ToString();
                    newSalsePlan[0, 2] = "";

                    //既存部分の移動 →以降はnewSalsePlanで描画する。
                    for (int i = 0; i < salsePlan.GetLength(0); i++)
                    {
                        newSalsePlan[1 + i, 0] = salsePlan[i, 0];
                        newSalsePlan[1 + i, 1] = salsePlan[i, 1];
                        newSalsePlan[1 + i, 2] = salsePlan[i, 2];
                    }


                    //ラインを配列に代入する-----------------------------------------------------------
                    //【注意】newSalsePlanで描画する
                    for (int d1 = 0; d1 < newSalsePlan.GetLength(0); d1++) //1次元の操作
                    {
                        年月日 = DateTime.Parse(newSalsePlan[d1, 0]);   //年月日の確保
                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;

                        累積売上計画 += int.Parse(newSalsePlan[d1, 1]);     //売上数を累積していく


                        //Console.WriteLine("年月日  : " + 年月日.ToString());
                        //Console.WriteLine("累積売上: " + 累積売上.ToString());
                        //Console.WriteLine("");

                        x = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)yPix * (Double)累積売上計画 / (Double)y軸長);

                        Array.Resize(ref ps累積売上計画, d1 + 1);  //配列リサイズ
                        ps累積売上計画[d1] = new Point(x, y);


                        //ドット描画(□4ピクセル)
                        int dotx = x - 2;
                        int doty = y + 2;
                        g.FillEllipse(Brushes.Red, dotx, Y座標_実座標変換(doty), 5, 5);


                        //ツールチップの情報を保存する。
                        //【注意】Y座標の値はここで反転させておく。
                        // 代入例) toolTipErea.Add(new ToolTipErea(10, 30, 10, 30, "構造体の\nテストだよ"));

                        string 表示内容 = "売上計画\n" +
                                            年月日.ToString("M月度: ") + 桁区切り挿入(newSalsePlan[d1, 1]) + "\n" +
                                            " X座標: " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                                            " Y座標: (累積)" + 桁区切り挿入(累積売上計画.ToString());

                        toolTipErea.Add(new ToolTipErea(dotx, dotx + 4, Y座標_実座標変換(doty), Y座標_実座標変換(doty) + 4, 表示内容));


                        //回帰直線計算用の空の配列に値をセットする。
                        Array.Resize(ref xD, d1 + 1);  //配列のリサイズ。既存値はそのまま。
                        xD[d1] = (Double)日差; //x軸は日差を格納して、表示前にピクセルに変換する。
                                             //Console.WriteLine(xD[d1]);

                        Array.Resize(ref yD, d1 + 1);  //配列のリサイズ。既存値はそのまま。
                        yD[d1] = (Double)累積売上計画;
                        //Console.WriteLine(yD[d1]);
                    }
                    g.DrawLines(pen売上計画, Line_実座標変換(ps累積売上計画));

                    //出力用プロパティに最終点をセットする。
                    salesPlan_FinalQty = 累積売上計画;
                }



                //Console.WriteLine("「使用期限切れ廃棄を含んだ「修正生産計画ライン」(青階段)を描画する」を実行します。");
                //使用期限切れ廃棄を含んだ「修正生産計画ライン」(青階段)を描画する*************************************
                //ツールチップ有り。
                //【ポイント】
                //  ①このグラフは「赤階段」より先(下)に描画する
                //  ②修正生産計画と期限切れ廃棄の配列を合体させ、廃棄数をマイナスに変更し、日付順に並び変える。
                //     並び替えは、二つの配列を「SortedDictionary」に代入し、再び配列に戻す。
                // ③階段描画のロジックは「赤階段」と同じ。
                //Console.WriteLine("青線書くよ!!!");

                DateTime 青線_始点_日付 = DateTime.Parse("0001/01/01");
                int 青線_始点_数量 = 0;
                DateTime 青線_終点_日付 = DateTime.Parse("0001/01/01");
                int 青線_終点_数量 = 0;


                if (prodPlan == null)
                {
                    //【注意】「修正生産計画ライン(赤階段)」が無いと描画エラーになるのでスキップ
                }
                else
                {
                    if (expiredDisposal == null)
                    {
                        //使用期限切れ廃棄数がセットされていないのでスキップ
                    }
                    else
                    {
                        //Console.WriteLine("「使用期限切れ廃棄…1)修正生産計画と期限切れ」を実行します。");
                        //1)修正生産計画と期限切れ廃棄の配列を合体させ、廃棄数をマイナスに変更し、日付順に並び変える。

                        var dic = new SortedDictionary<DateTime, string>();  //←key順に並べてくれる!!!

                        //修正生産計画をSortedDictionaryに代入
                        for (int i = 0; i < prodPlan.GetLength(0); i++)
                        {
                            DateTime tmp年月日 = DateTime.Parse(prodPlan[i, 0].ToString());
                            string  = prodPlan[i, 1].ToString();
                            string ツールチップ = prodPlan[i, 2].ToString();

                            dic[tmp年月日] =  + "_" + ツールチップ;
                        }

                        //廃棄予測をSortedDictionaryに代入
                        for (int i = 0; i < expiredDisposal.GetLength(0); i++)
                        {
                            DateTime tmp年月日 = DateTime.Parse(expiredDisposal[i, 0].ToString());
                            string  = "-" + expiredDisposal[i, 1].ToString();     //マイナスを付ける!!!
                            string ツールチップ = expiredDisposal[i, 2].ToString();

                            dic[tmp年月日] =  + "_" + ツールチップ;
                        }

                        //SortedDictionaryから配列に戻す。
                        string[,] 青線配列 = new string[dic.Count, 3];

                        int 添字1 = 0;
                        foreach (var n in dic)
                        {
                            //Console.WriteLine(n.Key + ": " + n.Value);

                            string[] splitted = n.Value.Split('_');

                            青線配列[添字1, 0] = DateTime.Parse(n.Key.ToString()).ToString("yyyy/MM/dd");
                            青線配列[添字1, 1] = splitted[0]; //数
                            青線配列[添字1, 2] = splitted[1]; //ツールチップ

                            添字1 += 1;
                        }

                        //for (int i = 0; i < 青線配列.GetLength(0); i++)
                        //{
                        //    //Console.WriteLine("i:" + i.ToString());
                        //    //Console.WriteLine("[0]" + 青線配列[i, 0].ToString());
                        //    //Console.WriteLine("[1]" + 青線配列[i, 1].ToString());
                        //    //Console.WriteLine("[2]" + 青線配列[i, 2].ToString());
                        //}

                        //Console.WriteLine("「使用期限切れ廃棄…2)「赤階段」と同じロジックで」を実行します。");
                        //2)「赤階段」と同じロジックで、「細青階段」を描画する。
                        //ライン描画
                        int 廃棄有り生産計画_累積 = 0;

                        DateTime 年月日;
                        Point[] ps初期生産予定 = new Point[0];  //空配列を作って、後で要素を追加する。

                        //x=先月末年月日、y=先月末累積入荷数を始点とする
                        ts = 先月末年月日 - xStartDate;   //【注意】先月末年月日からラインを引く
                        日差 = ts.Days;

                        廃棄有り生産計画_累積 = 先月末累積入荷数;  //【重要】初期値として、先月末累積入荷数を代入しておく。

                        int x起点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        int y起点 = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)廃棄有り生産計画_累積) / (Double)y軸長);

                        int  = 0;  //階段の折れたポイントの数


                        //ここで赤直線用の始点を確保する。---①
                        青線_始点_日付 = 先月末年月日;
                        青線_始点_数量 = initalQty + 廃棄有り生産計画_累積;


                        //Console.WriteLine("「使用期限切れ廃棄…起点を代入しておく」を実行します。");
                        //起点を代入しておく
                        Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                        ps初期生産予定[] = new Point(x起点, y起点);
                         += 1;

                        int x中間点 = 0;
                        int y中間点 = 0;

                        int x終点 = 0;
                        int y終点 = 0;

                        for (int d1 = 0; d1 < 青線配列.GetLength(0); d1++) //1次元の操作
                        {
                            //中間点
                            年月日 = DateTime.Parse(青線配列[d1, 0]);   //年月日の確保
                            ts = 年月日 - xStartDate;
                            日差 = ts.Days;

                            x中間点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                            y中間点 = y起点;


                            //終点
                            廃棄有り生産計画_累積 += int.Parse(青線配列[d1, 1]);     //売上数を累積していく

                            x終点 = x中間点;
                            y終点 = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)廃棄有り生産計画_累積) / (Double)y軸長);

                            //Console.WriteLine("年月日  : " + 年月日.ToString());
                            //Console.WriteLine("累積売上: " + 廃棄有り生産計画_累積.ToString());
                            //Console.WriteLine("");

                            //中間点を代入
                            Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                            ps初期生産予定[] = new Point(x中間点, y中間点);
                             += 1;

                            //終点を代入
                            Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                            ps初期生産予定[] = new Point(x終点, y終点);
                             += 1;

                            y起点 = y終点;  //次回の為に確保

                            if (int.Parse(青線配列[d1, 1].ToString()) < 0)  //数がマイナス表示(廃棄)の場合のみツールチップを表示する。
                            {
                                //ドット描画(□4ピクセル)
                                int dotx = x終点 - 2;
                                int doty = y終点 + 2;
                                g.FillEllipse(Brushes.Blue, dotx, Y座標_実座標変換(doty), 5, 5);


                                //ツールチップの情報を保存する。
                                //【注意】Y座標の値はここで反転させておく。
                                // 代入例) toolTipErea.Add(new ToolTipErea(10, 30, 10, 30, "構造体の\nテストだよ"));

                                string 表示内容 = "廃棄予測\n" +
                                                    青線配列[d1, 2] + "\n" +
                                                    " X座標: " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                                                    " Y座標: (累積)" + 桁区切り挿入((initalQty + 廃棄有り生産計画_累積).ToString());

                                toolTipErea.Add(new ToolTipErea(dotx, dotx + 4, Y座標_実座標変換(doty), Y座標_実座標変換(doty) + 4, 表示内容));
                            }
                        }

                        //ここで赤直線用の終点を確保する。---②
                        青線_終点_数量 = initalQty + 廃棄有り生産計画_累積;  //上記ループで最後に代入された値を確保する。

                        //出力用プロパティに最終点をセットする。
                        prodPlan_FinalQtyWithDisposal = 青線_終点_数量;


                        //【特別追加】生産計画の最後の生産計画から横に引くラインの終点を設定する。
                        // _ProdPlan_EndDateの値を終点とし、初期値(0001/01/01)であればXEndDateまで横線を引く
                        // (中間点を流用・一部改造する)
                        if (prodPlan_EndDate.ToString("yyyy/MM/dd") == "0001/01/01")
                        {
                            ts = xEndDate - xStartDate;
                            青線_終点_日付 = xEndDate;  //ここで赤直線用の終点を確保する。---②
                        }
                        else
                        {
                            ts = prodPlan_EndDate - xStartDate;
                            青線_終点_日付 = prodPlan_EndDate;  //ここで赤直線用の終点を確保する。---②
                        }
                        日差 = ts.Days;

                        x中間点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y中間点 = y起点;

                        Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                        ps初期生産予定[] = new Point(x中間点, y中間点);
                         += 1;

                        g.DrawLines(pen廃棄有り生産予定, Line_実座標変換(ps初期生産予定));

                    }

                }




                //Console.WriteLine("「修正生産計画ライン(赤階段)を描画する」を実行します。");
                //修正生産計画ライン(赤階段)を描画する******************************************************************
                //【ポイント】
                //  この階段状のグラフの始点は、当月15日の累積入荷実績⇒[先月末累積入荷数]を利用する。
                //
                //  またこの次のステップで、このラインの始点と終点を結ぶ直線(赤直線)を引く
                // なお、始点と終点は以下の①②で確保する。

                /*【階段ラインの引き方】
                 *    
                 *  1つのデータ(予定)に対し、  
                 *      起点   :一番最初の点
                 *      中間点 :該当データのx値、前のデータy値
                 *      終点   :該当データのx値、該当データy値
                 *  の連続をセットにしてラインを引く
                 * 
                */

                //赤直線用のポイント
                //Point p赤線_始点 = new Point();
                //Point p赤線_終点 = new Point();

                DateTime 赤線_始点_日付 = DateTime.Parse("0001/01/01");
                int      赤線_始点_数量 = 0;
                DateTime 赤線_終点_日付 = DateTime.Parse("0001/01/01");
                int      赤線_終点_数量 = 0;


                if (prodPlan == null)
                {
                    //修正生産計画がセットされていないのでスキップ
                }
                else
                {
                    //prodPlann配列の先頭に、起点情報をセットする。

                    //ライン描画
                    int 生産計画_累積 = 0;

                    DateTime 年月日;
                    Point[] ps初期生産予定 = new Point[0];  //空配列を作って、後で要素を追加する。

                    //x=先月末年月日、y=先月末累積入荷数を始点とする
                    ts = 先月末年月日 - xStartDate;   //【注意】先月末年月日からラインを引く
                    日差 = ts.Days;

                    生産計画_累積 = 先月末累積入荷数;  //【重要】初期値として、先月末累積入荷数を代入しておく。

                    int x起点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                    int y起点 = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)生産計画_累積) / (Double)y軸長);

                    int  = 0;  //階段の折れたポイントの数


                    //ここで赤直線用の始点を確保する。---①
                    赤線_始点_日付 = 先月末年月日;
                    赤線_始点_数量 = initalQty + 生産計画_累積;


                    //起点を代入しておく
                    Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                    ps初期生産予定[] = new Point(x起点, y起点);
                     += 1;

                    int x中間点 = 0;
                    int y中間点 = 0;

                    int x終点 = 0;
                    int y終点 = 0;

                    for (int d1 = 0; d1 < prodPlan.GetLength(0); d1++) //1次元の操作
                    {
                        //中間点
                        年月日 = DateTime.Parse(prodPlan[d1, 0]);   //年月日の確保
                        ts = 年月日 - xStartDate;
                        日差 = ts.Days;

                        x中間点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                        y中間点 = y起点;


                        //終点
                        生産計画_累積 += int.Parse(prodPlan[d1, 1]);     //売上数を累積していく

                        x終点 = x中間点;
                        y終点 = (int)((Double)p原点.Y + (Double)yPix * ((Double)initalQty + (Double)生産計画_累積) / (Double)y軸長);

                        //Console.WriteLine("終点d1:" + d1.ToString());
                        //Console.WriteLine("年月日  : " + 年月日.ToString());
                        //Console.WriteLine("累積売上: " + 生産計画_累積.ToString());
         
                        //中間点を代入
                        Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                        ps初期生産予定[] = new Point(x中間点, y中間点);
                         += 1;

                        //終点を代入
                        Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                        ps初期生産予定[] = new Point(x終点, y終点);
                         += 1;

                        y起点 = y終点;  //次回の為に確保


                        //ドット描画(□4ピクセル)
                        //int dotx = x - 2;
                        //int doty = y + 2;
                        //g.FillEllipse(Brushes.DarkGreen, dotx, Y座標_実座標変換(doty), 5, 5);

                        int dotx = x終点 - 2;
                        int doty = y終点 + 2;
                        g.FillEllipse(Brushes.DarkGreen, dotx, Y座標_実座標変換(doty), 5, 5);


                        //ツールチップの情報を保存する。
                        //【注意】Y座標の値はここで反転させておく。
                        // 代入例) toolTipErea.Add(new ToolTipErea(10, 30, 10, 30, "構造体の\nテストだよ"));


                        string 表示内容 = "生産計画\n" +
                                            prodPlan[d1, 2] + "\n" +
                                            " X座標: " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                                            " Y座標: (累積)" + 桁区切り挿入((initalQty + 生産計画_累積).ToString());



                        //string 表示内容 = "生産計画\n" +
                        //                    年月日.ToString("M月度:") + 桁区切り挿入(prodPlan[d1, 1]) + "\n" +
                        //                    " X座標: " + 年月日.ToString("yyyy/MM/dd") + "\n" +
                        //                    " Y座標: (累積)" + 桁区切り挿入(生産計画_累積.ToString());

                        toolTipErea.Add(new ToolTipErea(dotx, dotx + 4, Y座標_実座標変換(doty), Y座標_実座標変換(doty) + 4, 表示内容));

                    }

                    //ここで赤直線用の終点を確保する。---②
                    赤線_終点_数量 = initalQty + 生産計画_累積;  //上記ループで最後に代入された値を確保する。

                    //出力用プロパティに最終点をセットする。
                    prodPlan_FinalQty = 赤線_終点_数量;


                    //【特別追加】生産計画の最後の生産計画から横に引くラインの終点を設定する。
                    // _ProdPlan_EndDateの値を終点とし、初期値(0001/01/01)であればXEndDateまで横線を引く
                    // (中間点を流用・一部改造する)
                    if (prodPlan_EndDate.ToString("yyyy/MM/dd") == "0001/01/01")
                    {
                        ts = xEndDate - xStartDate;
                        赤線_終点_日付 = xEndDate;  //ここで赤直線用の終点を確保する。---②
                    }
                    else
                    {
                        ts = prodPlan_EndDate - xStartDate;
                        赤線_終点_日付 = prodPlan_EndDate;  //ここで赤直線用の終点を確保する。---②
                    }
                    日差 = ts.Days;

                    x中間点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);
                    y中間点 = y起点;

                    Array.Resize(ref ps初期生産予定,  + 1);  //配列リサイズ
                    ps初期生産予定[] = new Point(x中間点, y中間点);
                     += 1;

                    g.DrawLines(pen生産予定, Line_実座標変換(ps初期生産予定));
                }


                //Console.WriteLine("「修正生産計画の傾向ライン(赤直線)を描画する」を実行します。");
                //修正生産計画の傾向ライン(赤直線)を描画する***********************************************************
                if (prodPlan == null)
                {
                    //修正生産計画がセットされていないのでスキップ
                }
                else
                {
                    if (赤線_始点_数量 == 0 && 赤線_終点_数量 == 0)
                    {
                        //代入されていないのでスキップ!
                    }
                    else
                    {
                        a = 0;  //傾き
                        b = 0;  //Y軸切片

                        ts = 赤線_始点_日付 - xStartDate;
                        int 日差_始点 = ts.Days;

                        ts = 赤線_終点_日付 - xStartDate;
                        int 日差_終点 = ts.Days;

                        //赤直線のaとbを求める
                        二つの点を通る直線のab( 日差_始点,赤線_始点_数量, 日差_終点, 赤線_終点_数量, ref a, ref b);

                        //Console.WriteLine("日差_始点     : " + 日差_始点.ToString());
                        //Console.WriteLine("赤線_始点_数量: " + 赤線_始点_数量.ToString());
                        //Console.WriteLine("日差_終点     : " + 日差_終点.ToString());
                        //Console.WriteLine("赤線_終点_数量: " + 赤線_終点_数量.ToString());

                        //Console.WriteLine("a: " + a.ToString());
                        //Console.WriteLine("b: " + b.ToString());

                        //プロパティ出力
                        prodPlanLine_A = a;
                        prodPlanLine_B = b;

                        //始点の座標に変換する。
                        int x始点 = p原点.X;
                        int y始点 = (int)((Double)p原点.Y + (Double)yPix * b / (Double)y軸長);

                        //終点の座標に変換する。
                        ts = xEndDate - xStartDate; 
                        日差 = ts.Days;
                        int x終点 = (int)((Double)p原点.X + (Double)xPix * (Double)日差 / (Double)x軸長);

                        int y終点_数量 = (int)a * 日差 + (int)b;   //来年度の末日の数量
                        int y終点 = (int)((Double)p原点.Y + (Double)yPix * y終点_数量 / (Double)y軸長);

                        Point[] ps生産計画傾向ライン = {
                                new Point(x始点, y始点),
                                new Point(x終点, y終点)
                            };

                        //Console.WriteLine("x始点: " + x始点);   //80
                        //Console.WriteLine("y始点: " + y始点);   //49
                        //Console.WriteLine("x終点: " + x終点);   //490
                        //Console.WriteLine("y終点: " + y終点);   //490

                        g.DrawLines(penRed, Line_実座標変換(ps生産計画傾向ライン));

                    }
                }


                //Console.WriteLine("「X軸Y軸を描画する」を実行します。");
                //X軸Y軸を描画する*************************************************************************************
                // これは最後に書く
                Point[] ps = {
                         pYmax,
                         p原点,
                         pXmax,
                     };
                g.DrawLines(penBlack, Line_実座標変換(ps));



                //リソースを解放する***********************************************************************************
                penBlack.Dispose();
                penRed.Dispose();
                penPink.Dispose();
                penBlue.Dispose();
                penDarkOrange.Dispose();
                penLemonChiffon.Dispose();
                penGray.Dispose();
                penDarkGreen.Dispose();

                penBlackDash.Dispose();
                penPinkDash.Dispose();
                penBlackDashDot.Dispose();

                pen出荷実績.Dispose();
                pen売上計画.Dispose();

                pen初期在庫.Dispose();
                pen製造実績.Dispose();

                pen初期生産予定.Dispose();
                pen生産予定.Dispose();
                pen廃棄有り生産予定.Dispose();

                pen補助線.Dispose();
                pen補助線濃.Dispose();

                g.Dispose();
                

                //PictureBox1に表示する(やっとここで!)****************************************************************
                pictureBox.Image = canvas;


                //正常完了
                return("OK");

            }
            catch (Exception e) when (e.Message.Equals("ピクチャボックスサイズ異常"))
            {
                string エラーメッセージ = "グラフの高さ又は幅が0になっているのでグラフ描画できません。";

                return (エラーメッセージ);
            }
            catch (Exception e)
            {
                //エラーメッセージをそのまま返す。
                return (e.Message);
            }
        }
        


        //座標系関数///////////////////////////////////////////////////////////////////////////////////////////////////
        private Size 文字幅高サイズチェック(string 文字, Font f)
        {
            //実際にpictureBoxTestの(0,0)座標に文字を書き込んで、サイズを測る!
            //これしかなさそう(TT)
            //ht tps://dobon.net/vb/dotnet/graphics/measurestring.html

            Size strSize = new Size();

            //描画先とするImageオブジェクトを作成する
            Bitmap canvas = new Bitmap(pictureBoxTest.Width, pictureBoxTest.Height);
            //ImageオブジェクトのGraphicsオブジェクトを作成する
            Graphics g = Graphics.FromImage(canvas);

                //実際に書込む
                TextRenderer.DrawText(g, 文字, f, new Point(0, 0), Color.Black);
                //大きさを計測
                strSize = TextRenderer.MeasureText(g, 文字, f);


            //実際に書込む / NoPaddingにして、文字列を描画する
            TextRenderer.DrawText(g, 文字, f, new Point(0, 50), Color.Black,TextFormatFlags.NoPadding);
            //大きさを計測
            strSize = TextRenderer.MeasureText(g, 文字, f, new Size(100, 100), TextFormatFlags.NoPadding);



            //リソースを解放する
            g.Dispose();

            //pictureBoxTestに表示する
            pictureBoxTest.Image = canvas;

            return strSize;
        }


        private Point[] Line_実座標変換(Point[] point)
        {
            /*-------------------------------------------------------------------------------------
              仮想原点を基準とした座標の配列を受け取り、
              pictureBoxに描画する為の実際の座標に変換すした配列を戻す。
            -------------------------------------------------------------------------------------*/

            Point[] newP = new Point[point.Length];

            int newX = 0;
            int newY = 0;

            int i = 0;
            foreach (Point p in point)
            {
                newX = p.X;
                newY = pictureBox.Height - p.Y; //Yだけ反転させる
                newP[i] = new Point(newX, newY);
                i += 1;
            }

            return newP;
        }


        private int Y座標_実座標変換(int y)
        {
            return pictureBox.Height - y; //Yを反転させる
        }


        private string 桁区切り挿入(string str)
        {
            //数値の桁区切りのカンマを挿入する。
            string result = "";

            int len = str.Length;

            for (int i = 1; i <= len; i++)
            {
                if (i % 3 == 0) //ここでカンマを入れる
                {
                    result = "," + str.Substring(len - i, 1) + result;
                }
                else
                {
                    result = str.Substring(len - i, 1) + result;
                }
            }

            //先頭文字が、半角カンマになる場合があるので、その場合は先頭のカンマを削除する。(先頭=0番目)
            if (result.Substring(0, 1) == ",")
            {
                result = result.Substring(1);  //1番目以降の文字
            }

            return result; 
        }



        private void 二つの点を通る直線のab(int x1, int y1, int x2, int y2, ref Double a, ref Double b)
        {
            /*---------------------------------------------------------
             *  始点座標(x1, y1)
             *  終点座標(x2, y2)
             *  
             *  戻り値(参照渡し) ←注意!!!
             *      a: 傾き
             *      b: y軸切片
             ---------------------------------------------------------*/
            Double Dx1 = (Double)x1;
            Double Dx2 = (Double)x2;
            Double Dy1 = (Double)y1;
            Double Dy2 = (Double)y2;

            a = (Dy2 - Dy1) / (Dx2 - Dx1);
            b = Dy1 - a * Dx1 ;
        }



        private void button追記テスト_Click(object sender, EventArgs e)
        {
            Bitmap canvas = new Bitmap(pictureBox.Width, pictureBox.Height);
            canvas = new Bitmap(pictureBox.Image);

            //ImageオブジェクトのGraphicsオブジェクトを作成する
            Graphics g = Graphics.FromImage(canvas);

            Pen penBlack = new Pen(Color.Black, 1);

            g.DrawRectangle(penBlack, 50, 50, 15, 15);

            //リソースを解放する
            g.Dispose();

            //PictureBox1に表示する
            pictureBox.Image = canvas;

        }




        //ツールチップ/////////////////////////////////////////////////////////////////////////////////////////////////
        private void pictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            /*-------------------------------------------------------------------------------------
            自作ツールチップ

                【動作概要】
                pictureBoxに描画されたグラフのポイントエリアにカーソルが入ったら、labelを表示する。
                ラベルの表示位置は、ラベルの左下がカーソル座標になるようにする。

            -------------------------------------------------------------------------------------*/

            //Console.WriteLine("e.X: " + e.X.ToString() + "  e.Y: " + e.Y.ToString());  //pictureBox上のカーソル座標

            Boolean EreaHit = false;  //エリアに一度でもヒットすれば、true!

            //全てのtoolTipEreaを舐めてもヒ
            for (int i =  0; i < toolTipErea.Count; i++)
            {
                if (e.X >= toolTipErea[i].x1 && e.X <= toolTipErea[i].x2 && e.Y >= toolTipErea[i].y1 && e.Y <= toolTipErea[i].y2)
                {
                    EreaHit = true; //ヒットしたので、true!

                    if (labelToolTip.Visible == false)  //見えて居ない時のみ処理する(チャタリング防止)
                    {
                        labelToolTip.Text = toolTipErea[i].text;
                        //[細かい芸]カーソル位置がpictureBoxの左1/4にの場合はラベルはカーソルの右側、それ以外だと逆に表示。(上下は無視)
                        if (e.X > pictureBox.Width / 4)
                        {
                            labelToolTip.Location = new Point(e.X - labelToolTip.Size.Width + 2, e.Y - labelToolTip.Size.Height + 2);  //+2は微修正用
                        }
                        else
                        {
                            labelToolTip.Location = new Point(e.X , e.Y - labelToolTip.Size.Height + 2); //+2は微修正用
                        }
                        
                        labelToolTip.Visible = true;    //ラベル表示!
                    }
                }
            }


            if (EreaHit == false)  //全てのtoolTipEreaを舐めてもヒットしなかってことは、全ハズレなので、
            {
                labelToolTip.Visible = false; //ラベルを見えなくする。
            }


        }




        //近似直線関連/////////////////////////////////////////////////////////////////////////////////////////////////

        //y = a + bx に回帰させる
        //aを求める!
        static double calculateA(double[] x, double[] y, double average_x, double average_y)
        {

            ////どんな値がセットされてるのか?
            //Console.WriteLine("---------累積出荷実績の回帰直線-----------");
            //for (int i = 0; i < x.Length; i++)
            //{
            //    //Console.WriteLine("i   : " + i.ToString());
            //    //Console.WriteLine("xD  : " + x[i].ToString());
            //    //Console.WriteLine("yD  : " + y[i].ToString());
            //}

            //Console.WriteLine("average_x: " + average_x.ToString());
            //Console.WriteLine("average_y: " + average_y.ToString());

            //Console.WriteLine("---------ここまで-------------------------");

            double denominator = 0;
            foreach (double xi in x)
            {
                denominator += Math.Pow(xi - average_x, 2);     //※【注意】denominatorがゼロになるとエラー
            }
            double molecule = 0;
            for (int i = 0; i < x.Length; ++i)
            {
                molecule += (x[i] - average_x) * (y[i] - average_y);
            }

            return molecule / denominator;   //※【注意】denominatorがゼロになるとエラー

            /*------------------------------------------------------------------------------------
              ※【注意】denominatorがゼロになるとエラー
                トラブルがあった4月1日の売上実績の2点は、
                    (0,0) ⇒ (0,143400) 
                で、
                aを求める際、
                引数は、
                    xの配列 ⇒[0,0]
                    yの配列 ⇒[0,143400]
                    xの配列の平均 ⇒ 0
                    yの配列の平均 ⇒ 71700
                となり、
                    denominator += Math.Pow(xi - average_x, 2);  
                 ↑ここで、x[0]、x[1]、xの配列の平均も0なので、0の2条=0、
                 つまり、denominatorはゼロにりゼロ除算エラーが発生した。!!!
             ------------------------------------------------------------------------------------*/
        }

        //bを求める
        static double calculateB(double a, double average_x, double average_y)
        {
            return average_y - average_x * a;
        }


    }
}

1
1
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?