Changeset 13831 for branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/GradientChart.cs
- Timestamp:
- 05/04/16 15:00:49 (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/GradientChart.cs
r13830 r13831 22 22 using System; 23 23 using System.Collections.Generic; 24 using System.Drawing;25 24 using System.Globalization; 26 25 using System.Linq; … … 31 30 32 31 namespace HeuristicLab.Problems.DataAnalysis.Views { 33 public partial class GradientChart : EnhancedChart{34 private ModifiableDataset shared Dataset; // used for syncronising variable values between charts32 public partial class GradientChart : UserControl { 33 private ModifiableDataset sharedFixedVariables; // used for syncronising variable values between charts 35 34 private ModifiableDataset internalDataset; // used to cache values and speed up calculations 36 35 37 public bool ShowLegend { get; set; } 38 public bool ShowXAxisLabel { get; set; } 39 public bool ShowYAxisLabel { get; set; } 40 public bool ShowCursor { get; set; } 41 42 private bool useMedianValues; 43 public bool UseMedianValues { 44 get { return useMedianValues; } 36 public bool ShowLegend { 37 get { return chart.Legends[0].Enabled; } 38 set { chart.Legends[0].Enabled = value; } 39 } 40 public bool ShowXAxisLabel { 41 get { return chart.ChartAreas[0].AxisX.Enabled == AxisEnabled.True; } 42 set { chart.ChartAreas[0].AxisX.Enabled = value ? AxisEnabled.True : AxisEnabled.False; } 43 } 44 public bool ShowYAxisLabel { 45 get { return chart.ChartAreas[0].AxisY.Enabled == AxisEnabled.True; } 46 set { chart.ChartAreas[0].AxisY.Enabled = value ? AxisEnabled.True : AxisEnabled.False; } 47 } 48 public bool ShowCursor { 49 get { return chart.Annotations[0].Visible; } 50 set { chart.Annotations[0].Visible = value; } 51 } 52 53 private int xAxisTicks = 5; 54 public int XAxisTicks { 55 get { return xAxisTicks; } 56 set { if (xAxisTicks != value) { xAxisTicks = value; UpdateChart(); } } 57 } 58 private int yAxisTicks = 5; 59 public int YXAxisTicks { 60 get { return yAxisTicks; } 61 set { if (yAxisTicks != value) { yAxisTicks = value; UpdateChart(); } } 62 } 63 64 private double trainingMin = double.MinValue; 65 public double TrainingMin { 66 get { return trainingMin; } 67 set { if (!value.IsAlmost(trainingMin)) { trainingMin = value; UpdateChart(); } } 68 } 69 private double trainingMax = double.MaxValue; 70 public double TrainingMax { 71 get { return trainingMax; } 72 set { if (!value.IsAlmost(trainingMax)) { trainingMax = value; UpdateChart(); } } 73 } 74 75 private int drawingSteps = 1000; 76 public int DrawingSteps { 77 get { return drawingSteps; } 78 set { if (value != drawingSteps) { drawingSteps = value; UpdateChart(); } } 79 } 80 81 private string freeVariable; 82 public string FreeVariable { 83 get { return freeVariable; } 45 84 set { 46 if (value == useMedianValues) return; 47 useMedianValues = value; 48 OnChartPropertyChanged(this, EventArgs.Empty); 85 if (value == freeVariable) return; 86 if (solutions.Any(s => !s.ProblemData.Dataset.DoubleVariables.Contains(value))) { 87 throw new ArgumentException("Variable does not exist in the ProblemData of the Solutions."); 88 } 89 freeVariable = value; 90 RecalculateInternalDataset(); 49 91 UpdateChart(); 50 92 } 51 93 } 52 94 53 private int row; 54 public int Row { 55 get { return row; } 56 set { 57 if (row == value) return; 58 row = value; 59 OnChartPropertyChanged(this, EventArgs.Empty); 60 UpdateChart(); 61 } 62 } 63 64 private double min; 65 public double Min { 66 get { return min; } 67 set { 68 if (value.IsAlmost(min)) return; 69 min = value; 70 OnChartPropertyChanged(this, EventArgs.Empty); 71 UpdateChart(); 72 } 73 } 74 75 private double max; 76 public double Max { 77 get { return max; } 78 set { 79 if (value.IsAlmost(max)) return; 80 max = value; 81 OnChartPropertyChanged(this, EventArgs.Empty); 82 UpdateChart(); 83 } 84 } 85 86 private int points; 87 public int Points { 88 get { return points; } 89 set { 90 if (value == points) return; 91 points = value; 92 OnChartPropertyChanged(this, EventArgs.Empty); 93 UpdateChart(); 94 } 95 } 96 97 private IRegressionProblemData problemData; 98 public IRegressionProblemData ProblemData { 99 get { return problemData; } 100 set { 101 if (!SolutionsCompatibleWithProblemData(value, solutionList)) 102 throw new ArgumentException("The problem data provided does not contain all the variables required by the solutions."); 103 problemData = value; 104 UpdateDataset(); 105 UpdateChart(); 106 } 107 } 108 109 public string Target { 110 get { return Solutions.First().ProblemData.TargetVariable; } 111 } 112 113 private string variable; 114 public string Variable { 115 get { return variable; } 116 set { 117 if (variable == value) return; 118 if (!ProblemData.Dataset.DoubleVariables.Contains(value)) 119 throw new ArgumentException("The variable must be present in the problem dataset."); 120 OnChartPropertyChanged(this, EventArgs.Empty); 121 variable = value; 122 var values = ProblemData.Dataset.GetReadOnlyDoubleValues(variable); 123 min = values.Min(); 124 max = values.Max(); 125 UpdateChart(); 126 } 127 } 128 129 private List<IRegressionSolution> solutionList; 95 private bool updateChartAutomatically = false; 96 public bool UpdateChartAutomatically { 97 get { return updateChartAutomatically; } 98 set { updateChartAutomatically = value; if (updateChartAutomatically) UpdateChart(); } 99 } 100 101 private readonly List<IRegressionSolution> solutions = new List<IRegressionSolution>(); 130 102 public IEnumerable<IRegressionSolution> Solutions { 131 get { return solutionList; } 132 set { 133 if (!value.Any()) 134 throw new ArgumentException("At least one solution must be provided."); 135 if (SolutionsCompatibleWithProblemData(problemData, value)) 136 solutionList = new List<IRegressionSolution>(value); 137 else 138 throw new ArgumentException("The provided solution collection is not compatible with the existing problem data."); 139 UpdateChart(); 140 } 141 } 142 143 public VerticalLineAnnotation VerticalLineAnnotation { 144 get { return (VerticalLineAnnotation)Annotations.SingleOrDefault(x => x is VerticalLineAnnotation); } 103 get { return solutions; } 104 } 105 106 private VerticalLineAnnotation VerticalLineAnnotation { 107 get { return (VerticalLineAnnotation)chart.Annotations.SingleOrDefault(x => x is VerticalLineAnnotation); } 145 108 } 146 109 147 110 public GradientChart() { 148 111 InitializeComponent(); 149 RegisterEvents(); 150 } 151 152 public void AddSolution(IRegressionSolution solution) { 153 if (!SolutionsCompatibleWithProblemData(problemData, new[] { solution })) { 154 throw new ArgumentException("The solution is not compatible with the problem data."); 155 } 156 solutionList.Add(solution); 157 UpdateChart(); 158 } 159 160 public void RemoveSolution(IRegressionSolution solution) { 161 var removed = solutionList.RemoveAll(x => x == solution); 162 if (removed > 0) 163 UpdateChart(); 164 } 165 166 private static bool SolutionsCompatibleWithProblemData(IRegressionProblemData pd, IEnumerable<IRegressionSolution> solutions) { 167 if (pd == null || !solutions.Any()) return true; 168 if (solutions.Any(x => x.ProblemData.TargetVariable != pd.TargetVariable)) return false; 169 var variables = new HashSet<string>(pd.Dataset.DoubleVariables); 170 return solutions.SelectMany(x => x.ProblemData.Dataset.DoubleVariables).All(variables.Contains); 171 } 172 173 public void Configure(IEnumerable<IRegressionSolution> solutions, IRegressionProblemData pd, ModifiableDataset dataset, string variable, double min, double max, int points) { 174 if (!SolutionsCompatibleWithProblemData(pd, solutions)) 112 } 113 114 public void Configure(IEnumerable<IRegressionSolution> solutions, ModifiableDataset sharedFixedVariables, string freeVariable, int drawingSteps) { 115 if (!SolutionsCompatible(solutions)) 175 116 throw new ArgumentException("Solutions are not compatible with the problem data."); 176 this.solutionList = new List<IRegressionSolution>(solutions); 177 this.problemData = pd; 178 this.variable = variable; 179 this.sharedDataset = dataset; 180 this.min = min; 181 this.max = max; 182 this.points = points; 117 this.solutions.Clear(); 118 this.solutions.AddRange(solutions); 119 this.freeVariable = freeVariable; 120 this.drawingSteps = drawingSteps; 183 121 184 122 // add an event such that whenever a value is changed in the shared dataset, 185 123 // this change is reflected in the internal dataset (where the value becomes a whole column) 186 var variables = sharedDataset.DoubleVariables.ToList(); 187 sharedDataset.ItemChanged += (o, e) => { 188 var rowIndex = e.Value; 189 var columnIndex = e.Value2; 190 var ds = (ModifiableDataset)o; 191 var variableName = variables[columnIndex]; 192 if (variableName == Variable) return; 193 var v = ds.GetDoubleValue(variableName, rowIndex); 194 var values = new List<double>(Enumerable.Repeat(v, Points)); 195 internalDataset.ReplaceVariable(variableName, values); 196 }; 197 198 // configure internal dataset. we also expand the range in order to get nice tick intervals on the x axis 199 const int tics = 5; 124 if (this.sharedFixedVariables != null) 125 this.sharedFixedVariables.ItemChanged -= sharedFixedVariables_ItemChanged; 126 this.sharedFixedVariables = sharedFixedVariables; 127 this.sharedFixedVariables.ItemChanged += sharedFixedVariables_ItemChanged; 128 129 trainingMin = solutions.Select(s => s.ProblemData.Dataset.GetDoubleValues(freeVariable, s.ProblemData.TrainingIndices).Min()).Max(); 130 trainingMax = solutions.Select(s => s.ProblemData.Dataset.GetDoubleValues(freeVariable, s.ProblemData.TrainingIndices).Max()).Min(); 131 132 RecalculateInternalDataset(); 133 } 134 135 private void sharedFixedVariables_ItemChanged(object o, EventArgs<int, int> e) { 136 var sender = (ModifiableDataset)o; 137 var variables = sharedFixedVariables.DoubleVariables.ToList(); 138 var rowIndex = e.Value; 139 var columnIndex = e.Value2; 140 141 var variableName = variables[columnIndex]; 142 if (variableName == FreeVariable) return; 143 var v = sender.GetDoubleValue(variableName, rowIndex); 144 var values = new List<double>(Enumerable.Repeat(v, DrawingSteps)); 145 internalDataset.ReplaceVariable(variableName, values); 146 147 if (UpdateChartAutomatically) 148 UpdateChart(); 149 } 150 151 private void RecalculateInternalDataset() { 152 // we expand the range in order to get nice tick intervals on the x axis 200 153 double xmin, xmax, xinterval; 201 ChartUtil.CalculateAxisInterval(min, max, tics, out xmin, out xmax, out xinterval); 202 var step = (xmax - xmin) / points; 154 ChartUtil.CalculateAxisInterval(trainingMin, trainingMax, XAxisTicks, out xmin, out xmax, out xinterval); 155 double step = (xmax - xmin) / drawingSteps; 156 203 157 var xvalues = new List<double>(); 204 for (int i = 0; i < points; ++i) { xvalues.Add(xmin + i * step); } 205 internalDataset = new ModifiableDataset(variables, variables.Select(x => x == Variable ? xvalues : new List<double>(Enumerable.Repeat(sharedDataset.GetDoubleValue(x, 0), xvalues.Count)))); 158 for (int i = 0; i < drawingSteps; i++) 159 xvalues.Add(xmin + i * step); 160 161 var variables = sharedFixedVariables.DoubleVariables.ToList(); 162 internalDataset = new ModifiableDataset(variables, 163 variables.Select(x => x == FreeVariable 164 ? xvalues 165 : Enumerable.Repeat(sharedFixedVariables.GetDoubleValue(x, 0), xvalues.Count).ToList() 166 ) 167 ); 206 168 } 207 169 208 170 public void UpdateChart() { 209 171 // throw exceptions? 210 if (shared Dataset == null || solutionList == null || !solutionList.Any())172 if (sharedFixedVariables == null || solutions == null || !solutions.Any()) 211 173 return; 212 if ( min.IsAlmost(max) || min > max || points == 0)174 if (trainingMin.IsAlmost(trainingMax) || trainingMin > trainingMax || drawingSteps == 0) 213 175 return; 214 Series.Clear(); 215 var vla = VerticalLineAnnotation; 216 Annotations.Clear(); 217 var defaultValue = sharedDataset.GetDoubleValue(variable, 0); 218 vla.Visible = ShowCursor; 219 Annotations.Add(vla); 220 vla.X = defaultValue; 221 176 177 // Set cursor 178 var defaultValue = sharedFixedVariables.GetDoubleValue(freeVariable, 0); 179 VerticalLineAnnotation.X = defaultValue; 180 181 // Calculate X-axis interval 222 182 double axisMin, axisMax, axisInterval; 223 // calculate X-axis interval 224 ChartUtil.CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval); 225 var axis = ChartAreas[0].AxisX; 183 ChartUtil.CalculateAxisInterval(trainingMin, trainingMax, XAxisTicks, out axisMin, out axisMax, out axisInterval); 184 var axis = chart.ChartAreas[0].AxisX; 226 185 axis.Minimum = axisMin; 227 186 axis.Maximum = axisMax; 228 187 axis.Interval = axisInterval; 229 188 230 for (int i = 0; i < solutionList.Count; ++i) { 231 var solution = solutionList[i]; 232 var series = PlotSeries(solution); 233 series.Name = Target + " " + i; 234 Series.Add(series); 235 } 236 // calculate Y-axis interval 237 double ymin = 0, ymax = 0; 238 foreach (var v in Series[0].Points.Select(x => x.YValues[0])) { 239 if (ymin > v) ymin = v; 240 if (ymax < v) ymax = v; 241 } 242 ChartUtil.CalculateAxisInterval(ymin, ymax, 5, out axisMin, out axisMax, out axisInterval); 243 axis = ChartAreas[0].AxisY; 244 axis.Minimum = axisMin; 245 axis.Maximum = axisMax; 246 axis.Interval = axisInterval; 247 248 if (ShowXAxisLabel) { 249 ChartAreas[0].AxisX.Title = Variable + " : " + defaultValue.ToString("N3", CultureInfo.CurrentCulture); // set axis title 250 } 251 252 AddStripLines(); // add strip lines 253 if (ShowLegend) 254 AddLegends(); 255 } 256 257 private void UpdateDataset() { 258 var variables = ProblemData.Dataset.DoubleVariables.ToList(); 259 var variableValues = new List<double>[variables.Count]; 260 261 if (UseMedianValues) { 262 for (int i = 0; i < variables.Count; ++i) { 263 var median = ProblemData.Dataset.GetDoubleValues(variables[i], ProblemData.TrainingIndices).Median(); 264 variableValues[i] = new List<double> { median }; 189 // Create series 190 chart.Series.Clear(); 191 for (int i = 0; i < solutions.Count; ++i) { 192 var solution = solutions[i]; 193 Series confidenceIntervalPlotSeries; 194 var series = CreateSeries(solution, out confidenceIntervalPlotSeries); 195 series.Name = Solutions.First().ProblemData.TargetVariable + " " + i; 196 if (confidenceIntervalPlotSeries != null) 197 chart.Series.Add(confidenceIntervalPlotSeries); 198 chart.Series.Add(series); 199 } 200 //// calculate Y-axis interval 201 //double ymin = 0, ymax = 0; 202 //foreach (var v in chart.Series[0].Points.Select(x => x.YValues[0])) { 203 // if (ymin > v) ymin = v; 204 // if (ymax < v) ymax = v; 205 //} 206 //ChartUtil.CalculateAxisInterval(ymin, ymax, YXAxisTicks, out axisMin, out axisMax, out axisInterval); 207 //axis = chart.ChartAreas[0].AxisY; 208 //axis.Minimum = axisMin; 209 //axis.Maximum = axisMax; 210 //axis.Interval = axisInterval; 211 212 // set axis title 213 chart.ChartAreas[0].AxisX.Title = FreeVariable + " : " + defaultValue.ToString("N3", CultureInfo.CurrentCulture); 214 215 UpdateStripLines(); 216 } 217 218 private Series CreateSeries(IRegressionSolution solution, out Series confidenceIntervalPlotSeries) { 219 var series = new Series { 220 ChartType = SeriesChartType.Line 221 }; 222 223 var xvalues = internalDataset.GetDoubleValues(FreeVariable).ToList(); 224 var yvalues = solution.Model.GetEstimatedValues(internalDataset, Enumerable.Range(0, internalDataset.Rows)).ToList(); 225 series.Points.DataBindXY(xvalues, yvalues); 226 227 var confidenceBoundSolution = solution as IConfidenceBoundRegressionSolution; 228 if (confidenceBoundSolution != null) { 229 var variances = confidenceBoundSolution.Model.GetEstimatedVariances(internalDataset, Enumerable.Range(0, internalDataset.Rows)).ToList(); 230 231 var lower = yvalues.Zip(variances, (m, s2) => m - 1.96 * Math.Sqrt(s2)).ToList(); 232 var upper = yvalues.Zip(variances, (m, s2) => m + 1.96 * Math.Sqrt(s2)).ToList(); 233 234 confidenceIntervalPlotSeries = new Series { 235 ChartType = SeriesChartType.Range, 236 YValuesPerPoint = 2 237 }; 238 confidenceIntervalPlotSeries.Points.DataBindXY(xvalues, lower, upper); 239 } else { 240 confidenceIntervalPlotSeries = null; 241 } 242 243 return series; 244 } 245 246 public void AddSolution(IRegressionSolution solution) { 247 if (!SolutionsCompatible(solutions.Concat(new[] { solution }))) 248 throw new ArgumentException("The solution is not compatible with the problem data."); 249 if (solutions.Contains(solution)) return; 250 solutions.Add(solution); 251 UpdateChart(); 252 } 253 public void RemoveSolution(IRegressionSolution solution) { 254 bool removed = solutions.Remove(solution); 255 if (removed) 256 UpdateChart(); 257 } 258 259 private static bool SolutionsCompatible(IEnumerable<IRegressionSolution> solutions) { 260 foreach (var solution1 in solutions) { 261 var variables1 = solution1.ProblemData.Dataset.DoubleVariables; 262 foreach (var solution2 in solutions) { 263 if (solution1 == solution2) 264 continue; 265 var variables2 = solution2.ProblemData.Dataset.DoubleVariables; 266 if (!variables1.All(variables2.Contains)) 267 return false; 265 268 } 266 } else { 267 for (int i = 0; i < variables.Count; ++i) { 268 var variableValue = ProblemData.Dataset.GetDoubleValue(variables[i], Row); 269 variableValues[i] = new List<double> { variableValue }; 270 } 271 } 272 sharedDataset = new ModifiableDataset(variables, variableValues); 273 } 274 275 private Series PlotSeries(IRegressionSolution solution) { 276 var v = sharedDataset.GetDoubleValue(variable, 0); 277 var series = new Series { ChartType = SeriesChartType.Point }; 278 // get values from series 279 var xvalues = internalDataset.GetReadOnlyDoubleValues(Variable); 280 var yvalues = solution.Model.GetEstimatedValues(internalDataset, Enumerable.Range(0, internalDataset.Rows)); 281 int i = 0; 282 foreach (var y in yvalues) { 283 var x = xvalues[i++]; 284 series.Points.Add(new DataPoint(x, y) { MarkerSize = 2, MarkerColor = Color.DodgerBlue }); 285 } 286 if (ShowCursor) { 287 var y = solution.Model.GetEstimatedValues(sharedDataset, new[] { 0 }).Single(); 288 series.Points.Add(new DataPoint(v, y) { MarkerSize = 5, MarkerColor = Color.Red }); 289 } 290 if (ShowLegend) { 291 series.IsVisibleInLegend = true; 292 } 293 return series; 294 } 295 296 private void AddLegends() { 297 Legends.Clear(); 298 var legend = new Legend(); 299 legend.Alignment = StringAlignment.Center; 300 legend.LegendStyle = LegendStyle.Row; 301 legend.Docking = Docking.Top; 302 Legends.Add(legend); 303 foreach (var s in Series) { 304 s.Legend = legend.Name; 305 } 306 } 307 308 private void AddStripLines() { 309 var axisX = ChartAreas[0].AxisX; 310 axisX.StripLines.Clear(); 311 axisX.StripLines.Add(new StripLine { BackColor = Color.FromArgb(30, Color.Green), IntervalOffset = axisX.Minimum, StripWidth = min - axisX.Minimum }); 312 axisX.StripLines.Add(new StripLine { BackColor = Color.FromArgb(30, Color.Green), IntervalOffset = max, StripWidth = axisX.Maximum - max }); 313 } 314 315 private void RegisterEvents() { 316 AnnotationPositionChanging += chart_AnnotationPositionChanging; 317 MouseMove += chart_MouseMove; 318 FormatNumber += chart_FormatNumber; 269 } 270 return true; 271 } 272 273 private void UpdateStripLines() { 274 var axisX = chart.ChartAreas[0].AxisX; 275 var lowerStripLine = axisX.StripLines[0]; 276 var upperStripLine = axisX.StripLines[1]; 277 278 lowerStripLine.IntervalOffset = axisX.Minimum; 279 lowerStripLine.StripWidth = trainingMin - axisX.Minimum; 280 281 upperStripLine.IntervalOffset = trainingMax; 282 upperStripLine.StripWidth = axisX.Maximum - trainingMax; 319 283 } 320 284 … … 327 291 } 328 292 329 public event EventHandler ChartPropertyChanged;330 public void OnChartPropertyChanged(object sender, EventArgs args) {331 var changed = ChartPropertyChanged;332 if (changed == null) return;333 changed(sender, args);334 }335 336 293 private void chart_AnnotationPositionChanged(object sender, EventArgs e) { 337 294 var annotation = VerticalLineAnnotation; 338 295 var x = annotation.X; 339 sharedDataset.SetVariableValue(x, Variable, 0); 340 for (int i = 0; i < solutionList.Count; ++i) { 341 var y = solutionList[i].Model.GetEstimatedValues(sharedDataset, new[] { 0 }).Single(); 342 var s = Series[i]; 343 var n = s.Points.Count; 344 s.Points[n - 1] = new DataPoint(x, y) { MarkerColor = Color.Red, MarkerSize = 5 }; 345 } 346 if (ShowXAxisLabel) { 347 ChartAreas[0].AxisX.Title = Variable + " : " + x.ToString("N3", CultureInfo.CurrentCulture); 348 } 349 Update(); 296 sharedFixedVariables.SetVariableValue(x, FreeVariable, 0); 297 298 chart.ChartAreas[0].AxisX.Title = FreeVariable + " : " + x.ToString("N3", CultureInfo.CurrentCulture); 299 chart.Update(); 300 350 301 OnVariableValueChanged(this, EventArgs.Empty); 351 302 } 352 303 353 304 private void chart_AnnotationPositionChanging(object sender, AnnotationPositionChangingEventArgs e) { 354 var step = (max - min) / points; 355 e.NewLocationX = step * (long)Math.Round(e.NewLocationX / step); 356 var axisX = ChartAreas[0].AxisX; 357 if (e.NewLocationX > axisX.Maximum) 358 e.NewLocationX = axisX.Maximum; 359 if (e.NewLocationX < axisX.Minimum) 360 e.NewLocationX = axisX.Minimum; 305 //var step = (trainingMax - trainingMin) / drawingSteps; 306 //e.NewLocationX = step * (long)Math.Round(e.NewLocationX / step); 307 //var axisX = chart.ChartAreas[0].AxisX; 308 //if (e.NewLocationX > axisX.Maximum) 309 // e.NewLocationX = axisX.Maximum; 310 //if (e.NewLocationX < axisX.Minimum) 311 // e.NewLocationX = axisX.Minimum; 312 313 var annotation = VerticalLineAnnotation; 314 var x = annotation.X; 315 sharedFixedVariables.SetVariableValue(x, FreeVariable, 0); 316 317 chart.ChartAreas[0].AxisX.Title = FreeVariable + " : " + x.ToString("N3", CultureInfo.CurrentCulture); 318 chart.Update(); 319 320 OnVariableValueChanged(this, EventArgs.Empty); 361 321 } 362 322 363 323 private void chart_MouseMove(object sender, MouseEventArgs e) { 364 this.Cursor =HitTest(e.X, e.Y).ChartElementType == ChartElementType.Annotation ? Cursors.VSplit : Cursors.Default;324 chart.Cursor = chart.HitTest(e.X, e.Y).ChartElementType == ChartElementType.Annotation ? Cursors.VSplit : Cursors.Default; 365 325 } 366 326 … … 388 348 } 389 349 } 390 391 350 private void GradientChart_DragEnter(object sender, DragEventArgs e) { 392 351 if (!e.Data.GetDataPresent(HeuristicLab.Common.Constants.DragDropDataFormat)) return;
Note: See TracChangeset
for help on using the changeset viewer.