#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using HeuristicLab.Common.Resources;
using HeuristicLab.Core.Views;
using HeuristicLab.Encodings.IntegerVectorEncoding;
using HeuristicLab.MainForm;
using HeuristicLab.MainForm.WindowsForms;
namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Views {
[View("GQAPAssignmentView")]
[Content(typeof(GQAPAssignment), IsDefaultView = true)]
public partial class GQAPAssignmentView : ItemView {
private static readonly double BrightnessLevel = 0.5;
public new GQAPAssignment Content {
get { return (GQAPAssignment)base.Content; }
set { base.Content = value; }
}
public GQAPAssignmentView() {
InitializeComponent();
recalculateButton.Text = string.Empty;
recalculateButton.Image = VSImageLibrary.Refresh;
}
#region Register Content Events
protected override void DeregisterContentEvents() {
DeregisterSolutionEvents();
Content.PropertyChanged -= Content_PropertyChanged;
base.DeregisterContentEvents();
}
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
Content.PropertyChanged += Content_PropertyChanged;
RegisterSolutionEvents();
}
private void DeregisterSolutionEvents() {
if (Content.Solution != null) Content.Solution.PropertyChanged -= Content_SolutionPropertyChanged;
}
private void RegisterSolutionEvents() {
if (Content.Solution != null) Content.Solution.PropertyChanged += Content_SolutionPropertyChanged;
}
#endregion
protected override void OnContentChanged() {
base.OnContentChanged();
UpdateEvaluation();
UpdateAssignment();
if (Content != null && Content.Solution != null) assignmentView.Content = Content.Solution.Assignment;
else assignmentView.Content = null;
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
recalculateButton.Enabled = Content != null && Content.Solution != null && !Locked && !ReadOnly;
}
#region Content Event Handlers
private void Content_PropertyChanged(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName) {
case nameof(Content.Solution): RegisterSolutionEvents(); UpdateEvaluation(); UpdateAssignment(); break;
case nameof(Content.ProblemInstance): UpdateEvaluation(); UpdateAssignment(); break;
default: break;
}
SetEnabledStateOfControls();
}
private void Content_SolutionPropertyChanged(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName) {
case nameof(Content.Solution.Assignment): UpdateAssignment(); break;
case nameof(Content.Solution.Evaluation): UpdateEvaluation(); break;
default: break;
}
}
private void Content_Assignment_ToStringChanged(object sender, EventArgs e) {
UpdateAssignment();
}
#endregion
#region Event Handlers
private void assignmentTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
if (Content != null && Content.Solution != null) {
assignmentTreeView.BeginUpdate();
try {
if (assignmentTreeView.SelectedNode == null) {
ColorByInstallationCosts();
} else {
var equipmentNode = (assignmentTreeView.SelectedNode as EquipmentNode);
if (equipmentNode != null) {
ColorByWeight(equipmentNode);
}
var locationNode = (assignmentTreeView.SelectedNode as LocationNode);
if (locationNode != null) {
ColorByWeight(locationNode);
}
}
} finally { assignmentTreeView.EndUpdate(); }
}
}
private void assignmentTreeView_MouseUp(object sender, MouseEventArgs e) {
var hit = assignmentTreeView.HitTest(e.X, e.Y);
if (hit.Node == null || hit.Location == TreeViewHitTestLocations.None || hit.Location == TreeViewHitTestLocations.RightOfLabel) {
assignmentTreeView.SelectedNode = null;
ColorByInstallationCosts();
}
}
#endregion
#region Helpers
private void UpdateEvaluation() {
if (InvokeRequired) Invoke((Action)UpdateEvaluation);
else {
if (Content == null || Content.Solution == null) {
qualityLabel.Text = "-";
flowDistanceQualityLabel.Text = "-";
installationQualityLabel.Text = "-";
overbookedCapacityLabel.Text = "-";
} else {
qualityLabel.Text = Content.ProblemInstance.ToSingleObjective(Content.Solution.Evaluation).ToString();
flowDistanceQualityLabel.Text = Content.Solution.Evaluation.FlowCosts.ToString();
installationQualityLabel.Text = Content.Solution.Evaluation.InstallationCosts.ToString();
overbookedCapacityLabel.Text = Content.Solution.Evaluation.ExcessDemand.ToString();
}
}
}
private void UpdateAssignment() {
if (InvokeRequired) Invoke((Action)UpdateAssignment);
else {
assignmentTreeView.Nodes.Clear();
if (Content != null && Content.Solution != null) {
IntegerVector assignment = Content.Solution.Assignment;
Dictionary locationNodes = new Dictionary();
for (int i = 0; i < assignment.Length; i++) {
if (!locationNodes.ContainsKey(assignment[i])) {
string locationName = assignment[i].ToString();
if (Content.ProblemInstance.LocationNames != null && Content.ProblemInstance.LocationNames.Length > assignment[i])
locationName = Content.ProblemInstance.LocationNames[assignment[i]];
locationNodes.Add(assignment[i], new LocationNode(assignment[i], locationName));
}
string equipmentName = i.ToString();
if (Content.ProblemInstance.EquipmentNames != null && Content.ProblemInstance.EquipmentNames.Length > i)
equipmentName = Content.ProblemInstance.EquipmentNames[i];
locationNodes[assignment[i]].Nodes.Add(new EquipmentNode(i, equipmentName));
}
assignmentTreeView.BeginUpdate();
try {
foreach (var node in locationNodes.OrderBy(x => x.Key).Select(x => x.Value)) {
assignmentTreeView.Nodes.Add(node);
node.Expand();
}
ColorByInstallationCosts();
} finally { assignmentTreeView.EndUpdate(); }
}
}
}
private void ColorByInstallationCosts() {
var installationCosts = new Dictionary();
foreach (var node in GetAllSubNodes(assignmentTreeView.Nodes).OfType()) {
int location = Content.Solution.Assignment[node.Equipment];
installationCosts[node] = Content.ProblemInstance.InstallationCosts[node.Equipment, location];
}
double max = installationCosts.Values.Max();
foreach (var kvp in installationCosts) {
if (max == 0) {
kvp.Key.BackColor = assignmentTreeView.BackColor;
kvp.Key.ForeColor = assignmentTreeView.ForeColor;
} else {
int colorComponent = (int)(255 * Math.Pow((max - kvp.Value) / max, 2));
kvp.Key.BackColor = Color.FromArgb(255, colorComponent, colorComponent);
if (kvp.Key.BackColor.GetBrightness() < BrightnessLevel) kvp.Key.ForeColor = Color.White;
else kvp.Key.ForeColor = Color.Black;
}
}
}
private void ColorByWeight(EquipmentNode selectedNode) {
int equipment = selectedNode.Equipment;
double rowSum = 0, colSum = 0;
for (int i = 0; i < Content.ProblemInstance.Weights.Columns; i++)
if (i != equipment) rowSum += Content.ProblemInstance.Weights[equipment, i];
for (int i = 0; i < Content.ProblemInstance.Weights.Rows; i++)
if (i != equipment) colSum += Content.ProblemInstance.Weights[i, equipment];
var otherEquipments = GetAllSubNodes(assignmentTreeView.Nodes)
.OfType()
.Where(x => x != selectedNode);
selectedNode.BackColor = assignmentTreeView.BackColor;
selectedNode.ForeColor = assignmentTreeView.ForeColor;
foreach (var other in otherEquipments) {
if (rowSum == 0 && colSum == 0) {
other.BackColor = assignmentTreeView.BackColor;
other.ForeColor = assignmentTreeView.ForeColor;
} else {
double rowInvProportion = rowSum == 0 ? 1.0 : (rowSum - Content.ProblemInstance.Weights[equipment, other.Equipment]) / rowSum;
double colInvProportion = colSum == 0 ? 1.0 : (colSum - Content.ProblemInstance.Weights[other.Equipment, equipment]) / colSum;
int colorComponent = (int)(255 * Math.Pow(Math.Min(rowInvProportion, colInvProportion), 2));
other.BackColor = Color.FromArgb(colorComponent, 255, colorComponent);
if (other.BackColor.GetBrightness() < BrightnessLevel) other.ForeColor = Color.White;
else other.ForeColor = Color.Black;
}
}
}
private void ColorByWeight(LocationNode selectedNode) {
var equipments = new HashSet(Content.Solution.Assignment.Select((v, i) => new { Index = i, Value = v })
.Where(x => x.Value == selectedNode.Location).Select(x => x.Index));
var rowSums = new Dictionary(equipments.Count);
var colSums = new Dictionary(equipments.Count);
foreach (var e in equipments) {
rowSums[e] = 0;
colSums[e] = 0;
for (int i = 0; i < Content.ProblemInstance.Weights.Columns; i++) {
if (!equipments.Contains(i)) rowSums[e] += Content.ProblemInstance.Weights[e, i];
if (!equipments.Contains(i)) colSums[e] += Content.ProblemInstance.Weights[i, e];
}
}
var equipmentNodes = GetAllSubNodes(assignmentTreeView.Nodes)
.OfType();
var relevantEquipments = new HashSet(equipments.Where(x => rowSums[x] > 0 || colSums[x] > 0));
foreach (var other in equipmentNodes) {
if (!relevantEquipments.Any() || equipments.Contains(other.Equipment)) {
other.BackColor = assignmentTreeView.BackColor;
other.ForeColor = assignmentTreeView.ForeColor;
} else {
double rowInvProportion = relevantEquipments.Min(x => rowSums[x] == 0 ? 1.0 : (rowSums[x] - Content.ProblemInstance.Weights[x, other.Equipment]) / rowSums[x]);
double colInvProportion = relevantEquipments.Min(x => colSums[x] == 0 ? 1.0 : (colSums[x] - Content.ProblemInstance.Weights[other.Equipment, x]) / colSums[x]);
int colorComponent = (int)(255 * Math.Pow(Math.Min(rowInvProportion, colInvProportion), 2));
other.BackColor = Color.FromArgb(colorComponent, 255, colorComponent);
if (other.BackColor.GetBrightness() < BrightnessLevel) other.ForeColor = Color.White;
else other.ForeColor = Color.Black;
}
}
}
private IEnumerable GetAllSubNodes(TreeNodeCollection nodes) {
foreach (TreeNode node in nodes) {
yield return node;
foreach (TreeNode subNode in GetAllSubNodes(node.Nodes))
yield return subNode;
}
}
#endregion
#region Helper Classes
private class LocationNode : TreeNode {
public int Location { get; set; }
public LocationNode() : base() { }
public LocationNode(int location, string name)
: base(name) {
Location = location;
}
}
private class EquipmentNode : TreeNode {
public int Equipment { get; set; }
public EquipmentNode() : base() { }
public EquipmentNode(int equipment, string name)
: base(name) {
Equipment = equipment;
}
}
#endregion
private void recalculateButton_Click(object sender, EventArgs e) {
Content.Solution.Evaluation = Content.ProblemInstance.Evaluate(Content.Solution.Assignment);
}
}
}