標準のグラフコンポ―ネントはショボいし、市販のグラフコンポ―ネントもこちらの要求を満たしていなかったのでグラフコンポーネントを自作した。
その時の覚え書き↓
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 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("「入荷実績を描画する」を実行します。");
//入荷実績(生産実績)のを描画する*********************************************************************************
//【注意1】入荷系は面で表示するので、一番最初に書込む
//【重要】累積入荷数の先月末までの最終値をここで確保し、(修正)入荷計画の始点として利用する。
// 先月末年月日確保する。
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("「期初生産計画」を実行します。");
//期初生産計画を描画する*******************************************************************************
//黒破線の階段ラインにする。
/*【黒破線の階段ラインの引き方】
*
* 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)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日の累積入荷実績⇒[先月末累積入荷数]を利用する。
//
// またこの次のステップで、このラインの始点と終点を結ぶ直線(赤直線)を引く
// なお、始点と終点は以下の①②で確保する。
/*【階段ラインの引き方】
*
* 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)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;
}
}
//先頭文字が、半角カンマになる場合があるので、その場合は先頭のカンマを削除する。(先頭=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;
}
}
}