/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { Size s; GH_ObjectWrapper gobj = null; DA.GetData(0, ref gobj); DA.GetDataTree(1, out GH_Structure <GH_Number> data); DA.GetDataTree(2, out GH_Structure <GH_String> keys); DA.GetDataTree(3, out GH_Structure <GH_Colour> clrs); #region get size if (gobj.Value is GH_Rectangle grec) { s = new Size((int)grec.Value.X.Length, (int)grec.Value.Y.Length); } else if (gobj.Value is GH_Vector gvec) { s = new Size((int)gvec.Value.X, (int)gvec.Value.Y); } else if (gobj.Value is GH_ComplexNumber gcomp) { s = new Size((int)gcomp.Value.Real, (int)gcomp.Value.Imaginary); } else if (gobj.Value is GH_Point gpt) { s = new Size((int)gpt.Value.X, (int)gpt.Value.Y); } else if (gobj.Value is GH_Integer gint) { s = new Size(gint.Value, gint.Value); } else if (gobj.Value is GH_Number gn) { s = new Size((int)gn.Value, (int)gn.Value); } else if (gobj.Value is GH_String gstr) { string str = gstr.Value; if (str.Contains(",")) { string[] split = str.Split(','); if (split.Length != 2) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } else { bool a = int.TryParse(split[0], out int xi); bool b = int.TryParse(split[1], out int yi); if (a && b) { s = new Size(xi, yi); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } } else if (int.TryParse(str, out int i)) { s = new Size(i, i); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } else if (gobj.Value is Size etosize) { s = etosize; } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " size object not valid\n use a point, integer, vector, complex number or rectangle\n an actual Eto.Drawing.Size object would be better!"); return; } #endregion Bitmap bitmap = new Bitmap(s, PixelFormat.Format32bppRgba); Graphics graphics = new Graphics(bitmap); ChartAxis axis = new ChartAxis(new RectangleF(s), graphics); if (s.Height <= 10 || s.Width <= 10) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " size too small to draw"); return; } IEnumerable <double> flatdata = data.FlattenData().Select(i => i.Value); double h_incr = (s.Height - 10) / flatdata.Max(); // -10 is leaving space for axes double[] avg = new double[data.Branches.Count]; Color[] etoclrs = new Color[data.Branches.Count]; string[] txtkeys = new string[data.Branches.Count]; for (int bi = 0; bi < data.Branches.Count; bi++) { try { etoclrs.SetValue(clrs.Branches[bi][0].Value.ToEto(), bi); txtkeys.SetValue(keys.Branches[bi][0].Value, bi); } catch (ArgumentOutOfRangeException) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, " mismatch of data length\n if N has 3 branches each with 5 numbers, K and C must be 3 branches each has one item\n did you forget to graft or duplicate data as necessary?"); return; } double[] nums = data.Branches[bi].Select(i => i.Value).ToArray(); avg.SetValue(nums.Average(), bi); double barwidth = (s.Width - 10) / (double)nums.Length; // -10 leaving left for axis PointF[] nodes = new PointF[nums.Length]; Pen pen = new Pen(etoclrs[bi], 2f); for (int i = 0; i < nums.Length; i++) { double x = (i + 0.5) * barwidth + 10; // +10 moving right, leaving left for axis double h = nums[i] * h_incr; double y = s.Height - 10 + 4 - h; // -10 moving up leaving space for x axis, additional +4 moving down to avoid top chop off nodes.SetValue(new PointF((float)x, (float)y), i); } graphics.DrawLines(pen, nodes); foreach (PointF p in nodes) { graphics.DrawArc(pen, p.X - 4, p.Y - 4, 8f, 8f, 0f, 360f); } } axis.Draw(); graphics.Flush(); ImageView graph = new ImageView() { Image = bitmap, }; ChartData bardata = new ChartData(txtkeys, ChartType.Trend) { AppdVals = avg, Colors = etoclrs, }; DA.SetData(0, new GH_ObjectWrapper(graph)); DA.SetData(1, new GH_ObjectWrapper(bardata)); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { Size s; GH_ObjectWrapper gobj = null; List <double> nums = new List <double>(); List <string> keys = new List <string>(); List <GH_Colour> gclrs = new List <GH_Colour>(); DA.GetData(0, ref gobj); DA.GetDataList(1, nums); DA.GetDataList(2, keys); DA.GetDataList(3, gclrs); #region get size if (gobj.Value is GH_Rectangle grec) { double d = new double[] { grec.Value.X.Length, grec.Value.Y.Length }.Min(); s = new Size((int)d, (int)d); } else if (gobj.Value is GH_Vector gvec) { double d = new double[] { gvec.Value.X, gvec.Value.Y }.Min(); s = new Size((int)d, (int)d); } else if (gobj.Value is GH_ComplexNumber gcomp) { double d = new double[] { gcomp.Value.Real, gcomp.Value.Imaginary }.Min(); s = new Size((int)d, (int)d); } else if (gobj.Value is GH_Point gpt) { double d = new double[] { gpt.Value.X, gpt.Value.Y }.Min(); s = new Size((int)d, (int)d); } else if (gobj.Value is GH_Integer gint) { s = new Size(gint.Value, gint.Value); } else if (gobj.Value is GH_Number gn) { s = new Size((int)gn.Value, (int)gn.Value); } else if (gobj.Value is GH_String gstr) { string str = gstr.Value; if (str.Contains(",")) { string[] split = str.Split(','); if (split.Length != 2) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } else { bool a = int.TryParse(split[0], out int xi); bool b = int.TryParse(split[1], out int yi); if (a && b) { int d = new int[] { xi, yi }.Min(); s = new Size(d, d); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string into integers"); return; } } } else if (int.TryParse(str, out int i)) { s = new Size(i, i); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } else if (gobj.Value is Size etosize) { s = etosize; } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " size object not valid\n use a point, integer, vector, complex number or rectangle\n an actual Eto.Drawing.Size object would be better!"); return; } #endregion #region fill defaults List <Color> clrs = new List <Color>(); if (keys.Count == 0) { for (int i = 0; i < nums.Count; i++) { keys.Add(string.Format("[{0}]", i)); } } else if (keys.Count != nums.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " must be same number of data and keys"); return; } if (gclrs.Count == 0) { for (int i = 0; i < nums.Count; i++) { double r = Util.Rand.NextDouble() * 255; double g = Util.Rand.NextDouble() * 255; double b = Util.Rand.NextDouble() * 255; clrs.Add(Color.FromArgb((int)r, (int)g, (int)b)); } } else if (gclrs.Count != nums.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " must be same number of colors and data"); return; } else { foreach (GH_Colour gc in gclrs) { clrs.Add(Color.FromArgb(gc.Value.ToArgb())); } } #endregion Bitmap bitmap = new Bitmap(s, PixelFormat.Format32bppRgba); Graphics graphics = new Graphics(bitmap); double[] pct = new double[nums.Count]; for (int i = 0; i < pct.Length; i++) { pct.SetValue(nums[i] / nums.Sum(), i); } double start = 0; double sweep = pct[0] * 360; for (int i = 0; i < pct.Length; i++) { RectangleF r = new RectangleF(s); graphics.FillPie(clrs[i], r, (float)start, (float)sweep); if (i == pct.Length - 1) { break; } start += sweep; sweep = pct[i + 1] * 360; } graphics.Flush(); ImageView graph = new ImageView() { Image = bitmap, }; ChartData pd = new ChartData(keys, ChartType.Pie) { AppdVals = pct, Colors = clrs.ToArray(), }; DA.SetData(0, new GH_ObjectWrapper(graph)); DA.SetData(1, new GH_ObjectWrapper(pd)); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { Size s; GH_ObjectWrapper gobj = null; List <double> nums = new List <double>(); List <string> keys = new List <string>(); List <GH_Colour> gclrs = new List <GH_Colour>(); DA.GetData(0, ref gobj); DA.GetDataList(1, nums); DA.GetDataList(2, keys); DA.GetDataList(3, gclrs); #region get size if (gobj.Value is GH_Rectangle grec) { s = new Size((int)grec.Value.X.Length, (int)grec.Value.Y.Length); } else if (gobj.Value is GH_Vector gvec) { s = new Size((int)gvec.Value.X, (int)gvec.Value.Y); } else if (gobj.Value is GH_ComplexNumber gcomp) { s = new Size((int)gcomp.Value.Real, (int)gcomp.Value.Imaginary); } else if (gobj.Value is GH_Point gpt) { s = new Size((int)gpt.Value.X, (int)gpt.Value.Y); } else if (gobj.Value is GH_Integer gint) { s = new Size(gint.Value, gint.Value); } else if (gobj.Value is GH_Number gn) { s = new Size((int)gn.Value, (int)gn.Value); } else if (gobj.Value is GH_String gstr) { string str = gstr.Value; if (str.Contains(",")) { string[] split = str.Split(','); if (split.Length != 2) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } else { bool a = int.TryParse(split[0], out int xi); bool b = int.TryParse(split[1], out int yi); if (a && b) { s = new Size(xi, yi); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } } else if (int.TryParse(str, out int i)) { s = new Size(i, i); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } else if (gobj.Value is Size etosize) { s = etosize; } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " size object not valid\n use a point, integer, vector, complex number or rectangle\n an actual Eto.Drawing.Size object would be better!"); return; } #endregion #region fill defaults List <Color> clrs = new List <Color>(); if (keys.Count == 0) { for (int i = 0; i < nums.Count; i++) { keys.Add(string.Format("[{0}]", i)); } } else if (keys.Count != nums.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " must be same number of data and keys"); return; } if (gclrs.Count == 0) { for (int i = 0; i < nums.Count; i++) { double r = Util.Rand.NextDouble() * 255; double g = Util.Rand.NextDouble() * 255; double b = Util.Rand.NextDouble() * 255; clrs.Add(Color.FromArgb((int)r, (int)g, (int)b)); } } else if (gclrs.Count != nums.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " must be same number of colors and data"); return; } else { foreach (GH_Colour gc in gclrs) { clrs.Add(Color.FromArgb(gc.Value.ToArgb())); } } #endregion Bitmap bitmap = new Bitmap(s, PixelFormat.Format32bppRgba); Graphics graphics = new Graphics(bitmap); double[] pct = new double[nums.Count]; for (int i = 0; i < pct.Length; i++) { pct.SetValue(nums[i] / nums.Sum(), i); } ChartData pd = new ChartData(keys, ChartType.Pie) { AppdVals = pct.ToArray(), // make a copy Colors = clrs.ToArray(), }; // doing this before shufflinig pct int[] idx = new int[nums.Count]; for (int i = 0; i < idx.Length; i++) { idx.SetValue(i, i); } Array.Sort(pct, idx); pct = pct.Reverse().ToArray(); idx = idx.Reverse().ToArray(); float ex = s.Width; float ey = s.Height; float x0 = 0; float y0 = 0; double A = ex * ey; for (int i = 0; i < pct.Length; i++) { double pp = pct[i]; int id = idx[i]; if (i == pct.Length - 1) { // last rect, fill whatever is left graphics.FillRectangle(clrs[id], x0, y0, s.Width - x0, s.Height - y0); } else if (ex > ey) { double area = A * pp; double rectx = area / ey; graphics.FillRectangle(clrs[id], x0, y0, (float)rectx, ey); ex -= (float)rectx; // remainder edge x length x0 += (float)rectx; // move rect origin } else { double area = A * pp; double recty = area / ex; graphics.FillRectangle(clrs[id], x0, y0, ex, (float)recty); ey -= (float)recty; // remainder edge y length y0 += (float)recty; // move rect origin } } graphics.Flush(); ImageView graph = new ImageView() { Image = bitmap, }; DA.SetData(0, new GH_ObjectWrapper(graph)); DA.SetData(1, new GH_ObjectWrapper(pd)); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { GH_ObjectWrapper gobj = new GH_ObjectWrapper(); Size s; List <double> nums = new List <double>(); List <string> keys = new List <string>(); List <GH_Colour> gclrs = new List <GH_Colour>(); DA.GetData(0, ref gobj); DA.GetDataList(1, nums); DA.GetDataList(2, keys); DA.GetDataList(3, gclrs); List <Color> clrs = new List <Color>(); #region get size if (gobj.Value is GH_Rectangle grec) { s = new Size((int)grec.Value.X.Length, (int)grec.Value.Y.Length); } else if (gobj.Value is GH_Vector gvec) { s = new Size((int)gvec.Value.X, (int)gvec.Value.Y); } else if (gobj.Value is GH_ComplexNumber gcomp) { s = new Size((int)gcomp.Value.Real, (int)gcomp.Value.Imaginary); } else if (gobj.Value is GH_Point gpt) { s = new Size((int)gpt.Value.X, (int)gpt.Value.Y); } else if (gobj.Value is GH_Integer gint) { s = new Size(gint.Value, gint.Value); } else if (gobj.Value is GH_Number gn) { s = new Size((int)gn.Value, (int)gn.Value); } else if (gobj.Value is GH_String gstr) { string str = gstr.Value; if (str.Contains(",")) { string[] split = str.Split(','); if (split.Length != 2) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } else { bool a = int.TryParse(split[0], out int xi); bool b = int.TryParse(split[1], out int yi); if (a && b) { s = new Size(xi, yi); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } } else if (int.TryParse(str, out int i)) { s = new Size(i, i); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " cannot parse size input string"); return; } } else if (gobj.Value is Size etosize) { s = etosize; } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " size object not valid\n use a point, integer, vector, complex number or rectangle\n an actual Eto.Drawing.Size object would be better!"); return; } #endregion #region fill defaults if (keys.Count == 0) { for (int i = 0; i < nums.Count; i++) { keys.Add(string.Format("[{0}]", i)); } } else if (keys.Count != nums.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " must be same number of data and keys"); return; } if (gclrs.Count == 0) { for (int i = 0; i < nums.Count; i++) { double r = Util.Rand.NextDouble() * 255; double g = Util.Rand.NextDouble() * 255; double b = Util.Rand.NextDouble() * 255; clrs.Add(Color.FromArgb((int)r, (int)g, (int)b)); } } else if (gclrs.Count != nums.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " must be same number of colors and data"); return; } else { foreach (GH_Colour gc in gclrs) { clrs.Add(Color.FromArgb(gc.Value.ToArgb())); } } #endregion Bitmap bitmap = new Bitmap(s, PixelFormat.Format32bppRgba); Graphics graphics = new Graphics(bitmap); ChartAxis axis = new ChartAxis(new RectangleF(s), graphics); double[] pct = new double[nums.Count]; for (int i = 0; i < pct.Length; i++) { pct.SetValue(nums[i] / nums.Sum(), i); } if (s.Height <= 10 || s.Width <= 10) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, " size too small to draw"); return; } double barwidth = (s.Width - 10) / (double)pct.Length; double h_incr = (s.Height - 10) / pct.Max(); // 10 is space for axes for (int i = 0; i < pct.Length; i++) { // Graphics draw from screen top left so +y means going down the screen // drawing rectangle always goes from rectangle top left corner double h = pct[i] * h_incr; double x = i * barwidth + 10; double y = s.Height - 10 - h; // -10 is y bottom, minus height to get rectangle *top* left corner graphics.FillRectangle(clrs[i], (float)x, (float)y, (float)barwidth, (float)h); } axis.Draw(); graphics.Flush(); ImageView graph = new ImageView() { Image = bitmap, }; ChartData bardata = new ChartData(keys, ChartType.Bar) { AppdVals = nums.Select(n => Math.Round(n, 2)).ToArray(), Colors = clrs.ToArray(), }; DA.SetData(0, new GH_ObjectWrapper(graph)); DA.SetData(1, new GH_ObjectWrapper(bardata)); }