|
///////////////////////////////////////////////////////////////////////////////
//
// SolutionVisualizer.cs
//
// By Philip R. Braica (HoshiKata@aol.com, VeryMadSci@gmail.com)
//
// Distributed under the The Code Project Open License (CPOL)
// http://www.codeproject.com/info/cpol10.aspx
///////////////////////////////////////////////////////////////////////////////
// Using.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
// Namespace.
namespace SolverDemo
{
/// <summary>
/// Visualize the error with two different axises.
/// </summary>
public partial class SolutionVisualizer : UserControl
{
#region Protected data.
/// <summary>
/// Lock object.
/// </summary>
protected object m_lockObject = new object();
/// <summary>
/// Parameter list.
/// </summary>
protected List<Solver.Parameter> m_parameters = null;
/// <summary>
/// Error Delegate.
/// </summary>
protected Solver.ComputeError_Delegate m_computeErrorDelegate = null;
/// <summary>
/// Best point.
/// </summary>
protected Solver.Step m_best = null;
/// <summary>
/// Setup the checkboxes once, that way the selection doesn't keep changing.
/// </summary>
protected bool once = true;
#endregion
#region Public Interface: SolutionVisualizer(), void Setup( ... )
/// <summary>
/// Constructor.
/// </summary>
public SolutionVisualizer()
{
InitializeComponent();
ComboBox[] cbs = { comboBox1, comboBox2 };
for (int i = 0; i < cbs.Length; i++)
{
cbs[i].SelectedIndexChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
}
checkBox1.CheckedChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
checkBox2.CheckedChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
numericUpDown1.ValueChanged += new EventHandler(SolutionVisualizer_DebounceUpdate);
}
/// <summary>
/// Setup.
/// </summary>
/// <param name="p"></param>
/// <param name="computeErrorDelegate"></param>
/// <param name="best"></param>
public void Setup(
List<Solver.Parameter> p,
Solver.ComputeError_Delegate computeErrorDelegate,
Solver.Step best)
{
lock (m_lockObject)
{
m_parameters = p;
m_computeErrorDelegate = computeErrorDelegate;
m_best = best;
List<string> names = new List<string>();
for (int i = 0; i < p.Count; i++)
{
names.Add(p[i].Name);
}
ComboBox[] cbs = { comboBox1, comboBox2 };
for (int i = 0; i < cbs.Length; i++)
{
// If the number of names changed then reset indexes and selected.
if (cbs[i].Items.Count != names.Count)
{
once = true;
}
cbs[i].Items.Clear();
cbs[i].Items.AddRange(names.ToArray());
if (once)
{
cbs[i].SelectedIndex = i < names.Count ? i : names.Count - 1;
cbs[i].Text = cbs[i].SelectedItem.ToString();
}
}
once = false;
}
SolutionVisualizer_DebounceUpdate(this, EventArgs.Empty);
}
#endregion
#region Simple redraw debounce: SolutionVisualizer_DebounceUpdate() -> timer1_Tick() -> redraw()
/// <summary>
/// Trigger redraw.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SolutionVisualizer_DebounceUpdate(object sender, EventArgs e)
{
timer1.Enabled = true;
}
/// <summary>
/// Redraw timer.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
lock (m_lockObject)
{
redraw();
}
}
#endregion
/// <summary>
/// Redraw chart.
/// </summary>
protected virtual void redraw()
{
string pstr = comboBox1.Text;
string sstr = comboBox2.Text;
int pind = 0;
int sind = 0;
for (int i = 0; i < m_parameters.Count; i++)
{
if (pstr == m_parameters[i].Name) pind = i;
if (sstr == m_parameters[i].Name) sind = i;
}
List<Series> set = null;
if (checkBox2.Checked)
{
// Stacked set of lines.
set = makeStacked(
m_parameters[pind].Name, // Label name.
Color.Blue, // Base color.
pind, // Principal index.
sind, // Secondary index.
10); // Number of lines.
}
else
{
// Two seperate indexes as they pass through the center point.
Series s1 = makeSeries(m_parameters[pind].Name, Color.Green, pind);
Series s2 = makeSeries(m_parameters[sind].Name, Color.Blue, sind);
s2.YAxisType = AxisType.Secondary;
set.Add(s1);
set.Add(s2);
}
chart1.Series.Clear();
chart1.ChartAreas[0].RecalculateAxesScale();
for (int i = 0; i < set.Count; i++)
{
chart1.Series.Add(set[i]);
}
}
/// <summary>
/// Adjust color brightness.
/// </summary>
/// <param name="c"></param>
/// <param name="change"></param>
/// <returns></returns>
protected Color adjustColorBrightness(Color c, float change)
{
change = change < -100 ? -100 : change > 100 ? 100 : change * 0.01f;
return (change < 0) ? ControlPaint.Dark(c, -change) : change == 0 ? c : ControlPaint.Light(c, change);
}
/// <summary>
/// Make an array of colors light to dark of color c where c is in the middle.
/// </summary>
/// <param name="c"></param>
/// <param name="div"></param>
/// <returns></returns>
protected Color[] makeColorArray(Color c, int div)
{
Color[] clrs = new Color[div];
for (int i = 0; i < div; i++)
{
float change = (i - (div >> 1)) * 30;
clrs[i] = adjustColorBrightness(c, change);
}
return clrs;
}
/// <summary>
/// Make a series.
/// </summary>
/// <param name="name"></param>
/// <param name="color"></param>
/// <param name="primary"></param>
/// <param name="secondary"></param>
/// <returns></returns>
protected List<Series> makeStacked(string name, Color color, int primary, int secondary, int divisions)
{
List<Series> set = new List<Series>();
bool full = checkBox1.Checked;
double delta = (double)numericUpDown1.Value;
divisions = divisions < 1 ? 1 : divisions;
double sMin = full ? m_parameters[secondary].MinValue : m_best.ParamValues[secondary] - delta;
double sMax = full ? m_parameters[secondary].MaxValue : m_best.ParamValues[secondary] + delta;
double pMin = full ? m_parameters[primary].MinValue : m_best.ParamValues[primary] - delta;
double pMax = full ? m_parameters[primary].MaxValue : m_best.ParamValues[primary] + delta;
double sScale = (sMax - sMin) / divisions;
double pScale = (pMax - pMin) / 200;
Color [] clrs = makeColorArray(color, divisions + 1);
int ind = 0;
double[] p = new double[m_best.ParamValues.Length];
for (int i = 0; i < p.Length; i++)
{
p[i] = m_best.ParamValues[i];
}
for (double sx = sMin; sx < sMax; sx += sScale)
{
Series s = new Series();
s.Name = name + " " + sx.ToString();
s.Color = clrs[ind];
ind++;
s.ChartType = SeriesChartType.Line;
p[secondary] = sx;
surfacePlot1.YAxis.Add(sx);
for (double px = pMin; px < pMax; px += pScale)
{
p[primary] = px;
double y = m_computeErrorDelegate(p);
surfacePlot1.Values.Add(y);
s.Points.Add(new DataPoint(px, y));
}
set.Add(s);
}
surfacePlot1.XAxis.Clear();
surfacePlot1.YAxis.Clear();
surfacePlot1.Values.Clear();
surfacePlot1.ColorList.Clear();
// surfacePlot1.ColorList.AddRange(new Color []{Color.Red, Color.Orange, Color.Yellow, Color.Green,Color.Blue,Color.Indigo, Color.Violet});
surfacePlot1.ColorList.AddRange(new Color[] { Color.White, Color.Black });
sScale = (sMax-sMin) / 200;
pScale = (pMax - pMin) / 200;
for (double px = pMin; px < pMax; px += pScale)
{
surfacePlot1.XAxis.Add(px);
}
for (double sx = sMin; sx < sMax; sx += sScale)
{
ind++;
p[secondary] = sx;
surfacePlot1.YAxis.Add(sx);
for (double px = pMin; px < pMax; px += pScale)
{
p[primary] = px;
double y = m_computeErrorDelegate(p);
surfacePlot1.Values.Add(y);
}
}
surfacePlot1.Redraw();
return set;
}
/// <summary>
/// Make a series.
/// </summary>
/// <param name="name"></param>
/// <param name="color"></param>
/// <param name="paramIndex"></param>
/// <returns></returns>
protected Series makeSeries(string name, Color color, int paramIndex)
{
Series s = new Series();
s.Color = color;
s.ChartType = SeriesChartType.Line;
double[] p = new double[m_best.ParamValues.Length];
for (int i =0; i< p.Length; i++)
{
p[i] = m_best.ParamValues[i];
}
if (checkBox1.Checked)
{
for (double x = m_parameters[paramIndex].MinValue; x < m_parameters[paramIndex].MaxValue; x += m_parameters[paramIndex].InitialStepSize / 10)
{
p[paramIndex] = x;
double y = m_computeErrorDelegate(p);
s.Points.Add(new DataPoint(x, y));
}
}
else
{
double delta = (double)numericUpDown1.Value;
for (double x = m_best.ParamValues[paramIndex] - delta; x < m_best.ParamValues[paramIndex] + delta; x += delta / 20)
{
p[paramIndex] = x;
double y = m_computeErrorDelegate(p);
s.Points.Add(new DataPoint(x, y));
}
}
return s;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Phil is a Principal Software developer focusing on weird yet practical algorithms that run the gamut of embedded and desktop (PID loops, Kalman filters, FFTs, client-server SOAP bindings, ASIC design, communication protocols, game engines, robotics).
In his personal life he is a part time mad scientist, full time dad, and studies small circle jujitsu, plays guitar and piano.