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

標準のグラフコンポ―ネントはショボいし、市販のグラフコンポ―ネントもこちらの要求を満たしていなかったのでグラフコンポーネントを自作した。
その時の覚え書き↓
![グラフ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157849/c74f091c-be26-46bc-90ee-dd6c6683cf5a.png)

```C#: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;     //グラフのサイズが小さい場合、ｘ軸のラベルを一つ置きに表示する。
                                //　true　⇒　すべて表示する。(デフォルト)
                                //　false　⇒　一つ飛ばしに表示
        [Description("[グラフの表示サイズが小さい場合、ｘ軸のラベルを一つ飛ばしで表示する。"), 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 y軸Pix数 = pYmax.Y - p原点.Y;

                ts = xEndDate - xStartDate;
                int x軸長 = ts.Days;         //例)　1年間なら365
                int x軸Pix数 = 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("「入荷実績を描画する」を実行します。");
                //入荷実績(生産実績)のを描画する*********************************************************************************
                //【注意１】入荷系は面で表示するので、一番最初に書込む

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

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


                //期初在庫(塗りつぶし四角で表示する。)-----------------------------
                ts = DateTime.Now - xStartDate;
                日差 = ts.Days;
                x = (int)((Double)x軸Pix数 * (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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((Double)initalQty + (Double)累積製造) / (Double)y軸長);


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

                        w = (int)((Double)x軸Pix数 * (Double)width日差 / (Double)x軸長);
                        h = (int)((Double)y軸Pix数 * (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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)y軸Pix数 * (Double)月末在庫 / (Double)y軸長);

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

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

                        w = (int)((Double)x軸Pix数 * (Double)width日差 / (Double)x軸長);
                        h = (int)((Double)y軸Pix数 * (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)y軸Pix数 / (Double)yDivideNum * (Double)i);

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

                    //Y軸の補助線の書込み
                    int y補助線 = (int)((Double)p原点.Y + (Double)y軸Pix数 / (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)x軸Pix数 * (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)x軸Pix数 * (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("「期初生産計画」を実行します。");
                //期初生産計画を描画する*******************************************************************************
                //黒破線の階段ラインにする。

                /*【黒破線の階段ラインの引き方】
                 *    
                 *    １つのデータ(予定)に対し、  
                 *      起点   ：一番最初の点
                 *      中間点 ：該当データの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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                    int y起点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y中間点 = y起点;


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

                        x終点 = x中間点;
                        y終点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)y軸Pix数 * (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)x軸Pix数 * (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)x軸Pix数 * (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)x軸Pix数 * (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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)y軸Pix数 * (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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        int yRgr0 = (int)((Double)p原点.Y + (Double)y軸Pix数 * (a * (Double)日差 + b) / (Double)y軸長);

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

                        int xRgr1 = (int)((Double)p原点.X + (Double)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        int yRgr1 = (int)((Double)p原点.Y + (Double)y軸Pix数 * (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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y = (int)((Double)p原点.Y + (Double)y軸Pix数 * (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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        int y起点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                            y中間点 = y起点;


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

                            x終点 = x中間点;
                            y終点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y中間点 = y起点;

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

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

                    }

                }




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

                /*【階段ラインの引き方】
                 *    
                 *  １つのデータ(予定)に対し、  
                 *      起点   ：一番最初の点
                 *      中間点 ：該当データの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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                    int y起点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((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)x軸Pix数 * (Double)日差 / (Double)x軸長);
                        y中間点 = y起点;


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

                        x終点 = x中間点;
                        y終点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * ((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)x軸Pix数 * (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)y軸Pix数 * b / (Double)y軸長);

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

                        int y終点_数量 = (int)a * 日差 + (int)b;   //来年度の末日の数量
                        int y終点 = (int)((Double)p原点.Y + (Double)y軸Pix数 * 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;
                }
            }

            //先頭文字が、半角カンマになる場合があるので、その場合は先頭のカンマを削除する。(先頭＝０番目)
            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;
        }


    }
}

```