#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); } } }