Add explanatory comments to heatmap code

This commit is contained in:
2026-07-01 10:09:24 +09:00
parent 74306da88b
commit 0d36c11ea4
5 changed files with 59 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ using System.Globalization;
namespace mbn_stock namespace mbn_stock
{ {
// 등락률 구간과 해당 구간에 사용할 색상 값을 보관하는 바인딩용 모델입니다.
public class ColorRangeRule : INotifyPropertyChanged public class ColorRangeRule : INotifyPropertyChanged
{ {
private decimal _fromRate; private decimal _fromRate;
@@ -78,11 +79,13 @@ namespace mbn_stock
} }
} }
// 입력한 등락률이 이 색상 구간에 포함되는지 확인합니다.
public bool Contains(decimal value) public bool Contains(decimal value)
{ {
return value >= MinRate && value < MaxRate; return value >= MinRate && value < MaxRate;
} }
// #RRGGBB 문자열을 실제 Drawing Color로 변환할 수 있는지 확인합니다.
public static bool TryParseColor(string text, out Color color) public static bool TryParseColor(string text, out Color color)
{ {
color = Color.Empty; color = Color.Empty;
@@ -104,6 +107,7 @@ namespace mbn_stock
} }
} }
// 사용자가 입력한 색상 문자열을 #RRGGBB 형태에 가깝게 정리합니다.
public static string NormalizeColorHex(string text) public static string NormalizeColorHex(string text)
{ {
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
@@ -129,6 +133,7 @@ namespace mbn_stock
return normalized; return normalized;
} }
// DataGridView 바인딩에 변경 사실을 알려 화면을 갱신합니다.
private void OnPropertyChanged(string propertyName) private void OnPropertyChanged(string propertyName)
{ {
PropertyChangedEventHandler handler = PropertyChanged; PropertyChangedEventHandler handler = PropertyChanged;

View File

@@ -9,6 +9,7 @@ using System.Windows.Forms;
namespace mbn_stock namespace mbn_stock
{ {
// 업종 데이터와 색상 규칙을 편집하고 히트맵 이미지를 미리보기/저장하는 테스트 폼입니다.
public partial class Form1 : Form public partial class Form1 : Form
{ {
private readonly BindingList<SectorHeatMapItem> _items = new BindingList<SectorHeatMapItem>(); private readonly BindingList<SectorHeatMapItem> _items = new BindingList<SectorHeatMapItem>();
@@ -16,6 +17,7 @@ namespace mbn_stock
private readonly Random _random = new Random(); private readonly Random _random = new Random();
private bool _loading; private bool _loading;
// 디자이너 UI를 초기화하고 런타임에서는 기본 데이터와 색상 규칙을 연결합니다.
public Form1() public Form1()
{ {
InitializeComponent(); InitializeComponent();
@@ -32,6 +34,7 @@ namespace mbn_stock
RandomizeValues(); RandomizeValues();
} }
// 미리보기 이미지 리소스를 해제해 Bitmap 핸들이 남지 않게 정리합니다.
protected override void OnFormClosed(FormClosedEventArgs e) protected override void OnFormClosed(FormClosedEventArgs e)
{ {
if (_previewBox != null && _previewBox.Image != null) if (_previewBox != null && _previewBox.Image != null)
@@ -43,6 +46,7 @@ namespace mbn_stock
base.OnFormClosed(e); base.OnFormClosed(e);
} }
// 국내 증시 업종 테스트용 기본 행을 그리드에 채웁니다.
private void LoadDefaultItems() private void LoadDefaultItems()
{ {
_loading = true; _loading = true;
@@ -81,6 +85,7 @@ namespace mbn_stock
_loading = false; _loading = false;
} }
// 렌더러가 제공하는 기본 등락률 색상 구간을 그리드에 채웁니다.
private void LoadDefaultColorRules() private void LoadDefaultColorRules()
{ {
_loading = true; _loading = true;
@@ -95,6 +100,7 @@ namespace mbn_stock
_colorRuleGrid.Refresh(); _colorRuleGrid.Refresh();
} }
// 각 업종의 시가총액과 등락률을 테스트용 임의 값으로 갱신합니다.
private void RandomizeValues() private void RandomizeValues()
{ {
_loading = true; _loading = true;
@@ -110,6 +116,7 @@ namespace mbn_stock
RefreshPreview(); RefreshPreview();
} }
// 현재 업종 데이터와 색상 규칙으로 1920x1080 히트맵 미리보기를 다시 그립니다.
private void RefreshPreview() private void RefreshPreview()
{ {
if (_loading || _previewBox == null) if (_loading || _previewBox == null)
@@ -143,11 +150,13 @@ namespace mbn_stock
_colorRules.Count); _colorRules.Count);
} }
// 랜덤 값 버튼 클릭 시 테스트 데이터를 다시 생성합니다.
private void RandomButton_Click(object sender, EventArgs e) private void RandomButton_Click(object sender, EventArgs e)
{ {
RandomizeValues(); RandomizeValues();
} }
// PNG 저장 버튼 클릭 시 현재 설정으로 이미지를 렌더링해 파일로 저장합니다.
private void SaveButton_Click(object sender, EventArgs e) private void SaveButton_Click(object sender, EventArgs e)
{ {
if (!_grid.EndEdit()) if (!_grid.EndEdit())
@@ -172,21 +181,24 @@ namespace mbn_stock
bitmap.Save(dialog.FileName, ImageFormat.Png); bitmap.Save(dialog.FileName, ImageFormat.Png);
} }
_statusLabel.Text = "저장 완료: " + dialog.FileName; _statusLabel.Text = "저장 완료: " + dialog.FileName;
} }
} }
// 기본 색상 버튼 클릭 시 등락률 색상 규칙을 초기값으로 되돌립니다.
private void ResetColorRulesButton_Click(object sender, EventArgs e) private void ResetColorRulesButton_Click(object sender, EventArgs e)
{ {
LoadDefaultColorRules(); LoadDefaultColorRules();
RefreshPreview(); RefreshPreview();
} }
// 업종 그리드 편집이 끝나면 미리보기를 갱신합니다.
private void Grid_CellEndEdit(object sender, DataGridViewCellEventArgs e) private void Grid_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{ {
RefreshPreview(); RefreshPreview();
} }
// 업종 그리드의 숫자 입력값을 decimal로 변환합니다.
private void Grid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) private void Grid_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{ {
if (_grid.Columns[e.ColumnIndex].DataPropertyName == "Name") if (_grid.Columns[e.ColumnIndex].DataPropertyName == "Name")
@@ -204,6 +216,7 @@ namespace mbn_stock
} }
} }
// 업종 그리드에 잘못된 숫자나 0 이하 시가총액이 들어오지 않게 검증합니다.
private void Grid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) private void Grid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{ {
if (_grid.Columns[e.ColumnIndex].DataPropertyName == "Name") if (_grid.Columns[e.ColumnIndex].DataPropertyName == "Name")
@@ -231,12 +244,14 @@ namespace mbn_stock
_grid.Rows[e.RowIndex].ErrorText = string.Empty; _grid.Rows[e.RowIndex].ErrorText = string.Empty;
} }
// 업종 그리드 바인딩 오류가 앱 예외로 번지지 않도록 상태 메시지로 처리합니다.
private void Grid_DataError(object sender, DataGridViewDataErrorEventArgs e) private void Grid_DataError(object sender, DataGridViewDataErrorEventArgs e)
{ {
e.ThrowException = false; e.ThrowException = false;
_statusLabel.Text = "입력값을 확인해 주세요."; _statusLabel.Text = "입력값을 확인해 주세요.";
} }
// 색상 값 또는 미리보기 칸을 더블클릭하면 색상 선택 창을 엽니다.
private void ColorRuleGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e) private void ColorRuleGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{ {
if (e.RowIndex < 0 || if (e.RowIndex < 0 ||
@@ -268,12 +283,14 @@ namespace mbn_stock
} }
} }
// 색상 규칙 편집이 끝나면 표 색상과 미리보기를 갱신합니다.
private void ColorRuleGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e) private void ColorRuleGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{ {
_colorRuleGrid.Invalidate(); _colorRuleGrid.Invalidate();
RefreshPreview(); RefreshPreview();
} }
// 색상 미리보기 칸을 실제 색상 배경으로 표시합니다.
private void ColorRuleGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) private void ColorRuleGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{ {
if (e.RowIndex < 0 || _colorRuleGrid.Columns[e.ColumnIndex] != _colorPreviewColumn) if (e.RowIndex < 0 || _colorRuleGrid.Columns[e.ColumnIndex] != _colorPreviewColumn)
@@ -293,6 +310,7 @@ namespace mbn_stock
e.FormattingApplied = true; e.FormattingApplied = true;
} }
// 색상 규칙 그리드의 숫자와 색상 문자열을 모델 값으로 변환합니다.
private void ColorRuleGrid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) private void ColorRuleGrid_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{ {
string propertyName = _colorRuleGrid.Columns[e.ColumnIndex].DataPropertyName; string propertyName = _colorRuleGrid.Columns[e.ColumnIndex].DataPropertyName;
@@ -315,6 +333,7 @@ namespace mbn_stock
} }
} }
// 색상 규칙 구간값과 #RRGGBB 색상 형식을 검증합니다.
private void ColorRuleGrid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) private void ColorRuleGrid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{ {
string propertyName = _colorRuleGrid.Columns[e.ColumnIndex].DataPropertyName; string propertyName = _colorRuleGrid.Columns[e.ColumnIndex].DataPropertyName;
@@ -344,12 +363,14 @@ namespace mbn_stock
_colorRuleGrid.Rows[e.RowIndex].ErrorText = string.Empty; _colorRuleGrid.Rows[e.RowIndex].ErrorText = string.Empty;
} }
// 색상 규칙 바인딩 오류가 앱 예외로 번지지 않도록 상태 메시지로 처리합니다.
private void ColorRuleGrid_DataError(object sender, DataGridViewDataErrorEventArgs e) private void ColorRuleGrid_DataError(object sender, DataGridViewDataErrorEventArgs e)
{ {
e.ThrowException = false; e.ThrowException = false;
_statusLabel.Text = "색상 규칙 입력값을 확인해 주세요."; _statusLabel.Text = "색상 규칙 입력값을 확인해 주세요.";
} }
// 새 색상 규칙 행을 추가할 때 기본값을 넣습니다.
private void ColorRuleGrid_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e) private void ColorRuleGrid_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
{ {
e.Row.Cells[_fromRateColumn.Index].Value = 0m; e.Row.Cells[_fromRateColumn.Index].Value = 0m;
@@ -357,11 +378,13 @@ namespace mbn_stock
e.Row.Cells[_colorHexColumn.Index].Value = "#808080"; e.Row.Cells[_colorHexColumn.Index].Value = "#808080";
} }
// 색상 규칙 행이 삭제되면 남은 규칙으로 미리보기를 다시 그립니다.
private void ColorRuleGrid_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e) private void ColorRuleGrid_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{ {
RefreshPreview(); RefreshPreview();
} }
// 퍼센트 기호가 붙은 문자열도 decimal 숫자로 읽을 수 있게 변환합니다.
private static bool TryParseDecimal(string text, out decimal value) private static bool TryParseDecimal(string text, out decimal value)
{ {
value = 0m; value = 0m;
@@ -376,6 +399,7 @@ namespace mbn_stock
|| decimal.TryParse(normalized, NumberStyles.Number, CultureInfo.InvariantCulture, out value); || decimal.TryParse(normalized, NumberStyles.Number, CultureInfo.InvariantCulture, out value);
} }
// ColorDialog에서 선택한 색상을 #RRGGBB 문자열로 변환합니다.
private static string ToHex(Color color) private static string ToHex(Color color)
{ {
return string.Format(CultureInfo.InvariantCulture, "#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B); return string.Format(CultureInfo.InvariantCulture, "#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);

View File

@@ -9,6 +9,7 @@ using System.Linq;
namespace mbn_stock namespace mbn_stock
{ {
// 업종별 데이터를 시가총액 면적 기반 트리맵 이미지로 렌더링하는 정적 유틸리티입니다.
public static class HeatMapRenderer public static class HeatMapRenderer
{ {
public const int ExportWidth = 1920; public const int ExportWidth = 1920;
@@ -18,21 +19,25 @@ namespace mbn_stock
private const float TileGap = 6f; private const float TileGap = 6f;
private static readonly float TileRadius = 0f; private static readonly float TileRadius = 0f;
// 기본 이미지 크기와 기본 색상 규칙으로 히트맵을 렌더링합니다.
public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems) public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems)
{ {
return Render(sourceItems, new Size(ExportWidth, ExportHeight), CreateDefaultColorRules()); return Render(sourceItems, new Size(ExportWidth, ExportHeight), CreateDefaultColorRules());
} }
// 기본 이미지 크기에 사용자가 넘긴 색상 규칙을 적용해 히트맵을 렌더링합니다.
public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems, IEnumerable<ColorRangeRule> colorRules) public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems, IEnumerable<ColorRangeRule> colorRules)
{ {
return Render(sourceItems, new Size(ExportWidth, ExportHeight), colorRules); return Render(sourceItems, new Size(ExportWidth, ExportHeight), colorRules);
} }
// 지정한 이미지 크기와 기본 색상 규칙으로 히트맵을 렌더링합니다.
public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems, Size size) public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems, Size size)
{ {
return Render(sourceItems, size, CreateDefaultColorRules()); return Render(sourceItems, size, CreateDefaultColorRules());
} }
// 지정한 이미지 크기와 색상 규칙으로 전체 히트맵 Bitmap을 생성합니다.
public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems, Size size, IEnumerable<ColorRangeRule> colorRules) public static Bitmap Render(IEnumerable<SectorHeatMapItem> sourceItems, Size size, IEnumerable<ColorRangeRule> colorRules)
{ {
Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb); Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
@@ -65,6 +70,7 @@ namespace mbn_stock
return bitmap; return bitmap;
} }
// 초기 화면에서 사용할 기본 등락률 구간 색상 규칙을 만듭니다.
public static List<ColorRangeRule> CreateDefaultColorRules() public static List<ColorRangeRule> CreateDefaultColorRules()
{ {
return new List<ColorRangeRule> return new List<ColorRangeRule>
@@ -78,6 +84,7 @@ namespace mbn_stock
}; };
} }
// 기본 색상 규칙 목록에 넣을 한 구간을 생성합니다.
private static ColorRangeRule CreateColorRule(decimal fromRate, decimal toRate, string colorHex) private static ColorRangeRule CreateColorRule(decimal fromRate, decimal toRate, string colorHex)
{ {
return new ColorRangeRule return new ColorRangeRule
@@ -88,6 +95,7 @@ namespace mbn_stock
}; };
} }
// 시가총액 비중을 전체 캔버스 면적으로 환산해 배치 노드를 만듭니다.
private static List<TreeMapNode> BuildNodes(IList<SectorHeatMapItem> items, RectangleF bounds) private static List<TreeMapNode> BuildNodes(IList<SectorHeatMapItem> items, RectangleF bounds)
{ {
decimal total = items.Sum(item => item.MarketCap); decimal total = items.Sum(item => item.MarketCap);
@@ -105,6 +113,7 @@ namespace mbn_stock
return result; return result;
} }
// 노드 목록을 면적 균형이 맞도록 좌우 또는 상하로 재귀 분할합니다.
private static void LayoutBinary(IList<TreeMapNode> nodes, RectangleF bounds, List<TreeMapNode> result) private static void LayoutBinary(IList<TreeMapNode> nodes, RectangleF bounds, List<TreeMapNode> result)
{ {
if (nodes.Count == 0) if (nodes.Count == 0)
@@ -145,6 +154,7 @@ namespace mbn_stock
LayoutBinary(secondGroup, bottomBounds, result); LayoutBinary(secondGroup, bottomBounds, result);
} }
// 현재 노드 목록을 두 그룹으로 나눌 가장 균형 잡힌 지점을 찾습니다.
private static int FindBalancedSplit(IList<TreeMapNode> nodes, double totalArea) private static int FindBalancedSplit(IList<TreeMapNode> nodes, double totalArea)
{ {
double target = totalArea / 2d; double target = totalArea / 2d;
@@ -167,6 +177,7 @@ namespace mbn_stock
return splitIndex; return splitIndex;
} }
// 계산된 각 사각형 영역을 색상으로 채우고 텍스트를 그립니다.
private static void DrawTiles(Graphics graphics, IList<TreeMapNode> nodes, IList<ColorRangeRule> colorRules) private static void DrawTiles(Graphics graphics, IList<TreeMapNode> nodes, IList<ColorRangeRule> colorRules)
{ {
foreach (TreeMapNode node in nodes) foreach (TreeMapNode node in nodes)
@@ -200,6 +211,7 @@ namespace mbn_stock
} }
} }
// 업종명과 등락률을 사각형 중앙에 흰색 텍스트로 그립니다.
private static void DrawTileText(Graphics graphics, SectorHeatMapItem item, RectangleF tileBounds, Color fill) private static void DrawTileText(Graphics graphics, SectorHeatMapItem item, RectangleF tileBounds, Color fill)
{ {
RectangleF textBounds = Inset(tileBounds, Math.Max(10f, Math.Min(tileBounds.Width, tileBounds.Height) * 0.07f)); RectangleF textBounds = Inset(tileBounds, Math.Max(10f, Math.Min(tileBounds.Width, tileBounds.Height) * 0.07f));
@@ -235,6 +247,7 @@ namespace mbn_stock
} }
} }
// 업종명과 등락률이 사각형 안에 들어가도록 최대 폰트 크기를 찾습니다.
private static float FindFittingFontSize(Graphics graphics, string name, string rateText, RectangleF bounds) private static float FindFittingFontSize(Graphics graphics, string name, string rateText, RectangleF bounds)
{ {
float maxSize = Math.Min(96f, Math.Max(12f, Math.Min(bounds.Width * 0.18f, bounds.Height * 0.34f))); float maxSize = Math.Min(96f, Math.Max(12f, Math.Min(bounds.Width * 0.18f, bounds.Height * 0.34f)));
@@ -260,6 +273,7 @@ namespace mbn_stock
return 8f; return 8f;
} }
// 색상 규칙에 맞는 채움색을 찾고, 없으면 기본 빨강/파랑 계산으로 대체합니다.
private static Color GetFillColor(decimal changeRate, IList<ColorRangeRule> colorRules) private static Color GetFillColor(decimal changeRate, IList<ColorRangeRule> colorRules)
{ {
if (colorRules != null && colorRules.Count > 0) if (colorRules != null && colorRules.Count > 0)
@@ -274,6 +288,7 @@ namespace mbn_stock
return GetDefaultFillColor(changeRate); return GetDefaultFillColor(changeRate);
} }
// 등락률이 속한 색상 규칙을 찾고 범위를 벗어나면 양 끝 색상으로 보정합니다.
private static bool TryGetRuleColor(decimal changeRate, IList<ColorRangeRule> colorRules, out Color color) private static bool TryGetRuleColor(decimal changeRate, IList<ColorRangeRule> colorRules, out Color color)
{ {
color = Color.Empty; color = Color.Empty;
@@ -306,6 +321,7 @@ namespace mbn_stock
return false; return false;
} }
// 색상 규칙이 없을 때 ±30% 기준으로 빨강/파랑 농도를 계산합니다.
private static Color GetDefaultFillColor(decimal changeRate) private static Color GetDefaultFillColor(decimal changeRate)
{ {
double intensity = Math.Min(1d, Math.Abs(decimal.ToDouble(changeRate)) / decimal.ToDouble(MaxRatePercent)); double intensity = Math.Min(1d, Math.Abs(decimal.ToDouble(changeRate)) / decimal.ToDouble(MaxRatePercent));
@@ -323,6 +339,7 @@ namespace mbn_stock
return Color.FromArgb(226, 228, 232); return Color.FromArgb(226, 228, 232);
} }
// 비어 있거나 잘못된 색상 규칙을 제거하고 등락률 시작값 기준으로 정렬합니다.
private static List<ColorRangeRule> NormalizeColorRules(IEnumerable<ColorRangeRule> colorRules) private static List<ColorRangeRule> NormalizeColorRules(IEnumerable<ColorRangeRule> colorRules)
{ {
List<ColorRangeRule> rules = colorRules == null List<ColorRangeRule> rules = colorRules == null
@@ -336,12 +353,14 @@ namespace mbn_stock
return rules.Count == 0 ? CreateDefaultColorRules() : rules; return rules.Count == 0 ? CreateDefaultColorRules() : rules;
} }
// 색상 규칙이 실제 렌더링에 사용할 수 있는 색상인지 확인합니다.
private static bool IsValidColorRule(ColorRangeRule rule) private static bool IsValidColorRule(ColorRangeRule rule)
{ {
Color color; Color color;
return ColorRangeRule.TryParseColor(rule.ColorHex, out color); return ColorRangeRule.TryParseColor(rule.ColorHex, out color);
} }
// 두 색상 사이를 amount 비율로 보간해 중간 색상을 만듭니다.
private static Color Interpolate(Color from, Color to, double amount) private static Color Interpolate(Color from, Color to, double amount)
{ {
amount = Math.Max(0d, Math.Min(1d, amount)); amount = Math.Max(0d, Math.Min(1d, amount));
@@ -351,6 +370,7 @@ namespace mbn_stock
return Color.FromArgb(r, g, b); return Color.FromArgb(r, g, b);
} }
// 등락률 숫자를 +1.23% 또는 -1.23% 형태의 문자열로 바꿉니다.
private static string FormatRate(decimal value) private static string FormatRate(decimal value)
{ {
string format = value > 0m ? "+0.00'%'" string format = value > 0m ? "+0.00'%'"
@@ -360,6 +380,7 @@ namespace mbn_stock
return value.ToString(format, CultureInfo.InvariantCulture); return value.ToString(format, CultureInfo.InvariantCulture);
} }
// 사각형 안쪽에 여백을 적용한 새 영역을 계산합니다.
private static RectangleF Inset(RectangleF rectangle, float padding) private static RectangleF Inset(RectangleF rectangle, float padding)
{ {
float horizontal = Math.Min(padding, rectangle.Width / 2f - 0.5f); float horizontal = Math.Min(padding, rectangle.Width / 2f - 0.5f);
@@ -382,6 +403,7 @@ namespace mbn_stock
Math.Max(1f, rectangle.Height - vertical * 2f)); Math.Max(1f, rectangle.Height - vertical * 2f));
} }
// 둥근 모서리 옵션을 사용할 때 필요한 사각형 경로를 만듭니다.
private static GraphicsPath CreateRoundedRectangle(RectangleF bounds, float radius) private static GraphicsPath CreateRoundedRectangle(RectangleF bounds, float radius)
{ {
float diameter = radius * 2f; float diameter = radius * 2f;
@@ -394,6 +416,7 @@ namespace mbn_stock
return path; return path;
} }
// 렌더링 가능한 업종 데이터가 없을 때 안내 문구를 그립니다.
private static void DrawEmptyMessage(Graphics graphics, RectangleF bounds) private static void DrawEmptyMessage(Graphics graphics, RectangleF bounds)
{ {
using (Font font = new Font("Malgun Gothic", 40f, FontStyle.Bold, GraphicsUnit.Pixel)) using (Font font = new Font("Malgun Gothic", 40f, FontStyle.Bold, GraphicsUnit.Pixel))
@@ -406,8 +429,10 @@ namespace mbn_stock
} }
} }
// 트리맵 배치 중 한 업종의 면적과 최종 사각형 영역을 함께 보관합니다.
private sealed class TreeMapNode private sealed class TreeMapNode
{ {
// 업종 데이터와 비율 기반 면적을 묶어 배치 노드를 만듭니다.
public TreeMapNode(SectorHeatMapItem item, double area) public TreeMapNode(SectorHeatMapItem item, double area)
{ {
Item = item; Item = item;

View File

@@ -6,11 +6,10 @@ using System.Windows.Forms;
namespace mbn_stock namespace mbn_stock
{ {
// WinForms 애플리케이션을 시작하고 메인 폼을 여는 진입점입니다.
static class Program static class Program
{ {
/// <summary> // 애플리케이션 기본 스타일을 설정한 뒤 Form1을 실행합니다.
/// 해당 애플리케이션의 주 진입점입니다.
/// </summary>
[STAThread] [STAThread]
static void Main() static void Main()
{ {

View File

@@ -2,6 +2,7 @@ using System.ComponentModel;
namespace mbn_stock namespace mbn_stock
{ {
// 업종명, 시가총액, 등락률을 한 행으로 보관하는 히트맵 입력 모델입니다.
public class SectorHeatMapItem : INotifyPropertyChanged public class SectorHeatMapItem : INotifyPropertyChanged
{ {
private string _name; private string _name;
@@ -55,6 +56,7 @@ namespace mbn_stock
} }
} }
// DataGridView 바인딩에 변경 사실을 알려 화면을 갱신합니다.
private void OnPropertyChanged(string propertyName) private void OnPropertyChanged(string propertyName)
{ {
PropertyChangedEventHandler handler = PropertyChanged; PropertyChangedEventHandler handler = PropertyChanged;