refactor: write shit

This commit is contained in:
2025-09-07 22:12:41 +08:00
parent 83e581f086
commit c7ca8cde8a
7 changed files with 378 additions and 43 deletions

View File

@ -18,32 +18,6 @@ namespace HFUTCourseSimulation.Dialog {
/// Interaction logic for Simulator.xaml
/// </summary>
public partial class Simulator : Window {
private static readonly string[] WEEK_NAMES = new string[] {
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
"星期日"
};
private static readonly ColorPair HEADBAR_COLOR = Util.ColorPreset.MdColors.Grey;
private static ColorPair GetSidebarColor(Kernel.Data.Presentation.IndexKind kind) {
switch (kind) {
case Kernel.Data.Presentation.IndexKind.Dawn:
return Util.ColorPreset.MdColors.Cyan;
case Kernel.Data.Presentation.IndexKind.Morning:
return Util.ColorPreset.MdColors.LightBlue;
case Kernel.Data.Presentation.IndexKind.Afternoon:
return Util.ColorPreset.MdColors.Amber;
case Kernel.Data.Presentation.IndexKind.Night:
return Util.ColorPreset.MdColors.BlueGrey;
default:
return Util.ColorPreset.MdColors.Grey;
}
}
public Simulator() {
InitializeComponent();
@ -62,7 +36,7 @@ namespace HFUTCourseSimulation.Dialog {
// Initialize UI layout by semester
// Setup grid rows and columns
uiCoreGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
for (int i = 0; i < WEEK_NAMES.Length; ++i) {
for (int i = 0; i < WeekNames.Length; ++i) {
uiCoreGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
}
uiCoreGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
@ -72,17 +46,17 @@ namespace HFUTCourseSimulation.Dialog {
}
// Set head bar
var headbar = new Rectangle();
headbar.Fill = new SolidColorBrush(HEADBAR_COLOR.Background);
headbar.Fill = new SolidColorBrush(ColorConsistency.HeadbarColor.Background);
Grid.SetRow(headbar, 0);
Grid.SetRowSpan(headbar, 2);
Grid.SetColumn(headbar, 0);
Grid.SetColumnSpan(headbar, WEEK_NAMES.Length + 1);
Grid.SetColumnSpan(headbar, WeekNames.Length + 1);
uiCoreGrid.Children.Add(headbar);
// Setup week text block
for (int week = 1; week <= WEEK_NAMES.Length; ++week) {
for (int week = 1; week <= WeekNames.Length; ++week) {
var weekLabel = new TextBlock();
weekLabel.Text = WEEK_NAMES[week - 1];
weekLabel.Foreground = new SolidColorBrush(HEADBAR_COLOR.Foreground);
weekLabel.Text = WeekNames.Names[week - 1];
weekLabel.Foreground = new SolidColorBrush(ColorConsistency.HeadbarColor.Foreground);
weekLabel.HorizontalAlignment = HorizontalAlignment.Center;
Grid.SetRow(weekLabel, 0);
Grid.SetColumn(weekLabel, week - 1 + 1);
@ -90,7 +64,7 @@ namespace HFUTCourseSimulation.Dialog {
var weekDateLabel = new TextBlock();
weekDateLabel.Text = "N/A";
weekDateLabel.Foreground = new SolidColorBrush(HEADBAR_COLOR.Foreground);
weekDateLabel.Foreground = new SolidColorBrush(ColorConsistency.HeadbarColor.Foreground);
weekDateLabel.HorizontalAlignment = HorizontalAlignment.Center;
Grid.SetRow(weekDateLabel, 1);
Grid.SetColumn(weekDateLabel, week - 1 + 1);
@ -100,7 +74,7 @@ namespace HFUTCourseSimulation.Dialog {
}
// Setup sidebar and index text block
for (int index = 1; index <= CurrentSemester.indexCount; ++index) {
var color = GetSidebarColor(CurrentSemester.GetIndexKind(index));
var color = ColorConsistency.GetSidebarColor(CurrentSemester.GetIndexKind(index));
var sidebar = new Rectangle();
sidebar.Fill = new SolidColorBrush(color.Background);
@ -127,12 +101,12 @@ namespace HFUTCourseSimulation.Dialog {
Grid.SetRowSpan(cornerLabel, 2);
uiCoreGrid.Children.Add(cornerLabel);
// Add chessboard
for (int week = 1; week <= WEEK_NAMES.Length; ++week) {
for (int week = 1; week <= WeekNames.Length; ++week) {
for (int index = 1; index <= CurrentSemester.indexCount; ++index) {
if ((week + index) % 2 != 0) continue;
var chessboard = new Rectangle();
chessboard.Fill = new SolidColorBrush(Color.FromArgb(10, 0, 0, 0));
chessboard.Fill = new SolidColorBrush(ColorConsistency.ChessboardColor);
Grid.SetColumn(chessboard, week - 1 + 1);
Grid.SetRow(chessboard, index - 1 + 2);
uiCoreGrid.Children.Add((chessboard));

View File

@ -89,10 +89,13 @@
<Compile Include="Kernel\Data\Presentation.cs" />
<Compile Include="Kernel\Data\Storage.cs" />
<Compile Include="Kernel\Data\Ui.cs" />
<Compile Include="Kernel\Render.cs" />
<Compile Include="Kernel\Reporter.cs" />
<Compile Include="Kernel\Data\LegacyStorage\V1.cs" />
<Compile Include="Util\ColorConsistency.cs" />
<Compile Include="Util\ColorPair.cs" />
<Compile Include="Util\ColorTrans.cs" />
<Compile Include="Util\WeekNames.cs" />
<Compile Include="Util\Win32Dialog.cs" />
<Page Include="Dialog\EditCourse.xaml">
<SubType>Designer</SubType>

View File

@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using HFUTCourseSimulation.Util;
namespace HFUTCourseSimulation.Kernel {
/// <summary>
/// 渲染器的图片的几何形状
///
/// 渲染后的图片中,教学周从左到右从上到下排布。
/// 整个图片的长和宽上的教学周数量,要使得最终的图形尽量为正方形。
/// 每个教学周外围有MARGIN的边框留白。
/// 教学周中最上方是Title书写当前的教学周号。
/// 然后是表格表格上有Headbar显示星期几和日期左有Sidebar显示节次。
/// 最后是内中表格。
/// </summary>
internal class RenderGeometry {
/// <summary>
/// 教学周标题所占的高度
/// </summary>
public static readonly int TITLE_HEIGHT = 50;
/// <summary>
/// 表头(星期几和日期)所占的高度
/// </summary>
public static readonly int HEADBAR_HEIGHT = 40;
/// <summary>
/// 侧边(节次号)所占的高度
/// </summary>
public static readonly int SIDEBAR_WIDTH = 25;
/// <summary>
/// 每个节次Cell的宽度
/// </summary>
public static readonly int CELL_WIDTH = 150;
/// <summary>
/// 每个节次Cell的高度
/// </summary>
public static readonly int CELL_HEIGHT = 80;
/// <summary>
/// 每个表格之间的长宽上的间距
/// </summary>
public static readonly int MARGIN = 20;
public RenderGeometry(int weekCount, int indexCount) {
_horizontalCount = (int)Math.Sqrt(weekCount) + 1;
_verticalCount = weekCount / _horizontalCount;
if (weekCount % _horizontalCount != 0) _verticalCount++;
_weekWidth = SIDEBAR_WIDTH + 7 * CELL_WIDTH + 2 * MARGIN;
_weekHeight = TITLE_HEIGHT + HEADBAR_HEIGHT + indexCount * CELL_HEIGHT + 2 * MARGIN;
_width = _weekWidth * _horizontalCount;
_height = _weekHeight * _verticalCount;
}
int _horizontalCount;
int _verticalCount;
int _weekWidth;
int _weekHeight;
int _width;
int _height;
/// <summary>
/// 图片宽度
/// </summary>
public int Width { get { return _width; } }
/// <summary>
/// 图片高度
/// </summary>
public int Height { get { return _height; } }
/// <summary>
/// 获取教学周的左上角坐标实际开始画图的开始排除Margin
/// </summary>
private Point GetWeekPos(int week) {
week -= 1;
var row = week / _horizontalCount;
var column = week % _verticalCount;
return new Point(column * _weekWidth + MARGIN, row * _weekHeight + MARGIN);
}
public Point GetTitleTextPos(int week) {
return GetWeekPos(week);
}
/// <summary>
/// 获取课表主体的左上角的坐标
/// </summary>
private Point GetTablePos(int week) {
var pos = GetWeekPos(week);
pos.Offset(0, TITLE_HEIGHT);
return pos;
}
public Rectangle GetHeadbarBoxRect(int week) {
var pos = GetTablePos(week);
return new Rectangle(pos, new Size(7 * CELL_WIDTH, HEADBAR_HEIGHT));
}
public Point GetHeadbarTextPos(int week, int day) {
day -= 1;
var pos = GetTablePos(week);
pos.Offset(day * CELL_WIDTH, 0);
return pos;
}
public Rectangle GetSidebarBoxRect(int week, int index) {
index -= 1;
var pos = GetTablePos(week);
return new Rectangle(pos, new Size(0, index * CELL_HEIGHT));
}
public Point GetSidebarTextPos(int week, int index) {
index -= 1;
var pos = GetTablePos(week);
pos.Offset(0, index * CELL_HEIGHT);
return pos;
}
/// <summary>
/// 获取课表课程区域的左上角坐标(排除顶栏和侧栏)
/// </summary>
private Point GetBodyPos(int week) {
var pos = GetTablePos(week);
pos.Offset(SIDEBAR_WIDTH, HEADBAR_HEIGHT);
return pos;
}
/// <summary>
/// 获取指定周,指定星期,指定节次的表格的左上角坐标
/// </summary>
private Point GetCellPos(int week, int day, int index) {
day -= 1;
index -= 1;
var pos = GetBodyPos(week);
pos.Offset(day * CELL_WIDTH, index * CELL_HEIGHT);
return pos;
}
public Point GetCellTextPos(int week, int day, int index) {
return GetCellPos(week, day, index);
}
public Rectangle GetCellBoxRect(int week, int day, int index, int index_span = 1) {
var pos = GetCellPos(week, day, index);
return new Rectangle(pos, new Size(CELL_WIDTH, index_span * CELL_HEIGHT));
}
}
/// <summary>
/// 渲染器笔刷集合。
/// 渲染器工作时,需要频繁创建各种不同颜色的笔刷,此处为其缓存这些笔刷。
/// </summary>
public class RenderBrushes {
public RenderBrushes() {
_brushes = new Dictionary<Color, SolidBrush>();
}
public SolidBrush GetBrush(Color color) {
if (_brushes.TryGetValue(color, out SolidBrush brush)) {
return brush;
} else {
var new_brush = new SolidBrush(color);
_brushes.Add(color, new_brush);
return new_brush;
}
}
public SolidBrush GetBrush(System.Windows.Media.Color color) {
return GetBrush(ColorTrans.ToGdiColor(color));
}
Dictionary<Color, SolidBrush> _brushes;
}
/// <summary>
/// 核心图片渲染器
/// </summary>
public static class Render {
/// <summary>
/// 渲染给定课表到给定图片中。
/// </summary>
/// <param name="semester">要渲染的课表</param>
/// <param name="filepath">要渲染到的图片路径</param>
/// <returns>如果无误返回true否则返回false。</returns>
public static bool Rending(Kernel.Data.Presentation.Semester semester, string filepath) {
try {
UnderlyingRending(semester, filepath);
return true;
} catch (Exception) {
return false;
}
}
private static void UnderlyingRending(Kernel.Data.Presentation.Semester semester, string filepath) {
// 确定画布基本属性
var geometry = new RenderGeometry(semester.weekCount, semester.indexCount);
// 创建图像
using (var img = new Bitmap(geometry.Width, geometry.Height)) {
// 创建画布
Graphics g = Graphics.FromImage(img);
// 创建绘画资源
var brushes = new RenderBrushes();
var font = new Font("Source Hans Sans CN", 12);
// 填充背景为白色
g.Clear(Color.White);
// 按教学周周分别输出
for (int week = 1; week <= semester.weekCount; ++week) {
var week_instance = semester.weeks[week - 1];
// 教学周文本
g.DrawString($"教学周:{week}", font, brushes.GetBrush(Color.Black), geometry.GetTitleTextPos(week));
// 绘制Headbar底层
g.FillRectangle(brushes.GetBrush(ColorConsistency.HeadbarColor.Background), geometry.GetHeadbarBoxRect(week));
// 绘制Header文本
for (int day = 1; day <= WeekNames.Length; ++day) {
var day_instance = week_instance.days[day - 1];
g.DrawString($@"{WeekNames.Names[day - 1]}
{day_instance.date}", font, brushes.GetBrush(ColorConsistency.HeadbarColor.Foreground), geometry.GetHeadbarTextPos(week, day));
}
// 绘制Sidebar底层和文本
for (int index = 1; index <= semester.indexCount; ++index) {
var colorPair = ColorConsistency.GetSidebarColor(semester.GetIndexKind(index));
g.FillRectangle(brushes.GetBrush(colorPair.Background), geometry.GetSidebarBoxRect(week, index));
g.DrawString(index.ToString(), font, brushes.GetBrush(colorPair.Foreground), geometry.GetSidebarTextPos(week, index));
}
// 绘制Chessboard
for (int day = 1; day <= WeekNames.Length; ++day) {
for (int index = 1; index <= semester.indexCount; ++index) {
if ((day + index) % 2 == 0) continue;
g.FillRectangle(brushes.GetBrush(ColorConsistency.ChessboardColor), geometry.GetCellBoxRect(week, day, index));
}
}
// 绘制课程
for (int day = 1; day <= WeekNames.Length; ++day) {
var day_instance = week_instance.days[day - 1];
foreach (var lesson_instance in day_instance.lessons) {
g.FillRectangle(brushes.GetBrush(lesson_instance.color.Background), geometry.GetCellBoxRect(week, day, lesson_instance.startIndex, lesson_instance.indexSpan));
g.DrawString($@"{lesson_instance.name}
{lesson_instance.description}", font, brushes.GetBrush(lesson_instance.color.Foreground), geometry.GetCellTextPos(week, day, lesson_instance.startIndex));
}
}
}
// 保存到文件
using (var fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) {
img.Save(fs, ImageFormat.Png);
}
}
}
}
}

View File

@ -278,6 +278,21 @@ namespace HFUTCourseSimulation {
//var res = ImageExport.Export(originDate, weekCount, getFile);
//if (!res) MessageBox.Show("当前课程安排存在冲突,请通过实时预览消除所有错误后才能使用此功能来导出为图片", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
//else MessageBox.Show("导出成功", "关于", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
var semester = CurrentSemester.ToStorage();
var reporter = new Kernel.Reporter();
var rv = Kernel.Arranger.Arrange(semester, reporter);
if (rv is null) return;
// Fetch file path.
var filepath = Util.Win32Dialog.SaveRender();
if (filepath is null) return;
if (Kernel.Render.Rending(rv, filepath)) {
Win32Dialog.Info("导出成功", "导出结果");
} else {
Win32Dialog.Error("导出失败。请检查文件是否被占用或检查GDI是否能正常使用。", "导出结果");
}
}
private void uiMenuAbout_Click(object sender, RoutedEventArgs e) {

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace HFUTCourseSimulation.Util {
/// <summary>
/// 用于在Simulator和Render之间确保采用的色彩的一致性
/// </summary>
public static class ColorConsistency {
/// <summary>
/// 获取标题栏的配色(星期几和日期)
/// </summary>
public static ColorPair HeadbarColor => ColorPreset.MdColors.Grey;
/// <summary>
/// 获取侧边栏的配色(节次号)
/// </summary>
/// <param name="kind"></param>
/// <returns></returns>
public static ColorPair GetSidebarColor(Kernel.Data.Presentation.IndexKind kind) {
switch (kind) {
case Kernel.Data.Presentation.IndexKind.Dawn:
return ColorPreset.MdColors.Cyan;
case Kernel.Data.Presentation.IndexKind.Morning:
return ColorPreset.MdColors.LightBlue;
case Kernel.Data.Presentation.IndexKind.Afternoon:
return ColorPreset.MdColors.Amber;
case Kernel.Data.Presentation.IndexKind.Night:
return ColorPreset.MdColors.BlueGrey;
default:
return ColorPreset.MdColors.Grey;
}
}
/// <summary>
/// 棋盘中的异色颜色的配置
/// </summary>
public static Color ChessboardColor => Color.FromArgb(10, 0, 0, 0);
}
}

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using WpfColor = System.Windows.Media.Color;
using WinformColor = System.Drawing.Color;
using GdiColor = System.Drawing.Color;
using StandardColorPair = HFUTCourseSimulation.Util.ColorPair;
using StorageColorPair = HFUTCourseSimulation.Kernel.Data.Storage.ColorPair;
@ -16,12 +16,12 @@ namespace HFUTCourseSimulation.Util {
#region Color Transform
public static WinformColor ToWinformColor(int argb) {
return WinformColor.FromArgb(argb);
public static GdiColor ToGdiColor(int argb) {
return GdiColor.FromArgb(argb);
}
public static WinformColor ToWinformColor(WpfColor c) {
return WinformColor.FromArgb(c.A, c.R, c.G, c.B);
public static GdiColor ToGdiColor(WpfColor c) {
return GdiColor.FromArgb(c.A, c.R, c.G, c.B);
}
public static WpfColor ToWpfColor(int _argb) {
@ -33,11 +33,11 @@ namespace HFUTCourseSimulation.Util {
return WpfColor.FromArgb((byte)a, (byte)r, (byte)g, (byte)b);
}
public static WpfColor ToWpfColor(WinformColor c) {
public static WpfColor ToWpfColor(GdiColor c) {
return WpfColor.FromArgb(c.A, c.R, c.G, c.B);
}
public static int ToInt(WinformColor c) {
public static int ToInt(GdiColor c) {
return c.ToArgb();
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HFUTCourseSimulation.Util {
public static class WeekNames {
public static readonly string[] Names = new string[] {
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
"星期日"
};
public static int Length => Names.Length;
}
}