diff --git a/HFUTCourseSimulation/Dialog/Simulator.xaml.cs b/HFUTCourseSimulation/Dialog/Simulator.xaml.cs
index 8e89aaf..1ce7033 100644
--- a/HFUTCourseSimulation/Dialog/Simulator.xaml.cs
+++ b/HFUTCourseSimulation/Dialog/Simulator.xaml.cs
@@ -18,32 +18,6 @@ namespace HFUTCourseSimulation.Dialog {
/// Interaction logic for Simulator.xaml
///
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));
diff --git a/HFUTCourseSimulation/HFUTCourseSimulation.csproj b/HFUTCourseSimulation/HFUTCourseSimulation.csproj
index 03cd914..917f1a0 100644
--- a/HFUTCourseSimulation/HFUTCourseSimulation.csproj
+++ b/HFUTCourseSimulation/HFUTCourseSimulation.csproj
@@ -89,10 +89,13 @@
+
+
+
Designer
diff --git a/HFUTCourseSimulation/Kernel/Render.cs b/HFUTCourseSimulation/Kernel/Render.cs
new file mode 100644
index 0000000..d9e1ab2
--- /dev/null
+++ b/HFUTCourseSimulation/Kernel/Render.cs
@@ -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 {
+
+ ///
+ /// 渲染器的图片的几何形状
+ ///
+ /// 渲染后的图片中,教学周从左到右从上到下排布。
+ /// 整个图片的长和宽上的教学周数量,要使得最终的图形尽量为正方形。
+ /// 每个教学周外围有MARGIN的边框留白。
+ /// 教学周中,最上方是Title,书写当前的教学周号。
+ /// 然后是表格,表格上有Headbar显示星期几和日期,左有Sidebar显示节次。
+ /// 最后是内中表格。
+ ///
+ internal class RenderGeometry {
+ ///
+ /// 教学周标题所占的高度
+ ///
+ public static readonly int TITLE_HEIGHT = 50;
+ ///
+ /// 表头(星期几和日期)所占的高度
+ ///
+ public static readonly int HEADBAR_HEIGHT = 40;
+ ///
+ /// 侧边(节次号)所占的高度
+ ///
+ public static readonly int SIDEBAR_WIDTH = 25;
+ ///
+ /// 每个节次Cell的宽度
+ ///
+ public static readonly int CELL_WIDTH = 150;
+ ///
+ /// 每个节次Cell的高度
+ ///
+ public static readonly int CELL_HEIGHT = 80;
+ ///
+ /// 每个表格之间的长宽上的间距
+ ///
+ 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;
+
+ ///
+ /// 图片宽度
+ ///
+ public int Width { get { return _width; } }
+ ///
+ /// 图片高度
+ ///
+ public int Height { get { return _height; } }
+
+ ///
+ /// 获取教学周的左上角坐标(实际开始画图的开始,排除Margin)
+ ///
+ 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);
+ }
+
+ ///
+ /// 获取课表主体的左上角的坐标
+ ///
+ 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;
+ }
+
+ ///
+ /// 获取课表课程区域的左上角坐标(排除顶栏和侧栏)
+ ///
+ private Point GetBodyPos(int week) {
+ var pos = GetTablePos(week);
+ pos.Offset(SIDEBAR_WIDTH, HEADBAR_HEIGHT);
+ return pos;
+ }
+
+ ///
+ /// 获取指定周,指定星期,指定节次的表格的左上角坐标
+ ///
+ 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));
+ }
+
+ }
+
+ ///
+ /// 渲染器笔刷集合。
+ /// 渲染器工作时,需要频繁创建各种不同颜色的笔刷,此处为其缓存这些笔刷。
+ ///
+ public class RenderBrushes {
+ public RenderBrushes() {
+ _brushes = new Dictionary();
+ }
+
+ 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 _brushes;
+ }
+
+ ///
+ /// 核心图片渲染器
+ ///
+ public static class Render {
+
+ ///
+ /// 渲染给定课表到给定图片中。
+ ///
+ /// 要渲染的课表
+ /// 要渲染到的图片路径
+ /// 如果无误,返回true,否则返回false。
+ 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);
+ }
+ }
+ }
+ }
+}
diff --git a/HFUTCourseSimulation/MainWindow.xaml.cs b/HFUTCourseSimulation/MainWindow.xaml.cs
index 832c3da..bff5a37 100644
--- a/HFUTCourseSimulation/MainWindow.xaml.cs
+++ b/HFUTCourseSimulation/MainWindow.xaml.cs
@@ -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) {
diff --git a/HFUTCourseSimulation/Util/ColorConsistency.cs b/HFUTCourseSimulation/Util/ColorConsistency.cs
new file mode 100644
index 0000000..9d0cddb
--- /dev/null
+++ b/HFUTCourseSimulation/Util/ColorConsistency.cs
@@ -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 {
+
+ ///
+ /// 用于在Simulator和Render之间确保采用的色彩的一致性
+ ///
+ public static class ColorConsistency {
+
+ ///
+ /// 获取标题栏的配色(星期几和日期)
+ ///
+ public static ColorPair HeadbarColor => ColorPreset.MdColors.Grey;
+
+ ///
+ /// 获取侧边栏的配色(节次号)
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 棋盘中的异色颜色的配置
+ ///
+ public static Color ChessboardColor => Color.FromArgb(10, 0, 0, 0);
+
+ }
+
+}
diff --git a/HFUTCourseSimulation/Util/ColorTrans.cs b/HFUTCourseSimulation/Util/ColorTrans.cs
index 9178e8b..6e858c0 100644
--- a/HFUTCourseSimulation/Util/ColorTrans.cs
+++ b/HFUTCourseSimulation/Util/ColorTrans.cs
@@ -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();
}
diff --git a/HFUTCourseSimulation/Util/WeekNames.cs b/HFUTCourseSimulation/Util/WeekNames.cs
new file mode 100644
index 0000000..7839531
--- /dev/null
+++ b/HFUTCourseSimulation/Util/WeekNames.cs
@@ -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;
+
+ }
+}