//--------------------------------------------------------------
//	波形の補間と描画のためのメソッド
//		SetFrequency	周波数の設定と入出力波形描画の指令
//		SineGen			正弦波の発生
//		DrawWaveform	標本化点と波形の描画
//		DrawAxis		座標軸と目盛の描画
//
//		作成者：三上直樹，2023/01/28 作成，ⓒ三上直樹 2023
//--------------------------------------------------------------

using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;	// SmoothingMode, LineJoin で使用

using static System.Math;

namespace 正弦波_補間有無
{
	public partial class Form1 : Form
	{
		// 正弦波の発生
		private void SineGen(double frq)
		{
			double pi2fT = 2*PI*frq/FS_;
			for (int n=0; n<N_DATA_; n++) xn_[n] = (float)Sin(pi2fT*(n-N_START_));
		}

		// 標本化点と波形の描画
		//		ovs: オーバーサンプリング比
		private void DrawWaveform(Graphics gr, float[] xn, int nStart, int ovs)
		{
			float dt = dpiFactor_*float.Parse(scaleH_.Checked);	// 横方向の倍率を反映	
			if (ovs == 1) dt = dt*OVS_;
			float nWidth = dpiFactor_*100*ovs;
			float width = (ovs == 1) ? nWidth*OVS_ : nWidth;
			int nDisp = (ovs == 1) ? (int)(nWidth*OVS_/dt) : (int)(nWidth/dt);
			float hUnit = (pictureBox1.Height/2)/1.5f;

			gr.TranslateTransform(36*dpiFactor_, pictureBox1.Height/2 - 12*dpiFactor_);

			// 座標軸と目盛の描画
			DrawAxis(gr, hUnit, width);

			// 波形表示の準備
			PointF[] pt = new PointF[nDisp+1];
			for (int n=0; n<=nDisp;n++) pt[n] = new PointF(n*dt, -hUnit*xn[n+nStart]);
	
			// クリッッピング領域の設定
			gr.SetClip(new RectangleF(-4*dpiFactor_, -hUnit-5,
									  width+8*dpiFactor_, 2*hUnit+10));

			// 0 次ホールドの処理
			PointF[] pt0H = new PointF[pt.Length*2-1];
			if (radioButton11.Checked)
			{
				for (int n=0; n<pt.Length-1; n++)
				{
					pt0H[2*n] = pt[n];
					pt0H[2*n+1] = new PointF(pt[n+1].X, pt[n].Y);
				}
				pt0H[(pt.Length-1)*2] = pt[pt.Length-1];
			}

			Pen myPen = new Pen(Color.Navy, 2) { LineJoin = LineJoin.Round };
			gr.SmoothingMode = SmoothingMode.AntiAlias;
			if (radioButton10.Checked)	// 0 次ホールドの処理なし
			{
				if (radioButton6.Checked) gr.DrawLines(myPen, pt);		// 直線で結ぶ
				else					  gr.DrawCurve(myPen, pt);		// スプライン関数で結ぶ
			}
			else						// 0 次ホールドの処理あり
			{
				if (radioButton6.Checked) gr.DrawLines(myPen, pt0H);	// 直線で結ぶ
				else					  gr.DrawCurve(myPen, pt0H);	// スプライン関数で結ぶ
			}
			float r0 = 3.0f*dpiFactor_;

			// 補間点を青色の丸で表示
			if ((radioButton9.Checked) && (ovs != 1))
				for (int n=0; n<=nDisp; n++)
				{
					gr.FillEllipse(Brushes.White, pt[n].X-r0, pt[n].Y-r0, 2*r0, 2*r0);
					gr.DrawEllipse(Pens.Cyan, pt[n].X-r0, pt[n].Y-r0, 2*r0, 2*r0);
				}
			// 標本化点をマゼンタ色の丸で表示
			for (int n=0; n<=nDisp/ovs; n++)
			{
				int n1 = n*ovs;
				gr.FillEllipse(Brushes.White, pt[n1].X-r0, pt[n1].Y-r0, 2*r0, 2*r0);
				gr.DrawEllipse(Pens.Magenta, pt[n1].X-r0, pt[n1].Y-r0, 2*r0, 2*r0);
			}
			gr.SmoothingMode = SmoothingMode.Default;
		}

		// 座標軸と目盛の描画
		private void DrawAxis(Graphics gr, float h_unit, float width)
		{
			Brush myBrush = Brushes.Black;
			float y1 = pictureBox1.Height/2 - 22*dpiFactor_;

			// 横軸の目盛の表示
			for (int n=0; n<=10; n++)
				gr.DrawLine(Pens.Silver, n*width/10, h_unit, n*width/10, -h_unit);

			gr.DrawString("0", Font, myBrush, -4*dpiFactor_, y1);
			string tMax = (10.0f/float.Parse(scaleH_.Checked)).ToString();
			// 時間軸の最大値
			gr.DrawString(tMax, Font, myBrush, +width-(3+2*tMax.Length)*dpiFactor_, y1);

			gr.DrawString("時間 [ms]", Font, myBrush, width/2-32*dpiFactor_, y1+16*dpiFactor_);

			// 縦軸の目盛の表示
			for (int n=0; n<5; n++)
				gr.DrawLine(Pens.Silver, 0, (n-2)*h_unit/2, width, (n-2)*h_unit/2);

			gr.DrawString("1", Font, myBrush, -14*dpiFactor_, -h_unit-9*dpiFactor_);
			gr.DrawString("0", Font, myBrush, -15*dpiFactor_, -9*dpiFactor_);
			gr.DrawString("-1", Font, myBrush, -19*dpiFactor_, h_unit-9*dpiFactor_);
		}
	}
}
