using System; using System.Collections; using System.Drawing; using System.Text; using Zanetti.Data; using Travis.Storage; namespace Zanetti.UI { /// /// Fibonacci の概要の説明です。 /// internal class Fibonacci { private PointF _pivot; private PointF _destination; private int _id; //SolidFibonacciからの作成時のみセット、それ以外は-1 private LineDrawMode _mode; public enum LineDrawMode { Normal, Hilight,//☆Fibonacci 20180816 カンマ「,」追加 Move//☆Fibonacci 20180816 追加 } public Fibonacci(PointF p) { _pivot = p; _destination = p; _id = -1; } public Fibonacci(PointF p1, PointF p2) { _pivot = p1; _destination = p2; _id = -1; } //リサイズしたときなどの再計算 public Fibonacci(DataFarm farm, int firstdateindex, SolidFibonacci fl, Trans value_to_y) { int p = Env.Layout.DatePitch; _pivot = new PointF((farm.DateToIndex(fl._date1) - firstdateindex) * p + p / 2, (float)value_to_y.TransValue(fl._value1)); _destination = new PointF((farm.DateToIndex(fl._date2) - firstdateindex) * p + p / 2, (float)value_to_y.TransValue(fl._value2)); _id = fl._id; } public PointF Pivot { get { return _pivot; } //☆Fibonacci 20180816 setは不要 } public PointF Destination { get { return _destination; } set { _destination = value; } } public LineDrawMode DrawMode { get { return _mode; } set { _mode = value; } } public int ID { get { return _id; } } public void SetHighPriceAndLowPrice(DataFarm farm, Trans pricetrans, float x1, float x2, int FirstDateIndex) { var range_max = double.MinValue;//☆Fibonacci 20180816 新規追加 var range_min = double.MaxValue;//☆Fibonacci 20180816 新規追加 var y1 = double.MinValue;//☆Fibonacci 20180816 新規追加 var y2 = double.MinValue;//☆Fibonacci 20180816 新規追加 if (x1 > Env.Layout.ChartAreaWidth || x2 > Env.Layout.ChartAreaWidth) return; int ind1 = FirstDateIndex + (int)Math.Floor((double)Math.Min(x1, x2) / Env.Layout.DatePitch); int ind2 = FirstDateIndex + (int)Math.Floor((double)Math.Max(x1, x2) / Env.Layout.DatePitch); if (ind1 < 0) ind1 = 0; if (ind2 < 0) ind2 = 0; if (ind1 >= farm.FilledLength) ind1 = farm.FilledLength - 1;//☆Fibonacci 20180816 修正 if (ind2 >= farm.FilledLength) ind2 = farm.FilledLength - 1;//☆Fibonacci 20180816 修正 //最高値と最安値の株価 range_max = Max(farm, ind1, ind2);//☆Fibonacci 20180816 修正 range_min = Min(farm, ind1, ind2);//☆Fibonacci 20180816 修正 //最高値と最安値の画面上の位置 //var y1 = pricetrans.TransValue(_pivot.Y > _destination.Y ? Env.Preference.InverseChart ? range_max : range_min : Env.Preference.InverseChart ? range_min : range_max);//☆Fibonacci 20180816 コメントアウト //var y2 = pricetrans.TransValue(_pivot.Y > _destination.Y ? Env.Preference.InverseChart ? range_min : range_max : Env.Preference.InverseChart ? range_max : range_min);//☆Fibonacci 20180816 コメントアウト //☆Fibonacci 20180816 新規追加ここから if (_mode != Fibonacci.LineDrawMode.Move && _id < 0)//モードが移動モード、かつ引いたばかりのフィボナッチラインではない { //_pivot.Y > _destination.Y は下から上に引いた線の意味 y1 = pricetrans.TransValue(_pivot.Y > _destination.Y ? Env.Preference.InverseChart ? range_max : range_min : Env.Preference.InverseChart ? range_min : range_max);//☆Fibonacci 20180816 変更 y2 = pricetrans.TransValue(_pivot.Y > _destination.Y ? Env.Preference.InverseChart ? range_min : range_max : Env.Preference.InverseChart ? range_max : range_min);//☆Fibonacci 20180816 変更 } else { y1 = _pivot.Y < _destination.Y ? Env.Preference.InverseChart ? _pivot.Y : _destination.Y : Env.Preference.InverseChart ? _destination.Y : _pivot.Y; y2 = _pivot.Y < _destination.Y ? Env.Preference.InverseChart ? _destination.Y : _pivot.Y : Env.Preference.InverseChart ? _pivot.Y : _destination.Y; } //ここまで //_pivot = new PointF(x1, (float)y1);//☆Fibonacci 20180816 コメントアウト //_destination = new PointF(x2, (float)y2);//☆Fibonacci 20180816 コメントアウト _pivot = new PointF(_pivot.X < _destination.X ? Math.Min(x1, x2) : Math.Max(x1, x2), _pivot.Y < _destination.Y ? (float)Math.Min(y1, y2) : (float)Math.Max(y1, y2));//☆Fibonacci 20180816 新規追加 _destination = new PointF(_pivot.X < _destination.X ? Math.Max(x1, x2) : Math.Min(x1, x2), _pivot.Y < _destination.Y ? (float)Math.Max(y1, y2) : (float)Math.Min(y1, y2));//☆Fibonacci 20180816 新規追加 } private double Max(DataFarm farm, int ind1, int ind2) { var pos = ind1; var max = double.MinValue; var last = ind2 > farm.FilledLength ? farm.FilledLength - 1 : ind2; try { while (pos <= last) { var a = BitConverter.ToInt32(farm.RawDataImage, pos * 32 + 4 * 2) * farm.Brand.PriceScale / (Env.Preference.AdjustSplit ? farm.CalcSplitRatio(BitConverter.ToInt32(farm.RawDataImage, pos * 32)) : 1); Console.WriteLine(pos); if (max < a) max = a; pos++; } }catch(Exception ex) { Console.WriteLine(ex.Message); } return max; } private double Min(DataFarm farm, int ind1, int ind2) { var pos = ind1; var min = double.MaxValue; var last = ind2 > farm.FilledLength ? farm.FilledLength - 1 : ind2; while (pos <= last) { var a = BitConverter.ToInt32(farm.RawDataImage, pos * 32 + 4 * 3) * farm.Brand.PriceScale / (Env.Preference.AdjustSplit ? farm.CalcSplitRatio(BitConverter.ToInt32(farm.RawDataImage, pos * 32)) : 1); if (min > a) min = a; pos++; } return min; } public Rectangle GetInclusion(Rectangle rect) { if (_pivot == _destination) return new Rectangle(new Point((int)_pivot.X, (int)_pivot.Y), new Size(0, 0)); Point[] es = GetEdge(rect, new Point((int)_pivot.X, (int)_pivot.Y), new Point((int)_destination.X, (int)_destination.Y)); return new Rectangle(Math.Min(es[0].X, es[1].X), Math.Min(es[0].Y, es[1].Y), Math.Abs(es[0].X - es[1].X), Math.Abs(es[0].Y - es[1].Y)); } //十分に離した位置でないと線を確定させないようにする public bool PivotHasEnoughDistanceTo(PointF pt) { return Math.Abs(_pivot.X - pt.X) > 10 || Math.Abs(_pivot.Y - pt.Y) > 10; } //☆Fibonacci 20180816 Drawはまるまる書き直し ここから public void Draw(Trans pricetrans, Rectangle rect, IntPtr hdc) { Win32.POINT pt = new Win32.POINT(); //Win32.SelectObject(hdc, _mode == LineDrawMode.Normal ? Env.Preference.FibonacciPen.Handle : Env.Preference.FibonacciBoldPen.Handle);//☆Fibonacci 20180816 コメントアウト //var fibo = new double[] { -2.618, -1.618, 0, .236, .382, .5, .618, .764, 1, 1.618, 2.618 }; //var fibo = new double[] { 0, .236, .382, .5, .618, .764, 1 }; var fibo = new double[] { 0, .382, .5, .618, 1 }; foreach (var f in fibo) { Win32.SelectObject(hdc, f == 0 ? Env.Preference.FibonacciBoldPen.Handle : f == 1 ? Env.Preference.FibonacciPen.Handle : f == .5 ? Env.Preference.FibonacciPen.Handle : Env.Preference.FibonacciDottedPen.Handle);//☆Fibonacci 20180816 追加 FibonacciDrawLines fdl = new FibonacciDrawLines(_pivot, _destination, f, fibo); fdl.Draw(pricetrans, rect, hdc, out pt); } }//ここまで //☆Fibonacci 20180816 追加 private void DrawTheLine(IntPtr hdc, float px1, float px2, float py, double kabuka, string text, out Win32.POINT pt) { Win32.MoveToEx(hdc, (int)px1, (int)py, out pt); Win32.LineTo(hdc, (int)px2, (int)py); DrawText(hdc, (int)px2, (int)py, kabuka); DrawText(hdc, (int)px1 - 30, (int)py, text); } private void DrawText(IntPtr hdc, int x, int y, string s) { Win32.SetTextColor(hdc, Util.ToCOLORREF(Color.White)); ChartUtil.DrawText(hdc, x + 3, y - 6, s); } private void DrawTheLine(IntPtr hdc, float px1, float px2, float py, double kabuka, out Win32.POINT pt) { Win32.MoveToEx(hdc, (int)px1, (int)py, out pt); Win32.LineTo(hdc, (int)px2, (int)py); DrawText(hdc, (int)px2, (int)py, kabuka); } private void DrawText(IntPtr hdc, int x, int y, double d) { Win32.SetTextColor(hdc, Util.ToCOLORREF(Color.White)); ChartUtil.DrawText(hdc, x + 3, y - 6, string.Format("{0:N}", d)); } //p1,p2がrectに入っているとき、p1からp2に伸ばした線とp2からp1に伸ばした線がそれぞれrectと交差するところを返す private static Point[] GetEdge(Rectangle rect, Point p1, Point p2) { Point[] result = new Point[2]; ArrayList ar = GetAllEdges(rect, p1, p2); if (ar.Count == 2) return (Point[])ar.ToArray(typeof(Point)); result[0] = (Point)(p1.X > p2.X ? ar[1] : ar[2]); result[1] = (Point)(p1.X > p2.X ? ar[2] : ar[1]); return result; } //p1とp2を結ぶ線がrectを構成する4直線と交差する点を返す。水平・垂直のときは2個になる private static ArrayList GetAllEdges(Rectangle rect, Point p1, Point p2) { ArrayList ar = new ArrayList(); if (p1.X == p2.X) { ar.Add(new Point(p1.X, p1.Y < p2.Y ? rect.Bottom : rect.Top)); ar.Add(new Point(p1.X, p1.Y > p2.Y ? rect.Bottom : rect.Top)); return ar; } else if (p1.Y == p2.Y) { ar.Add(new Point(p1.X < p2.X ? rect.Right : rect.Left, p1.Y)); ar.Add(new Point(p1.X > p2.X ? rect.Right : rect.Left, p1.Y)); return ar; } LinearTrans lt = LinearTrans.Solve(p1.X, p1.Y, p2.X, p2.Y); ar.Add(new Point(rect.Left, (int)lt.TransValue(rect.Left))); ar.Add(new Point(rect.Right, (int)lt.TransValue(rect.Right))); ar.Add(new Point((int)lt.Inverse(rect.Top), rect.Top)); ar.Add(new Point((int)lt.Inverse(rect.Bottom), rect.Bottom)); //この4点をX座標の順に並べ、2番目と3番目が答え ar.Sort(new PointComparer()); return ar; } private class PointComparer : IComparer { public int Compare(object x, object y) { return ((Point)x).X - ((Point)y).X; } } public SolidFibonacci ToSolid(DataFarm farm, int firstdateindex, Trans value_to_y) { SolidFibonacci fl = new SolidFibonacci(); int ind1 = firstdateindex + (int)Math.Floor((double)_pivot.X / Env.Layout.DatePitch); int ind2 = firstdateindex + (int)Math.Floor((double)_destination.X / Env.Layout.DatePitch); LinearTrans tr = LinearTrans.Solve(ind1, _pivot.Y, ind2, _destination.Y); double y1 = _pivot.Y; if (ind1 >= farm.FilledLength) { ind1 = farm.FilledLength - 1; //y1 = tr.TransValue(ind1);//☆Fibonacci 20180816 コメントアウト freelineと違ってy軸、つまり株価は移動しなくていい } else if (ind1 < 0) { ind1 = 0; y1 = tr.TransValue(ind1); } fl._date1 = farm.GetByIndex(ind1).Date; fl._value1 = value_to_y.Inverse(y1); double y2 = _destination.Y; if (ind2 >= farm.FilledLength) { ind2 = farm.FilledLength - 1; //y2 = tr.TransValue(ind2);//☆Fibonacci 20180816 コメントアウト freelineと違ってy軸、つまり株価は移動しなくていい } else if (ind2 < 0) { ind2 = 0; y2 = tr.TransValue(0); } fl._date2 = farm.GetByIndex(ind2).Date; fl._value2 = value_to_y.Inverse(y2); //_id==-1は作ったばかりのフィボナッチで、0以上は既存フィボナッチ if (_id == -1) { fl._id = SolidFibonacci.NextID++; _id = fl._id; } else { fl._id = _id; } return fl; } //☆Fibonacci 20180816 変更 GetDistanceは全面的に書き直し public double GetDistance(Point p) { return Math.Min(GetDistanceUpside(p), GetDistanceDownside(p)); } //☆Fibonacci 20180816 新規 //最上部のフィボナッチラインとマウスカーソルとの直線距離 //マウスカーソルがフィボナッチラインの幅内にあった場合のみ、マウスカーソルと最上部フィボナッチラインの縦幅を計測して返す public double GetDistanceUpside(Point p) { var y = Math.Min(_pivot.Y, _destination.Y); var xl = Math.Min(_pivot.X, _destination.X); var xr = Math.Max(_pivot.X, _destination.X); return (p.X < xl || xr < p.X) ? double.MaxValue : Math.Abs(y - p.Y); } //☆Fibonacci 20180816 新規 //最下部のフィボナッチラインとマウスカーソルとの直線距離 //マウスカーソルがフィボナッチラインの幅内にあった場合のみ、マウスカーソルと最下部フィボナッチラインの縦幅を計測して返す public double GetDistanceDownside(Point p) { var y = Math.Max(_pivot.Y, _destination.Y); var xl = Math.Min(_pivot.X, _destination.X); var xr = Math.Max(_pivot.X, _destination.X); return (p.X < xl || xr < p.X) ? double.MaxValue : Math.Abs(y - p.Y); } //両端を通る直線を ax+by+c=0, a^2+b^2=1 となる形式でa,b,cの配列で返す private double[] GetNormalizedParam() { double a, b, c; if (_pivot.X == _destination.X) { a = 1; b = 0; } else { double d = -(_pivot.Y - _destination.Y) / (double)(_pivot.X - _destination.X); b = Math.Sqrt(1 / (1 + d * d)); a = b * d; } c = -(a * _pivot.X + b * _pivot.Y); return new double[] { a, b, c }; } } internal class SolidFibonacci { public int _id; public int _code; public ChartFormat _targetFormat; public bool _logScale; public int _date1; public double _value1; public int _date2; public double _value2; private static int _nextID; public static int NextID { get { return _nextID; } set { _nextID = value; } } } internal class FibonacciCollection { private ArrayList _data; public FibonacciCollection() { _data = new ArrayList(); } public int Count { get { return _data.Count; } } public void Add(AbstractBrand br, ChartFormat format, bool logScale, SolidFibonacci fl) { fl._code = br.Code; fl._targetFormat = format; fl._logScale = logScale; _data.Add(fl); } public SolidFibonacci[] Find(AbstractBrand br, ChartFormat format, bool logScale) { ArrayList t = new ArrayList(); foreach (SolidFibonacci fl in _data) { if (fl._code == br.Code && fl._targetFormat == format && fl._logScale == logScale) t.Add(fl); } return (SolidFibonacci[])t.ToArray(typeof(SolidFibonacci)); } public void ClearAll() { _data.Clear(); } public void Clear(AbstractBrand br, ChartFormat format, bool logScale) { ArrayList temp = new ArrayList(); IEnumerator ie = _data.GetEnumerator(); while (ie.MoveNext()) { SolidFibonacci fl = (SolidFibonacci)ie.Current; if (!(fl._code == br.Code && fl._targetFormat == format && fl._logScale == logScale)) temp.Add(fl); } _data = temp; } public void Remove(int id) { for (int i = 0; i < _data.Count; i++) { SolidFibonacci l = (SolidFibonacci)_data[i]; if (l._id == id) { _data.RemoveAt(i); break; } } } public void Load(StorageNode parent) { string t = parent["fibonacci-lines"]; if (t == null) return; foreach (string ee in t.Split(',')) { if (ee == null) continue; if (ee.Trim() == string.Empty) continue; SolidFibonacci fl = new SolidFibonacci(); string[] e = ee.Split(':'); fl._code = Int32.Parse(e[0]); switch (e[1]) { case "D": fl._targetFormat = ChartFormat.Daily; fl._logScale = false; break; case "W": fl._targetFormat = ChartFormat.Weekly; fl._logScale = false; break; case "M": fl._targetFormat = ChartFormat.Monthly; fl._logScale = false; break; case "Y": fl._targetFormat = ChartFormat.Yearly; fl._logScale = false; break; case "DL": fl._targetFormat = ChartFormat.Daily; fl._logScale = true; break; case "WL": fl._targetFormat = ChartFormat.Weekly; fl._logScale = true; break; case "ML": fl._targetFormat = ChartFormat.Monthly; fl._logScale = true; break; case "YL": fl._targetFormat = ChartFormat.Yearly; fl._logScale = true; break; } fl._date1 = Int32.Parse(e[2]); fl._value1 = Double.Parse(e[3]); fl._date2 = Int32.Parse(e[4]); fl._value2 = Double.Parse(e[5]); fl._id = SolidFibonacci.NextID++; _data.Add(fl); } } public void SaveTo(StorageNode parent) { StringBuilder bld = new StringBuilder(); foreach (SolidFibonacci sl in _data) { if (bld.Length > 0) bld.Append(","); String format; switch (sl._targetFormat) { case ChartFormat.Daily: default: format = "D"; break; case ChartFormat.Weekly: format = "W"; break; case ChartFormat.Monthly: format = "M"; break; case ChartFormat.Yearly: format = "Y"; break; } if (sl._logScale) { format += "L"; } bld.Append(String.Format("{0}:{1}:{2}:{3:F2}:{4}:{5:F2}", sl._code, format, sl._date1, sl._value1, sl._date2, sl._value2)); } parent["fibonacci-lines"] = bld.ToString(); } } //☆Fibonacci 20180816 新規追加 internal class FibonacciDrawLines { bool _log = false; PointF _pivot; PointF _dest; double _fibopos = .0; double[] _fibonacci; string _text = string.Empty; public FibonacciDrawLines(PointF pivot, PointF dest, double fibopos, double[] fibonacci) { _pivot = pivot; _dest = dest; _fibopos = fibopos; _fibonacci = fibonacci; } public void Draw(Trans pricetrans, Rectangle rect, IntPtr hdc, out Win32.POINT pt) { var k = retKabuka(pricetrans); var y = (int)retY(k, pricetrans); Win32.MoveToEx(hdc, (int)Math.Min(_pivot.X, _dest.X), y, out pt); Win32.LineTo(hdc, (int)Math.Max(_pivot.X, _dest.X), y); DrawText(hdc, (int)Math.Max(_pivot.X, _dest.X), y, k); DrawText(hdc, (int)Math.Min(_pivot.X, _dest.X) - 30, y, retText()); } private string retText() { return _fibopos == 0 ? _pivot.Y < _dest.Y ? "100" : "0" : //マウスが上から下に動いてフィボナッチリトレースメントを作ったとき逆転させる _fibopos == 1 ? _pivot.Y < _dest.Y ? "0" : "100" : //マウスが上から下に動いてフィボナッチリトレースメントを作ったとき逆転させる string.Format("{0:00.0}", _fibopos * 100);//天底以外の数値(0.618とか)はretKabukaでハンドリングするのでここでは逆転させない } private double retKabuka(Trans pricetrans) { var ret = .0; if (_log)//対数チャートの場合 { //上下逆転チャートではない、かつマウスを下から上に引いて作った、 もしくは上下逆転チャート、かつマウスを上から下に引いて作った if ((!Env.Preference.InverseChart && _pivot.Y > _dest.Y) || (Env.Preference.InverseChart && _pivot.Y < _dest.Y)) { ret = _fibopos == 0 ? pricetrans.Inverse(Math.Min(_pivot.Y, _dest.Y)) : _fibopos == 1 ? pricetrans.Inverse(Math.Max(_pivot.Y, _dest.Y)) : pricetrans.Inverse(Math.Max(_pivot.Y, _dest.Y) - Math.Abs(_pivot.Y - _dest.Y) * _fibopos); } else { ret = _fibopos == 0 ? pricetrans.Inverse(Math.Min(_pivot.Y, _dest.Y)) : _fibopos == 1 ? pricetrans.Inverse(Math.Max(_pivot.Y, _dest.Y)) : pricetrans.Inverse(Math.Min(_pivot.Y, _dest.Y) + Math.Abs(_pivot.Y - _dest.Y) * _fibopos); } } else { var k1 = pricetrans.Inverse(Math.Min(_pivot.Y, _dest.Y)); var k5 = pricetrans.Inverse(Math.Max(_pivot.Y, _dest.Y)); //上下逆転チャートではない、かつマウスを下から上に引いて作った、 もしくは上下逆転チャート、かつマウスを上から下に引いて作った if ((!Env.Preference.InverseChart && _pivot.Y > _dest.Y) || (Env.Preference.InverseChart && _pivot.Y < _dest.Y)) { ret = _fibopos == 0 ? k1 : _fibopos == 1 ? k5 : Math.Max(k1, k5) - Math.Abs(k1 - k5) * _fibopos; } else { ret = _fibopos == 0 ? k1 : _fibopos == 1 ? k5 : Math.Min(k1, k5) + Math.Abs(k1 - k5) * _fibopos; } } return ret; } private float retY(double k, Trans pricetrans) { var ret = 0F; if (_log) { ret = _fibopos == 0 ? Math.Min(_pivot.Y, _dest.Y) : _fibopos == 1 ? Math.Max(_pivot.Y, _dest.Y) : (float)(Math.Abs(_pivot.Y - _dest.Y) * _fibopos + Math.Min(_pivot.Y, _dest.Y)); } else { ret = _fibopos == 0 ? Math.Min(_pivot.Y, _dest.Y) : _fibopos == 1 ? Math.Max(_pivot.Y, _dest.Y) : (float)pricetrans.TransValue(k); } return ret; } private void DrawText(IntPtr hdc, int x, int y, string s) { Win32.SetTextColor(hdc, Util.ToCOLORREF(Color.White)); ChartUtil.DrawText(hdc, x + 3, y - 6, s); } private void DrawText(IntPtr hdc, int x, int y, double d) { Win32.SetTextColor(hdc, Util.ToCOLORREF(Color.White)); ChartUtil.DrawText(hdc, x + 3, y - 6, string.Format("{0:N}", d)); } } }