diff --git a/Doc/2Z443 Distributed Controller补偿校准.pdf b/Doc/2Z443 Distributed Controller补偿校准.pdf
new file mode 100644
index 0000000..64027c9
Binary files /dev/null and b/Doc/2Z443 Distributed Controller补偿校准.pdf differ
diff --git a/HSI_HexagonMI_EF3/ACS/ACSCL_x64.LIB b/HSI_HexagonMI_EF3/ACS/ACSCL_x64.LIB
deleted file mode 100644
index 4260762..0000000
Binary files a/HSI_HexagonMI_EF3/ACS/ACSCL_x64.LIB and /dev/null differ
diff --git a/HSI_HexagonMI_EF3/version.h b/HSI_HexagonMI_EF3/version.h
index bb35120..649320e 100644
--- a/HSI_HexagonMI_EF3/version.h
+++ b/HSI_HexagonMI_EF3/version.h
@@ -12,5 +12,5 @@
#define HSI_VERSION_REVNUM
#define HSI_VERSION_BUILD_DATE _T(__DATE__ )
#define HSI_VERSION_BUILD_TIME _T(__TIME__ )
-#define HSI_FILE_DESCRIPTION "2025.03.05 / 16:50 "
-#define HSI_FILE_CSDESCRIPTION _T("2025.03.05 / 16:50 ")
+#define HSI_FILE_DESCRIPTION "2025.03.11 / 17:19 "
+#define HSI_FILE_CSDESCRIPTION _T("2025.03.11 / 17:19 ")
diff --git a/HSI_SEVENOCEAN_EF1_CsTest/bin/Debug/Config/EF3_Config.ini b/HSI_SEVENOCEAN_EF1_CsTest/bin/Debug/Config/EF3_Config.ini
index b316dc1..92ed18c 100644
Binary files a/HSI_SEVENOCEAN_EF1_CsTest/bin/Debug/Config/EF3_Config.ini and b/HSI_SEVENOCEAN_EF1_CsTest/bin/Debug/Config/EF3_Config.ini differ
diff --git a/HexcalMC/Base/BaseFunction.cs b/HexcalMC/Base/BaseFunction.cs
index 1c60f15..8d57e0e 100644
--- a/HexcalMC/Base/BaseFunction.cs
+++ b/HexcalMC/Base/BaseFunction.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
@@ -10,9 +9,7 @@ using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading.Tasks;
using System.Windows.Forms;
-using System.Net.NetworkInformation;
namespace HexcalMC.Base
{
@@ -21,6 +18,7 @@ namespace HexcalMC.Base
public class RichTextUnit
{
public static float MSize = 16;
+
public static void SetFont(RichTextBox mRichTextBox, Color mColor, bool bBold = false, float size = 16)
{
mRichTextBox.SelectionColor = mColor;
@@ -70,6 +68,7 @@ namespace HexcalMC.Base
RichTextUnit.SetFont(mRichTextBox, Color.DarkRed, false, size);
}
break;
+
case '>':
{
RichTextUnit.SetFont(mRichTextBox, Color.Blue, false, size);
@@ -77,12 +76,14 @@ namespace HexcalMC.Base
RichTextUnit.SetFont(mRichTextBox, Color.Black, false, size);
}
break;
+
case '/':
{
RichTextUnit.SetFont(mRichTextBox, Color.Blue, false, size);
mRichTextBox.SelectedText = strText[i].ToString();
}
break;
+
case '=':
{
if (strText[i + 1] == '"')
@@ -93,6 +94,7 @@ namespace HexcalMC.Base
}
}
break;
+
case '"':
{
RichTextUnit.SetFont(mRichTextBox, Color.Blue, false, size);
@@ -103,12 +105,14 @@ namespace HexcalMC.Base
RichTextUnit.SetFont(mRichTextBox, Color.DarkRed, false, size);
}
break;
+
case '!':
{
RichTextUnit.SetFont(mRichTextBox, Color.Green, false, size);
mRichTextBox.SelectedText = strText[i].ToString();
}
break;
+
case '\r':
{
if (strText[i + 1] == '\n')
@@ -118,13 +122,14 @@ namespace HexcalMC.Base
}
}
break;
+
default:
mRichTextBox.SelectedText = strText[i].ToString();
break;
}
}
}
- #endregion
+ #endregion PARSE THROUGH TEXT DATA
mRichTextBox.SelectedText = Environment.NewLine;
}));
@@ -143,6 +148,7 @@ namespace HexcalMC.Base
RichTextUnit.SetFont(mRichTextBox, Color.Red, false, size);
mRichTextBox.SelectedText = strText[i].ToString();
break;
+
case '0':
case '1':
case '2':
@@ -156,16 +162,18 @@ namespace HexcalMC.Base
RichTextUnit.SetFont(mRichTextBox, Color.DeepSkyBlue, false, size);
mRichTextBox.SelectedText = strText[i].ToString();
break;
+
default:
RichTextUnit.SetFont(mRichTextBox, Color.White, false, size);
mRichTextBox.SelectedText = strText[i].ToString();
break;
}
}
- #endregion
+ #endregion PARSE THROUGH TEXT DATA
}));
}
}
+
public class MyBase
{
#region 内存回收
@@ -198,7 +206,7 @@ namespace HexcalMC.Base
}
}
- #endregion
+ #endregion 内存回收
public static void KillSoftware(string strSoftwareName)
{
@@ -316,8 +324,8 @@ namespace HexcalMC.Base
if (mColor == new Color())
{
if (str.ToUpper().Contains("ERROR") || str.ToUpper().Contains("错误") ||
- str.ToUpper().Contains("出错") || str.ToUpper().Contains("EXCEPTION") ||
- str.ToUpper().Contains("异常") || str.ToUpper().Contains("失败"))
+ str.ToUpper().Contains("出错") || str.ToUpper().Contains("EXCEPTION") ||
+ str.ToUpper().Contains("异常") || str.ToUpper().Contains("失败"))
{
setColor = Color.Red;
}
@@ -331,7 +339,7 @@ namespace HexcalMC.Base
setColor = mColor;
}
- string strText = str + Environment.NewLine; //DateTime.Now.ToString("HH:mm:ss.fff") + "--" +
+ string strText = str + Environment.NewLine; //DateTime.Now.ToString("HH:mm:ss.fff") + "--" +
rtb.SelectionStart = rtb.TextLength;
if (string.IsNullOrEmpty(str))
RichTextUnit.SetText(rtb, " " + Environment.NewLine, setColor, false, 14);
@@ -353,7 +361,7 @@ namespace HexcalMC.Base
}
///
- /// 写debug文件,记录程序过程
+ /// 写debug文件,记录程序过程
///
/// 要写入日志的内容
public static void TraceWriteLine(string str)
@@ -369,7 +377,6 @@ namespace HexcalMC.Base
}
}
-
public static string InputBox(string caption, string hint, string defaultTxt, string btn1 = "OK",
string btn2 = "Cancel", char strstyle = '*', bool bShowData = false)
{
@@ -407,7 +414,7 @@ namespace HexcalMC.Base
btnok.Height = 30;
btnok.Parent = inputForm;
btnok.Text = btn1;
- inputForm.AcceptButton = btnok; //回车响应
+ inputForm.AcceptButton = btnok; //回车响应
btnok.DialogResult = DialogResult.OK;
Button btncancal = new Button();
@@ -477,7 +484,7 @@ namespace HexcalMC.Base
btnYes.Text = btnName1;
btnYes.DialogResult = DialogResult.Yes;
- errorForm.AcceptButton = btnYes; //回车响应
+ errorForm.AcceptButton = btnYes; //回车响应
Button btnNo = new Button();
btnNo.Location = new System.Drawing.Point(190, 210);
@@ -485,7 +492,7 @@ namespace HexcalMC.Base
btnNo.Parent = errorForm;
btnNo.Text = btnName2;
btnNo.DialogResult = DialogResult.No;
- errorForm.AcceptButton = btnNo; //回车响应
+ errorForm.AcceptButton = btnNo; //回车响应
Button btncancal = new Button();
btncancal.Location = new System.Drawing.Point(350, 210);
@@ -493,7 +500,7 @@ namespace HexcalMC.Base
btncancal.Parent = errorForm;
btncancal.Text = btnName3;
btncancal.DialogResult = DialogResult.Cancel;
- errorForm.AcceptButton = btncancal; //回车响应
+ errorForm.AcceptButton = btncancal; //回车响应
try
{
btnYes.Select();
@@ -513,17 +520,17 @@ namespace HexcalMC.Base
[DllImport("User32.dll")]
private static extern bool SetCursorPos(int x, int y);
- static public void SetCursorPosXy(int dx, int dy)
+ public static void SetCursorPosXy(int dx, int dy)
{
SetCursorPos(dx, dy);
}
- static public void SetCursorPosXy(Point point)
+ public static void SetCursorPosXy(Point point)
{
System.Windows.Forms.Cursor.Position = point;
}
- #endregion
+ #endregion 界面控件操作
///
/// CopyFiles 函数
@@ -563,7 +570,9 @@ namespace HexcalMC.Base
public class HardwareInfoBase
{
- /// 获取指定驱动器的空间总大小(单位为B) ,只需输入代表驱动器的字母即可
+ ///
+ /// 获取指定驱动器的空间总大小(单位为B) ,只需输入代表驱动器的字母即可
+ ///
public static long GetHardDiskSpace(string strHardDiskName)
{
long totalSize = new long();
@@ -581,7 +590,9 @@ namespace HexcalMC.Base
return totalSize;
}
- /// 获取指定驱动器的剩余空间总大小(单位为B) ,只需输入代表驱动器的字母即可
+ ///
+ /// 获取指定驱动器的剩余空间总大小(单位为B) ,只需输入代表驱动器的字母即可
+ ///
public static long GetHardDiskFreeSpace(string strHardDiskName)
{
long freeSpace = new long();
@@ -599,19 +610,25 @@ namespace HexcalMC.Base
return freeSpace;
}
- /// 获取指定驱动器的剩余空间总大小(单位为K) ,只需输入代表驱动器的字母即可
+ ///
+ /// 获取指定驱动器的剩余空间总大小(单位为K) ,只需输入代表驱动器的字母即可
+ ///
public static long GetHardDiskFreeSpace_K(string strHardDiskName)
{
return GetHardDiskFreeSpace(strHardDiskName) / 1024;
}
- /// 获取指定驱动器的剩余空间总大小(单位为M) ,只需输入代表驱动器的字母即可
+ ///
+ /// 获取指定驱动器的剩余空间总大小(单位为M) ,只需输入代表驱动器的字母即可
+ ///
public static long GetHardDiskFreeSpace_M(string strHardDiskName)
{
return GetHardDiskFreeSpace_K(strHardDiskName) / 1024;
}
- /// 获取指定驱动器的剩余空间总大小(单位为G) ,只需输入代表驱动器的字母即可
+ ///
+ /// 获取指定驱动器的剩余空间总大小(单位为G) ,只需输入代表驱动器的字母即可
+ ///
public static long GetHardDiskFreeSpace_G(string strHardDiskName)
{
return GetHardDiskFreeSpace_M(strHardDiskName) / 1024;
@@ -644,7 +661,6 @@ namespace HexcalMC.Base
return false;
}
-
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal,
int size, string filePath);
@@ -744,7 +760,7 @@ namespace HexcalMC.Base
WritePrivateProfileString(section, key, strWrite, path);
}
- #endregion
+ #endregion 写INI参数
//=====================//=====================//=====================Read data
@@ -759,7 +775,7 @@ namespace HexcalMC.Base
/// 要读取的string类型内容
public static string ReadString(string path, string section, string key)
{
- // 每次从ini中读取多少字节 // section=配置节,key=键名,temp=上面,path=路径
+ // 每次从ini中读取多少字节 // section=配置节,key=键名,temp=上面,path=路径
System.Text.StringBuilder temp = new System.Text.StringBuilder(255);
GetPrivateProfileString(section, key, "", temp, 255, path);
String str = temp.ToString();
@@ -814,10 +830,9 @@ namespace HexcalMC.Base
return false;
}
- #endregion
+ #endregion 读INI参数
}
-
//==================================================================================================SoundBase
///
/// 声音播放类
@@ -852,7 +867,7 @@ namespace HexcalMC.Base
// FileDialog.AddExtension = true;
// FileDialog.CheckFileExists = true;
// FileDialog.CheckPathExists = true;
- // //the next sentence must be in single line
+ // //the next sentence must be in single line
// FileDialog.Filter = "WAV文件(*.wav)|*.wav|MP3文件(*.mp3)|*.mp3|VCD文件(*.dat)|*.dat|Audio文件(*.avi)|*.avi|所有文件 (*.*)|*.*";
// FileDialog.DefaultExt = "*.mp3";
// if (FileDialog.ShowDialog() == DialogResult.OK)
@@ -867,7 +882,7 @@ namespace HexcalMC.Base
// FileDialog.AddExtension = true;
// FileDialog.CheckFileExists = true;
// FileDialog.CheckPathExists = true;
- // //the next sentence must be in single line
+ // //the next sentence must be in single line
// FileDialog.Filter = "WAV文件(*.wav)|*.wav|MP3文件(*.mp3)|*.mp3|VCD文件(*.dat)|*.dat|Audio文件(*.avi)|*.avi|所有文件 (*.*)|*.*";
// FileDialog.DefaultExt = "*.mp3";
// if (FileDialog.ShowDialog() == DialogResult.OK)
@@ -914,12 +929,11 @@ namespace HexcalMC.Base
}
}
-
//==================================================================================================
///
/// 数据格式化或校验检测
///
- class FormatCheckBase
+ internal class FormatCheckBase
{
///
/// 检测是否为十六进制字符串,长度不够在前面添加0
@@ -1019,14 +1033,14 @@ namespace HexcalMC.Base
return true;
}
- ///
- /// 每隔n个字符插入一个字符
- ///
- /// 从右边开始插入
- /// 源字符串
- /// 间隔字符数
- /// 待插入值
- /// 待补充值,最后不足间隔字符数时,用此字符补齐;Supplement=""时,不补任何字符。
+ ///
+ /// 每隔n个字符插入一个字符
+ ///
+ /// 从右边开始插入
+ /// 源字符串
+ /// 间隔字符数
+ /// 待插入值
+ /// 待补充值,最后不足间隔字符数时,用此字符补齐;Supplement=""时,不补任何字符。
/// 返回新生成字符串
public static string InsertFormat(bool isRight, string input, int interval, string value, string supplement)
{
@@ -1125,7 +1139,7 @@ namespace HexcalMC.Base
#region
- static readonly byte[] AuchCrcHi = new byte[]
+ private static readonly byte[] AuchCrcHi = new byte[]
{
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
@@ -1161,7 +1175,7 @@ namespace HexcalMC.Base
#region
- static readonly byte[] AuchCrcLo = new byte[]
+ private static readonly byte[] AuchCrcLo = new byte[]
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
@@ -1218,19 +1232,18 @@ namespace HexcalMC.Base
}
}
-
//==================================================================================================ConvertBase
///
/// 数据转换类
///
- class ConvertBase
+ internal class ConvertBase
{
#region 图像 <--> 数组
///
/// 图像转换为Byte数组
///
- static public byte[] ImageToByteArray(Image imageIn)
+ public static byte[] ImageToByteArray(Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
@@ -1240,7 +1253,7 @@ namespace HexcalMC.Base
///
/// Byte数组转换为图像
///
- static public Image ByteArrayToImage(byte[] byteArrayIn)
+ public static Image ByteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
@@ -1442,9 +1455,9 @@ namespace HexcalMC.Base
for (int i = 0; i < str.Length; i++)
{
if (!
- (((str[i] >= '0') && (str[i] <= '9')) ||
- ((str[i] >= 'a') && (str[i] <= 'f')) ||
- ((str[i] >= 'A') && (str[i] <= 'F')))
+ (((str[i] >= '0') && (str[i] <= '9')) ||
+ ((str[i] >= 'a') && (str[i] <= 'f')) ||
+ ((str[i] >= 'A') && (str[i] <= 'F')))
)
{
return false;
@@ -1644,12 +1657,12 @@ namespace HexcalMC.Base
#region C++转换程序(C#里面有完整的转换函数)
- //十进制转二制
+ //十进制转二制
public static string DtoB(int d)
{
//Console.WriteLine(Convert.ToString(5,2))
string str = "";
- //判断该数如果小于2,则直接输出
+ //判断该数如果小于2,则直接输出
if (d < 2)
{
str = d.ToString();
@@ -1724,7 +1737,7 @@ namespace HexcalMC.Base
return o;
}
- //十进制转十六进制
+ //十进制转十六进制
public static string DtoX(int d)
{
string x = "";
@@ -1749,7 +1762,7 @@ namespace HexcalMC.Base
do
{
c = d / 16;
- m[i++] = Chang(d % 16); //判断是否大于10,如果大于10,则转换为A~F的格式
+ m[i++] = Chang(d % 16); //判断是否大于10,如果大于10,则转换为A~F的格式
d = c;
} while (c >= 16);
@@ -1763,7 +1776,7 @@ namespace HexcalMC.Base
return x;
}
- //判断是否为10~15之间的数,如果是则进行转换
+ //判断是否为10~15之间的数,如果是则进行转换
public static string Chang(int d)
{
string x = "";
@@ -1772,21 +1785,27 @@ namespace HexcalMC.Base
case 10:
x = "A";
break;
+
case 11:
x = "B";
break;
+
case 12:
x = "C";
break;
+
case 13:
x = "D";
break;
+
case 14:
x = "E";
break;
+
case 15:
x = "F";
break;
+
default:
x = d.ToString();
break;
@@ -1807,12 +1826,11 @@ namespace HexcalMC.Base
}
}
-
//==================================================================================================TcpBase
///
/// 网络通讯通用类函数库
///
- class TcpBase
+ internal class TcpBase
{
///
/// 用CMD命令测试网络连接状态
@@ -1851,12 +1869,11 @@ namespace HexcalMC.Base
}
}
-
//==================================================================================================MyMath
///
/// 数学函数库(算法)
///
- class MyMath
+ internal class MyMath
{
public static double GetMax(double[] datas)
{
@@ -1981,36 +1998,52 @@ namespace HexcalMC.Base
{
case 0:
return (data & 0x0001) > 0;
+
case 1:
return (data & 0x0002) > 0;
+
case 2:
return (data & 0x0004) > 0;
+
case 3:
return (data & 0x0008) > 0;
+
case 4:
return (data & 0x0010) > 0;
+
case 5:
return (data & 0x0020) > 0;
+
case 6:
return (data & 0x0040) > 0;
+
case 7:
return (data & 0x0080) > 0;
+
case 8:
return (data & 0x0100) > 0;
+
case 9:
return (data & 0x0200) > 0;
+
case 10:
return (data & 0x0400) > 0;
+
case 11:
return (data & 0x0800) > 0;
+
case 12:
return (data & 0x1000) > 0;
+
case 13:
return (data & 0x2000) > 0;
+
case 14:
return (data & 0x4000) > 0;
+
case 15:
return (data & 0x8000) > 0;
+
default:
return false;
}
@@ -2022,37 +2055,52 @@ namespace HexcalMC.Base
{
case 0:
return (data & 0x0100) > 0;
+
case 1:
return (data & 0x0200) > 0;
+
case 2:
return (data & 0x0400) > 0;
+
case 3:
return (data & 0x0800) > 0;
+
case 4:
return (data & 0x1000) > 0;
+
case 5:
return (data & 0x2000) > 0;
+
case 6:
return (data & 0x4000) > 0;
+
case 7:
return (data & 0x8000) > 0;
case 8:
return (data & 0x0001) > 0;
+
case 9:
return (data & 0x0002) > 0;
+
case 10:
return (data & 0x0004) > 0;
+
case 11:
return (data & 0x0008) > 0;
+
case 12:
return (data & 0x0010) > 0;
+
case 13:
return (data & 0x0020) > 0;
+
case 14:
return (data & 0x0040) > 0;
+
case 15:
return (data & 0x0080) > 0;
+
default:
return false;
}
@@ -2077,7 +2125,6 @@ namespace HexcalMC.Base
return bResult;
}
-
///
/// 获取单精度浮点数(float)数据
///
@@ -2187,6 +2234,7 @@ namespace HexcalMC.Base
}
break;
+
case 10:
foreach (byte b in arrLength)
{
@@ -2194,6 +2242,7 @@ namespace HexcalMC.Base
}
break;
+
case 16:
foreach (byte b in arrLength)
{
@@ -2220,7 +2269,6 @@ namespace HexcalMC.Base
}
}
-
//======================================================================
///
/// 字节数组(byte 8位)转化为ushort(16位)数组
@@ -2282,7 +2330,6 @@ namespace HexcalMC.Base
return ushorts;
}
-
//======================================================================
///
/// 字节数组(byte 8位)转化为short(16位)数组
@@ -2355,13 +2402,13 @@ namespace HexcalMC.Base
}
}
- class CodeDfn
+ internal class CodeDfn
{
public const string BlankSpace = " ";
public const string StrEnter = "\r\n";
}
- class PlcMath
+ internal class PlcMath
{
#region S7协议数据处理(以字节为基础)
@@ -2412,7 +2459,7 @@ namespace HexcalMC.Base
if (byteData.Length < (dataAddr - startAddr))
{
MessageBox.Show("错误:获取S7字节数据, 要读取的偏移地址超出数组长度,偏移=" + (dataAddr - startAddr) + ", 数组长度=" +
- byteData.Length);
+ byteData.Length);
return 0;
}
else
@@ -2547,6 +2594,7 @@ namespace HexcalMC.Base
}
break;
+
case 10:
foreach (byte b in arrLength)
{
@@ -2554,6 +2602,7 @@ namespace HexcalMC.Base
}
break;
+
case 16:
foreach (byte b in arrLength)
{
@@ -2561,6 +2610,7 @@ namespace HexcalMC.Base
}
break;
+
default:
foreach (byte b in arrLength)
{
@@ -2582,7 +2632,6 @@ namespace HexcalMC.Base
#endregion
-
#region ModbusTCP/FinsTCP协议数据处理(以字为基础)
///
@@ -2708,36 +2757,52 @@ namespace HexcalMC.Base
{
case 0:
return (data & 0x0001) > 0;
+
case 1:
return (data & 0x0002) > 0;
+
case 2:
return (data & 0x0004) > 0;
+
case 3:
return (data & 0x0008) > 0;
+
case 4:
return (data & 0x0010) > 0;
+
case 5:
return (data & 0x0020) > 0;
+
case 6:
return (data & 0x0040) > 0;
+
case 7:
return (data & 0x0080) > 0;
+
case 8:
return (data & 0x0100) > 0;
+
case 9:
return (data & 0x0200) > 0;
+
case 10:
return (data & 0x0400) > 0;
+
case 11:
return (data & 0x0800) > 0;
+
case 12:
return (data & 0x1000) > 0;
+
case 13:
return (data & 0x2000) > 0;
+
case 14:
return (data & 0x4000) > 0;
+
case 15:
return (data & 0x8000) > 0;
+
default:
return false;
}
@@ -2749,37 +2814,52 @@ namespace HexcalMC.Base
{
case 0:
return (data & 0x0100) > 0;
+
case 1:
return (data & 0x0200) > 0;
+
case 2:
return (data & 0x0400) > 0;
+
case 3:
return (data & 0x0800) > 0;
+
case 4:
return (data & 0x1000) > 0;
+
case 5:
return (data & 0x2000) > 0;
+
case 6:
return (data & 0x4000) > 0;
+
case 7:
return (data & 0x8000) > 0;
case 8:
return (data & 0x0001) > 0;
+
case 9:
return (data & 0x0002) > 0;
+
case 10:
return (data & 0x0004) > 0;
+
case 11:
return (data & 0x0008) > 0;
+
case 12:
return (data & 0x0010) > 0;
+
case 13:
return (data & 0x0020) > 0;
+
case 14:
return (data & 0x0040) > 0;
+
case 15:
return (data & 0x0080) > 0;
+
default:
return false;
}
@@ -2913,6 +2993,7 @@ namespace HexcalMC.Base
}
break;
+
case 10:
foreach (byte b in arrLength)
{
@@ -2920,6 +3001,7 @@ namespace HexcalMC.Base
}
break;
+
case 16:
foreach (byte b in arrLength)
{
@@ -2927,6 +3009,7 @@ namespace HexcalMC.Base
}
break;
+
default:
foreach (byte b in arrLength)
{
@@ -2949,11 +3032,8 @@ namespace HexcalMC.Base
#endregion
}
-
-
- class Internet
+ internal class Internet
{
-
public static bool IsIpReachable(string ipAddress)
{
try
diff --git a/HexcalMC/Base/DebugDfn.cs b/HexcalMC/Base/DebugDfn.cs
index 3c2e731..439129c 100644
--- a/HexcalMC/Base/DebugDfn.cs
+++ b/HexcalMC/Base/DebugDfn.cs
@@ -1,11 +1,7 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows.Forms;
namespace HexcalMC.Base
@@ -20,7 +16,6 @@ namespace HexcalMC.Base
public static RichTextBox textBox_Msg;
-
//=================================================================
public static void StartDebugObj()
{
@@ -122,26 +117,21 @@ namespace HexcalMC.Base
}
}
- #endregion
+ #endregion 信息显示
}
public class Errors
{
///
- /// 0:无故障 PLC按位求 1对应101
- /// 1:通讯故障
- /// 2:扫描枪读取故障
- /// 3: 控制柜急停被按下
- /// 4:外部急停被按下
- /// 5:PCL急停,辊道在CMM测量时上料
+ /// 0:无故障 PLC按位求 1对应101 1:通讯故障 2:扫描枪读取故障
+ /// 3: 控制柜急停被按下 4:外部急停被按下 5:PCL急停,辊道在CMM测量时上料
///
public static int iErrors = 0;
- /// 101:通讯故障
- /// 102:扫描枪读取故障
- /// 103: 控制柜急停被按下
- /// 104:外部急停被按下
- /// 105:PCL急停,辊道在CMM测量时上料
+
+ /// 101:通讯故障 102:扫描枪读取故障
+ /// 103: 控制柜急停被按下 104:外部急停被按下 105:PCL急停,辊道在CMM测量时上料
public static bool bCommError = false;
+
public static bool bReaderError = false;
public static bool bEStop_Controller = false;
public static bool bEStop_Out = false;
@@ -158,7 +148,7 @@ namespace HexcalMC.Base
try
{
if (Errors.ErrorWrite == null)
- Errors.ErrorWrite = new StreamWriter(DebugDfn.StrDebugSavePath + "Error_(" + DateTime.Now.ToString("yyyy-MM-dd") + ").txt", true);
+ Errors.ErrorWrite = new StreamWriter(DebugDfn.StrDebugSavePath + "Error_(" + DateTime.Now.ToString("yyyy-MM-dd") + ").txt", true);
Errors.ErrorWrite.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + strError);
Errors.ErrorWrite.Flush();
}
@@ -179,4 +169,4 @@ namespace HexcalMC.Base
{ }
}
}
-}
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/Editor3D/Editor3D.cs b/HexcalMC/Base/Editor3D/Editor3D.cs
index c44e6a7..0318a99 100644
--- a/HexcalMC/Base/Editor3D/Editor3D.cs
+++ b/HexcalMC/Base/Editor3D/Editor3D.cs
@@ -1,5 +1,4 @@
-
-/*****************************************************************************
+/*****************************************************************************
This class has been written by Elmü (elmue@gmx.de)
@@ -7,18 +6,18 @@ Check if you have the latest version on:
https://www.codeproject.com/Articles/5293980/Graph3D-A-Windows-Forms-Render-Control-in-Csharp
=======================================
-
+
IMPORTANT:
This class has been written by a software developer with 45 years programming exprience.
This class is optimized for the highest possible speed in every line of it's code.
If you found a fork of this code on Github or elsewhere you do NOT have the original high quality code!
-This code with 5000 lines is extremely complex and there is a very high risk that a beginner has
+This code with 5000 lines is extremely complex and there is a very high risk that a beginner has
broken this code by modifying it without properly understanding it.
=======================================
-
+
NAMING CONVENTIONS which allow to see the type of a variable immediately without having to jump to the variable definition:
-
+
cName for class definitions
tName for type definitions
eName for enum definitions
@@ -35,7 +34,7 @@ NAMING CONVENTIONS which allow to see the type of a variable immediately without
r_Name for Rectangle
s_Name for strings
o_Name for objects
-
+
s8_Name for signed 8 Bit (sbyte)
s16_Name for signed 16 Bit (short)
s32_Name for signed 32 Bit (int)
@@ -47,5102 +46,5110 @@ NAMING CONVENTIONS which allow to see the type of a variable immediately without
An additional "m" is prefixed for all member variables (e.g. ms_String)
-
*****************************************************************************/
// Print render speed to Trace output
// ATTENTION: The times in the very first trace output are always wrong because of JIT compilation delays.
#if DEBUG
-// #define DEBUG_SPEED
+// #define DEBUG_SPEED
#endif
using System;
-using System.Text;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
-using System.ComponentModel;
-using System.Collections.Generic;
using System.Globalization;
+using System.Text;
using System.Windows.Forms;
-using System.Diagnostics;
namespace Plot3D
{
- ///
- /// ATTENTION: This class is not thread safe.
- /// Call all functions only from the GUI thread or use Control.Invoke()
- ///
- public class Editor3D : UserControl
- {
- #region enums
+ ///
+ /// ATTENTION: This class is not thread safe. Call all functions only from the GUI thread or use Control.Invoke()
+ ///
+ public class Editor3D : UserControl
+ {
+ #region enums
- public enum eColorScheme
- {
- Autumn = 0,
- Cool,
- Copper,
- Hot,
- Hsv,
- Monochrome,
- Pink,
- Rainbow_Sweep, // This creates a 100% cyclic rainbow with 1536 colors. The end color is the same as the start color.
- Rainbow_Bright, // This creates a rainbow without magenta with 1024 colors. It goes from blue to red.
- Rainbow_Dark, // This is similar to RainbowBright, but darker and only with 64 colors.
- Spring,
- Summer,
- Winter,
+ public enum eColorScheme
+ {
+ Autumn = 0,
+ Cool,
+ Copper,
+ Hot,
+ Hsv,
+ Monochrome,
+ Pink,
+ Rainbow_Sweep, // This creates a 100% cyclic rainbow with 1536 colors. The end color is the same as the start color.
+ Rainbow_Bright, // This creates a rainbow without magenta with 1024 colors. It goes from blue to red.
+ Rainbow_Dark, // This is similar to RainbowBright, but darker and only with 64 colors.
+ Spring,
+ Summer,
+ Winter,
Green
}
- public enum eRaster
- {
- Off, // turn off coordinate system
- MainAxes, // draw only solid main axes for X,Y,Z
- Raster, // draw additional thin raster lines
- Labels, // draw additional labels in quadrant 3
- }
-
- ///
- /// If a function has an asymetric range for X and Y as demo "Callback" a separate normalization
- /// would always lead to a square X,Y pane which would be a distortion for the relation between X and Y values.
- /// MaintainXY guarantees that the relation between X and Y values is maintained.
- /// MaintainXYZ additionally guarantees that the relation between X, Y and Z values is maintained.
- ///
- public enum eNormalize
- {
- Separate, // Normalize X,Y,Z separately (use this for discrete values)
- MaintainXY, // Normalize X,Y without changing their relation (use this for math functions)
- MaintainXYZ, // Normalize X,Y,Z without changing their relation (use this for math functions)
- }
-
- public enum eScatterShape
- {
- // 0 is invalid
- Circle = 1,
- Square = 2,
- Triangle = 3,
- // Star = 4, you can implement your own shapes here
- }
-
- ///
- /// Used internally for coordinate system
- ///
- public enum eCoord
- {
- // for axis in coordinate system
- X = 0,
- Y = 1,
- Z = 2,
- Invalid,
- }
-
- ///
- /// These flags define which axes are allowed to be mirrored.
- /// For user objects option "All" is used.
- /// Coordinate system axes and raster lines are mirrored individually.
- ///
- [FlagsAttribute]
- public enum eMirror
- {
- None = 0,
- X = 1,
- Y = 2,
- Z = 4,
- XY = X | Y,
- XZ = X | Z,
- YZ = Y | Z,
- All = X | Y | Z,
- }
-
- ///
- /// Mouse operations
- ///
- public enum eMouseAction
- {
- None = 0,
- Move, // Move the coordinate system with the mouse
- Theta, // Elevate the coordinate system with the mouse
- Phi, // Rotate the coordinate system with the mouse
- ThetaAndPhi, // Elevate + Rotate the coordinate system with the mouse
- Rho, // Zoom in / out
- SelectObj, // Select a 3D object or call the selection callback function if defined
- Callback, // Call the selection callback function if defined
- }
-
- public enum eSelEvent
- {
- MouseDown, // Inform selection callback function that the pre-defined mouse button goes down
- MouseDrag, // Inform selection callback function that the mouse is moved while the pre-defined mouse button is down
- MouseUp, // Inform selection callback function that the pre-defined mouse button goes up
- }
-
- public enum ePolygonMode
- {
- Fill, // Fill polygons with Brush
- Lines, // Draw only polygon border lines
- }
-
- ///
- /// This enum defines of which type is a cObject3D
- ///
- public enum eObjType
- {
- Point,
- Line,
- Shape,
- Polygon,
- }
-
- ///
- /// These flags are used to filter the 3D objects that are selected.
- ///
- [FlagsAttribute]
- public enum eSelType
- {
- Line = 0x1,
- Shape = 0x2,
- Polygon = 0x4,
- All = 0x7,
- }
-
- [FlagsAttribute]
- public enum eTooltip
- {
- Off = 0x0, // Tooltip is disabled
- UserText = 0x1, // Show user defined tooltip text that has been set in cPoint3D.Tooltip
- Coord = 0x2, // Show coordinates X,Y,Z of cPoint3D
- All = 0x3, // Show all
- }
-
- // This enum is to get the maximum speed out of your CPU.
- // Re-calculation is done only if required.
- [FlagsAttribute]
- private enum eRecalculate
- {
- Nothing = 0x0, // repaint objects after changed selection --> recalculate nothing
- Objects = 0x1, // Projection, Brush, LineWidth,... has changed --> recalculate 3D objects
- CoordSystem = 0x2, // The coordinate system must be recalculated --> recalculate Min/Max and Coord System
- AddRemove = 0x4, // Draw Objects have been added or removed --> refresh lists and recalculate all
- }
-
- ///
- /// These are the possible return values of the Selection callback.
- /// See description of SelectionCallback() at the end of this class.
- ///
- public enum eInvalidate
- {
- NoChange, // The callback has not modified anything --> do nothing.
- Invalidate, // Calls Invalidate() to redraw what is required depending on the flags in me_Recalculate.
- CoordSystem, // The coordinate system will be recalculated, then Invalidate() is called.
- // Use this option after moving a 3D object with the mouse.
- }
-
- ///
- /// This defines with which mouse button the user controls rotation and elevation
- ///
- public enum eMouseCtrl
- {
- L_Theta_R_Phi, // Left mouse button vertical: Theta, Right mouse button horizontal: Phi
- L_Theta_L_Phi, // Left mouse button vertical: Theta, Left mouse button horizontal: Phi
- M_Theta_M_Phi, // Middle mouse button vertical: Theta, Middle mouse button horizontal: Phi
- }
-
- ///
- /// 3D object properties that the user may change are stored in the Undo Buffer
- ///
- public enum eUndoProp
- {
- // cObject3D
- Tag,
- Selected,
- CanSelect,
-
- // cPoint3D
- X,
- Y,
- Z,
- Tooltip,
-
- // cLine3D
- Pen,
- Width,
-
- // cShape3D
- Shape,
- Radius,
-
- // cShape3D + cPolygon3D
- Brush,
- }
-
- public enum eLegendPos
- {
- BottomLeft, // The main axis legends are drawn in the bottom left corner of the graph pane. (recommended)
- AxisEnd, // The main axis legends are drawn at the end of the axis and rotate with the graph. (not recommended)
- }
-
- #endregion
-
- // ================== PUBLIC =================
-
- #region cObject3D
-
- ///
- /// Base class for cPoint3D, cShape3D, cLine3D, cPloygon3D
- ///
- public abstract class cObject3D
- {
- public Editor3D mi_Inst;
- protected Object mo_Tag;
- protected bool mb_Selected;
- protected bool mb_CanSelect = true;
- protected cPoint3D[] mi_Points; // This instance must never be replaced by a new array!
- protected int ms32_Col = -1;
- protected int ms32_Row = -1;
-
- public virtual void Restore(eUndoProp e_Property, Object o_Value)
- {
- switch (e_Property)
- {
- case eUndoProp.Tag: mo_Tag = o_Value; break;
- case eUndoProp.Selected: mb_Selected = (bool)o_Value; break;
- case eUndoProp.CanSelect: mb_CanSelect = (bool)o_Value; break;
- default: throw new ArgumentException();
- }
- }
-
- // --------------------------------------------------
-
- ///
- /// Gets the type of this cObject3D
- ///
- public virtual eObjType ObjType
- {
- get { throw new NotImplementedException(); }
- }
-
- ///
- /// 1 point for cPoint3D
- /// 1 point for cShape3D
- /// 2 points for cLine3D
- /// n points for cPolygon3D
- ///
- public cPoint3D[] Points
- {
- get { return mi_Points; }
- // set must NOT be allowed here !!!!
- }
-
- ///
- /// The column in the grid.
- /// This is only valid for Polygons and Scatter circles created by cSurfaceData, otherwise -1
- ///
- public int Column
- {
- get { return ms32_Col; } // the grid column cannot be changed
- }
-
- ///
- /// The row in the grid.
- /// This is only valid for Polygons and Scatter circles created by cSurfaceData, otherwise -1
- ///
- public int Row
- {
- get { return ms32_Row; } // the grid row cannot be changed
- }
-
- ///
- /// Here you can store your private data which is passed in Selection.Callback to your code.
- ///
- public Object Tag
- {
- get { return mo_Tag; }
- set
- {
- if (mo_Tag == value)
- return;
-
- if (mi_Inst != null)
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Tag, mo_Tag, value);
-
- mo_Tag = value;
- }
- }
-
- ///
- /// The draw object has been selected by ALT + click
- ///
- public virtual bool Selected
- {
- get { return mb_Selected; }
- set
- {
- if (mb_Selected == value)
- return;
-
- if (mi_Inst != null)
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Selected, mb_Selected, value);
-
- mb_Selected = value;
- // me_Recalculate needs no change here
- }
- }
-
- ///
- /// Defines if the user is allowed to select this object
- ///
- public virtual bool CanSelect
- {
- get { return mb_CanSelect; }
- set
- {
- if (mb_CanSelect == value)
- return;
-
- if (mi_Inst != null)
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.CanSelect, mb_CanSelect, value);
-
- mb_CanSelect = value;
- }
- }
-
- ///
- /// Move the object in the 3D space
- ///
- public void Move(double d_DeltaX, double d_DeltaY, double d_DeltaZ)
- {
- foreach (cPoint3D i_Point in mi_Points)
- {
- i_Point.X += d_DeltaX;
- i_Point.Y += d_DeltaY;
- i_Point.Z += d_DeltaZ;
- }
- }
- }
-
- #endregion
-
- #region cPoint3D
-
- public class cPoint3D : cObject3D
- {
- private double md_X;
- private double md_Y;
- private double md_Z;
- private String ms_Tooltip;
-
- public override void Restore(eUndoProp e_Property, Object o_Value)
- {
- switch (e_Property)
- {
- case eUndoProp.X: md_X = (double)o_Value; break;
- case eUndoProp.Y: md_Y = (double)o_Value; break;
- case eUndoProp.Z: md_Z = (double)o_Value; break;
- case eUndoProp.Tooltip: ms_Tooltip = (String)o_Value; break;
- default: base.Restore(e_Property, o_Value); break;
- }
- }
-
- // --------------------------------------------------
-
- ///
- /// Gets the type of this cObject3D
- ///
- public override eObjType ObjType
- {
- get { return eObjType.Point; }
- }
-
- ///
- /// 3D coordinate
- ///
- public double X
- {
- get { return md_X; }
- set
- {
- if (md_X == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.X, md_X, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
-
- if (value < mi_Inst.mi_Bounds.X.Min || value > mi_Inst.mi_Bounds.X.Max)
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
- }
- md_X = value;
- }
- }
-
- ///
- /// 3D coordinate
- ///
- public double Y
- {
- get { return md_Y; }
- set
- {
- if (md_Y == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Y, md_Y, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
-
- if (value < mi_Inst.mi_Bounds.Y.Min || value > mi_Inst.mi_Bounds.Y.Max)
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
- }
- md_Y = value;
- }
- }
-
- ///
- /// 3D coordinate
- ///
- public double Z
- {
- get { return md_Z; }
- set
- {
- if (md_Z == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Z, md_Z, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
-
- if (value < mi_Inst.mi_Bounds.Z.Min || value > mi_Inst.mi_Bounds.Z.Max)
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
- }
- md_Z = value;
- }
- }
-
- ///
- /// Optional tooltip text to be displayed when the mouse is over this point
- ///
- public String Tooltip
- {
- get { return ms_Tooltip; }
- set
- {
- if (ms_Tooltip == value)
- return;
-
- if (mi_Inst != null)
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Tooltip, ms_Tooltip, value);
-
- ms_Tooltip = value;
- }
- }
-
- // --------------------------------------------------
-
- ///
- /// Constructor
- /// s_ToolTip is displayed when eTooltip.UserText is enabled
- /// In o_Tag you can store any data that you need when the Selection callback is called after the user has selected a point.
- ///
- public cPoint3D(double d_X, double d_Y, double d_Z, String s_ToolTip = null, Object o_Tag = null)
- {
- md_X = d_X;
- md_Y = d_Y;
- md_Z = d_Z;
- mo_Tag = o_Tag;
- mi_Points = new cPoint3D[] { this };
-
- if (s_ToolTip != null)
- ms_Tooltip = s_ToolTip.Trim();
- }
-
- // =================== used for coordinate system ===================
-
- public cPoint3D Clone()
- {
- return new cPoint3D(md_X, md_Y, md_Z, ms_Tooltip, Tag);
- }
-
- public bool CoordEquals(cPoint3D i_Point)
- {
- return md_X == i_Point.md_X && md_Y == i_Point.md_Y && md_Z == i_Point.md_Z;
- }
-
- public double GetValue(eCoord e_Coord)
- {
- switch (e_Coord)
- {
- case eCoord.X: return md_X;
- case eCoord.Y: return md_Y;
- case eCoord.Z: return md_Z;
- default: return 0;
- }
- }
-
- public void SetValue(eCoord e_Coord, double d_Value)
- {
- switch (e_Coord)
- {
- case eCoord.X: X = d_Value; break;
- case eCoord.Y: Y = d_Value; break;
- case eCoord.Z: Z = d_Value; break;
- }
- }
-
- // For debugging in Visual Studio
- public override string ToString()
- {
- return String.Format("cPoint3D (X={0}, Y={1}, Z={2})", FormatDouble(md_X), FormatDouble(md_Y), FormatDouble(md_Z));
- }
- }
-
- #endregion
-
- #region cLine3D
-
- public class cLine3D : cObject3D
- {
- private Pen mi_Pen;
- private int ms32_Width;
- private int ms32_Parts;
-
- public override void Restore(eUndoProp e_Property, Object o_Value)
- {
- switch (e_Property)
- {
- case eUndoProp.Pen: mi_Pen = (Pen)o_Value; break;
- case eUndoProp.Width: ms32_Width = (int)o_Value; break;
- default: base.Restore(e_Property, o_Value); break;
- // ms32_Parts is constant
- }
- }
-
- // --------------------------------------------------
-
- ///
- /// Gets the type of this cObject3D
- ///
- public override eObjType ObjType
- {
- get { return eObjType.Line; }
- }
-
- ///
- /// The line width in pixels
- ///
- public int Width
- {
- get { return ms32_Width; }
- set
- {
- if (ms32_Width == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Width, ms32_Width, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
- }
- ms32_Width = value;
- }
- }
-
- ///
- /// If Pen is null, a Pen from the ColorScheme will be used.
- /// The width of the Pen does not matter. It will be set to the property Width.
- /// IMPORTANT:
- /// If you use the UndoBuffer you must never modify any property of the Pen assigned here.
- /// If you externally change for example Pen.Color the Undo will not work correctly.
- /// Always assign a different instance of Pen to avoid this.
- ///
- public Pen Pen
- {
- get { return mi_Pen; }
- set
- {
- if (mi_Pen == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Pen, mi_Pen, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
- }
- mi_Pen = value;
- }
- }
-
- ///
- /// Parts of different color in multi-color lines
- ///
- public int ColorParts
- {
- get { return ms32_Parts; }
- }
-
- ///
- /// Constructor
- /// IMPORTANT:
- /// If you use the UndoBuffer you must never modify any property of the Pen assigned here.
- /// If you externally change for example Pen.Color the Undo will not work correctly.
- /// Always assign a different instance of Pen to cLine3D.Pen to avoid this.
- ///
- public cLine3D(cPoint3D i_Start, cPoint3D i_End, int s32_Width, Pen i_Pen, int s32_Parts, Object o_Tag = null)
- {
- mi_Points = new cPoint3D[] { i_Start, i_End };
- ms32_Width = s32_Width;
- mi_Pen = i_Pen;
- ms32_Parts = s32_Parts;
- mo_Tag = o_Tag;
- }
-
- // For debugging in Visual Studio
- public override string ToString()
- {
- return "cLine3D from " + mi_Points[0] + " to " + mi_Points[1];
- }
- }
-
- #endregion
-
- #region cShape3D
-
- public class cShape3D : cObject3D
- {
- private eScatterShape me_Shape;
- private int ms32_Radius;
- private Brush mi_Brush;
-
- public override void Restore(eUndoProp e_Property, Object o_Value)
- {
- switch (e_Property)
- {
- case eUndoProp.Shape: me_Shape = (eScatterShape)o_Value; break;
- case eUndoProp.Radius: ms32_Radius = (int) o_Value; break;
- case eUndoProp.Brush: mi_Brush = (Brush) o_Value; break;
- default: base.Restore(e_Property, o_Value); break;
- }
- }
-
- // --------------------------------------------------
-
- ///
- /// Gets the type of this cObject3D
- ///
- public override eObjType ObjType
- {
- get { return eObjType.Shape; }
- }
-
- ///
- /// The type of shape to be painted
- ///
- public eScatterShape Shape
- {
- get { return me_Shape; }
- set
- {
- if (me_Shape == value)
- return;
-
- // Circle and rectangle are drawn by the framework --> no recalculation required.
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Shape, me_Shape, value);
-
- if (value != eScatterShape.Circle && value != eScatterShape.Square)
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
- }
- me_Shape = value;
- }
- }
-
- ///
- /// The radius of the circle, square or triangle
- ///
- public int Radius
- {
- get { return ms32_Radius; }
- set
- {
- if (ms32_Radius == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Radius, ms32_Radius, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
- }
- ms32_Radius = value;
- }
- }
-
- ///
- /// The color of the Shape or null to use a color from the ColorScheme
- /// IMPORTANT:
- /// If you use the UndoBuffer you must never modify any property of the Brush assigned here.
- /// If you externally change for example SolidBrush.Color the Undo will not work correctly.
- /// Always assign a different instance of Brush to avoid this.
- ///
- public Brush Brush
- {
- get { return mi_Brush; }
- set
- {
- if (mi_Brush == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Brush, mi_Brush, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
- }
- mi_Brush = value;
- }
- }
-
- ///
- /// The shape is selected
- ///
- public override bool Selected
- {
- get { return mi_Points[0].Selected; }
- set { mi_Points[0].Selected = value; } // me_Recalculate needs no change
- }
-
- ///
- /// Defines if the user is allowed to select this shape
- ///
- public override bool CanSelect
- {
- get { return mi_Points[0].CanSelect; }
- set { mi_Points[0].CanSelect = value; }
- }
-
- ///
- /// Constructor
- /// IMPORTANT:
- /// If you use the UndoBuffer you must never modify any property of the Brush assigned here.
- /// If you externally change for example SolidBrush.Color the Undo will not work correctly.
- /// Always assign a different instance of Brush to cShape3D.Brush to avoid this.
- ///
- public cShape3D(int s32_Col, int s32_Row, cPoint3D i_Point, eScatterShape e_Shape, int s32_Radius, Brush i_Brush, Object o_Tag = null)
- {
- ms32_Col = s32_Col;
- ms32_Row = s32_Row;
- i_Point.Tag = o_Tag;
- mi_Points = new cPoint3D[] { i_Point };
- me_Shape = e_Shape;
- ms32_Radius = s32_Radius;
- mi_Brush = i_Brush;
- mo_Tag = o_Tag;
- }
-
- // For debugging in Visual Studio
- public override string ToString()
- {
- return "cShape3D " + me_Shape + " at " + mi_Points[0];
- }
- }
-
- #endregion
-
- #region cPolygon3D
-
- public class cPolygon3D : cObject3D
- {
- private Brush mi_Brush;
-
- public override void Restore(eUndoProp e_Property, Object o_Value)
- {
- switch (e_Property)
- {
- case eUndoProp.Brush: mi_Brush = (Brush)o_Value; break;
- default: base.Restore(e_Property, o_Value); break;
- // ms32_Col is constant
- // ms32_Row is constant
- }
- }
-
- // --------------------------------------------------
-
- ///
- /// Gets the type of this cObject3D
- ///
- public override eObjType ObjType
- {
- get { return eObjType.Polygon; }
- }
-
- ///
- /// The color of the Polygon or null to use a color from the ColorScheme
- /// IMPORTANT:
- /// If you use the UndoBuffer you must never modify any property of the Brush assigned here.
- /// If you externally change for example SolidBrush.Color the Undo will not work correctly.
- /// Always assign a different instance of Brush to avoid this.
- ///
- public Brush Brush
- {
- get { return mi_Brush; }
- set
- {
- if (mi_Brush == value)
- return;
-
- if (mi_Inst != null)
- {
- mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Brush, mi_Brush, value);
- mi_Inst.me_Recalculate |= eRecalculate.Objects;
- }
- mi_Brush = value;
- }
- }
-
- ///
- /// Constructor
- /// IMPORTANT:
- /// If you use the UndoBuffer you must never modify any property of the Brush assigned here.
- /// If you externally change for example SolidBrush.Color the Undo will not work correctly.
- /// Always assign a different instance of Brush to cPolygon3D.Brush to avoid this.
- ///
- public cPolygon3D(int s32_Col, int s32_Row, Brush i_Brush, params cPoint3D[] i_Points)
- {
- if (i_Points.Length < 3)
- throw new ArgumentException("At least 3 points are required to draw a polygon.");
-
- mi_Points = i_Points;
- mi_Brush = i_Brush;
- ms32_Col = s32_Col;
- ms32_Row = s32_Row;
- }
-
- // For debugging in Visual Studio
- public override string ToString()
- {
- return "cPolygon3D (" + mi_Points.Length + " points)";
- }
- }
-
- #endregion
-
- // ---------------------
-
- #region cAxis
-
- ///
- /// Used for the main axes and raster lines of the coordinate system
- ///
- [Serializable]
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public class cAxis
- {
- private Editor3D mi_Inst;
- private eCoord me_Coord;
- private Pen mi_RasterPen; // Raster lines (brigther)
- private Pen mi_AxisPen; // Main coordinate axis (darker)
- private SolidBrush mi_LegendBrush; // Label text
- private String ms_LegendText;
- private bool mb_Mirror;
- private bool mb_IncludeZero;
-
- public cAxis(Editor3D i_Inst, eCoord e_Coord, Color c_Color)
- {
- mi_Inst = i_Inst;
- me_Coord = e_Coord;
- Color = c_Color;
- Reset();
- }
-
- public void Reset()
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
- ms_LegendText = null;
- mb_Mirror = false;
- mb_IncludeZero = me_Coord == eCoord.Z;
- }
-
- ///
- /// The color of the axis lines and the legend.
- /// This change will become visible the next time you call Invalidate()
- /// Not modified by Reset()
- ///
- public Color Color
- {
- get { return mi_AxisPen.Color; }
- set
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
- mi_LegendBrush = new SolidBrush(value);
- mi_AxisPen = new Pen(value, 3);
- mi_RasterPen = new Pen(BrightenColor(value), 1);
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
- }
- }
-
- ///
- /// Internally used to draw raster lines (brighter)
- ///
- [Browsable(false)]
- public Pen RasterPen
- {
- get { return mi_RasterPen; }
- }
-
- ///
- /// Internally used to draw main coordinates (darker)
- ///
- [Browsable(false)]
- public Pen AxisPen
- {
- get { return mi_AxisPen; }
- }
-
- ///
- /// Internally used for label text
- ///
- [Browsable(false)]
- public SolidBrush LegendBrush
- {
- get { return mi_LegendBrush; }
- }
-
- ///
- /// Here you can add an optional legend which is displayed for the axis.
- /// With the property Editor3D.LegendPos you can define where the legend is drawn.
- /// If the string is null or empty, no legend will be displayed for this axis.
- /// This change will become visible the next time you call Invalidate()
- ///
- public String LegendText
- {
- get { return ms_LegendText; }
- set { ms_LegendText = value; }
- }
-
- ///
- /// In the default rotation angle the axis values are normally increasing
- /// from right to left (X), back to front (Y) and bottom to top (Z).
- /// If Mirror = true they will decrease instead of increase.
- /// This change will become visible the next time you call Invalidate()
- ///
- public bool Mirror
- {
- get { return mb_Mirror; }
- set
- {
- if (mb_Mirror == value)
- return;
-
- if (me_Coord == eCoord.Z)
- throw new NotImplementedException("Mirroring the Z axis is not implemented because there are multiple issues and it does not make sense to draw the Z values reverse.");
-
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
- mb_Mirror = value;
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
- }
- }
-
- ///
- /// Example: If the axis has values from 4 to 10 and
- /// IncludeZero = false --> the coordinate axis has a range from 4 to 10
- /// IncludeZero = true --> the coordinate axis has a range from 0 to 10 (default)
- /// ATTENTION: This setting is ignored if the coordinate system is not drawn (eRaster.Off)
- /// This change will become visible the next time you call Invalidate()
- ///
- public bool IncludeZero
- {
- get { return mb_IncludeZero; }
- set
- {
- if (mb_IncludeZero == value)
- return;
-
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
- mb_IncludeZero = value;
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
- }
- }
- }
-
- #endregion
-
- #region cSelection
-
- ///
- /// This class controls the user selection of points, lines, shapes and polygons
- ///
- [Serializable]
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public class cSelection
- {
- private Editor3D mi_Inst;
- private bool mb_Enabled;
- private bool mb_MultiSel;
- private bool mb_SinglePoints;
- private delSelectHandler mf_Callback;
- private Color mc_HighlightColor = Color.Empty;
- private Brush mi_HighlightBrush;
- private Pen mi_HighlightPen;
-
- public cSelection(Editor3D i_Inst)
- {
- mi_Inst = i_Inst;
- }
-
- ///
- /// This property defines if the user is allowed to select 3D objects.
- /// The selection will only be visible if HighlightColor has also been set.
- /// The callback will only be called if Callback has also been assigned.
- ///
- public bool Enabled
- {
- get { return mb_Enabled; }
- set { mb_Enabled = value; }
- }
-
- ///
- /// This defines the color with which selected draw objects / points are painted.
- /// If you pass Color.Empty draw objects will not be highlighted although they are selected!
- /// This change will become visible the next time you call Invalidate()
- ///
- public Color HighlightColor
- {
- set
- {
- if (value.A > 0)
- {
- mi_HighlightBrush = new SolidBrush(value);
- mi_HighlightPen = new Pen (value);
- mi_HighlightPen.StartCap = LineCap.Round;
- mi_HighlightPen.EndCap = LineCap.Round;
- }
- else
- {
- mi_HighlightBrush = null;
- mi_HighlightPen = null;
- }
- mc_HighlightColor = value;
- }
- get
- {
- return mc_HighlightColor;
- }
- }
-
- [Browsable(false)]
- public Pen HighlightPen
- {
- get
- {
- if (mb_Enabled) return mi_HighlightPen;
- else return null;
- }
- }
-
- [Browsable(false)]
- public Brush HighlightBrush
- {
- get
- {
- if (mb_Enabled) return mi_HighlightBrush;
- else return null;
- }
- }
-
- ///
- /// true --> allow selection of multiple 3D objects at once
- /// false --> allow only selection of a single 3D object at a time
- /// This setting will be ignored when a Callback is assigned.
- /// The callback is responsible for any selection changes!
- ///
- public bool MultiSelect
- {
- get { return mb_MultiSel; }
- set { mb_MultiSel = value; }
- }
-
- ///
- /// Enables selection of single points.
- /// true --> the user can only select single points of a polygon or the end points of a line.
- /// false --> the user can only select an entire polygon or an entire line.
- /// For scatter shapes this setting does not matter because a scatter shape corresponds to one point.
- /// IMPORTANT: Selecting entire polygons makes only sense if Fill mode is used. Otherwise polygons are transparent
- /// and a click would hit the background behind the polygon, so this setting is ignored for polygons in Line mode.
- /// This change will become visible the next time you call Invalidate()
- ///
- public bool SinglePoints
- {
- get { return mb_SinglePoints; }
- set
- {
- mb_SinglePoints = value;
-
- // A mix of selected lines or polygons and selected points is possible but the user will be confused.
- DeSelectAll();
- }
- }
-
- ///
- /// IMPORTANT: Read the detailed comment of function SelectionCallback() at the end of this class.
- /// You can set null here to turn off the callback.
- ///
- [Browsable(false)]
- public delSelectHandler Callback
- {
- set { mf_Callback = value; }
- get { return mf_Callback; }
- }
-
- ///
- /// Returns selected cLine3D, cShape3D or cPoygon3D objects.
- /// Multiple enums can be combined. Example: GetSelectedObjects(eSelType.Line | eSelType.Shape)
- /// NOTE: For Shape3D the selection of the shape itself and it's point is always the same.
- ///
- public cObject3D[] GetSelectedObjects(eSelType e_SelType)
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
-
- List i_List = new List();
- foreach (cDrawObj i_Object in mi_Inst.mi_UserObjects)
- {
- if (!i_Object.Selected || (i_Object.SelType & e_SelType) == 0)
- continue;
-
- i_List.Add(i_Object.mi_Object3D);
- }
- return i_List.ToArray();
- }
-
- ///
- /// Gets the points that the user may select individually if Selection.SinglePoints = true
- /// and gets all points of a Line3D or Polygon3D if it is selected.
- /// Multiple enums can be combined. Example: GetSelectedPoints(eSelType.Line | eSelType.Shape)
- /// NOTE: The returned array contains only unique points.
- /// NOTE: For Shape3D the selection of the shape itself and it's point is always the same.
- ///
- public cPoint3D[] GetSelectedPoints(eSelType e_SelType)
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
-
- List i_Unique = new List();
-
- foreach (cDrawObj i_Object in mi_Inst.mi_UserObjects)
- {
- if ((i_Object.SelType & e_SelType) == 0)
- continue;
-
- // If the object itself is selected return all it's points, no matter if the points are selected or not.
- if (i_Object.Selected)
- {
- foreach (cPoint i_Point in i_Object.mi_Points)
- {
- if (!i_Unique.Contains(i_Point.mi_P3D))
- i_Unique.Add (i_Point.mi_P3D);
- }
- }
- else // Object not selected --> check if single points of the object are selected
- {
- foreach (cPoint i_Point in i_Object.mi_Points)
- {
- if (i_Point.mi_P3D.Selected &&
- !i_Unique.Contains(i_Point.mi_P3D))
- i_Unique.Add (i_Point.mi_P3D);
- }
- }
- }
- return i_Unique.ToArray();
- }
-
- ///
- /// Remove the selection from all draw objects
- /// This change will become visible the next time you call Invalidate()
- ///
- public void DeSelectAll()
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
-
- foreach (cDrawObj i_Obj in mi_Inst.mi_UserObjects)
- {
- i_Obj.Selected = false;
-
- foreach (cPoint i_Point in i_Obj.mi_Points)
- {
- i_Point.mi_P3D.Selected = false;
- }
- }
-
- mi_Inst.mi_UndoBuffer.Store();
- }
- }
-
- #endregion
-
- #region cUndoBuffer
-
- ///
- /// This public base class is exposed to the user.
- /// The implementation is in cUndoImpl.
- ///
- [Serializable]
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public abstract class cUndoBuffer
- {
- ///
- /// Enable the Undo Buffer only if you need the Undo functionality.
- /// By default Undo / Redo is disabled.
- ///
- public virtual bool Enabled
- {
- get;
- set;
- }
-
- ///
- /// Clears the Undo Buffer.
- ///
- public virtual void Clear()
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Undo the last user operation.
- /// return false if Undo is not available.
- /// This is also called when hitting CTRL + Z while the 3D Editor has the keyboard focus.
- ///
- public virtual bool Undo()
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Redo the last user operation.
- /// return false if Redo is not available.
- /// This is also called when hitting CTRL + Y while the 3D Editor has the keyboard focus.
- ///
- public virtual bool Redo()
- {
- throw new NotImplementedException();
- }
- }
-
- #endregion
-
- #region cColorScheme
-
- ///
- /// Pens and Brushes are GDI+ objects which must not be created on the fly in Draw()
- /// For speed optimization these are created only once and stored in this class.
- ///
- public class cColorScheme
- {
- // ========================= STATIC ===========================
-
- public static Color[] GetSchema(eColorScheme e_Scheme)
- {
- Byte[,] u8_RGB;
- switch (e_Scheme)
- {
- case eColorScheme.Green: return new Color[] { Color.Green };
- case eColorScheme.Rainbow_Sweep: return CalcRainbow(6); // all colors, also magenta
- case eColorScheme.Rainbow_Bright: return CalcRainbow(4); // from red to blue, no magenta
- case eColorScheme.Monochrome: return new Color[] { Color.Goldenrod };
- case eColorScheme.Autumn: u8_RGB = new byte[,] { { 255, 0, 0 }, { 255, 4, 0 }, { 255, 8, 0 }, { 255, 12, 0 }, { 255, 16, 0 }, { 255, 20, 0 }, { 255, 24, 0 }, { 255, 28, 0 }, { 255, 32, 0 }, { 255, 36, 0 }, { 255, 40, 0 }, { 255, 45, 0 }, { 255, 49, 0 }, { 255, 53, 0 }, { 255, 57, 0 }, { 255, 61, 0 }, { 255, 65, 0 }, { 255, 69, 0 }, { 255, 73, 0 }, { 255, 77, 0 }, { 255, 81, 0 }, { 255, 85, 0 }, { 255, 89, 0 }, { 255, 93, 0 }, { 255, 97, 0 }, { 255, 101, 0 }, { 255, 105, 0 }, { 255, 109, 0 }, { 255, 113, 0 }, { 255, 117, 0 }, { 255, 121, 0 }, { 255, 125, 0 }, { 255, 130, 0 }, { 255, 134, 0 }, { 255, 138, 0 }, { 255, 142, 0 }, { 255, 146, 0 }, { 255, 150, 0 }, { 255, 154, 0 }, { 255, 158, 0 }, { 255, 162, 0 }, { 255, 166, 0 }, { 255, 170, 0 }, { 255, 174, 0 }, { 255, 178, 0 }, { 255, 182, 0 }, { 255, 186, 0 }, { 255, 190, 0 }, { 255, 194, 0 }, { 255, 198, 0 }, { 255, 202, 0 }, { 255, 206, 0 }, { 255, 210, 0 }, { 255, 215, 0 }, { 255, 219, 0 }, { 255, 223, 0 }, { 255, 227, 0 }, { 255, 231, 0 }, { 255, 235, 0 }, { 255, 239, 0 }, { 255, 243, 0 }, { 255, 247, 0 }, { 255, 251, 0 }, { 255, 255, 0 } }; break;
- case eColorScheme.Cool: u8_RGB = new byte[,] { { 0, 255, 255 }, { 4, 251, 255 }, { 8, 247, 255 }, { 12, 243, 255 }, { 16, 239, 255 }, { 20, 235, 255 }, { 24, 231, 255 }, { 28, 227, 255 }, { 32, 223, 255 }, { 36, 219, 255 }, { 40, 215, 255 }, { 45, 210, 255 }, { 49, 206, 255 }, { 53, 202, 255 }, { 57, 198, 255 }, { 61, 194, 255 }, { 65, 190, 255 }, { 69, 186, 255 }, { 73, 182, 255 }, { 77, 178, 255 }, { 81, 174, 255 }, { 85, 170, 255 }, { 89, 166, 255 }, { 93, 162, 255 }, { 97, 158, 255 }, { 101, 154, 255 }, { 105, 150, 255 }, { 109, 146, 255 }, { 113, 142, 255 }, { 117, 138, 255 }, { 121, 134, 255 }, { 125, 130, 255 }, { 130, 125, 255 }, { 134, 121, 255 }, { 138, 117, 255 }, { 142, 113, 255 }, { 146, 109, 255 }, { 150, 105, 255 }, { 154, 101, 255 }, { 158, 97, 255 }, { 162, 93, 255 }, { 166, 89, 255 }, { 170, 85, 255 }, { 174, 81, 255 }, { 178, 77, 255 }, { 182, 73, 255 }, { 186, 69, 255 }, { 190, 65, 255 }, { 194, 61, 255 }, { 198, 57, 255 }, { 202, 53, 255 }, { 206, 49, 255 }, { 210, 45, 255 }, { 215, 40, 255 }, { 219, 36, 255 }, { 223, 32, 255 }, { 227, 28, 255 }, { 231, 24, 255 }, { 235, 20, 255 }, { 239, 16, 255 }, { 243, 12, 255 }, { 247, 8, 255 }, { 251, 4, 255 }, { 255, 0, 255 } }; break;
- case eColorScheme.Copper: u8_RGB = new byte[,] { { 0, 0, 0 }, { 5, 3, 2 }, { 10, 6, 4 }, { 15, 9, 6 }, { 20, 13, 8 }, { 25, 16, 10 }, { 30, 19, 12 }, { 35, 22, 14 }, { 40, 25, 16 }, { 46, 28, 18 }, { 51, 32, 20 }, { 56, 35, 22 }, { 61, 38, 24 }, { 66, 41, 26 }, { 71, 44, 28 }, { 76, 47, 30 }, { 81, 51, 32 }, { 86, 54, 34 }, { 91, 57, 36 }, { 96, 60, 38 }, { 101, 63, 40 }, { 106, 66, 42 }, { 111, 70, 44 }, { 116, 73, 46 }, { 121, 76, 48 }, { 126, 79, 50 }, { 132, 82, 52 }, { 137, 85, 54 }, { 142, 89, 56 }, { 147, 92, 58 }, { 152, 95, 60 }, { 157, 98, 62 }, { 162, 101, 64 }, { 167, 104, 66 }, { 172, 108, 68 }, { 177, 111, 70 }, { 182, 114, 72 }, { 187, 117, 75 }, { 192, 120, 77 }, { 197, 123, 79 }, { 202, 126, 81 }, { 207, 130, 83 }, { 212, 133, 85 }, { 218, 136, 87 }, { 223, 139, 89 }, { 228, 142, 91 }, { 233, 145, 93 }, { 238, 149, 95 }, { 243, 152, 97 }, { 248, 155, 99 }, { 253, 158, 101 }, { 255, 161, 103 }, { 255, 164, 105 }, { 255, 168, 107 }, { 255, 171, 109 }, { 255, 174, 111 }, { 255, 177, 113 }, { 255, 180, 115 }, { 255, 183, 117 }, { 255, 187, 119 }, { 255, 190, 121 }, { 255, 193, 123 }, { 255, 196, 125 }, { 255, 199, 127 } }; break;
- case eColorScheme.Hot: u8_RGB = new byte[,] { { 11, 0, 0 }, { 21, 0, 0 }, { 32, 0, 0 }, { 43, 0, 0 }, { 53, 0, 0 }, { 64, 0, 0 }, { 74, 0, 0 }, { 85, 0, 0 }, { 96, 0, 0 }, { 106, 0, 0 }, { 117, 0, 0 }, { 128, 0, 0 }, { 138, 0, 0 }, { 149, 0, 0 }, { 159, 0, 0 }, { 170, 0, 0 }, { 181, 0, 0 }, { 191, 0, 0 }, { 202, 0, 0 }, { 213, 0, 0 }, { 223, 0, 0 }, { 234, 0, 0 }, { 244, 0, 0 }, { 255, 0, 0 }, { 255, 11, 0 }, { 255, 21, 0 }, { 255, 32, 0 }, { 255, 43, 0 }, { 255, 53, 0 }, { 255, 64, 0 }, { 255, 74, 0 }, { 255, 85, 0 }, { 255, 96, 0 }, { 255, 106, 0 }, { 255, 117, 0 }, { 255, 128, 0 }, { 255, 138, 0 }, { 255, 149, 0 }, { 255, 159, 0 }, { 255, 170, 0 }, { 255, 181, 0 }, { 255, 191, 0 }, { 255, 202, 0 }, { 255, 213, 0 }, { 255, 223, 0 }, { 255, 234, 0 }, { 255, 244, 0 }, { 255, 255, 0 }, { 255, 255, 16 }, { 255, 255, 32 }, { 255, 255, 48 }, { 255, 255, 64 }, { 255, 255, 80 }, { 255, 255, 96 }, { 255, 255, 112 }, { 255, 255, 128 }, { 255, 255, 143 }, { 255, 255, 159 }, { 255, 255, 175 }, { 255, 255, 191 }, { 255, 255, 207 }, { 255, 255, 223 }, { 255, 255, 239 }, { 255, 255, 255 } }; break;
- case eColorScheme.Hsv: u8_RGB = new byte[,] { { 255, 0, 0 }, { 255, 24, 0 }, { 255, 48, 0 }, { 255, 72, 0 }, { 255, 96, 0 }, { 255, 120, 0 }, { 255, 143, 0 }, { 255, 167, 0 }, { 255, 191, 0 }, { 255, 215, 0 }, { 255, 239, 0 }, { 247, 255, 0 }, { 223, 255, 0 }, { 199, 255, 0 }, { 175, 255, 0 }, { 151, 255, 0 }, { 128, 255, 0 }, { 104, 255, 0 }, { 80, 255, 0 }, { 56, 255, 0 }, { 32, 255, 0 }, { 8, 255, 0 }, { 0, 255, 16 }, { 0, 255, 40 }, { 0, 255, 64 }, { 0, 255, 88 }, { 0, 255, 112 }, { 0, 255, 135 }, { 0, 255, 159 }, { 0, 255, 183 }, { 0, 255, 207 }, { 0, 255, 231 }, { 0, 255, 255 }, { 0, 231, 255 }, { 0, 207, 255 }, { 0, 183, 255 }, { 0, 159, 255 }, { 0, 135, 255 }, { 0, 112, 255 }, { 0, 88, 255 }, { 0, 64, 255 }, { 0, 40, 255 }, { 0, 16, 255 }, { 8, 0, 255 }, { 32, 0, 255 }, { 56, 0, 255 }, { 80, 0, 255 }, { 104, 0, 255 }, { 128, 0, 255 }, { 151, 0, 255 }, { 175, 0, 255 }, { 199, 0, 255 }, { 223, 0, 255 }, { 247, 0, 255 }, { 255, 0, 239 }, { 255, 0, 215 }, { 255, 0, 191 }, { 255, 0, 167 }, { 255, 0, 143 }, { 255, 0, 120 }, { 255, 0, 96 }, { 255, 0, 72 }, { 255, 0, 48 }, { 255, 0, 24 } }; break;
- case eColorScheme.Rainbow_Dark: u8_RGB = new byte[,] { { 0, 0, 143 }, { 0, 0, 159 }, { 0, 0, 175 }, { 0, 0, 191 }, { 0, 0, 207 }, { 0, 0, 223 }, { 0, 0, 239 }, { 0, 0, 255 }, { 0, 16, 255 }, { 0, 32, 255 }, { 0, 48, 255 }, { 0, 64, 255 }, { 0, 80, 255 }, { 0, 96, 255 }, { 0, 112, 255 }, { 0, 128, 255 }, { 0, 143, 255 }, { 0, 159, 255 }, { 0, 175, 255 }, { 0, 191, 255 }, { 0, 207, 255 }, { 0, 223, 255 }, { 0, 239, 255 }, { 0, 255, 255 }, { 16, 255, 239 }, { 32, 255, 223 }, { 48, 255, 207 }, { 64, 255, 191 }, { 80, 255, 175 }, { 96, 255, 159 }, { 112, 255, 143 }, { 128, 255, 128 }, { 143, 255, 112 }, { 159, 255, 96 }, { 175, 255, 80 }, { 191, 255, 64 }, { 207, 255, 48 }, { 223, 255, 32 }, { 239, 255, 16 }, { 255, 255, 0 }, { 255, 239, 0 }, { 255, 223, 0 }, { 255, 207, 0 }, { 255, 191, 0 }, { 255, 175, 0 }, { 255, 159, 0 }, { 255, 143, 0 }, { 255, 128, 0 }, { 255, 112, 0 }, { 255, 96, 0 }, { 255, 80, 0 }, { 255, 64, 0 }, { 255, 48, 0 }, { 255, 32, 0 }, { 255, 16, 0 }, { 255, 0, 0 }, { 239, 0, 0 }, { 223, 0, 0 }, { 207, 0, 0 }, { 191, 0, 0 }, { 175, 0, 0 }, { 159, 0, 0 }, { 143, 0, 0 }, { 128, 0, 0 } }; break;
- case eColorScheme.Pink: u8_RGB = new byte[,] { { 30, 0, 0 }, { 50, 26, 26 }, { 64, 37, 37 }, { 75, 45, 45 }, { 85, 52, 52 }, { 94, 59, 59 }, { 102, 64, 64 }, { 110, 69, 69 }, { 117, 74, 74 }, { 123, 79, 79 }, { 130, 83, 83 }, { 136, 87, 87 }, { 141, 91, 91 }, { 147, 95, 95 }, { 152, 98, 98 }, { 157, 102, 102 }, { 162, 105, 105 }, { 167, 108, 108 }, { 172, 111, 111 }, { 176, 114, 114 }, { 181, 117, 117 }, { 185, 120, 120 }, { 189, 123, 123 }, { 194, 126, 126 }, { 195, 132, 129 }, { 197, 138, 131 }, { 199, 144, 134 }, { 201, 149, 136 }, { 202, 154, 139 }, { 204, 159, 141 }, { 206, 164, 144 }, { 207, 169, 146 }, { 209, 174, 148 }, { 211, 178, 151 }, { 212, 183, 153 }, { 214, 187, 155 }, { 216, 191, 157 }, { 217, 195, 160 }, { 219, 199, 162 }, { 220, 203, 164 }, { 222, 207, 166 }, { 223, 211, 168 }, { 225, 215, 170 }, { 226, 218, 172 }, { 228, 222, 174 }, { 229, 225, 176 }, { 231, 229, 178 }, { 232, 232, 180 }, { 234, 234, 185 }, { 235, 235, 191 }, { 237, 237, 196 }, { 238, 238, 201 }, { 240, 240, 206 }, { 241, 241, 211 }, { 243, 243, 216 }, { 244, 244, 221 }, { 245, 245, 225 }, { 247, 247, 230 }, { 248, 248, 234 }, { 250, 250, 238 }, { 251, 251, 243 }, { 252, 252, 247 }, { 254, 254, 251 }, { 255, 255, 255 } }; break;
- case eColorScheme.Spring: u8_RGB = new byte[,] { { 255, 0, 255 }, { 255, 4, 251 }, { 255, 8, 247 }, { 255, 12, 243 }, { 255, 16, 239 }, { 255, 20, 235 }, { 255, 24, 231 }, { 255, 28, 227 }, { 255, 32, 223 }, { 255, 36, 219 }, { 255, 40, 215 }, { 255, 45, 210 }, { 255, 49, 206 }, { 255, 53, 202 }, { 255, 57, 198 }, { 255, 61, 194 }, { 255, 65, 190 }, { 255, 69, 186 }, { 255, 73, 182 }, { 255, 77, 178 }, { 255, 81, 174 }, { 255, 85, 170 }, { 255, 89, 166 }, { 255, 93, 162 }, { 255, 97, 158 }, { 255, 101, 154 }, { 255, 105, 150 }, { 255, 109, 146 }, { 255, 113, 142 }, { 255, 117, 138 }, { 255, 121, 134 }, { 255, 125, 130 }, { 255, 130, 125 }, { 255, 134, 121 }, { 255, 138, 117 }, { 255, 142, 113 }, { 255, 146, 109 }, { 255, 150, 105 }, { 255, 154, 101 }, { 255, 158, 97 }, { 255, 162, 93 }, { 255, 166, 89 }, { 255, 170, 85 }, { 255, 174, 81 }, { 255, 178, 77 }, { 255, 182, 73 }, { 255, 186, 69 }, { 255, 190, 65 }, { 255, 194, 61 }, { 255, 198, 57 }, { 255, 202, 53 }, { 255, 206, 49 }, { 255, 210, 45 }, { 255, 215, 40 }, { 255, 219, 36 }, { 255, 223, 32 }, { 255, 227, 28 }, { 255, 231, 24 }, { 255, 235, 20 }, { 255, 239, 16 }, { 255, 243, 12 }, { 255, 247, 8 }, { 255, 251, 4 }, { 255, 255, 0 } }; break;
- case eColorScheme.Summer: u8_RGB = new byte[,] { { 0, 128, 102 }, { 4, 130, 102 }, { 8, 132, 102 }, { 12, 134, 102 }, { 16, 136, 102 }, { 20, 138, 102 }, { 24, 140, 102 }, { 28, 142, 102 }, { 32, 144, 102 }, { 36, 146, 102 }, { 40, 148, 102 }, { 45, 150, 102 }, { 49, 152, 102 }, { 53, 154, 102 }, { 57, 156, 102 }, { 61, 158, 102 }, { 65, 160, 102 }, { 69, 162, 102 }, { 73, 164, 102 }, { 77, 166, 102 }, { 81, 168, 102 }, { 85, 170, 102 }, { 89, 172, 102 }, { 93, 174, 102 }, { 97, 176, 102 }, { 101, 178, 102 }, { 105, 180, 102 }, { 109, 182, 102 }, { 113, 184, 102 }, { 117, 186, 102 }, { 121, 188, 102 }, { 125, 190, 102 }, { 130, 192, 102 }, { 134, 194, 102 }, { 138, 196, 102 }, { 142, 198, 102 }, { 146, 200, 102 }, { 150, 202, 102 }, { 154, 204, 102 }, { 158, 206, 102 }, { 162, 208, 102 }, { 166, 210, 102 }, { 170, 212, 102 }, { 174, 215, 102 }, { 178, 217, 102 }, { 182, 219, 102 }, { 186, 221, 102 }, { 190, 223, 102 }, { 194, 225, 102 }, { 198, 227, 102 }, { 202, 229, 102 }, { 206, 231, 102 }, { 210, 233, 102 }, { 215, 235, 102 }, { 219, 237, 102 }, { 223, 239, 102 }, { 227, 241, 102 }, { 231, 243, 102 }, { 235, 245, 102 }, { 239, 247, 102 }, { 243, 249, 102 }, { 247, 251, 102 }, { 251, 253, 102 }, { 255, 255, 102 } }; break;
- case eColorScheme.Winter: u8_RGB = new byte[,] { { 0, 0, 255 }, { 0, 4, 253 }, { 0, 8, 251 }, { 0, 12, 249 }, { 0, 16, 247 }, { 0, 20, 245 }, { 0, 24, 243 }, { 0, 28, 241 }, { 0, 32, 239 }, { 0, 36, 237 }, { 0, 40, 235 }, { 0, 45, 233 }, { 0, 49, 231 }, { 0, 53, 229 }, { 0, 57, 227 }, { 0, 61, 225 }, { 0, 65, 223 }, { 0, 69, 221 }, { 0, 73, 219 }, { 0, 77, 217 }, { 0, 81, 215 }, { 0, 85, 213 }, { 0, 89, 210 }, { 0, 93, 208 }, { 0, 97, 206 }, { 0, 101, 204 }, { 0, 105, 202 }, { 0, 109, 200 }, { 0, 113, 198 }, { 0, 117, 196 }, { 0, 121, 194 }, { 0, 125, 192 }, { 0, 130, 190 }, { 0, 134, 188 }, { 0, 138, 186 }, { 0, 142, 184 }, { 0, 146, 182 }, { 0, 150, 180 }, { 0, 154, 178 }, { 0, 158, 176 }, { 0, 162, 174 }, { 0, 166, 172 }, { 0, 170, 170 }, { 0, 174, 168 }, { 0, 178, 166 }, { 0, 182, 164 }, { 0, 186, 162 }, { 0, 190, 160 }, { 0, 194, 158 }, { 0, 198, 156 }, { 0, 202, 154 }, { 0, 206, 152 }, { 0, 210, 150 }, { 0, 215, 148 }, { 0, 219, 146 }, { 0, 223, 144 }, { 0, 227, 142 }, { 0, 231, 140 }, { 0, 235, 138 }, { 0, 239, 136 }, { 0, 243, 134 }, { 0, 247, 132 }, { 0, 251, 130 }, { 0, 255, 128 } }; break;
- default:
- throw new ArgumentException("Invalid enum");
- }
-
- Color[] c_Schema = new Color[u8_RGB.GetLength(0)];
- for (int i=0; i
- /// s32_Sweeps = 4 --> Colors from blue to red, not including magenta (1024 colors)
- /// s32_Sweeps = 6 --> Complete sweep with all rainbow colors, also magenta (1536 colors)
- ///
- private static Color[] CalcRainbow(int s32_Sweeps)
- {
- Color[] c_Colors = new Color[s32_Sweeps * 256];
- int R,G,B,P=0;
- for (int L=0; L
- /// Constructor 1
- ///
- public cColorScheme(eColorScheme e_Scheme)
- : this (GetSchema(e_Scheme))
- {
- }
-
- ///
- /// Constructor 2
- /// If you want to draw the entire graph with only one color, you can pass a single Color here.
- ///
- public cColorScheme(params Color[] c_Colors)
- {
- if (c_Colors.Length == 0)
- throw new ArgumentException("A ColorScheme needs at least one color.");
-
- mi_Brushes = new SolidBrush[c_Colors.Length];
- mi_Pens = new Pen [c_Colors.Length];
-
- for (int i=0; i < c_Colors.Length; i++)
- {
- mi_Brushes[i] = new SolidBrush(c_Colors[i]);
- mi_Pens [i] = new Pen (c_Colors[i], 1); // the width of the Pen will be modified later.
- }
- }
-
- public Brush GetBrush(int s32_Index)
- {
- s32_Index = Math.Max(0, s32_Index);
- return mi_Brushes[s32_Index % mi_Brushes.Length];
- }
-
- public Pen GetPen(int s32_Index)
- {
- s32_Index = Math.Max(0, s32_Index);
- return mi_Pens[s32_Index % mi_Pens.Length];
- }
-
- public int CalcIndex(double d_FactorZ)
- {
- if (double.IsNaN(d_FactorZ))
- return -1;
-
- d_FactorZ = Math.Min(1.0, d_FactorZ);
- d_FactorZ = Math.Max(0.0, d_FactorZ);
-
- // d_FactorZ is a value between 0.0 and 1.0
- return (int)(d_FactorZ * (mi_Brushes.Length - 1));
- }
- }
-
- #endregion
-
- #region cUserInput
-
- public class cUserInput
- {
- private MouseButtons me_MouseButton;
- private Keys me_Modifiers;
- private eMouseAction me_Action;
- private Cursor mi_Cursor;
-
- ///
- /// A unique identifier for the combination of mouse button and modifier(s)
- /// Keys.Shift = 0x00010000
- /// Keys.Control = 0x00020000
- /// Keys.Alt = 0x00040000
- /// MouseButtons.Left = 0x00100000
- /// MouseButtons.Right = 0x00200000
- /// MouseButtons.Middle = 0x00400000
- /// MouseButtons.XButton1 = 0x00800000
- /// MouseButtons.XButton2 = 0x01000000
- ///
- public int UID
- {
- get { return (int)me_MouseButton | (int)me_Modifiers; }
- }
-
- public MouseButtons MouseButton
- {
- get { return me_MouseButton; }
- }
- public Keys Modifiers
- {
- get { return me_Modifiers; }
- }
- public eMouseAction Action
- {
- get { return me_Action; }
- }
- public Cursor Cursor
- {
- get { return mi_Cursor; }
- }
-
- public cUserInput(MouseButtons e_MouseButton, Keys e_Modifiers, eMouseAction e_Action, Cursor i_Cursor = null)
- {
- Debug.Assert((e_Modifiers & Keys.KeyCode) == 0, "Invalid parameter e_Modifiers");
- Debug.Assert(e_MouseButton != MouseButtons.None, "Invalid parameter e_MouseButton");
- Debug.Assert(e_Action != eMouseAction.None, "Invalid parameter e_Action");
-
- me_MouseButton = e_MouseButton;
- me_Modifiers = e_Modifiers;
- me_Action = e_Action;
- mi_Cursor = i_Cursor;
-
- if (mi_Cursor == null)
- {
- switch (e_Action)
- {
- case eMouseAction.ThetaAndPhi: mi_Cursor = Cursors.SizeAll; break;
- case eMouseAction.Theta: mi_Cursor = Cursors.NoMoveVert; break;
- case eMouseAction.Phi: mi_Cursor = Cursors.NoMoveHoriz; break;
- case eMouseAction.Rho: mi_Cursor = Cursors.SizeNS; break;
- case eMouseAction.Move: mi_Cursor = Cursors.NoMove2D; break;
- default: mi_Cursor = Cursors.Arrow; break;
- }
- }
- }
-
- ///
- /// For debugging in Visual Studio
- ///
- public override string ToString()
- {
- return String.Format("MouseButton: {0}, Modifiers: {1} --> Action: {2}", me_MouseButton, me_Modifiers, me_Action);
- }
- }
-
- #endregion
-
- // ---------------------
-
- #region cRenderData
-
- ///
- /// Base class for cSurfaceData, cScatterData, cLineData, cPolygonData
- ///
- public abstract class cRenderData
- {
- public virtual void AddDrawObjects(Editor3D i_Inst)
- {
- throw new NotImplementedException();
- }
-
- ///
- /// The width and color of the Pen may be modified later.
- /// So the immutable framework collection like Pens.Black,... cannot be used here.
- ///
- protected static void CheckPenMutable(Pen i_Pen, cColorScheme i_ColorScheme)
- {
- if (i_Pen != null && i_ColorScheme != null)
- {
- try { i_Pen.Color = Color.BlanchedAlmond; }
- catch { throw new ArgumentException("To use a color scheme create a new Pen. Do not use the immutable Pens.XYZ collection."); }
- }
- }
- }
-
- #endregion
-
- #region cSurfaceData
-
- public class cSurfaceData : cRenderData
- {
- private bool mb_Fill;
- private bool mb_Missing;
- private int ms32_Cols;
- private int ms32_Rows;
- private int ms32_Radius;
- private Pen mi_Pen;
- private cPoint3D[,] mi_PointArray;
- private cPolygon3D[,] mi_PolygonArray;
- private cColorScheme mi_ColorScheme;
-
- public cColorScheme ColorScheme
- {
- get { return mi_ColorScheme; }
- }
-
- ///
- /// The count of points in one column of the surface grid
- ///
- public int Cols
- {
- get { return ms32_Cols; }
- }
-
- ///
- /// The count of points in one row of the surface grid
- ///
- public int Rows
- {
- get { return ms32_Rows; }
- }
-
- ///
- /// Fill Mode:
- /// ------------
- /// Polygons are filled with a color from the ColorScheme.
- /// If you want only one color, set a ColorScheme which contains only one color.
- /// The Pen is used to draw the thin lines between the polygons (mostly black, 1 pixel)
- /// If Pen is null, no lines are drawn.
- ///
- /// Line Mode:
- /// ------------
- /// Only the border lines of the polygons are drawn.
- /// The Pen is used to draw these lines. The Pen's color and width will be modified.
- ///
- /// Missing Mode:
- /// --------------
- /// s32_Radius > 0 allows missing points.
- /// s32_Radius defines the radius of cicles that represent points which have not enough neigbours to draw a polygon.
- ///
- public cSurfaceData(int s32_Cols, int s32_Rows, ePolygonMode e_Mode, Pen i_Pen, cColorScheme i_ColorScheme,
- int s32_Radius = 0)
- {
- if (s32_Cols < 3 || s32_Rows < 3)
- throw new ArgumentException("cSurfaceData needs at least 3 columns and 3 rows");
-
- if (e_Mode == ePolygonMode.Fill)
- {
- if (i_ColorScheme == null)
- throw new ArgumentException("In Fill mode you must specify a ColorScheme");
-
- // The border pen is allowed to be immutable. It will not be changed.
- }
- else // Lines
- {
- if (i_Pen == null)
- throw new ArgumentException("In Line mode you must specify a Line Pen");
-
- CheckPenMutable(i_Pen, i_ColorScheme);
- }
-
- mb_Fill = e_Mode == ePolygonMode.Fill;
- mb_Missing = s32_Radius > 0;
- ms32_Radius = s32_Radius;
- ms32_Cols = s32_Cols;
- ms32_Rows = s32_Rows;
- mi_Pen = i_Pen;
- mi_ColorScheme = i_ColorScheme;
- mi_PointArray = new cPoint3D[s32_Cols, s32_Rows];
- }
-
- ///
- /// Here you can set a callback function which will be called with X,Y to calculate the Z values of the points.
- ///
- public void ExecuteFunction(delRendererFunction f_Function, PointF k_Start, PointF k_End)
- {
- mb_Missing = false;
-
- double d_StepX = (k_End.X - k_Start.X) / (ms32_Cols - 1);
- double d_StepY = (k_End.Y - k_Start.Y) / (ms32_Rows - 1);
-
- for (int C = 0; C < ms32_Cols; C++)
- {
- double d_X = k_Start.X + d_StepX * C;
-
- for (int R = 0; R < ms32_Rows; R++)
- {
- double d_Y = k_Start.Y + d_StepY * R;
- double d_Z = f_Function(d_X, d_Y);
-
- SetPointAt(C, R, new cPoint3D(d_X, d_Y, d_Z));
- }
- }
- }
-
- ///
- /// IMPORTANT:
- /// The X coordinate of the point must be related to the column.
- /// The Y coordinate of the point must be related to the row.
- ///
- public void SetPointAt(int s32_Column, int s32_Row, cPoint3D i_Point3D)
- {
- if (mi_PolygonArray != null)
- throw new Exception("You cannot call cSurfaceData.SetPointAt() or ExecuteFunction() anymore after calling GetPolygonAt() "
- + "or Editor3D.AddRenderData(). To modify a point after the polygons have been created call "
- + "GetPointAt() and modify the X,Y,Z values of the returned point.");
-
- mi_PointArray[s32_Column, s32_Row] = i_Point3D;
- }
-
- ///
- /// ATTENTION:
- /// In mode 'Missing' null may be returned or polygons with only 3 corners!
- ///
- public cPoint3D GetPointAt(int s32_Column, int s32_Row)
- {
- return mi_PointArray[s32_Column, s32_Row];
- }
-
- ///
- /// ATTENTION:
- /// The polygons have one row less than cSurfaceData.Rows and one column less than cSurfaceData.Cols
- ///
- public cPolygon3D GetPolygonAt(int s32_Column, int s32_Row)
- {
- CreatePolygons();
- return mi_PolygonArray[s32_Column, s32_Row];
- }
-
- private void CreatePolygons()
- {
- if (mi_PolygonArray != null)
- return;
-
- cPolygon3D[,] i_TempArr = new cPolygon3D[ms32_Cols -1, ms32_Rows -1];
-
- List i_Valid = new List();
- for (int C=0; C < ms32_Cols -1; C++)
- {
- for (int R=0; R < ms32_Rows -1; R++)
- {
- i_Valid.Clear();
- if (mi_PointArray[C, R] != null) i_Valid.Add(mi_PointArray[C, R]);
- if (mi_PointArray[C, R+1] != null) i_Valid.Add(mi_PointArray[C, R+1]);
- if (mi_PointArray[C+1, R+1] != null) i_Valid.Add(mi_PointArray[C+1, R+1]);
- if (mi_PointArray[C+1, R] != null) i_Valid.Add(mi_PointArray[C+1, R]);
-
- if (i_Valid.Count < 4 && !mb_Missing)
- throw new Exception("cSurfaceData: You must call cSurfaceData.SetPointAt() for all points!");
-
- if (i_Valid.Count < 3)
- continue; // A polygon needs at least 3 points
-
- i_TempArr[C, R] = new cPolygon3D(C, R, null, i_Valid.ToArray());
- }
- }
-
- mi_PolygonArray = i_TempArr;
- }
-
- // =============================================================================
-
- ///
- /// Called from AddRenderData()
- ///
- public override void AddDrawObjects(Editor3D i_Inst)
- {
- CreatePolygons();
-
- bool b_Added = false;
- List i_Used = new List();
-
- foreach (cPolygon3D i_Poly3D in mi_PolygonArray)
- {
- if (i_Poly3D == null)
- continue;
-
- i_Inst.AddDrawObject(new cPolygon(mb_Fill, i_Poly3D, mi_Pen, mi_ColorScheme));
- b_Added = true;
-
- foreach (cPoint3D i_Point3D in i_Poly3D.Points)
- {
- if (!i_Used.Contains(i_Point3D))
- i_Used.Add(i_Point3D);
- }
- }
-
- // Add all the remaining points as Scatter circles that are not part of a polygon.
- for (int C = 0; C < ms32_Cols; C++)
- {
- for (int R = 0; R < ms32_Rows; R++)
- {
- cPoint3D i_Point3D = mi_PointArray[C, R];
- if (i_Point3D == null || i_Used.Contains(i_Point3D))
- continue;
-
- cShape3D i_Shape3D = new cShape3D(C, R, i_Point3D, eScatterShape.Circle, ms32_Radius, null);
- i_Inst.AddDrawObject(new cShape (i_Shape3D, mi_ColorScheme));
- b_Added = true;
- }
- }
-
- if (!b_Added)
- throw new Exception("You cannot draw a completely empty SurfaceData");
- }
- }
-
- #endregion
-
- #region cScatterData
-
- public class cScatterData : cRenderData
- {
- private List mi_Shapes3D = new List();
- private cColorScheme mi_ColorScheme;
-
- public cShape3D[] AllShapes
- {
- get { return mi_Shapes3D.ToArray(); }
- }
-
- public cColorScheme ColorScheme
- {
- get { return mi_ColorScheme; }
- }
-
- ///
- /// Constructor
- /// If all Scatter shapes contain a valid Brush, you can pass i_ColorScheme == null here
- ///
- public cScatterData(cColorScheme i_ColorScheme)
- {
- mi_ColorScheme = i_ColorScheme;
- }
-
- public cScatterData()
- {
- }
+ public enum eRaster
+ {
+ Off, // turn off coordinate system
+ MainAxes, // draw only solid main axes for X,Y,Z
+ Raster, // draw additional thin raster lines
+ Labels, // draw additional labels in quadrant 3
+ }
+
+ ///
+ /// If a function has an asymetric range for X and Y as demo "Callback" a separate
+ /// normalization would always lead to a square X,Y pane which would be a distortion for the
+ /// relation between X and Y values. MaintainXY guarantees that the relation between X and Y
+ /// values is maintained. MaintainXYZ additionally guarantees that the relation between X, Y
+ /// and Z values is maintained.
+ ///
+ public enum eNormalize
+ {
+ Separate, // Normalize X,Y,Z separately (use this for discrete values)
+ MaintainXY, // Normalize X,Y without changing their relation (use this for math functions)
+ MaintainXYZ, // Normalize X,Y,Z without changing their relation (use this for math functions)
+ }
+
+ public enum eScatterShape
+ {
+ // 0 is invalid
+ Circle = 1,
+
+ Square = 2,
+ Triangle = 3,
+ // Star = 4, you can implement your own shapes here
+ }
+
+ ///
+ /// Used internally for coordinate system
+ ///
+ public enum eCoord
+ {
+ // for axis in coordinate system
+ X = 0,
+
+ Y = 1,
+ Z = 2,
+ Invalid,
+ }
+
+ ///
+ /// These flags define which axes are allowed to be mirrored. For user objects option "All"
+ /// is used. Coordinate system axes and raster lines are mirrored individually.
+ ///
+ [FlagsAttribute]
+ public enum eMirror
+ {
+ None = 0,
+ X = 1,
+ Y = 2,
+ Z = 4,
+ XY = X | Y,
+ XZ = X | Z,
+ YZ = Y | Z,
+ All = X | Y | Z,
+ }
+
+ ///
+ /// Mouse operations
+ ///
+ public enum eMouseAction
+ {
+ None = 0,
+ Move, // Move the coordinate system with the mouse
+ Theta, // Elevate the coordinate system with the mouse
+ Phi, // Rotate the coordinate system with the mouse
+ ThetaAndPhi, // Elevate + Rotate the coordinate system with the mouse
+ Rho, // Zoom in / out
+ SelectObj, // Select a 3D object or call the selection callback function if defined
+ Callback, // Call the selection callback function if defined
+ }
+
+ public enum eSelEvent
+ {
+ MouseDown, // Inform selection callback function that the pre-defined mouse button goes down
+ MouseDrag, // Inform selection callback function that the mouse is moved while the pre-defined mouse button is down
+ MouseUp, // Inform selection callback function that the pre-defined mouse button goes up
+ }
+
+ public enum ePolygonMode
+ {
+ Fill, // Fill polygons with Brush
+ Lines, // Draw only polygon border lines
+ }
+
+ ///
+ /// This enum defines of which type is a cObject3D
+ ///
+ public enum eObjType
+ {
+ Point,
+ Line,
+ Shape,
+ Polygon,
+ }
+
+ ///
+ /// These flags are used to filter the 3D objects that are selected.
+ ///
+ [FlagsAttribute]
+ public enum eSelType
+ {
+ Line = 0x1,
+ Shape = 0x2,
+ Polygon = 0x4,
+ All = 0x7,
+ }
+
+ [FlagsAttribute]
+ public enum eTooltip
+ {
+ Off = 0x0, // Tooltip is disabled
+ UserText = 0x1, // Show user defined tooltip text that has been set in cPoint3D.Tooltip
+ Coord = 0x2, // Show coordinates X,Y,Z of cPoint3D
+ All = 0x3, // Show all
+ }
+
+ // This enum is to get the maximum speed out of your CPU. Re-calculation is done only if required.
+ [FlagsAttribute]
+ private enum eRecalculate
+ {
+ Nothing = 0x0, // repaint objects after changed selection --> recalculate nothing
+ Objects = 0x1, // Projection, Brush, LineWidth,... has changed --> recalculate 3D objects
+ CoordSystem = 0x2, // The coordinate system must be recalculated --> recalculate Min/Max and Coord System
+ AddRemove = 0x4, // Draw Objects have been added or removed --> refresh lists and recalculate all
+ }
+
+ ///
+ /// These are the possible return values of the Selection callback. See description of
+ /// SelectionCallback() at the end of this class.
+ ///
+ public enum eInvalidate
+ {
+ NoChange, // The callback has not modified anything --> do nothing.
+ Invalidate, // Calls Invalidate() to redraw what is required depending on the flags in me_Recalculate.
+ CoordSystem, // The coordinate system will be recalculated, then Invalidate() is called.
+ // Use this option after moving a 3D object with the mouse.
+ }
+
+ ///
+ /// This defines with which mouse button the user controls rotation and elevation
+ ///
+ public enum eMouseCtrl
+ {
+ L_Theta_R_Phi, // Left mouse button vertical: Theta, Right mouse button horizontal: Phi
+ L_Theta_L_Phi, // Left mouse button vertical: Theta, Left mouse button horizontal: Phi
+ M_Theta_M_Phi, // Middle mouse button vertical: Theta, Middle mouse button horizontal: Phi
+ }
+
+ ///
+ /// 3D object properties that the user may change are stored in the Undo Buffer
+ ///
+ public enum eUndoProp
+ {
+ // cObject3D
+ Tag,
+
+ Selected,
+ CanSelect,
+
+ // cPoint3D
+ X,
+
+ Y,
+ Z,
+ Tooltip,
+
+ // cLine3D
+ Pen,
+
+ Width,
+
+ // cShape3D
+ Shape,
+
+ Radius,
+
+ // cShape3D + cPolygon3D
+ Brush,
+ }
+
+ public enum eLegendPos
+ {
+ BottomLeft, // The main axis legends are drawn in the bottom left corner of the graph pane. (recommended)
+ AxisEnd, // The main axis legends are drawn at the end of the axis and rotate with the graph. (not recommended)
+ }
+
+ #endregion enums
+
+ // ================== PUBLIC =================
+
+ #region cObject3D
+
+ ///
+ /// Base class for cPoint3D, cShape3D, cLine3D, cPloygon3D
+ ///
+ public abstract class cObject3D
+ {
+ public Editor3D mi_Inst;
+ protected Object mo_Tag;
+ protected bool mb_Selected;
+ protected bool mb_CanSelect = true;
+ protected cPoint3D[] mi_Points; // This instance must never be replaced by a new array!
+ protected int ms32_Col = -1;
+ protected int ms32_Row = -1;
+
+ public virtual void Restore(eUndoProp e_Property, Object o_Value)
+ {
+ switch (e_Property)
+ {
+ case eUndoProp.Tag: mo_Tag = o_Value; break;
+ case eUndoProp.Selected: mb_Selected = (bool)o_Value; break;
+ case eUndoProp.CanSelect: mb_CanSelect = (bool)o_Value; break;
+ default: throw new ArgumentException();
+ }
+ }
+
+ // --------------------------------------------------
+
+ ///
+ /// Gets the type of this cObject3D
+ ///
+ public virtual eObjType ObjType
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ ///
+ /// 1 point for cPoint3D 1 point for cShape3D 2 points for cLine3D n points for cPolygon3D
+ ///
+ public cPoint3D[] Points
+ {
+ get { return mi_Points; }
+ // set must NOT be allowed here !!!!
+ }
+
+ ///
+ /// The column in the grid. This is only valid for Polygons and Scatter circles created
+ /// by cSurfaceData, otherwise -1
+ ///
+ public int Column
+ {
+ get { return ms32_Col; } // the grid column cannot be changed
+ }
+
+ ///
+ /// The row in the grid. This is only valid for Polygons and Scatter circles created by
+ /// cSurfaceData, otherwise -1
+ ///
+ public int Row
+ {
+ get { return ms32_Row; } // the grid row cannot be changed
+ }
+
+ ///
+ /// Here you can store your private data which is passed in Selection.Callback to your code.
+ ///
+ public Object Tag
+ {
+ get { return mo_Tag; }
+ set
+ {
+ if (mo_Tag == value)
+ return;
+
+ if (mi_Inst != null)
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Tag, mo_Tag, value);
+
+ mo_Tag = value;
+ }
+ }
+
+ ///
+ /// The draw object has been selected by ALT + click
+ ///
+ public virtual bool Selected
+ {
+ get { return mb_Selected; }
+ set
+ {
+ if (mb_Selected == value)
+ return;
+
+ if (mi_Inst != null)
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Selected, mb_Selected, value);
+
+ mb_Selected = value;
+ // me_Recalculate needs no change here
+ }
+ }
+
+ ///
+ /// Defines if the user is allowed to select this object
+ ///
+ public virtual bool CanSelect
+ {
+ get { return mb_CanSelect; }
+ set
+ {
+ if (mb_CanSelect == value)
+ return;
+
+ if (mi_Inst != null)
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.CanSelect, mb_CanSelect, value);
+
+ mb_CanSelect = value;
+ }
+ }
+
+ ///
+ /// Move the object in the 3D space
+ ///
+ public void Move(double d_DeltaX, double d_DeltaY, double d_DeltaZ)
+ {
+ foreach (cPoint3D i_Point in mi_Points)
+ {
+ i_Point.X += d_DeltaX;
+ i_Point.Y += d_DeltaY;
+ i_Point.Z += d_DeltaZ;
+ }
+ }
+ }
+
+ #endregion cObject3D
+
+ #region cPoint3D
+
+ public class cPoint3D : cObject3D
+ {
+ private double md_X;
+ private double md_Y;
+ private double md_Z;
+ private String ms_Tooltip;
+
+ public override void Restore(eUndoProp e_Property, Object o_Value)
+ {
+ switch (e_Property)
+ {
+ case eUndoProp.X: md_X = (double)o_Value; break;
+ case eUndoProp.Y: md_Y = (double)o_Value; break;
+ case eUndoProp.Z: md_Z = (double)o_Value; break;
+ case eUndoProp.Tooltip: ms_Tooltip = (String)o_Value; break;
+ default: base.Restore(e_Property, o_Value); break;
+ }
+ }
+
+ // --------------------------------------------------
+
+ ///
+ /// Gets the type of this cObject3D
+ ///
+ public override eObjType ObjType
+ {
+ get { return eObjType.Point; }
+ }
+
+ ///
+ /// 3D coordinate
+ ///
+ public double X
+ {
+ get { return md_X; }
+ set
+ {
+ if (md_X == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.X, md_X, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+
+ if (value < mi_Inst.mi_Bounds.X.Min || value > mi_Inst.mi_Bounds.X.Max)
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
+ }
+ md_X = value;
+ }
+ }
+
+ ///
+ /// 3D coordinate
+ ///
+ public double Y
+ {
+ get { return md_Y; }
+ set
+ {
+ if (md_Y == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Y, md_Y, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+
+ if (value < mi_Inst.mi_Bounds.Y.Min || value > mi_Inst.mi_Bounds.Y.Max)
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
+ }
+ md_Y = value;
+ }
+ }
+
+ ///
+ /// 3D coordinate
+ ///
+ public double Z
+ {
+ get { return md_Z; }
+ set
+ {
+ if (md_Z == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Z, md_Z, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+
+ if (value < mi_Inst.mi_Bounds.Z.Min || value > mi_Inst.mi_Bounds.Z.Max)
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
+ }
+ md_Z = value;
+ }
+ }
+
+ ///
+ /// Optional tooltip text to be displayed when the mouse is over this point
+ ///
+ public String Tooltip
+ {
+ get { return ms_Tooltip; }
+ set
+ {
+ if (ms_Tooltip == value)
+ return;
+
+ if (mi_Inst != null)
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Tooltip, ms_Tooltip, value);
+
+ ms_Tooltip = value;
+ }
+ }
+
+ // --------------------------------------------------
+
+ ///
+ /// Constructor s_ToolTip is displayed when eTooltip.UserText is enabled In o_Tag you
+ /// can store any data that you need when the Selection callback is called after the
+ /// user has selected a point.
+ ///
+ public cPoint3D(double d_X, double d_Y, double d_Z, String s_ToolTip = null, Object o_Tag = null)
+ {
+ md_X = d_X;
+ md_Y = d_Y;
+ md_Z = d_Z;
+ mo_Tag = o_Tag;
+ mi_Points = new cPoint3D[] { this };
+
+ if (s_ToolTip != null)
+ ms_Tooltip = s_ToolTip.Trim();
+ }
+
+ // =================== used for coordinate system ===================
+
+ public cPoint3D Clone()
+ {
+ return new cPoint3D(md_X, md_Y, md_Z, ms_Tooltip, Tag);
+ }
+
+ public bool CoordEquals(cPoint3D i_Point)
+ {
+ return md_X == i_Point.md_X && md_Y == i_Point.md_Y && md_Z == i_Point.md_Z;
+ }
+
+ public double GetValue(eCoord e_Coord)
+ {
+ switch (e_Coord)
+ {
+ case eCoord.X: return md_X;
+ case eCoord.Y: return md_Y;
+ case eCoord.Z: return md_Z;
+ default: return 0;
+ }
+ }
+
+ public void SetValue(eCoord e_Coord, double d_Value)
+ {
+ switch (e_Coord)
+ {
+ case eCoord.X: X = d_Value; break;
+ case eCoord.Y: Y = d_Value; break;
+ case eCoord.Z: Z = d_Value; break;
+ }
+ }
+
+ // For debugging in Visual Studio
+ public override string ToString()
+ {
+ return String.Format("cPoint3D (X={0}, Y={1}, Z={2})", FormatDouble(md_X), FormatDouble(md_Y), FormatDouble(md_Z));
+ }
+ }
+
+ #endregion cPoint3D
+
+ #region cLine3D
+
+ public class cLine3D : cObject3D
+ {
+ private Pen mi_Pen;
+ private int ms32_Width;
+ private int ms32_Parts;
+
+ public override void Restore(eUndoProp e_Property, Object o_Value)
+ {
+ switch (e_Property)
+ {
+ case eUndoProp.Pen: mi_Pen = (Pen)o_Value; break;
+ case eUndoProp.Width: ms32_Width = (int)o_Value; break;
+ default: base.Restore(e_Property, o_Value); break;
+ // ms32_Parts is constant
+ }
+ }
+
+ // --------------------------------------------------
+
+ ///
+ /// Gets the type of this cObject3D
+ ///
+ public override eObjType ObjType
+ {
+ get { return eObjType.Line; }
+ }
+
+ ///
+ /// The line width in pixels
+ ///
+ public int Width
+ {
+ get { return ms32_Width; }
+ set
+ {
+ if (ms32_Width == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Width, ms32_Width, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+ }
+ ms32_Width = value;
+ }
+ }
+
+ ///
+ /// If Pen is null, a Pen from the ColorScheme will be used. The width of the Pen does
+ /// not matter. It will be set to the property Width. IMPORTANT: If you use the
+ /// UndoBuffer you must never modify any property of the Pen assigned here. If you
+ /// externally change for example Pen.Color the Undo will not work correctly. Always
+ /// assign a different instance of Pen to avoid this.
+ ///
+ public Pen Pen
+ {
+ get { return mi_Pen; }
+ set
+ {
+ if (mi_Pen == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Pen, mi_Pen, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+ }
+ mi_Pen = value;
+ }
+ }
+
+ ///
+ /// Parts of different color in multi-color lines
+ ///
+ public int ColorParts
+ {
+ get { return ms32_Parts; }
+ }
+
+ ///
+ /// Constructor IMPORTANT: If you use the UndoBuffer you must never modify any property
+ /// of the Pen assigned here. If you externally change for example Pen.Color the Undo
+ /// will not work correctly. Always assign a different instance of Pen to cLine3D.Pen to
+ /// avoid this.
+ ///
+ public cLine3D(cPoint3D i_Start, cPoint3D i_End, int s32_Width, Pen i_Pen, int s32_Parts, Object o_Tag = null)
+ {
+ mi_Points = new cPoint3D[] { i_Start, i_End };
+ ms32_Width = s32_Width;
+ mi_Pen = i_Pen;
+ ms32_Parts = s32_Parts;
+ mo_Tag = o_Tag;
+ }
+
+ // For debugging in Visual Studio
+ public override string ToString()
+ {
+ return "cLine3D from " + mi_Points[0] + " to " + mi_Points[1];
+ }
+ }
+
+ #endregion cLine3D
+
+ #region cShape3D
+
+ public class cShape3D : cObject3D
+ {
+ private eScatterShape me_Shape;
+ private int ms32_Radius;
+ private Brush mi_Brush;
+
+ public override void Restore(eUndoProp e_Property, Object o_Value)
+ {
+ switch (e_Property)
+ {
+ case eUndoProp.Shape: me_Shape = (eScatterShape)o_Value; break;
+ case eUndoProp.Radius: ms32_Radius = (int)o_Value; break;
+ case eUndoProp.Brush: mi_Brush = (Brush)o_Value; break;
+ default: base.Restore(e_Property, o_Value); break;
+ }
+ }
+
+ // --------------------------------------------------
+
+ ///
+ /// Gets the type of this cObject3D
+ ///
+ public override eObjType ObjType
+ {
+ get { return eObjType.Shape; }
+ }
+
+ ///
+ /// The type of shape to be painted
+ ///
+ public eScatterShape Shape
+ {
+ get { return me_Shape; }
+ set
+ {
+ if (me_Shape == value)
+ return;
+
+ // Circle and rectangle are drawn by the framework --> no recalculation required.
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Shape, me_Shape, value);
+
+ if (value != eScatterShape.Circle && value != eScatterShape.Square)
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+ }
+ me_Shape = value;
+ }
+ }
+
+ ///
+ /// The radius of the circle, square or triangle
+ ///
+ public int Radius
+ {
+ get { return ms32_Radius; }
+ set
+ {
+ if (ms32_Radius == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Radius, ms32_Radius, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+ }
+ ms32_Radius = value;
+ }
+ }
+
+ ///
+ /// The color of the Shape or null to use a color from the ColorScheme IMPORTANT: If you
+ /// use the UndoBuffer you must never modify any property of the Brush assigned here. If
+ /// you externally change for example SolidBrush.Color the Undo will not work correctly.
+ /// Always assign a different instance of Brush to avoid this.
+ ///
+ public Brush Brush
+ {
+ get { return mi_Brush; }
+ set
+ {
+ if (mi_Brush == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Brush, mi_Brush, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+ }
+ mi_Brush = value;
+ }
+ }
+
+ ///
+ /// The shape is selected
+ ///
+ public override bool Selected
+ {
+ get { return mi_Points[0].Selected; }
+ set { mi_Points[0].Selected = value; } // me_Recalculate needs no change
+ }
+
+ ///
+ /// Defines if the user is allowed to select this shape
+ ///
+ public override bool CanSelect
+ {
+ get { return mi_Points[0].CanSelect; }
+ set { mi_Points[0].CanSelect = value; }
+ }
+
+ ///
+ /// Constructor IMPORTANT: If you use the UndoBuffer you must never modify any property
+ /// of the Brush assigned here. If you externally change for example SolidBrush.Color
+ /// the Undo will not work correctly. Always assign a different instance of Brush to
+ /// cShape3D.Brush to avoid this.
+ ///
+ public cShape3D(int s32_Col, int s32_Row, cPoint3D i_Point, eScatterShape e_Shape, int s32_Radius, Brush i_Brush, Object o_Tag = null)
+ {
+ ms32_Col = s32_Col;
+ ms32_Row = s32_Row;
+ i_Point.Tag = o_Tag;
+ mi_Points = new cPoint3D[] { i_Point };
+ me_Shape = e_Shape;
+ ms32_Radius = s32_Radius;
+ mi_Brush = i_Brush;
+ mo_Tag = o_Tag;
+ }
+
+ // For debugging in Visual Studio
+ public override string ToString()
+ {
+ return "cShape3D " + me_Shape + " at " + mi_Points[0];
+ }
+ }
+
+ #endregion cShape3D
+
+ #region cPolygon3D
+
+ public class cPolygon3D : cObject3D
+ {
+ private Brush mi_Brush;
+
+ public override void Restore(eUndoProp e_Property, Object o_Value)
+ {
+ switch (e_Property)
+ {
+ case eUndoProp.Brush: mi_Brush = (Brush)o_Value; break;
+ default: base.Restore(e_Property, o_Value); break;
+ // ms32_Col is constant ms32_Row is constant
+ }
+ }
+
+ // --------------------------------------------------
+
+ ///
+ /// Gets the type of this cObject3D
+ ///
+ public override eObjType ObjType
+ {
+ get { return eObjType.Polygon; }
+ }
+
+ ///
+ /// The color of the Polygon or null to use a color from the ColorScheme IMPORTANT: If
+ /// you use the UndoBuffer you must never modify any property of the Brush assigned
+ /// here. If you externally change for example SolidBrush.Color the Undo will not work
+ /// correctly. Always assign a different instance of Brush to avoid this.
+ ///
+ public Brush Brush
+ {
+ get { return mi_Brush; }
+ set
+ {
+ if (mi_Brush == value)
+ return;
+
+ if (mi_Inst != null)
+ {
+ mi_Inst.mi_UndoBuffer.Backup(this, eUndoProp.Brush, mi_Brush, value);
+ mi_Inst.me_Recalculate |= eRecalculate.Objects;
+ }
+ mi_Brush = value;
+ }
+ }
+
+ ///
+ /// Constructor IMPORTANT: If you use the UndoBuffer you must never modify any property
+ /// of the Brush assigned here. If you externally change for example SolidBrush.Color
+ /// the Undo will not work correctly. Always assign a different instance of Brush to
+ /// cPolygon3D.Brush to avoid this.
+ ///
+ public cPolygon3D(int s32_Col, int s32_Row, Brush i_Brush, params cPoint3D[] i_Points)
+ {
+ if (i_Points.Length < 3)
+ throw new ArgumentException("At least 3 points are required to draw a polygon.");
+
+ mi_Points = i_Points;
+ mi_Brush = i_Brush;
+ ms32_Col = s32_Col;
+ ms32_Row = s32_Row;
+ }
+
+ // For debugging in Visual Studio
+ public override string ToString()
+ {
+ return "cPolygon3D (" + mi_Points.Length + " points)";
+ }
+ }
+
+ #endregion cPolygon3D
+
+ // ---------------------
+
+ #region cAxis
+
+ ///
+ /// Used for the main axes and raster lines of the coordinate system
+ ///
+ [Serializable]
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class cAxis
+ {
+ private Editor3D mi_Inst;
+ private eCoord me_Coord;
+ private Pen mi_RasterPen; // Raster lines (brigther)
+ private Pen mi_AxisPen; // Main coordinate axis (darker)
+ private SolidBrush mi_LegendBrush; // Label text
+ private String ms_LegendText;
+ private bool mb_Mirror;
+ private bool mb_IncludeZero;
+
+ public cAxis(Editor3D i_Inst, eCoord e_Coord, Color c_Color)
+ {
+ mi_Inst = i_Inst;
+ me_Coord = e_Coord;
+ Color = c_Color;
+ Reset();
+ }
+
+ public void Reset()
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+ ms_LegendText = null;
+ mb_Mirror = false;
+ mb_IncludeZero = me_Coord == eCoord.Z;
+ }
+
+ ///
+ /// The color of the axis lines and the legend. This change will become visible the next
+ /// time you call Invalidate() Not modified by Reset()
+ ///
+ public Color Color
+ {
+ get { return mi_AxisPen.Color; }
+ set
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+ mi_LegendBrush = new SolidBrush(value);
+ mi_AxisPen = new Pen(value, 3);
+ mi_RasterPen = new Pen(BrightenColor(value), 1);
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem;
+ }
+ }
+
+ ///
+ /// Internally used to draw raster lines (brighter)
+ ///
+ [Browsable(false)]
+ public Pen RasterPen
+ {
+ get { return mi_RasterPen; }
+ }
+
+ ///
+ /// Internally used to draw main coordinates (darker)
+ ///
+ [Browsable(false)]
+ public Pen AxisPen
+ {
+ get { return mi_AxisPen; }
+ }
+
+ ///
+ /// Internally used for label text
+ ///
+ [Browsable(false)]
+ public SolidBrush LegendBrush
+ {
+ get { return mi_LegendBrush; }
+ }
+
+ ///
+ /// Here you can add an optional legend which is displayed for the axis. With the
+ /// property Editor3D.LegendPos you can define where the legend is drawn. If the string
+ /// is null or empty, no legend will be displayed for this axis. This change will become
+ /// visible the next time you call Invalidate()
+ ///
+ public String LegendText
+ {
+ get { return ms_LegendText; }
+ set { ms_LegendText = value; }
+ }
+
+ ///
+ /// In the default rotation angle the axis values are normally increasing from right to
+ /// left (X), back to front (Y) and bottom to top (Z). If Mirror = true they will
+ /// decrease instead of increase. This change will become visible the next time you call Invalidate()
+ ///
+ public bool Mirror
+ {
+ get { return mb_Mirror; }
+ set
+ {
+ if (mb_Mirror == value)
+ return;
+
+ if (me_Coord == eCoord.Z)
+ throw new NotImplementedException("Mirroring the Z axis is not implemented because there are multiple issues and it does not make sense to draw the Z values reverse.");
+
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+ mb_Mirror = value;
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+ }
+
+ ///
+ /// Example: If the axis has values from 4 to 10 and IncludeZero = false --> the
+ /// coordinate axis has a range from 4 to 10 IncludeZero = true --> the coordinate
+ /// axis has a range from 0 to 10 (default)
+ /// ATTENTION: This setting is ignored if the coordinate system is not drawn (eRaster.Off)
+ /// This change will become visible the next time you call Invalidate()
+ ///
+ public bool IncludeZero
+ {
+ get { return mb_IncludeZero; }
+ set
+ {
+ if (mb_IncludeZero == value)
+ return;
+
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+ mb_IncludeZero = value;
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+ }
+ }
+
+ #endregion cAxis
+
+ #region cSelection
+
+ ///
+ /// This class controls the user selection of points, lines, shapes and polygons
+ ///
+ [Serializable]
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class cSelection
+ {
+ private Editor3D mi_Inst;
+ private bool mb_Enabled;
+ private bool mb_MultiSel;
+ private bool mb_SinglePoints;
+ private delSelectHandler mf_Callback;
+ private Color mc_HighlightColor = Color.Empty;
+ private Brush mi_HighlightBrush;
+ private Pen mi_HighlightPen;
+
+ public cSelection(Editor3D i_Inst)
+ {
+ mi_Inst = i_Inst;
+ }
+
+ ///
+ /// This property defines if the user is allowed to select 3D objects. The selection
+ /// will only be visible if HighlightColor has also been set. The callback will only be
+ /// called if Callback has also been assigned.
+ ///
+ public bool Enabled
+ {
+ get { return mb_Enabled; }
+ set { mb_Enabled = value; }
+ }
+
+ ///
+ /// This defines the color with which selected draw objects / points are painted. If you
+ /// pass Color.Empty draw objects will not be highlighted although they are selected!
+ /// This change will become visible the next time you call Invalidate()
+ ///
+ public Color HighlightColor
+ {
+ set
+ {
+ if (value.A > 0)
+ {
+ mi_HighlightBrush = new SolidBrush(value);
+ mi_HighlightPen = new Pen(value);
+ mi_HighlightPen.StartCap = LineCap.Round;
+ mi_HighlightPen.EndCap = LineCap.Round;
+ }
+ else
+ {
+ mi_HighlightBrush = null;
+ mi_HighlightPen = null;
+ }
+ mc_HighlightColor = value;
+ }
+ get
+ {
+ return mc_HighlightColor;
+ }
+ }
+
+ [Browsable(false)]
+ public Pen HighlightPen
+ {
+ get
+ {
+ if (mb_Enabled) return mi_HighlightPen;
+ else return null;
+ }
+ }
+
+ [Browsable(false)]
+ public Brush HighlightBrush
+ {
+ get
+ {
+ if (mb_Enabled) return mi_HighlightBrush;
+ else return null;
+ }
+ }
+
+ ///
+ /// true --> allow selection of multiple 3D objects at once false --> allow only
+ /// selection of a single 3D object at a time This setting will be ignored when a
+ /// Callback is assigned. The callback is responsible for any selection changes!
+ ///
+ public bool MultiSelect
+ {
+ get { return mb_MultiSel; }
+ set { mb_MultiSel = value; }
+ }
+
+ ///
+ /// Enables selection of single points. true --> the user can only select single
+ /// points of a polygon or the end points of a line. false --> the user can only
+ /// select an entire polygon or an entire line. For scatter shapes this setting does not
+ /// matter because a scatter shape corresponds to one point.
+ /// IMPORTANT: Selecting entire polygons makes only sense if Fill mode is used.
+ /// Otherwise polygons are transparent and a click would hit the background behind the
+ /// polygon, so this setting is ignored for polygons in Line mode. This change will
+ /// become visible the next time you call Invalidate()
+ ///
+ public bool SinglePoints
+ {
+ get { return mb_SinglePoints; }
+ set
+ {
+ mb_SinglePoints = value;
+
+ // A mix of selected lines or polygons and selected points is possible but the
+ // user will be confused.
+ DeSelectAll();
+ }
+ }
+
+ ///
+ /// IMPORTANT: Read the detailed comment of function SelectionCallback() at the end of
+ /// this class. You can set null here to turn off the callback.
+ ///
+ [Browsable(false)]
+ public delSelectHandler Callback
+ {
+ set { mf_Callback = value; }
+ get { return mf_Callback; }
+ }
+
+ ///
+ /// Returns selected cLine3D, cShape3D or cPoygon3D objects. Multiple enums can be
+ /// combined. Example: GetSelectedObjects(eSelType.Line | eSelType.Shape)
+ /// NOTE: For Shape3D the selection of the shape itself and it's point is always the same.
+ ///
+ public cObject3D[] GetSelectedObjects(eSelType e_SelType)
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+
+ List i_List = new List();
+ foreach (cDrawObj i_Object in mi_Inst.mi_UserObjects)
+ {
+ if (!i_Object.Selected || (i_Object.SelType & e_SelType) == 0)
+ continue;
+
+ i_List.Add(i_Object.mi_Object3D);
+ }
+ return i_List.ToArray();
+ }
+
+ ///
+ /// Gets the points that the user may select individually if Selection.SinglePoints =
+ /// true and gets all points of a Line3D or Polygon3D if it is selected. Multiple enums
+ /// can be combined. Example: GetSelectedPoints(eSelType.Line | eSelType.Shape)
+ /// NOTE: The returned array contains only unique points.
+ /// NOTE: For Shape3D the selection of the shape itself and it's point is always the same.
+ ///
+ public cPoint3D[] GetSelectedPoints(eSelType e_SelType)
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+
+ List i_Unique = new List();
+
+ foreach (cDrawObj i_Object in mi_Inst.mi_UserObjects)
+ {
+ if ((i_Object.SelType & e_SelType) == 0)
+ continue;
+
+ // If the object itself is selected return all it's points, no matter if the
+ // points are selected or not.
+ if (i_Object.Selected)
+ {
+ foreach (cPoint i_Point in i_Object.mi_Points)
+ {
+ if (!i_Unique.Contains(i_Point.mi_P3D))
+ i_Unique.Add(i_Point.mi_P3D);
+ }
+ }
+ else // Object not selected --> check if single points of the object are selected
+ {
+ foreach (cPoint i_Point in i_Object.mi_Points)
+ {
+ if (i_Point.mi_P3D.Selected &&
+ !i_Unique.Contains(i_Point.mi_P3D))
+ i_Unique.Add(i_Point.mi_P3D);
+ }
+ }
+ }
+ return i_Unique.ToArray();
+ }
+
+ ///
+ /// Remove the selection from all draw objects This change will become visible the next
+ /// time you call Invalidate()
+ ///
+ public void DeSelectAll()
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+
+ foreach (cDrawObj i_Obj in mi_Inst.mi_UserObjects)
+ {
+ i_Obj.Selected = false;
+
+ foreach (cPoint i_Point in i_Obj.mi_Points)
+ {
+ i_Point.mi_P3D.Selected = false;
+ }
+ }
+
+ mi_Inst.mi_UndoBuffer.Store();
+ }
+ }
+
+ #endregion cSelection
+
+ #region cUndoBuffer
+
+ ///
+ /// This public base class is exposed to the user. The implementation is in cUndoImpl.
+ ///
+ [Serializable]
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public abstract class cUndoBuffer
+ {
+ ///
+ /// Enable the Undo Buffer only if you need the Undo functionality. By default Undo /
+ /// Redo is disabled.
+ ///
+ public virtual bool Enabled
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Clears the Undo Buffer.
+ ///
+ public virtual void Clear()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Undo the last user operation. return false if Undo is not available. This is also
+ /// called when hitting CTRL + Z while the 3D Editor has the keyboard focus.
+ ///
+ public virtual bool Undo()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Redo the last user operation. return false if Redo is not available. This is also
+ /// called when hitting CTRL + Y while the 3D Editor has the keyboard focus.
+ ///
+ public virtual bool Redo()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion cUndoBuffer
+
+ #region cColorScheme
+
+ ///
+ /// Pens and Brushes are GDI+ objects which must not be created on the fly in Draw() For
+ /// speed optimization these are created only once and stored in this class.
+ ///
+ public class cColorScheme
+ {
+ // ========================= STATIC ===========================
+
+ public static Color[] GetSchema(eColorScheme e_Scheme)
+ {
+ Byte[,] u8_RGB;
+ switch (e_Scheme)
+ {
+ case eColorScheme.Green: return new Color[] { Color.Green };
+ case eColorScheme.Rainbow_Sweep: return CalcRainbow(6); // all colors, also magenta
+ case eColorScheme.Rainbow_Bright: return CalcRainbow(4); // from red to blue, no magenta
+ case eColorScheme.Monochrome: return new Color[] { Color.Goldenrod };
+ case eColorScheme.Autumn: u8_RGB = new byte[,] { { 255, 0, 0 }, { 255, 4, 0 }, { 255, 8, 0 }, { 255, 12, 0 }, { 255, 16, 0 }, { 255, 20, 0 }, { 255, 24, 0 }, { 255, 28, 0 }, { 255, 32, 0 }, { 255, 36, 0 }, { 255, 40, 0 }, { 255, 45, 0 }, { 255, 49, 0 }, { 255, 53, 0 }, { 255, 57, 0 }, { 255, 61, 0 }, { 255, 65, 0 }, { 255, 69, 0 }, { 255, 73, 0 }, { 255, 77, 0 }, { 255, 81, 0 }, { 255, 85, 0 }, { 255, 89, 0 }, { 255, 93, 0 }, { 255, 97, 0 }, { 255, 101, 0 }, { 255, 105, 0 }, { 255, 109, 0 }, { 255, 113, 0 }, { 255, 117, 0 }, { 255, 121, 0 }, { 255, 125, 0 }, { 255, 130, 0 }, { 255, 134, 0 }, { 255, 138, 0 }, { 255, 142, 0 }, { 255, 146, 0 }, { 255, 150, 0 }, { 255, 154, 0 }, { 255, 158, 0 }, { 255, 162, 0 }, { 255, 166, 0 }, { 255, 170, 0 }, { 255, 174, 0 }, { 255, 178, 0 }, { 255, 182, 0 }, { 255, 186, 0 }, { 255, 190, 0 }, { 255, 194, 0 }, { 255, 198, 0 }, { 255, 202, 0 }, { 255, 206, 0 }, { 255, 210, 0 }, { 255, 215, 0 }, { 255, 219, 0 }, { 255, 223, 0 }, { 255, 227, 0 }, { 255, 231, 0 }, { 255, 235, 0 }, { 255, 239, 0 }, { 255, 243, 0 }, { 255, 247, 0 }, { 255, 251, 0 }, { 255, 255, 0 } }; break;
+ case eColorScheme.Cool: u8_RGB = new byte[,] { { 0, 255, 255 }, { 4, 251, 255 }, { 8, 247, 255 }, { 12, 243, 255 }, { 16, 239, 255 }, { 20, 235, 255 }, { 24, 231, 255 }, { 28, 227, 255 }, { 32, 223, 255 }, { 36, 219, 255 }, { 40, 215, 255 }, { 45, 210, 255 }, { 49, 206, 255 }, { 53, 202, 255 }, { 57, 198, 255 }, { 61, 194, 255 }, { 65, 190, 255 }, { 69, 186, 255 }, { 73, 182, 255 }, { 77, 178, 255 }, { 81, 174, 255 }, { 85, 170, 255 }, { 89, 166, 255 }, { 93, 162, 255 }, { 97, 158, 255 }, { 101, 154, 255 }, { 105, 150, 255 }, { 109, 146, 255 }, { 113, 142, 255 }, { 117, 138, 255 }, { 121, 134, 255 }, { 125, 130, 255 }, { 130, 125, 255 }, { 134, 121, 255 }, { 138, 117, 255 }, { 142, 113, 255 }, { 146, 109, 255 }, { 150, 105, 255 }, { 154, 101, 255 }, { 158, 97, 255 }, { 162, 93, 255 }, { 166, 89, 255 }, { 170, 85, 255 }, { 174, 81, 255 }, { 178, 77, 255 }, { 182, 73, 255 }, { 186, 69, 255 }, { 190, 65, 255 }, { 194, 61, 255 }, { 198, 57, 255 }, { 202, 53, 255 }, { 206, 49, 255 }, { 210, 45, 255 }, { 215, 40, 255 }, { 219, 36, 255 }, { 223, 32, 255 }, { 227, 28, 255 }, { 231, 24, 255 }, { 235, 20, 255 }, { 239, 16, 255 }, { 243, 12, 255 }, { 247, 8, 255 }, { 251, 4, 255 }, { 255, 0, 255 } }; break;
+ case eColorScheme.Copper: u8_RGB = new byte[,] { { 0, 0, 0 }, { 5, 3, 2 }, { 10, 6, 4 }, { 15, 9, 6 }, { 20, 13, 8 }, { 25, 16, 10 }, { 30, 19, 12 }, { 35, 22, 14 }, { 40, 25, 16 }, { 46, 28, 18 }, { 51, 32, 20 }, { 56, 35, 22 }, { 61, 38, 24 }, { 66, 41, 26 }, { 71, 44, 28 }, { 76, 47, 30 }, { 81, 51, 32 }, { 86, 54, 34 }, { 91, 57, 36 }, { 96, 60, 38 }, { 101, 63, 40 }, { 106, 66, 42 }, { 111, 70, 44 }, { 116, 73, 46 }, { 121, 76, 48 }, { 126, 79, 50 }, { 132, 82, 52 }, { 137, 85, 54 }, { 142, 89, 56 }, { 147, 92, 58 }, { 152, 95, 60 }, { 157, 98, 62 }, { 162, 101, 64 }, { 167, 104, 66 }, { 172, 108, 68 }, { 177, 111, 70 }, { 182, 114, 72 }, { 187, 117, 75 }, { 192, 120, 77 }, { 197, 123, 79 }, { 202, 126, 81 }, { 207, 130, 83 }, { 212, 133, 85 }, { 218, 136, 87 }, { 223, 139, 89 }, { 228, 142, 91 }, { 233, 145, 93 }, { 238, 149, 95 }, { 243, 152, 97 }, { 248, 155, 99 }, { 253, 158, 101 }, { 255, 161, 103 }, { 255, 164, 105 }, { 255, 168, 107 }, { 255, 171, 109 }, { 255, 174, 111 }, { 255, 177, 113 }, { 255, 180, 115 }, { 255, 183, 117 }, { 255, 187, 119 }, { 255, 190, 121 }, { 255, 193, 123 }, { 255, 196, 125 }, { 255, 199, 127 } }; break;
+ case eColorScheme.Hot: u8_RGB = new byte[,] { { 11, 0, 0 }, { 21, 0, 0 }, { 32, 0, 0 }, { 43, 0, 0 }, { 53, 0, 0 }, { 64, 0, 0 }, { 74, 0, 0 }, { 85, 0, 0 }, { 96, 0, 0 }, { 106, 0, 0 }, { 117, 0, 0 }, { 128, 0, 0 }, { 138, 0, 0 }, { 149, 0, 0 }, { 159, 0, 0 }, { 170, 0, 0 }, { 181, 0, 0 }, { 191, 0, 0 }, { 202, 0, 0 }, { 213, 0, 0 }, { 223, 0, 0 }, { 234, 0, 0 }, { 244, 0, 0 }, { 255, 0, 0 }, { 255, 11, 0 }, { 255, 21, 0 }, { 255, 32, 0 }, { 255, 43, 0 }, { 255, 53, 0 }, { 255, 64, 0 }, { 255, 74, 0 }, { 255, 85, 0 }, { 255, 96, 0 }, { 255, 106, 0 }, { 255, 117, 0 }, { 255, 128, 0 }, { 255, 138, 0 }, { 255, 149, 0 }, { 255, 159, 0 }, { 255, 170, 0 }, { 255, 181, 0 }, { 255, 191, 0 }, { 255, 202, 0 }, { 255, 213, 0 }, { 255, 223, 0 }, { 255, 234, 0 }, { 255, 244, 0 }, { 255, 255, 0 }, { 255, 255, 16 }, { 255, 255, 32 }, { 255, 255, 48 }, { 255, 255, 64 }, { 255, 255, 80 }, { 255, 255, 96 }, { 255, 255, 112 }, { 255, 255, 128 }, { 255, 255, 143 }, { 255, 255, 159 }, { 255, 255, 175 }, { 255, 255, 191 }, { 255, 255, 207 }, { 255, 255, 223 }, { 255, 255, 239 }, { 255, 255, 255 } }; break;
+ case eColorScheme.Hsv: u8_RGB = new byte[,] { { 255, 0, 0 }, { 255, 24, 0 }, { 255, 48, 0 }, { 255, 72, 0 }, { 255, 96, 0 }, { 255, 120, 0 }, { 255, 143, 0 }, { 255, 167, 0 }, { 255, 191, 0 }, { 255, 215, 0 }, { 255, 239, 0 }, { 247, 255, 0 }, { 223, 255, 0 }, { 199, 255, 0 }, { 175, 255, 0 }, { 151, 255, 0 }, { 128, 255, 0 }, { 104, 255, 0 }, { 80, 255, 0 }, { 56, 255, 0 }, { 32, 255, 0 }, { 8, 255, 0 }, { 0, 255, 16 }, { 0, 255, 40 }, { 0, 255, 64 }, { 0, 255, 88 }, { 0, 255, 112 }, { 0, 255, 135 }, { 0, 255, 159 }, { 0, 255, 183 }, { 0, 255, 207 }, { 0, 255, 231 }, { 0, 255, 255 }, { 0, 231, 255 }, { 0, 207, 255 }, { 0, 183, 255 }, { 0, 159, 255 }, { 0, 135, 255 }, { 0, 112, 255 }, { 0, 88, 255 }, { 0, 64, 255 }, { 0, 40, 255 }, { 0, 16, 255 }, { 8, 0, 255 }, { 32, 0, 255 }, { 56, 0, 255 }, { 80, 0, 255 }, { 104, 0, 255 }, { 128, 0, 255 }, { 151, 0, 255 }, { 175, 0, 255 }, { 199, 0, 255 }, { 223, 0, 255 }, { 247, 0, 255 }, { 255, 0, 239 }, { 255, 0, 215 }, { 255, 0, 191 }, { 255, 0, 167 }, { 255, 0, 143 }, { 255, 0, 120 }, { 255, 0, 96 }, { 255, 0, 72 }, { 255, 0, 48 }, { 255, 0, 24 } }; break;
+ case eColorScheme.Rainbow_Dark: u8_RGB = new byte[,] { { 0, 0, 143 }, { 0, 0, 159 }, { 0, 0, 175 }, { 0, 0, 191 }, { 0, 0, 207 }, { 0, 0, 223 }, { 0, 0, 239 }, { 0, 0, 255 }, { 0, 16, 255 }, { 0, 32, 255 }, { 0, 48, 255 }, { 0, 64, 255 }, { 0, 80, 255 }, { 0, 96, 255 }, { 0, 112, 255 }, { 0, 128, 255 }, { 0, 143, 255 }, { 0, 159, 255 }, { 0, 175, 255 }, { 0, 191, 255 }, { 0, 207, 255 }, { 0, 223, 255 }, { 0, 239, 255 }, { 0, 255, 255 }, { 16, 255, 239 }, { 32, 255, 223 }, { 48, 255, 207 }, { 64, 255, 191 }, { 80, 255, 175 }, { 96, 255, 159 }, { 112, 255, 143 }, { 128, 255, 128 }, { 143, 255, 112 }, { 159, 255, 96 }, { 175, 255, 80 }, { 191, 255, 64 }, { 207, 255, 48 }, { 223, 255, 32 }, { 239, 255, 16 }, { 255, 255, 0 }, { 255, 239, 0 }, { 255, 223, 0 }, { 255, 207, 0 }, { 255, 191, 0 }, { 255, 175, 0 }, { 255, 159, 0 }, { 255, 143, 0 }, { 255, 128, 0 }, { 255, 112, 0 }, { 255, 96, 0 }, { 255, 80, 0 }, { 255, 64, 0 }, { 255, 48, 0 }, { 255, 32, 0 }, { 255, 16, 0 }, { 255, 0, 0 }, { 239, 0, 0 }, { 223, 0, 0 }, { 207, 0, 0 }, { 191, 0, 0 }, { 175, 0, 0 }, { 159, 0, 0 }, { 143, 0, 0 }, { 128, 0, 0 } }; break;
+ case eColorScheme.Pink: u8_RGB = new byte[,] { { 30, 0, 0 }, { 50, 26, 26 }, { 64, 37, 37 }, { 75, 45, 45 }, { 85, 52, 52 }, { 94, 59, 59 }, { 102, 64, 64 }, { 110, 69, 69 }, { 117, 74, 74 }, { 123, 79, 79 }, { 130, 83, 83 }, { 136, 87, 87 }, { 141, 91, 91 }, { 147, 95, 95 }, { 152, 98, 98 }, { 157, 102, 102 }, { 162, 105, 105 }, { 167, 108, 108 }, { 172, 111, 111 }, { 176, 114, 114 }, { 181, 117, 117 }, { 185, 120, 120 }, { 189, 123, 123 }, { 194, 126, 126 }, { 195, 132, 129 }, { 197, 138, 131 }, { 199, 144, 134 }, { 201, 149, 136 }, { 202, 154, 139 }, { 204, 159, 141 }, { 206, 164, 144 }, { 207, 169, 146 }, { 209, 174, 148 }, { 211, 178, 151 }, { 212, 183, 153 }, { 214, 187, 155 }, { 216, 191, 157 }, { 217, 195, 160 }, { 219, 199, 162 }, { 220, 203, 164 }, { 222, 207, 166 }, { 223, 211, 168 }, { 225, 215, 170 }, { 226, 218, 172 }, { 228, 222, 174 }, { 229, 225, 176 }, { 231, 229, 178 }, { 232, 232, 180 }, { 234, 234, 185 }, { 235, 235, 191 }, { 237, 237, 196 }, { 238, 238, 201 }, { 240, 240, 206 }, { 241, 241, 211 }, { 243, 243, 216 }, { 244, 244, 221 }, { 245, 245, 225 }, { 247, 247, 230 }, { 248, 248, 234 }, { 250, 250, 238 }, { 251, 251, 243 }, { 252, 252, 247 }, { 254, 254, 251 }, { 255, 255, 255 } }; break;
+ case eColorScheme.Spring: u8_RGB = new byte[,] { { 255, 0, 255 }, { 255, 4, 251 }, { 255, 8, 247 }, { 255, 12, 243 }, { 255, 16, 239 }, { 255, 20, 235 }, { 255, 24, 231 }, { 255, 28, 227 }, { 255, 32, 223 }, { 255, 36, 219 }, { 255, 40, 215 }, { 255, 45, 210 }, { 255, 49, 206 }, { 255, 53, 202 }, { 255, 57, 198 }, { 255, 61, 194 }, { 255, 65, 190 }, { 255, 69, 186 }, { 255, 73, 182 }, { 255, 77, 178 }, { 255, 81, 174 }, { 255, 85, 170 }, { 255, 89, 166 }, { 255, 93, 162 }, { 255, 97, 158 }, { 255, 101, 154 }, { 255, 105, 150 }, { 255, 109, 146 }, { 255, 113, 142 }, { 255, 117, 138 }, { 255, 121, 134 }, { 255, 125, 130 }, { 255, 130, 125 }, { 255, 134, 121 }, { 255, 138, 117 }, { 255, 142, 113 }, { 255, 146, 109 }, { 255, 150, 105 }, { 255, 154, 101 }, { 255, 158, 97 }, { 255, 162, 93 }, { 255, 166, 89 }, { 255, 170, 85 }, { 255, 174, 81 }, { 255, 178, 77 }, { 255, 182, 73 }, { 255, 186, 69 }, { 255, 190, 65 }, { 255, 194, 61 }, { 255, 198, 57 }, { 255, 202, 53 }, { 255, 206, 49 }, { 255, 210, 45 }, { 255, 215, 40 }, { 255, 219, 36 }, { 255, 223, 32 }, { 255, 227, 28 }, { 255, 231, 24 }, { 255, 235, 20 }, { 255, 239, 16 }, { 255, 243, 12 }, { 255, 247, 8 }, { 255, 251, 4 }, { 255, 255, 0 } }; break;
+ case eColorScheme.Summer: u8_RGB = new byte[,] { { 0, 128, 102 }, { 4, 130, 102 }, { 8, 132, 102 }, { 12, 134, 102 }, { 16, 136, 102 }, { 20, 138, 102 }, { 24, 140, 102 }, { 28, 142, 102 }, { 32, 144, 102 }, { 36, 146, 102 }, { 40, 148, 102 }, { 45, 150, 102 }, { 49, 152, 102 }, { 53, 154, 102 }, { 57, 156, 102 }, { 61, 158, 102 }, { 65, 160, 102 }, { 69, 162, 102 }, { 73, 164, 102 }, { 77, 166, 102 }, { 81, 168, 102 }, { 85, 170, 102 }, { 89, 172, 102 }, { 93, 174, 102 }, { 97, 176, 102 }, { 101, 178, 102 }, { 105, 180, 102 }, { 109, 182, 102 }, { 113, 184, 102 }, { 117, 186, 102 }, { 121, 188, 102 }, { 125, 190, 102 }, { 130, 192, 102 }, { 134, 194, 102 }, { 138, 196, 102 }, { 142, 198, 102 }, { 146, 200, 102 }, { 150, 202, 102 }, { 154, 204, 102 }, { 158, 206, 102 }, { 162, 208, 102 }, { 166, 210, 102 }, { 170, 212, 102 }, { 174, 215, 102 }, { 178, 217, 102 }, { 182, 219, 102 }, { 186, 221, 102 }, { 190, 223, 102 }, { 194, 225, 102 }, { 198, 227, 102 }, { 202, 229, 102 }, { 206, 231, 102 }, { 210, 233, 102 }, { 215, 235, 102 }, { 219, 237, 102 }, { 223, 239, 102 }, { 227, 241, 102 }, { 231, 243, 102 }, { 235, 245, 102 }, { 239, 247, 102 }, { 243, 249, 102 }, { 247, 251, 102 }, { 251, 253, 102 }, { 255, 255, 102 } }; break;
+ case eColorScheme.Winter: u8_RGB = new byte[,] { { 0, 0, 255 }, { 0, 4, 253 }, { 0, 8, 251 }, { 0, 12, 249 }, { 0, 16, 247 }, { 0, 20, 245 }, { 0, 24, 243 }, { 0, 28, 241 }, { 0, 32, 239 }, { 0, 36, 237 }, { 0, 40, 235 }, { 0, 45, 233 }, { 0, 49, 231 }, { 0, 53, 229 }, { 0, 57, 227 }, { 0, 61, 225 }, { 0, 65, 223 }, { 0, 69, 221 }, { 0, 73, 219 }, { 0, 77, 217 }, { 0, 81, 215 }, { 0, 85, 213 }, { 0, 89, 210 }, { 0, 93, 208 }, { 0, 97, 206 }, { 0, 101, 204 }, { 0, 105, 202 }, { 0, 109, 200 }, { 0, 113, 198 }, { 0, 117, 196 }, { 0, 121, 194 }, { 0, 125, 192 }, { 0, 130, 190 }, { 0, 134, 188 }, { 0, 138, 186 }, { 0, 142, 184 }, { 0, 146, 182 }, { 0, 150, 180 }, { 0, 154, 178 }, { 0, 158, 176 }, { 0, 162, 174 }, { 0, 166, 172 }, { 0, 170, 170 }, { 0, 174, 168 }, { 0, 178, 166 }, { 0, 182, 164 }, { 0, 186, 162 }, { 0, 190, 160 }, { 0, 194, 158 }, { 0, 198, 156 }, { 0, 202, 154 }, { 0, 206, 152 }, { 0, 210, 150 }, { 0, 215, 148 }, { 0, 219, 146 }, { 0, 223, 144 }, { 0, 227, 142 }, { 0, 231, 140 }, { 0, 235, 138 }, { 0, 239, 136 }, { 0, 243, 134 }, { 0, 247, 132 }, { 0, 251, 130 }, { 0, 255, 128 } }; break;
+ default:
+ throw new ArgumentException("Invalid enum");
+ }
+
+ Color[] c_Schema = new Color[u8_RGB.GetLength(0)];
+ for (int i = 0; i < c_Schema.Length; i++)
+ {
+ c_Schema[i] = Color.FromArgb(u8_RGB[i, 0], u8_RGB[i, 1], u8_RGB[i, 2]);
+ }
+ return c_Schema;
+ }
+
+ ///
+ /// s32_Sweeps = 4 --> Colors from blue to red, not including magenta (1024 colors)
+ /// s32_Sweeps = 6 --> Complete sweep with all rainbow colors, also magenta (1536 colors)
+ ///
+ private static Color[] CalcRainbow(int s32_Sweeps)
+ {
+ Color[] c_Colors = new Color[s32_Sweeps * 256];
+ int R, G, B, P = 0;
+ for (int L = 0; L < s32_Sweeps; L++)
+ {
+ for (int E = 0; E < 256; E++)
+ {
+ switch (L)
+ {
+ case 0: R = 0; G = E; B = 255; break; // Blue...Cyan
+ case 1: R = 0; G = 255; B = 255 - E; break; // Cyan...Green
+ case 2: R = E; G = 255; B = 0; break; // Green...Yellow
+ case 3: R = 255; G = 255 - E; B = 0; break; // Yellow...Red
+ case 4: R = 255; G = 0; B = E; break; // Red...Magenta
+ case 5: R = 255 - E; G = 0; B = 255; break; // Magenta...Blue
+ default: throw new ArgumentException();
+ }
+ c_Colors[P++] = Color.FromArgb(255, R, G, B);
+ }
+ }
+ return c_Colors;
+ }
+
+ // ========================= MEMBER ===========================
+
+ private Brush[] mi_Brushes;
+ private Pen[] mi_Pens;
+
+ public int ColorCount
+ {
+ get { return mi_Brushes.Length; }
+ }
+
+ ///
+ /// Constructor 1
+ ///
+ public cColorScheme(eColorScheme e_Scheme)
+ : this(GetSchema(e_Scheme))
+ {
+ }
+
+ ///
+ /// Constructor 2 If you want to draw the entire graph with only one color, you can pass
+ /// a single Color here.
+ ///
+ public cColorScheme(params Color[] c_Colors)
+ {
+ if (c_Colors.Length == 0)
+ throw new ArgumentException("A ColorScheme needs at least one color.");
+
+ mi_Brushes = new SolidBrush[c_Colors.Length];
+ mi_Pens = new Pen[c_Colors.Length];
+
+ for (int i = 0; i < c_Colors.Length; i++)
+ {
+ mi_Brushes[i] = new SolidBrush(c_Colors[i]);
+ mi_Pens[i] = new Pen(c_Colors[i], 1); // the width of the Pen will be modified later.
+ }
+ }
+
+ public Brush GetBrush(int s32_Index)
+ {
+ s32_Index = Math.Max(0, s32_Index);
+ return mi_Brushes[s32_Index % mi_Brushes.Length];
+ }
+
+ public Pen GetPen(int s32_Index)
+ {
+ s32_Index = Math.Max(0, s32_Index);
+ return mi_Pens[s32_Index % mi_Pens.Length];
+ }
+
+ public int CalcIndex(double d_FactorZ)
+ {
+ if (double.IsNaN(d_FactorZ))
+ return -1;
+
+ d_FactorZ = Math.Min(1.0, d_FactorZ);
+ d_FactorZ = Math.Max(0.0, d_FactorZ);
+
+ // d_FactorZ is a value between 0.0 and 1.0
+ return (int)(d_FactorZ * (mi_Brushes.Length - 1));
+ }
+ }
+
+ #endregion cColorScheme
+
+ #region cUserInput
+
+ public class cUserInput
+ {
+ private MouseButtons me_MouseButton;
+ private Keys me_Modifiers;
+ private eMouseAction me_Action;
+ private Cursor mi_Cursor;
+
+ ///
+ /// A unique identifier for the combination of mouse button and modifier(s) Keys.Shift =
+ /// 0x00010000 Keys.Control = 0x00020000 Keys.Alt = 0x00040000 MouseButtons.Left =
+ /// 0x00100000 MouseButtons.Right = 0x00200000 MouseButtons.Middle = 0x00400000
+ /// MouseButtons.XButton1 = 0x00800000 MouseButtons.XButton2 = 0x01000000
+ ///
+ public int UID
+ {
+ get { return (int)me_MouseButton | (int)me_Modifiers; }
+ }
+
+ public MouseButtons MouseButton
+ {
+ get { return me_MouseButton; }
+ }
+
+ public Keys Modifiers
+ {
+ get { return me_Modifiers; }
+ }
+
+ public eMouseAction Action
+ {
+ get { return me_Action; }
+ }
+
+ public Cursor Cursor
+ {
+ get { return mi_Cursor; }
+ }
+
+ public cUserInput(MouseButtons e_MouseButton, Keys e_Modifiers, eMouseAction e_Action, Cursor i_Cursor = null)
+ {
+ Debug.Assert((e_Modifiers & Keys.KeyCode) == 0, "Invalid parameter e_Modifiers");
+ Debug.Assert(e_MouseButton != MouseButtons.None, "Invalid parameter e_MouseButton");
+ Debug.Assert(e_Action != eMouseAction.None, "Invalid parameter e_Action");
+
+ me_MouseButton = e_MouseButton;
+ me_Modifiers = e_Modifiers;
+ me_Action = e_Action;
+ mi_Cursor = i_Cursor;
+
+ if (mi_Cursor == null)
+ {
+ switch (e_Action)
+ {
+ case eMouseAction.ThetaAndPhi: mi_Cursor = Cursors.SizeAll; break;
+ case eMouseAction.Theta: mi_Cursor = Cursors.NoMoveVert; break;
+ case eMouseAction.Phi: mi_Cursor = Cursors.NoMoveHoriz; break;
+ case eMouseAction.Rho: mi_Cursor = Cursors.SizeNS; break;
+ case eMouseAction.Move: mi_Cursor = Cursors.NoMove2D; break;
+ default: mi_Cursor = Cursors.Arrow; break;
+ }
+ }
+ }
+
+ ///
+ /// For debugging in Visual Studio
+ ///
+ public override string ToString()
+ {
+ return String.Format("MouseButton: {0}, Modifiers: {1} --> Action: {2}", me_MouseButton, me_Modifiers, me_Action);
+ }
+ }
+
+ #endregion cUserInput
+
+ // ---------------------
+
+ #region cRenderData
+
+ ///
+ /// Base class for cSurfaceData, cScatterData, cLineData, cPolygonData
+ ///
+ public abstract class cRenderData
+ {
+ public virtual void AddDrawObjects(Editor3D i_Inst)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// The width and color of the Pen may be modified later. So the immutable framework
+ /// collection like Pens.Black,... cannot be used here.
+ ///
+ protected static void CheckPenMutable(Pen i_Pen, cColorScheme i_ColorScheme)
+ {
+ if (i_Pen != null && i_ColorScheme != null)
+ {
+ try { i_Pen.Color = Color.BlanchedAlmond; }
+ catch { throw new ArgumentException("To use a color scheme create a new Pen. Do not use the immutable Pens.XYZ collection."); }
+ }
+ }
+ }
+
+ #endregion cRenderData
+
+ #region cSurfaceData
+
+ public class cSurfaceData : cRenderData
+ {
+ private bool mb_Fill;
+ private bool mb_Missing;
+ private int ms32_Cols;
+ private int ms32_Rows;
+ private int ms32_Radius;
+ private Pen mi_Pen;
+ private cPoint3D[,] mi_PointArray;
+ private cPolygon3D[,] mi_PolygonArray;
+ private cColorScheme mi_ColorScheme;
+
+ public cColorScheme ColorScheme
+ {
+ get { return mi_ColorScheme; }
+ }
+
+ ///
+ /// The count of points in one column of the surface grid
+ ///
+ public int Cols
+ {
+ get { return ms32_Cols; }
+ }
+
+ ///
+ /// The count of points in one row of the surface grid
+ ///
+ public int Rows
+ {
+ get { return ms32_Rows; }
+ }
+
+ ///
+ /// Fill Mode: ------------ Polygons are filled with a color from the ColorScheme. If
+ /// you want only one color, set a ColorScheme which contains only one color. The Pen is
+ /// used to draw the thin lines between the polygons (mostly black, 1 pixel) If Pen is
+ /// null, no lines are drawn.
+ ///
+ /// Line Mode: ------------ Only the border lines of the polygons are drawn. The Pen is
+ /// used to draw these lines. The Pen's color and width will be modified.
+ ///
+ /// Missing Mode: -------------- s32_Radius > 0 allows missing points. s32_Radius
+ /// defines the radius of cicles that represent points which have not enough neigbours
+ /// to draw a polygon.
+ ///
+ public cSurfaceData(int s32_Cols, int s32_Rows, ePolygonMode e_Mode, Pen i_Pen, cColorScheme i_ColorScheme,
+ int s32_Radius = 0)
+ {
+ if (s32_Cols < 3 || s32_Rows < 3)
+ throw new ArgumentException("cSurfaceData needs at least 3 columns and 3 rows");
+
+ if (e_Mode == ePolygonMode.Fill)
+ {
+ if (i_ColorScheme == null)
+ throw new ArgumentException("In Fill mode you must specify a ColorScheme");
+
+ // The border pen is allowed to be immutable. It will not be changed.
+ }
+ else // Lines
+ {
+ if (i_Pen == null)
+ throw new ArgumentException("In Line mode you must specify a Line Pen");
+
+ CheckPenMutable(i_Pen, i_ColorScheme);
+ }
+
+ mb_Fill = e_Mode == ePolygonMode.Fill;
+ mb_Missing = s32_Radius > 0;
+ ms32_Radius = s32_Radius;
+ ms32_Cols = s32_Cols;
+ ms32_Rows = s32_Rows;
+ mi_Pen = i_Pen;
+ mi_ColorScheme = i_ColorScheme;
+ mi_PointArray = new cPoint3D[s32_Cols, s32_Rows];
+ }
+
+ ///
+ /// Here you can set a callback function which will be called with X,Y to calculate the
+ /// Z values of the points.
+ ///
+ public void ExecuteFunction(delRendererFunction f_Function, PointF k_Start, PointF k_End)
+ {
+ mb_Missing = false;
+
+ double d_StepX = (k_End.X - k_Start.X) / (ms32_Cols - 1);
+ double d_StepY = (k_End.Y - k_Start.Y) / (ms32_Rows - 1);
+
+ for (int C = 0; C < ms32_Cols; C++)
+ {
+ double d_X = k_Start.X + d_StepX * C;
+
+ for (int R = 0; R < ms32_Rows; R++)
+ {
+ double d_Y = k_Start.Y + d_StepY * R;
+ double d_Z = f_Function(d_X, d_Y);
+
+ SetPointAt(C, R, new cPoint3D(d_X, d_Y, d_Z));
+ }
+ }
+ }
+
+ ///
+ /// IMPORTANT: The X coordinate of the point must be related to the column. The Y
+ /// coordinate of the point must be related to the row.
+ ///
+ public void SetPointAt(int s32_Column, int s32_Row, cPoint3D i_Point3D)
+ {
+ if (mi_PolygonArray != null)
+ throw new Exception("You cannot call cSurfaceData.SetPointAt() or ExecuteFunction() anymore after calling GetPolygonAt() "
+ + "or Editor3D.AddRenderData(). To modify a point after the polygons have been created call "
+ + "GetPointAt() and modify the X,Y,Z values of the returned point.");
+
+ mi_PointArray[s32_Column, s32_Row] = i_Point3D;
+ }
+
+ ///
+ /// ATTENTION: In mode 'Missing' null may be returned or polygons with only 3 corners!
+ ///
+ public cPoint3D GetPointAt(int s32_Column, int s32_Row)
+ {
+ return mi_PointArray[s32_Column, s32_Row];
+ }
+
+ ///
+ /// ATTENTION: The polygons have one row less than cSurfaceData.Rows and one column less
+ /// than cSurfaceData.Cols
+ ///
+ public cPolygon3D GetPolygonAt(int s32_Column, int s32_Row)
+ {
+ CreatePolygons();
+ return mi_PolygonArray[s32_Column, s32_Row];
+ }
+
+ private void CreatePolygons()
+ {
+ if (mi_PolygonArray != null)
+ return;
+
+ cPolygon3D[,] i_TempArr = new cPolygon3D[ms32_Cols - 1, ms32_Rows - 1];
+
+ List i_Valid = new List();
+ for (int C = 0; C < ms32_Cols - 1; C++)
+ {
+ for (int R = 0; R < ms32_Rows - 1; R++)
+ {
+ i_Valid.Clear();
+ if (mi_PointArray[C, R] != null) i_Valid.Add(mi_PointArray[C, R]);
+ if (mi_PointArray[C, R + 1] != null) i_Valid.Add(mi_PointArray[C, R + 1]);
+ if (mi_PointArray[C + 1, R + 1] != null) i_Valid.Add(mi_PointArray[C + 1, R + 1]);
+ if (mi_PointArray[C + 1, R] != null) i_Valid.Add(mi_PointArray[C + 1, R]);
+
+ if (i_Valid.Count < 4 && !mb_Missing)
+ throw new Exception("cSurfaceData: You must call cSurfaceData.SetPointAt() for all points!");
+
+ if (i_Valid.Count < 3)
+ continue; // A polygon needs at least 3 points
+
+ i_TempArr[C, R] = new cPolygon3D(C, R, null, i_Valid.ToArray());
+ }
+ }
+
+ mi_PolygonArray = i_TempArr;
+ }
+
+ // =============================================================================
+
+ ///
+ /// Called from AddRenderData()
+ ///
+ public override void AddDrawObjects(Editor3D i_Inst)
+ {
+ CreatePolygons();
+
+ bool b_Added = false;
+ List i_Used = new List();
+
+ foreach (cPolygon3D i_Poly3D in mi_PolygonArray)
+ {
+ if (i_Poly3D == null)
+ continue;
+
+ i_Inst.AddDrawObject(new cPolygon(mb_Fill, i_Poly3D, mi_Pen, mi_ColorScheme));
+ b_Added = true;
+
+ foreach (cPoint3D i_Point3D in i_Poly3D.Points)
+ {
+ if (!i_Used.Contains(i_Point3D))
+ i_Used.Add(i_Point3D);
+ }
+ }
+
+ // Add all the remaining points as Scatter circles that are not part of a polygon.
+ for (int C = 0; C < ms32_Cols; C++)
+ {
+ for (int R = 0; R < ms32_Rows; R++)
+ {
+ cPoint3D i_Point3D = mi_PointArray[C, R];
+ if (i_Point3D == null || i_Used.Contains(i_Point3D))
+ continue;
+
+ cShape3D i_Shape3D = new cShape3D(C, R, i_Point3D, eScatterShape.Circle, ms32_Radius, null);
+ i_Inst.AddDrawObject(new cShape(i_Shape3D, mi_ColorScheme));
+ b_Added = true;
+ }
+ }
+
+ if (!b_Added)
+ throw new Exception("You cannot draw a completely empty SurfaceData");
+ }
+ }
+
+ #endregion cSurfaceData
+
+ #region cScatterData
+
+ public class cScatterData : cRenderData
+ {
+ private List mi_Shapes3D = new List();
+ private cColorScheme mi_ColorScheme;
+
+ public cShape3D[] AllShapes
+ {
+ get { return mi_Shapes3D.ToArray(); }
+ }
+
+ public cColorScheme ColorScheme
+ {
+ get { return mi_ColorScheme; }
+ }
+
+ ///
+ /// Constructor If all Scatter shapes contain a valid Brush, you can pass i_ColorScheme
+ /// == null here
+ ///
+ public cScatterData(cColorScheme i_ColorScheme)
+ {
+ mi_ColorScheme = i_ColorScheme;
+ }
+
+ public cScatterData()
+ {
+ }
///
/// s32_Radius defines the size of the shape and i_Brush the color
///
public cShape3D AddShape(cPoint3D i_Point, eScatterShape e_Shape, int s32_Radius, Brush i_Brush, Object o_Tag = null)
- {
- cShape3D i_Shape3D = new cShape3D(-1, -1, i_Point, e_Shape, s32_Radius, i_Brush, o_Tag);
- mi_Shapes3D.Add(i_Shape3D);
- return i_Shape3D;
- }
-
- // =============================================================================
-
- ///
- /// Called from AddRenderData()
- ///
- public override void AddDrawObjects(Editor3D i_Inst)
- {
- foreach (cShape3D i_Shape3D in mi_Shapes3D)
- {
- i_Inst.AddDrawObject(new cShape(i_Shape3D, mi_ColorScheme));
- }
- }
- }
-
- #endregion
-
- #region cLineData
-
- public class cLineData : cRenderData
- {
- private List mi_Lines3D = new List();
- private cColorScheme mi_ColorScheme;
-
- public cLine3D[] AllLines
- {
- get { return mi_Lines3D.ToArray(); }
- }
-
- public cColorScheme ColorScheme
- {
- get { return mi_ColorScheme; }
- }
-
- ///
- /// Constructor
- /// If you use only solid lines and specify a valid Pen, you can pass i_ColorScheme == null here
- ///
- public cLineData(cColorScheme i_ColorScheme)
- {
- mi_ColorScheme = i_ColorScheme;
- }
-
- ///
- /// Add a line which will be drawn entirely in one color.
- ///
- public cLine3D AddSolidLine(cPoint3D i_Start, cPoint3D i_End, int s32_Width, Pen i_Pen, Object o_Tag = null)
- {
- CheckPenMutable(i_Pen, mi_ColorScheme);
-
- cLine3D i_Line3D = new cLine3D(i_Start, i_End, s32_Width, i_Pen, 1, o_Tag);
- mi_Lines3D.Add(i_Line3D);
- return i_Line3D;
- }
-
- ///
- /// Add a line which will appear with multiple colors of the ColorScheme by drawing it in multiple parts.
- /// If s32_Parts = 50, the line is rendered in 50 parts where each part has it's own color depending on the Z coordinate.
- ///
- public cLine3D AddMultiColorLine(int s32_Parts, cPoint3D i_Start, cPoint3D i_End, int s32_Width, Pen i_Pen, Object o_Tag = null)
- {
- if (s32_Parts < 3)
- throw new ArgumentException("Multi color lines require at least 3 parts");
-
- if (mi_ColorScheme == null)
- throw new Exception("To create a multi-color line you must specify a ColorScheme");
-
- CheckPenMutable(i_Pen, mi_ColorScheme);
-
- cLine3D i_Line3D = new cLine3D(i_Start, i_End, s32_Width, i_Pen, s32_Parts, o_Tag);
- mi_Lines3D.Add(i_Line3D);
- return i_Line3D;
- }
-
- ///
- /// Creates connected lines from the points in the given order
- ///
- public cLine3D[] AddConnectedLines(List i_Points, int s32_Width, Pen i_Pen)
- {
- CheckPenMutable(i_Pen, mi_ColorScheme);
-
- List i_NewLines = new List();
-
- cPoint3D i_Prev = null;
- for (int i=0; i
- /// Called from AddRenderData()
- ///
- public override void AddDrawObjects(Editor3D i_Inst)
- {
- foreach (cLine3D i_Line3D in mi_Lines3D)
- {
- i_Inst.AddDrawObject(new cLine(i_Line3D, mi_ColorScheme));
- }
- }
- }
-
- #endregion
-
- #region cPolygonData
-
- public class cPolygonData : cRenderData
- {
- private bool mb_Fill;
- private Pen mi_Pen;
- private cColorScheme mi_ColorScheme;
- private List mi_Polygons3D = new List();
-
- public cPolygon3D[] AllPolygons
- {
- get { return mi_Polygons3D.ToArray(); }
- }
-
- public cColorScheme ColorScheme
- {
- get { return mi_ColorScheme; }
- }
-
- ///
- /// Fill Mode:
- /// ------------
- /// Polygons are filled with a color from the ColorScheme.
- /// If you want only one color, set a ColorScheme which contains only one color.
- /// The Pen is used to draw the thin lines between the polygons (mostly black, 1 pixel)
- /// If Pen is null, no lines are drawn.
- ///
- /// Line Mode:
- /// ------------
- /// Only the border lines of the polygons are drawn.
- /// The Pen is used to draw these lines. The Pen's color and width will be modified.
- ///
- public cPolygonData(ePolygonMode e_Mode, Pen i_Pen, cColorScheme i_ColorScheme)
- {
- if (e_Mode == ePolygonMode.Lines)
- {
- if (i_Pen == null)
- throw new ArgumentException("In Line mode you must specify a Line Pen");
-
- CheckPenMutable(i_Pen, i_ColorScheme);
- }
-
- mb_Fill = e_Mode == ePolygonMode.Fill;
- mi_Pen = i_Pen;
- mi_ColorScheme = i_ColorScheme;
- }
-
- ///
- /// In contrast to other drawing libraries (like WPF or Direct3D) you can add polygons of any dimension here.
- /// A polygon can have any amount of corners (minimum 3).
- /// The Brush can be specified in Fill mode. To use a Brush from the ColorScheme set i_Brush = null.
- ///
- public cPolygon3D AddPolygon(Brush i_Brush, params cPoint3D[] i_Points3D)
- {
- cPolygon3D i_Polygon3D = new cPolygon3D(-1, -1, i_Brush, i_Points3D);
- mi_Polygons3D.Add(i_Polygon3D);
- return i_Polygon3D;
- }
-
- // =============================================================================
-
- ///
- /// Called from AddRenderData()
- ///
- public override void AddDrawObjects(Editor3D i_Inst)
- {
- foreach (cPolygon3D i_Polygon3D in mi_Polygons3D)
- {
- i_Inst.AddDrawObject(new cPolygon(mb_Fill, i_Polygon3D, mi_Pen, mi_ColorScheme));
- }
- }
- }
-
- #endregion
-
- #region cMessgData
-
- public class cMessgData
- {
- private String ms_Text;
- private Brush mi_Brush;
- private int ms32_PosX;
- private int ms32_PosY;
- private Font mi_Font;
- private SizeF mk_Size;
-
- ///
- /// Here you can change the text without loading all the render objects again.
- /// The change will become visible the next time you call Invalidate()
- ///
- public String Text
- {
- set
- {
- ms_Text = value;
- mk_Size = SizeF.Empty;
- }
- }
-
- ///
- /// Here you can change the text color without loading all the render objects again.
- /// The change will become visible the next time you call Invalidate()
- ///
- public Color TextColor
- {
- set { mi_Brush = new SolidBrush(value); }
- }
-
- ///
- /// If X is negative, it is displayed right aligned at X pixels from the right
- /// If Y is negative, it is displayed bottom aligned at Y pixels from the bottom
- ///
- public cMessgData(String s_Text, int X, int Y, Color c_Color,
- FontStyle e_FontStyle = FontStyle.Bold,
- int s32_FontSize = 9,
- String s_FontFace = "Tahoma")
- {
- ms_Text = s_Text;
- ms32_PosX = X;
- ms32_PosY = Y;
- mi_Brush = new SolidBrush(c_Color);
- mi_Font = new Font(s_FontFace, s32_FontSize, e_FontStyle);
- }
-
- public void Draw(Graphics i_Graph, Rectangle k_Client)
- {
- if (String.IsNullOrEmpty(ms_Text))
- return;
-
- float X = ms32_PosX;
- float Y = ms32_PosY;
-
- if (X < 0 || Y < 0)
- {
- // Speed optimization: Measure the size only once.
- if (mk_Size.IsEmpty)
- mk_Size = i_Graph.MeasureString(ms_Text, mi_Font);
-
- if (X < 0) X += k_Client.Width - mk_Size.Width;
- if (Y < 0) Y += k_Client.Height - mk_Size.Height;
- }
-
- i_Graph.DrawString(ms_Text, mi_Font, mi_Brush, X, Y);
- }
- }
-
- #endregion
-
- // ================= PRIVATE =================
-
- #region cPoint2D
-
- ///
- /// This class represents a point in the 2D space, in pixels.
- ///
- private class cPoint2D
- {
- public double md_X;
- public double md_Y;
-
- public cPoint2D()
- {
- }
-
- public cPoint2D(double X, double Y)
- {
- md_X = X;
- md_Y = Y;
- }
-
- public cPoint2D Clone()
- {
- return new cPoint2D(md_X, md_Y);
- }
-
- public PointF Coord
- {
- get { return new PointF((float)md_X, (float)md_Y); }
- }
-
- public bool IsValid
- {
- get
- {
- // The screen will always be smaller than 9999 pixels
- return (!Double.IsNaN(md_X) && Math.Abs(md_X) < 9999.9 &&
- !Double.IsNaN(md_Y) && Math.Abs(md_Y) < 9999.9);
- }
- }
-
- ///
- /// Use the good old Pythagoras to calculate the pixel distance between this point and X, Y.
- ///
- public int CalcDistanceTo(int X, int Y)
- {
- int s32_DiffX = (int)md_X - X;
- int s32_DiffY = (int)md_Y - Y;
- return (int)Math.Sqrt(s32_DiffX * s32_DiffX + s32_DiffY * s32_DiffY);
- }
-
- // For debugging in Visual Studio
- public override string ToString()
- {
- return String.Format("cPoint2D (X={0}, Y={1})", FormatDouble(md_X), FormatDouble(md_Y));
- }
- }
-
- #endregion
-
- #region cPoint
-
- ///
- /// This class contains the 3D point and it's projection into the 2D space.
- ///
- private class cPoint
- {
- public cPoint3D mi_P3D;
- public cPoint2D mi_P2D;
- public int ms32_RadiusTip; // if 0 --> no tooltip
-
- public cPoint(double d_X, double d_Y, double d_Z)
- {
- mi_P3D = new cPoint3D(d_X, d_Y, d_Z);
- mi_P2D = new cPoint2D();
- }
-
- ///
- /// The radius defines at which distance of the mouse from the 2D point the tooltip pops up.
- /// Radius = 0 --> no tooltip
- ///
- public cPoint(cPoint3D i_Point3D, int s32_RadiusTip)
- {
- mi_P3D = i_Point3D;
- mi_P2D = new cPoint2D();
-
- if (s32_RadiusTip > 0)
- ms32_RadiusTip = s32_RadiusTip + TOOLTIP_RADIUS;
- }
-
- ///
- /// Projects the 3D coordinates into the 2D space (pixels on the screen).
- ///
- public void Project3D(Editor3D i_Inst, eMirror e_Mirror)
- {
- mi_P2D = i_Inst.mi_Transform.Project3D(mi_P3D, e_Mirror);
- }
-
- ///
- /// For Debugging in Visual Studio
- ///
- public override string ToString()
- {
- return String.Format("cPoint [{0} = {1}]", mi_P3D, mi_P2D);
- }
- }
-
- #endregion
-
- #region cTooltip
-
- private class cTooltip
- {
- private Editor3D mi_Inst = null;
- private eTooltip me_Mode = eTooltip.All;
- private ToolTip mi_Tooltip = new ToolTip();
- private List mi_Points = new List();
- private cPoint mi_Last = null;
-
- public eTooltip Mode
- {
- get { return me_Mode; }
- set { me_Mode = value; }
- }
-
- // Constructor
- public cTooltip(Editor3D i_Inst)
- {
- mi_Inst = i_Inst;
-
- mi_Tooltip.AutoPopDelay = 30000; // The maximum that Windows allows are 32.767 seconds (0x7FFF milliseconds)
- mi_Tooltip.InitialDelay = 50;
- mi_Tooltip.ReshowDelay = 50;
- }
-
- public void Clear()
- {
- mi_Points.Clear();
- mi_Last = null;
- Hide();
- }
-
- public void AddPoint(cPoint i_Point)
- {
- // ATTENTION adding a
- // && !mi_Points.Contains(i_Point)
- // here would make this function 40 times slower!
- // The more points are already in mi_Points, the slower it would become.
- // Several points will be added multiple times here, but this does not affect the functioning of the tooltip.
- if (i_Point != null && i_Point.ms32_RadiusTip > 0)
- mi_Points.Add(i_Point);
- }
-
- public void Hide()
- {
- mi_Tooltip.Hide(mi_Inst);
- }
-
- public void OnMouseMove(MouseEventArgs e)
- {
- if (me_Mode == eTooltip.Off)
- {
- Hide();
- return;
- }
-
- int s32_MouseX = e.X - mi_Inst.mi_Mouse.mk_OffMove.X - mi_Inst.mi_Mouse.mk_OffCoord.X;
- int s32_MouseY = e.Y - mi_Inst.mi_Mouse.mk_OffMove.Y - mi_Inst.mi_Mouse.mk_OffCoord.Y;
-
- int s32_MinDist = int.MaxValue;
- cPoint i_Nearest = null;
- foreach (cPoint i_Point in mi_Points)
- {
- int s32_Dist = i_Point.mi_P2D.CalcDistanceTo(s32_MouseX, s32_MouseY);
- if (s32_Dist < s32_MinDist)
- {
- s32_MinDist = s32_Dist;
- i_Nearest = i_Point;
- }
- }
-
- if (i_Nearest != null && s32_MinDist < i_Nearest.ms32_RadiusTip)
- {
- if (mi_Last == i_Nearest)
- return; // The mouse is still over the same point
-
- mi_Last = i_Nearest;
-
- String s_TT = "";
- if ((me_Mode & eTooltip.Coord) > 0)
- s_TT = String.Format("X = {0}\nY = {1}\nZ = {2}\n", FormatDouble(i_Nearest.mi_P3D.X),
- FormatDouble(i_Nearest.mi_P3D.Y),
- FormatDouble(i_Nearest.mi_P3D.Z));
-
- if ((me_Mode & eTooltip.UserText) > 0 && i_Nearest.mi_P3D.Tooltip != null)
- s_TT += i_Nearest.mi_P3D.Tooltip;
-
- s_TT = s_TT.Trim();
- if (s_TT.Length > 0)
- {
- mi_Tooltip.Show(s_TT, mi_Inst, e.X + 10, e.Y + 10);
- return;
- }
- }
-
- mi_Last = null;
- Hide();
- }
- }
-
- #endregion
-
- #region cBackup
-
- private class cBackup
- {
- public Dictionary mi_Changes = new Dictionary();
-
- public void StoreProperty(eUndoProp e_Property, Object o_OldValue, Object o_NewValue)
- {
- Object[] o_Values;
- if (mi_Changes.TryGetValue(e_Property, out o_Values))
- {
- // The property has changed multiple times (dragging) --> store only the last value
- o_Values[1] = o_NewValue;
- }
- else
- {
- // The property has changed for the first time
- mi_Changes.Add(e_Property, new Object[] { o_OldValue, o_NewValue });
- }
- }
-
- public void Restore(cObject3D i_Object3D, bool b_Undo)
- {
- foreach (KeyValuePair i_Pair in mi_Changes)
- {
- if (b_Undo) i_Object3D.Restore(i_Pair.Key, i_Pair.Value[0]); // Undo: old value
- else i_Object3D.Restore(i_Pair.Key, i_Pair.Value[1]); // Redo: new value
- }
- }
-
- // for debugging in Visual Studio
- public override string ToString()
- {
- StringBuilder i_Dbg = new StringBuilder();
- foreach (eUndoProp e_Property in mi_Changes.Keys)
- {
- if (i_Dbg.Length > 0) i_Dbg.Append(", ");
- i_Dbg.Append(e_Property);
- }
- return "cBackup Properties: " + i_Dbg.ToString();
- }
- }
-
- #endregion
-
- #region cUndoEntry
-
- private class cUndoEntry
- {
- public Dictionary mi_Backups = new Dictionary();
- public List mi_Added = new List();
- public List mi_Removed = new List();
-
- public bool IsEmpty
- {
- get { return mi_Backups.Count == 0 && mi_Added.Count == 0 && mi_Removed.Count == 0; }
- }
-
- // for debugging in Visual Studio
- public override string ToString()
- {
- return String.Format("Backups: {0}, Added: {1}, Removed: {2}", (mi_Backups == null) ? 0 : mi_Backups.Count,
- (mi_Added == null) ? 0 : mi_Added .Count,
- (mi_Removed == null) ? 0 : mi_Removed.Count);
- }
- }
-
- #endregion
-
- #region cUndoImpl
-
- ///
- /// This private class stores the changes after each edit operation that the user has made.
- ///
- private class cUndoImpl : cUndoBuffer
- {
- private Editor3D mi_Inst;
- private int ms32_UndoIdx = 0; // Points to the next Undo item in mi_UndoList
- private int ms32_RedoIdx = 0; // Points to the next Redo item in mi_UndoList
- private bool mb_Enabled = false; // The Undo Buffer is by default disabled
- private bool mb_DoInit = false; // After Clear() execute Init() once.
- private bool mb_Pending = false; // User actions are pending that have not yet been stored
- private cUndoEntry mi_CurUndo = new cUndoEntry();
- private List mi_UndoList = new List();
-
- ///
- /// Do not enable the Undo buffer if you don't need the Undo functionality.
- /// By default it is disabled.
- ///
- public override bool Enabled
- {
- get { return mb_Enabled; }
- set
- {
- Clear();
- mb_Enabled = value;
- }
- }
-
- ///
- /// Constructor
- ///
- public cUndoImpl(Editor3D i_Inst)
- {
- mi_Inst = i_Inst;
- }
-
- public override void Clear()
- {
- ms32_UndoIdx = 0;
- ms32_RedoIdx = 0;
- mb_DoInit = true;
- mb_Pending = false;
- mi_CurUndo = new cUndoEntry();
- mi_UndoList.Clear();
- }
-
- ///
- /// This must only be called from Editor3D.Render() !
- /// The first time calling Invalidate() after Clear() --> clear all draw objets that have been added.
- /// Otherwise all added objects would be stored. (This class stores only user changes.)
- ///
- public void Init()
- {
- if (mb_DoInit)
- {
- Clear();
- mb_DoInit = false;
- }
- }
-
- ///
- /// Called when any public property of a 3D object was changed (e.g. X,Y,Z coordinates of a cPoint3D).
- /// These changes will be stored when Store() is called.
- ///
- public void Backup(cObject3D i_Object3D, eUndoProp e_Property, Object o_OldValue, Object o_NewValue)
- {
- if (!mb_Enabled)
- return;
-
- cBackup i_Backup;
- if (!mi_CurUndo.mi_Backups.TryGetValue(i_Object3D, out i_Backup))
- {
- i_Backup = new cBackup();
- mi_CurUndo.mi_Backups.Add(i_Object3D, i_Backup);
- }
-
- i_Backup.StoreProperty(e_Property, o_OldValue, o_NewValue);
- mb_Pending = true;
- }
-
- ///
- /// Called when the user removes a draw object.
- /// These changes will be stored when Store() is called.
- ///
- public void DrawObjectRemoved(cDrawObj i_DrawObject)
- {
- if (!mb_Enabled)
- return;
-
- mi_CurUndo.mi_Removed.Add(i_DrawObject);
- mb_Pending = true;
- }
-
- ///
- /// Called when the user adds a draw object.
- /// These changes will be stored when Store() is called.
- ///
- public void DrawObjectAdded(cDrawObj i_DrawObject)
- {
- if (!mb_Enabled)
- return;
-
- mi_CurUndo.mi_Added.Add(i_DrawObject);
- mb_Pending = true;
- }
-
- ///
- /// Store the changes on user objects in the Undo buffer on mouse-up.
- ///
- public void Store()
- {
- if (!mb_Enabled || !mb_Pending || mi_CurUndo.IsEmpty)
- return;
-
- // Remove any entries in the Undo Buffer behind the current index
- int s32_Remove = mi_UndoList.Count - ms32_UndoIdx - 1;
- if (s32_Remove > 0)
- mi_UndoList.RemoveRange(ms32_UndoIdx + 1, s32_Remove);
-
- mi_UndoList.Add(mi_CurUndo);
- mi_CurUndo = new cUndoEntry();
-
- ms32_UndoIdx = mi_UndoList.Count - 1;
- ms32_RedoIdx = mi_UndoList.Count - 1;
- mb_Pending = false;
- }
-
- ///
- /// This is also called when hitting CTRL + Z while the 3D Editor has the keyboard focus
- ///
- public override bool Undo()
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
-
- if (!mb_Enabled || mi_UndoList.Count == 0 || ms32_UndoIdx < 0)
- return false;
-
- Restore(true, ms32_UndoIdx);
-
- ms32_RedoIdx = ms32_UndoIdx;
- ms32_UndoIdx --;
- return true;
- }
-
- ///
- /// This is also called when hitting CTRL + Y while the 3D Editor has the keyboard focus
- ///
- public override bool Redo()
- {
- Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
-
- if (!mb_Enabled || mi_UndoList.Count == 0 || ms32_RedoIdx >= mi_UndoList.Count)
- return false;
-
- Restore(false, ms32_RedoIdx);
-
- ms32_UndoIdx = ms32_RedoIdx;
- ms32_RedoIdx ++;
- return true;
- }
-
- private void Restore(bool b_Undo, int s32_Index)
- {
- s32_Index = Math.Max(s32_Index, 0);
- s32_Index = Math.Min(s32_Index, mi_UndoList.Count - 1);
-
- cUndoEntry i_UndoItem = mi_UndoList[s32_Index];
-
- foreach (cDrawObj i_Removed in i_UndoItem.mi_Removed)
- {
- if (b_Undo) mi_Inst.mi_UserObjects.Add (i_Removed);
- else mi_Inst.mi_UserObjects.Remove(i_Removed);
-
- mi_Inst.me_Recalculate |= eRecalculate.AddRemove | eRecalculate.Objects | eRecalculate.CoordSystem;
- }
-
- foreach (cDrawObj i_Added in i_UndoItem.mi_Added)
- {
- if (b_Undo) mi_Inst.mi_UserObjects.Remove(i_Added);
- else mi_Inst.mi_UserObjects.Add (i_Added);
-
- mi_Inst.me_Recalculate |= eRecalculate.AddRemove | eRecalculate.Objects | eRecalculate.CoordSystem;
- }
-
- foreach (KeyValuePair i_Pair in i_UndoItem.mi_Backups)
- {
- i_Pair.Value.Restore(i_Pair.Key, b_Undo);
-
- mi_Inst.me_Recalculate |= eRecalculate.Objects | eRecalculate.CoordSystem;
- }
-
- mi_Inst.Invalidate();
- }
- }
-
- #endregion
-
- // ----- DrawObjects -----
-
- #region cDrawObj
-
- ///
- /// Base class for cLine, cShape, cPolygon
- ///
- private abstract class cDrawObj : IComparable
- {
- public Editor3D mi_Inst;
- public SmoothingMode me_SmoothMode;
- public cPoint[] mi_Points;
- public cObject3D mi_Object3D; // This is a cLine3D, cShape3D or cPolygon3D
- public double md_Sort; // sorting is important. Always draw first back, then front objects.
- public bool mb_IsAxis; // This is a line from the coordinate system
- protected double md_AvrgZ; // 3D center of the Z coordinates of all points in this object
- protected bool mb_IsValid;
- protected eMirror me_Mirror;
-
- // --------------------------------------------------
-
- ///
- /// Set after conversion 3D --> 2D. If invalid coordinates are found (NaN or > 9999) this returns false.
- /// If projection results in lines of thousands of pixels length, the drawing will become extremely slow.
- /// Do not draw lines or polygons outside the screen area.
- ///
- public bool IsValid
- {
- get { return mb_IsValid; }
- }
-
- ///
- /// The object is selected
- ///
- public bool Selected
- {
- get { return mi_Object3D.Selected; }
- set { mi_Object3D.Selected = value; }
- }
-
- ///
- /// The type of this object
- ///
- public virtual eSelType SelType
- {
- get { throw new NotImplementedException(); }
- }
-
- ///
- /// Calculates colors from color scheme
- ///
- public virtual void ProcessColors()
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Calculates the 2D screen coordinates for each 3D point.
- ///
- public virtual void Project3D()
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Draw into Graphics
- ///
- public virtual void Render(Graphics i_Graph)
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Check if a user click at mouse point X, Y matches this draw object
- ///
- public virtual cObject3D MatchesPoint2D(int X, int Y)
- {
- throw new NotImplementedException();
- }
-
- ///
- /// Uses the center of a draw object (e.g. the middle of a line) to calculate in which order the draw objects are rendered.
- /// This is called for all user defined objects, but not for the coordinate system which has it's own logic.
- ///
- public void CalcSortOrder()
- {
- Debug.Assert(!mb_IsAxis); // axes have their own value for md_Sort
-
- double d_AvrgX = 0.0;
- double d_AvrgY = 0.0;
- md_AvrgZ = 0.0;
-
- foreach (cPoint i_Point in mi_Points)
- {
- d_AvrgX += i_Point.mi_P3D.X;
- d_AvrgY += i_Point.mi_P3D.Y;
- md_AvrgZ += i_Point.mi_P3D.Z;
- }
-
- d_AvrgX /= mi_Points.Length;
- d_AvrgY /= mi_Points.Length;
- md_AvrgZ /= mi_Points.Length;
-
- if (mi_Object3D.Row > -1) // only Polygons and Scatter circles created by cSurfaceData
- {
- int X = mi_Object3D.Column + 1;
- int Y = mi_Object3D.Row + 1;
-
- // Mirror axis values increasing / decreasing
- if (mi_Inst.AxisX.Mirror && (me_Mirror & eMirror.X) > 0) X = 5000 - mi_Object3D.Column;
- if (mi_Inst.AxisY.Mirror && (me_Mirror & eMirror.Y) > 0) Y = 5000 - mi_Object3D.Row;
-
- // In case of a surface grid the Z value must be ignored because sorting is ALWAYS based on the position in the grid.
- // Using the Z value here may even result in wrong sort order.
- md_Sort = mi_Inst.mi_Transform.ProjectXY(X, Y);
- }
- else
- {
- double X = d_AvrgX;
- double Y = d_AvrgY;
- double Z = md_AvrgZ;
-
- // Mirror axis values increasing / decreasing
- if (mi_Inst.AxisX.Mirror && (me_Mirror & eMirror.X) > 0) X = mi_Inst.mi_Bounds.X.Max - (X - mi_Inst.mi_Bounds.X.Min);
- if (mi_Inst.AxisY.Mirror && (me_Mirror & eMirror.Y) > 0) Y = mi_Inst.mi_Bounds.Y.Max - (Y - mi_Inst.mi_Bounds.Y.Min);
- if (mi_Inst.AxisZ.Mirror && (me_Mirror & eMirror.Z) > 0) Z = mi_Inst.mi_Bounds.Z.Max - (Z - mi_Inst.mi_Bounds.Z.Min);
-
- // In case of any other 3D object the Z value must also be included in the calculation to avoid artifacts.
- // Demo Sphere shows that the Z value is required if you move Theta to an extreme.
- X = (X - mi_Inst.mi_Transform.mi_Center3D.X) * mi_Inst.mi_Transform.md_NormalizeX;
- Y = (Y - mi_Inst.mi_Transform.mi_Center3D.Y) * mi_Inst.mi_Transform.md_NormalizeY;
- Z = (Z - mi_Inst.mi_Transform.mi_Center3D.Z) * mi_Inst.mi_Transform.md_NormalizeZ;
-
- md_Sort = mi_Inst.mi_Transform.ProjectXY(X, Y, Z);
- }
- }
-
- ///
- /// Used for sorting all DrawObjects from back to front
- ///
- int IComparable.CompareTo(Object o_Comp)
- {
- return md_Sort.CompareTo(((cDrawObj)o_Comp).md_Sort);
- }
- }
-
- #endregion
-
- #region cLine
-
- private class cLine : cDrawObj
- {
- private cLine3D mi_Line3D; // object passed to and from the user
- private Pen mi_Pen;
- private Brush mi_Brush; // assigned to Pen
- private float mf_LineWidth; // Linewidth with zoom factor
- private float mf_SelSize; // size of selection points
-
- // -------- coordinate axes --------
- public double md_Angle; // needed to calculate current rotation quadrant of coordinate axis
- public eCoord me_Line = eCoord.Invalid; // main coordinate in coordinate direction
- public eCoord me_Offset = eCoord.Invalid; // secondary coordinate in coordinate direction
- public String ms_Label; // Label for axis
-
- // ---------- multicolor -----------
- private cPoint[] mi_ColorPoints; // all points on the line that are drawn separately
- private Brush[] mi_ColorBrushes; // all Brushes which are assigned to the Pen
- private cColorScheme mi_ColorScheme;
-
- public override eSelType SelType
- {
- get { return eSelType.Line; }
- }
-
- ///
- /// Constructor 1 for coordinate system.
- /// LineWidth is always 1.
- ///
- public cLine(Editor3D i_Inst, eCoord e_Line, eCoord e_Offset, eMirror e_Mirror)
- {
- if (e_Line == e_Offset) mi_Pen = i_Inst.mi_Axis[(int)e_Line] .AxisPen; // Main axis
- else mi_Pen = i_Inst.mi_Axis[(int)e_Offset].RasterPen; // Raster line
-
- mi_Inst = i_Inst;
- me_Line = e_Line;
- me_Offset = e_Offset;
- mi_Brush = mi_Pen.Brush;
- mi_Points = new cPoint[2];
- mi_Points[0] = new cPoint(0, 0, 0);
- mi_Points[1] = new cPoint(0, 0, 0);
- mb_IsAxis = true;
- me_Mirror = e_Mirror;
- me_SmoothMode = SmoothingMode.AntiAlias;
- }
-
- ///
- /// Constructor 2 for user lines
- /// if i_Line3D.Pen == null --> Pen from ColorScheme is used
- /// if i_Line3D.Pen != null --> ColorScheme will be ignored, even if a ColorScheme is specified
- /// An indvidual LineWidth can be defined in each cLine3D.
- ///
- public cLine(cLine3D i_Line3D, cColorScheme i_ColorScheme)
- {
- if (i_Line3D.Pen == null && i_ColorScheme == null)
- throw new ArgumentException("You must specify a Pen or a ColorScheme");
-
- mi_ColorScheme = i_ColorScheme;
- mi_Line3D = i_Line3D;
- mi_Object3D = i_Line3D;
- mi_Pen = i_Line3D.Pen; // get user's Pen or null
- mi_Points = new cPoint[2];
- mi_Points[0] = new cPoint(i_Line3D.Points[0], 1);
- mi_Points[1] = new cPoint(i_Line3D.Points[1], 1);
- mb_IsAxis = false;
- me_Mirror = eMirror.All;
- me_SmoothMode = SmoothingMode.AntiAlias;
-
- if (mi_Pen != null)
- {
- // The original Brush must be stored separately because the same Pen may be used for multiple instances of cLine
- // Changing the color of one line would affect all the others.
- mi_Brush = mi_Pen.Brush;
- }
- else
- {
- mi_Pen = new Pen(Brushes.Black); // color and width will be changed below
- mi_Brush = null; // Brush will be taken from colorscheme
- }
- mi_Pen.StartCap = LineCap.Round;
- mi_Pen.EndCap = LineCap.Round;
-
- // ---------- multi color ---------
-
- if (i_Line3D.ColorParts > 1)
- {
- mi_ColorBrushes = new Brush [i_Line3D.ColorParts];
- mi_ColorPoints = new cPoint[i_Line3D.ColorParts];
- }
- }
-
- public override void ProcessColors()
- {
- Debug.Assert(!mb_IsAxis); // axes have their own color
-
- // If the user has changed the Pen --> use the new Pen and it's Brush
- if (mi_Line3D.Pen != null)
- {
- mi_Pen = mi_Line3D.Pen;
- mi_Brush = mi_Pen.Brush;
-
- mi_Pen.StartCap = LineCap.Round;
- mi_Pen.EndCap = LineCap.Round;
- }
-
- if (mi_ColorPoints != null) // multicolor line
- {
- double d_X = mi_Points[0].mi_P3D.X;
- double d_Y = mi_Points[0].mi_P3D.Y;
- double d_Z = mi_Points[0].mi_P3D.Z;
-
- double d_DeltaX = (mi_Points[1].mi_P3D.X - d_X) / mi_ColorPoints.Length;
- double d_DeltaY = (mi_Points[1].mi_P3D.Y - d_Y) / mi_ColorPoints.Length;
- double d_DeltaZ = (mi_Points[1].mi_P3D.Z - d_Z) / mi_ColorPoints.Length;
-
- mi_ColorPoints[0] = mi_Points[0]; // Set Start Point
-
- cPoint i_Prev = mi_Points[0];
- for (int i=1; i
- /// Check if a user click at X, Y matches this draw object
- ///
- public override cObject3D MatchesPoint2D(int X, int Y)
- {
- if (mb_IsAxis)
- return null; // do not allow to select axis lines
-
- int s32_MaxDist = Math.Max(1, (int)mi_Pen.Width / 2) + SELECT_RADIUS;
-
- if (mi_Inst.Selection.SinglePoints)
- {
- foreach (cPoint i_Point in mi_Points)
- {
- if (i_Point.mi_P2D.CalcDistanceTo(X, Y) <= s32_MaxDist)
- return i_Point.mi_P3D;
- }
- }
- else // select entire line
- {
- if (IsPointOnLine(mi_Points[0].mi_P2D, mi_Points[1].mi_P2D, X, Y, s32_MaxDist))
- return mi_Line3D;
- }
-
- return null;
- }
-
- // ---------------- Coord System ---------------
-
- ///
- /// Used while creating coordinate system
- /// Check if 2 lines have the same coordinates.
- ///
- public bool CoordEquals(cLine i_Line)
- {
- return mi_Points[0].mi_P3D.CoordEquals(i_Line.mi_Points[0].mi_P3D) &&
- mi_Points[1].mi_P3D.CoordEquals(i_Line.mi_Points[1].mi_P3D);
- }
-
- ///
- /// Used while creating coordinate system
- /// Calculate the angle of the 3 main axes on the screen in a range from 0 to 360 degree.
- ///
- public void CalcAngle2D()
- {
- double d_DX = mi_Points[1].mi_P2D.md_X - mi_Points[0].mi_P2D.md_X;
- double d_DY = mi_Points[1].mi_P2D.md_Y - mi_Points[0].mi_P2D.md_Y;
- md_Angle = Math.Atan2(d_DY, d_DX) * 180.0 / Math.PI;
- if (md_Angle < 0.0) md_Angle += 360.0;
- }
-
- // For debugging in Visual Studio
- public override string ToString()
- {
- String s_Dbg = String.Format("cLine from {0} to {1}", mi_Points[0], mi_Points[1]);
- if (mb_IsAxis)
- s_Dbg += String.Format(" (Axis {0}, {1})", me_Line, me_Offset);
-
- return s_Dbg;
- }
- }
-
- #endregion
-
- #region cShape
-
- private class cShape : cDrawObj
- {
- private float mf_Radius; // radius of shape adapted with Zoom factor
- private float mf_Diameter; // diameter of shape adapted with Zoom factor
- private PointF mk_TopLeft; // top left corner in screen coordinates for all types of shapes
- private PointF[] mk_Polygon; // used for triangles or any future user objects
- private Brush mi_Brush;
- private cShape3D mi_Shape3D;
- private cColorScheme mi_ColorScheme;
-
- public override eSelType SelType
- {
- get { return eSelType.Shape; }
- }
-
- ///
- /// Constructor
- ///
- public cShape(cShape3D i_Shape3D, cColorScheme i_ColorScheme)
- {
- if (i_Shape3D.Brush == null && i_ColorScheme == null)
- throw new ArgumentException("You must specify a Brush or a ColorScheme");
-
- mi_Shape3D = i_Shape3D;
- mi_Object3D = i_Shape3D;
- mi_Points = new cPoint[1];
- mi_Points[0] = new cPoint(i_Shape3D.Points[0], i_Shape3D.Radius);
- mi_ColorScheme = i_ColorScheme;
- me_SmoothMode = SmoothingMode.AntiAlias;
- me_Mirror = eMirror.All;
- }
-
- public override void ProcessColors()
- {
- // If the user has specified an individual brush for this Shape --> always use it
- mi_Brush = mi_Shape3D.Brush;
-
- // Otherwise use Brush from ColorScheme
- if (mi_Brush == null)
- {
- double d_FactorZ = mi_Inst.mi_Bounds.CalcFactorZ(md_AvrgZ);
- int s32_Index = mi_ColorScheme.CalcIndex(d_FactorZ);
- mi_Brush = mi_ColorScheme.GetBrush (s32_Index);
- }
- }
-
- public override void Project3D()
- {
- mi_Points[0].Project3D(mi_Inst, me_Mirror);
-
- mb_IsValid = mi_Points[0].mi_P2D.IsValid;
- mf_Radius = (float)(mi_Shape3D.Radius * mi_Inst.mi_Transform.md_Zoom);
- mf_Diameter = mf_Radius * 2.0f;
-
- // Move coordinate from center to upper left corner of circle
- mk_TopLeft = mi_Points[0].mi_P2D.Coord;
- mk_TopLeft.X -= mf_Radius;
- mk_TopLeft.Y -= mf_Radius;
-
- switch (mi_Shape3D.Shape)
- {
- case eScatterShape.Triangle:
- mk_Polygon = new PointF[3];
- // top center
- mk_Polygon[0].X = mk_TopLeft.X + mf_Radius;
- mk_Polygon[0].Y = mk_TopLeft.Y;
- // bottom left
- mk_Polygon[1].X = mk_TopLeft.X;
- mk_Polygon[1].Y = mk_TopLeft.Y + mf_Diameter;
- // bottom right
- mk_Polygon[2].X = mk_TopLeft.X + mf_Diameter;
- mk_Polygon[2].Y = mk_TopLeft.Y + mf_Diameter;
- break;
-
- // case eScatterShape.Star:
- // Here you can implement your own shapes
- // break;
- }
- }
-
- public override void Render(Graphics i_Graph)
- {
- bool b_DrawSel = Selected && mi_Inst.mi_Selection.HighlightBrush != null;
- Brush i_DrawBrush = b_DrawSel ? mi_Inst.mi_Selection.HighlightBrush : mi_Brush;
-
- switch (mi_Shape3D.Shape)
- {
- case eScatterShape.Circle:
- i_Graph.FillEllipse (i_DrawBrush, mk_TopLeft.X, mk_TopLeft.Y, mf_Diameter, mf_Diameter);
- break;
- case eScatterShape.Square:
- i_Graph.FillRectangle(i_DrawBrush, mk_TopLeft.X, mk_TopLeft.Y, mf_Diameter, mf_Diameter);
- break;
- default:
- i_Graph.FillPolygon (i_DrawBrush, mk_Polygon);
- break;
- }
- }
-
- ///
- /// Check if a user click at X, Y matches this draw object
- ///
- public override cObject3D MatchesPoint2D(int X, int Y)
- {
- int s32_MaxDist = (int)mf_Radius + SELECT_RADIUS;
-
- if (mi_Points[0].mi_P2D.CalcDistanceTo(X, Y) <= s32_MaxDist)
- return mi_Shape3D;
-
- return null;
- }
-
- ///
- /// For Debugging in Visual Studio
- ///
- public override string ToString()
- {
- return String.Format("cShape {0} at {1}, Diameter {2}", mi_Shape3D.Shape, mi_Points[0], FormatDouble(mf_Diameter));
- }
- }
-
- #endregion
-
- #region cPolygon
-
- private class cPolygon : cDrawObj
- {
- private bool mb_Fill; // Fill / Line mode
- private float mf_SelSize; // size of selection points
- private PointF[] mk_Screen; // the 2D polygon corner points in screen coordinates
- private int ms32_OrgWidth; // original line width for Line Pen
- private float mf_LineWidth; // zoomed line width for Line Pen
- private Pen mi_LinePen; // used in Line mode
- private Pen mi_BorderPen; // used in Fill mode (not zoomed)
- private Brush mi_Brush; // used in Fill mode
- private cPolygon3D mi_Polygon3D;
- private cColorScheme mi_ColorScheme;
-
- public override eSelType SelType
- {
- get { return eSelType.Polygon; }
- }
-
- ///
- /// Constructor
- ///
- public cPolygon(bool b_Fill, cPolygon3D i_Polygon3D, Pen i_Pen, cColorScheme i_ColorScheme)
- {
- mi_Points = new cPoint[i_Polygon3D.Points.Length];
- for (int i=0; i always use it
- if (mi_Polygon3D.Brush != null)
- {
- mi_Brush = mi_Polygon3D.Brush;
- }
- else if (mi_ColorScheme != null) // ColorScheme is never null in Fill mode
- {
- double d_FactorZ = mi_Inst.mi_Bounds.CalcFactorZ(md_AvrgZ);
- int s32_Index = mi_ColorScheme.CalcIndex(d_FactorZ);
- mi_Brush = mi_ColorScheme.GetBrush (s32_Index); // used for Fill and assigned to LinePen
- }
- }
-
- public override void Project3D()
- {
- foreach (cPoint i_Point in mi_Points)
- {
- i_Point.Project3D(mi_Inst, me_Mirror);
- }
-
- // Line width for Line mode
- mf_LineWidth = (float)(ms32_OrgWidth * mi_Inst.mi_Transform.md_Zoom);
-
- // Diameter of circle for selected points
- mf_SelSize = (float)(Math.Max(6, ms32_OrgWidth * 2) * mi_Inst.mi_Transform.md_Zoom);
-
- mb_IsValid = true;
- for (int i=0; i draw thin black border lines around the polygons
- // Line mode --> draw thicker lines around transparent polygons
- if (i_DrawPen != null)
- {
- // ATTENTION: Graphics.DrawPolygon() with a Pen > 1 pixel is buggy in the .NET framework (artifacts)!
- // The lines must be drawn one by one manually here.
- int T = mk_Screen.Length - 1;
- for (int F=0; F
- /// Check if a user click at X, Y matches this draw object
- ///
- public override cObject3D MatchesPoint2D(int X, int Y)
- {
- // ATTENTION: Selecting entire polygons makes only sense with eSurfaceMode.Fill
- // In line mode polygons are transparent and a click into the polygon would go to the background.
- if (!mb_Fill || mi_Inst.mi_Selection.SinglePoints)
- {
- int s32_MaxDist = (int)mf_SelSize / 2 + SELECT_RADIUS;
- foreach (cPoint i_Point in mi_Points)
- {
- if (i_Point.mi_P2D.CalcDistanceTo(X, Y) <= s32_MaxDist)
- return i_Point.mi_P3D;
- }
- }
- else // select entire polygon
- {
- // Detect if the point is inside the polygon. Here SELECT_RADIUS is ignored.
- // But the user must only click into the middle of the polygon, which is easier than clicking a thin line.
- bool b_Result = false;
- int k = mi_Points.Length - 1;
- for (int i = 0; i < mi_Points.Length; i++)
- {
- cPoint2D i_Point1 = mi_Points[i].mi_P2D;
- cPoint2D i_Point2 = mi_Points[k].mi_P2D;
-
- if (i_Point1.md_Y < Y && i_Point2.md_Y >= Y ||
- i_Point2.md_Y < Y && i_Point1.md_Y >= Y)
- {
- if (i_Point1.md_X + (Y - i_Point1.md_Y) /
- (i_Point2.md_Y - i_Point1.md_Y) *
- (i_Point2.md_X - i_Point1.md_X) < X)
- {
- b_Result = !b_Result;
- }
- }
- k = i;
- }
- if (b_Result)
- return mi_Polygon3D;
- }
- return null;
- }
-
- ///
- /// For debugging in Visual Studio
- ///
- public override string ToString()
- {
- return String.Format("cPolygon ({0} points)", mk_Screen.Length);
- }
- }
-
- #endregion
-
- // ----- Math Stuff ------
-
- #region cMouse
-
- private class cMouse
- {
- public eMouseAction me_Action; // left mouse button action
- public Point mk_LastPos; // last mouse location
- public Point mk_OffMove; // Mouse offset after moving the graph with the mouse
- public Point mk_OffCoord; // Offset caused by labels in coordinate system
- public TrackBar mi_TrackRho; // Rho trackbar (optional)
- public TrackBar mi_TrackTheta; // Theta trackbar (optional)
- public TrackBar mi_TrackPhi; // Phi trackbar (optional)
- public double md_Rho = VALUES_RHO .Default;
- public double md_Theta = VALUES_THETA.Default;
- public double md_Phi = VALUES_PHI .Default;
-
- public void AssignTrackbar(eMouseAction e_Trackbar, TrackBar i_Trackbar, EventHandler i_OnScroll)
- {
- if (i_Trackbar == null)
- return;
-
- cDefault i_Default = null;
- switch (e_Trackbar)
- {
- case eMouseAction.Rho:
- i_Default = VALUES_RHO;
- mi_TrackRho = i_Trackbar;
- break;
- case eMouseAction.Theta:
- i_Default = VALUES_THETA;
- mi_TrackTheta = i_Trackbar;
- break;
- case eMouseAction.Phi:
- i_Default = VALUES_PHI;
- mi_TrackPhi = i_Trackbar;
- break;
- }
-
- i_Trackbar.Minimum = (int)i_Default.Min;
- i_Trackbar.Maximum = (int)i_Default.Max;
- i_Trackbar.Value = (int)i_Default.Default;
- i_Trackbar.Scroll += i_OnScroll;
- }
-
- ///
- /// User has moved the TrackBar
- ///
- public void OnTrackBarScroll()
- {
- if (mi_TrackRho != null) md_Rho = mi_TrackRho .Value;
- if (mi_TrackTheta != null) md_Theta = mi_TrackTheta.Value;
- if (mi_TrackPhi != null) md_Phi = mi_TrackPhi .Value;
- }
-
- public bool OnMouseWheel(int s32_Delta)
- {
- if (me_Action != eMouseAction.None)
- return false;
-
- me_Action = eMouseAction.Rho;
- OnMouseMove(0, s32_Delta / 10);
- me_Action = eMouseAction.None;
- return true;
- }
-
- ///
- /// User has dragged the mouse over the 3D control
- ///
- public void OnMouseMove(int s32_DiffX, int s32_DiffY)
- {
- if (me_Action == eMouseAction.Rho)
- {
- md_Rho += s32_DiffY * VALUES_RHO.MouseFactor;
- SetRho(md_Rho);
- }
- if (me_Action == eMouseAction.Theta || me_Action == eMouseAction.ThetaAndPhi)
- {
- md_Theta -= s32_DiffY * VALUES_THETA.MouseFactor;
- SetTheta(md_Theta);
- }
- if (me_Action == eMouseAction.Phi || me_Action == eMouseAction.ThetaAndPhi)
- {
- md_Phi -= s32_DiffX * VALUES_PHI.MouseFactor;
- SetPhi(md_Phi);
- }
- }
-
- public void SetRho(double d_Rho)
- {
- md_Rho = d_Rho;
- md_Rho = Math.Max(md_Rho, VALUES_RHO.Min);
- md_Rho = Math.Min(md_Rho, VALUES_RHO.Max);
- if (mi_TrackRho != null)
- mi_TrackRho.Value = (int)md_Rho;
- }
- public void SetTheta(double d_Theta)
- {
- md_Theta = d_Theta;
- md_Theta = Math.Max(md_Theta, VALUES_THETA.Min);
- md_Theta = Math.Min(md_Theta, VALUES_THETA.Max);
- if (mi_TrackTheta != null)
- mi_TrackTheta.Value = (int)md_Theta;
- }
- public void SetPhi(double d_Phi)
- {
- md_Phi = d_Phi;
- while (md_Phi > 360.0) md_Phi -= 360.0; // continuous rotation
- while (md_Phi < 0.0) md_Phi += 360.0; // continuous rotation
- if (mi_TrackPhi != null)
- mi_TrackPhi.Value = (int)md_Phi;
- }
- }
-
- #endregion
-
- #region cRange
-
- private class cRange
- {
- private double md_Min, md_Max;
-
- public double Min
- {
- get { return md_Min; }
- }
- public double Max
- {
- get { return md_Max; }
- }
- public double Range
- {
- get { return md_Max - md_Min; }
- }
-
- ///
- /// Constructor
- ///
- public cRange(double d_Min, double d_Max, bool b_IncludeZero, eRaster e_Raster)
- {
- md_Min = d_Min;
- md_Max = d_Max;
-
- if (md_Max == md_Min)
- {
- md_Min -= 1.0;
- md_Max += 1.0;
- }
-
- if (e_Raster == eRaster.Off)
- return;
-
- if (b_IncludeZero)
- {
- md_Min = Math.Min(0.0, md_Min);
- md_Max = Math.Max(0.0, md_Max);
- }
-
- // Add 10 % excess to all axes
- if (md_Min < 0.0 || (md_Min > 0.0 && !b_IncludeZero)) md_Min -= Math.Abs(md_Min) * AXIS_EXCESS;
- if (md_Max > 0.0 || (md_Max < 0.0 && !b_IncludeZero)) md_Max += Math.Abs(md_Max) * AXIS_EXCESS;
- }
- }
-
- #endregion
-
- #region cBounds
-
- private class cBounds
- {
- private Editor3D mi_Inst;
- private cRange mi_RangeX;
- private cRange mi_RangeY;
- private cRange mi_RangeZ;
- private double md_MinX, md_MaxX, md_MinY, md_MaxY, md_MinZ, md_MaxZ;
-
- public cRange X
- {
- get { return mi_RangeX; }
- }
- public cRange Y
- {
- get { return mi_RangeY; }
- }
- public cRange Z
- {
- get { return mi_RangeZ; }
- }
-
- ///
- /// Constructor
- ///
- public cBounds(Editor3D i_Inst)
- {
- mi_Inst = i_Inst;
- }
-
- ///
- /// Also assigns mi_Inst to all draw objects
- ///
- public void Calculate()
- {
- md_MinX = double.PositiveInfinity;
- md_MaxX = double.NegativeInfinity;
- md_MinY = double.PositiveInfinity;
- md_MaxY = double.NegativeInfinity;
- md_MinZ = double.PositiveInfinity;
- md_MaxZ = double.NegativeInfinity;
-
- foreach (cDrawObj i_DrawObj in mi_Inst.mi_UserObjects)
- {
- i_DrawObj.mi_Inst = mi_Inst;
- i_DrawObj.mi_Object3D.mi_Inst = mi_Inst;
-
- foreach (cPoint i_Point in i_DrawObj.mi_Points)
- {
- cPoint3D i_Point3D = i_Point.mi_P3D;
- i_Point3D.mi_Inst = mi_Inst;
-
- md_MinX = Math.Min(md_MinX, i_Point3D.X);
- md_MaxX = Math.Max(md_MaxX, i_Point3D.X);
- md_MinY = Math.Min(md_MinY, i_Point3D.Y);
- md_MaxY = Math.Max(md_MaxY, i_Point3D.Y);
- md_MinZ = Math.Min(md_MinZ, i_Point3D.Z);
- md_MaxZ = Math.Max(md_MaxZ, i_Point3D.Z);
- }
- }
-
- mi_RangeX = new cRange(md_MinX, md_MaxX, mi_Inst.AxisX.IncludeZero, mi_Inst.me_Raster);
- mi_RangeY = new cRange(md_MinY, md_MaxY, mi_Inst.AxisY.IncludeZero, mi_Inst.me_Raster);
- mi_RangeZ = new cRange(md_MinZ, md_MaxZ, mi_Inst.AxisZ.IncludeZero, mi_Inst.me_Raster);
- }
-
- ///
- /// Used to get the color from the ColorScheme
- ///
- public double CalcFactorZ(double d_Value)
- {
- return (d_Value - md_MinZ) / (md_MaxZ - md_MinZ);
- }
- }
-
- #endregion
-
- #region cQuadrant
-
- private class cQuadrant
- {
- public double md_SortXY; // Sort order of raster in area XY (red)
- public double md_SortXZ; // Sort order of X axis and raster in area XZ (blue)
- public double md_SortYZ; // Sort order of Y axis and raster in area YZ (green)
- public int ms32_Quadrant;
- public bool mb_BottomView;
-
- public void Calculate(double d_Phi, cLine i_AxisX, cLine i_AxisY, cLine i_AxisZ)
- {
- // Split rotation into 4 sections (0...3) which increment every 90° starting at 45°
- int s32_Section45 = (int)d_Phi + 45;
- if (s32_Section45 > 360) s32_Section45 -= 360;
- s32_Section45 = Math.Min(3, s32_Section45 / 90);
-
- // Theta elevation lets the camera watch the graph from the top or bottom
- switch (s32_Section45)
- {
- case 0: mb_BottomView = i_AxisX.md_Angle < 180.0; break;
- case 1: mb_BottomView = i_AxisY.md_Angle < 180.0; break;
- case 2: mb_BottomView = i_AxisX.md_Angle > 180.0; break;
- case 3: mb_BottomView = i_AxisY.md_Angle > 180.0; break;
- }
-
- // The quadrant changes when the 2D transformed Z axis is in line with the X or Y axis
- if (mb_BottomView)
- {
- switch (s32_Section45)
- {
- case 0: ms32_Quadrant = i_AxisX.md_Angle + 180.0 < i_AxisZ.md_Angle ? 1 : 0; break;
- case 1: ms32_Quadrant = i_AxisY.md_Angle + 180.0 < i_AxisZ.md_Angle ? 2 : 1; break;
- case 2: ms32_Quadrant = i_AxisX.md_Angle < i_AxisZ.md_Angle ? 3 : 2; break;
- case 3: ms32_Quadrant = i_AxisY.md_Angle < i_AxisZ.md_Angle ? 0 : 3; break;
- }
- }
- else // Top View
- {
- switch (s32_Section45)
- {
- case 0: ms32_Quadrant = i_AxisX.md_Angle > i_AxisZ.md_Angle ? 1 : 0; break;
- case 1: ms32_Quadrant = i_AxisY.md_Angle > i_AxisZ.md_Angle ? 2 : 1; break;
- case 2: ms32_Quadrant = i_AxisX.md_Angle + 180.0 > i_AxisZ.md_Angle ? 3 : 2; break;
- case 3: ms32_Quadrant = i_AxisY.md_Angle + 180.0 > i_AxisZ.md_Angle ? 0 : 3; break;
- }
- }
-
- md_SortXY = (mb_BottomView) ? 99999.9 : -99999.9;
- md_SortXZ = (ms32_Quadrant == 1 || ms32_Quadrant == 2) ? 99999.9 : -99999.9;
- md_SortYZ = (ms32_Quadrant == 0 || ms32_Quadrant == 1) ? 99999.9 : -99999.9;
-
- i_AxisX.md_Sort = md_SortXZ;
- i_AxisY.md_Sort = md_SortYZ;
- i_AxisZ.md_Sort = (ms32_Quadrant == 3) ? -99999.9 : 99999.9;
-
- // Debug.WriteLine(String.Format("Section: {0} Quadrant: {1}", s32_Section45, ms32_Quadrant));
- }
- }
-
- #endregion
-
- #region cTransform
-
- private class cTransform
- {
- // Camera distance. Smaller values result in ugly stretched egdes when rotating.
- private const double DISTANCE = 0.55;
-
- private double md_sf; // sf = sinus fi
- private double md_st; // st = sinus theta
- private double md_cf; // cf = cosinus fi
- private double md_ct; // ct = cosinus theta
- private double md_Rho;
- // ----------------
- private double md_FactX;
- private double md_OffsX;
- private double md_FactY;
- private double md_OffsY;
- private double md_Resize = 1.0;
- // ----------------
- public cPoint3D mi_Center3D = new cPoint3D(0,0,0);
- public double md_NormalizeX;
- public double md_NormalizeY;
- public double md_NormalizeZ;
- public double md_Zoom;
- // ----------------
- Size mk_InitialSize = Size.Empty;
- Editor3D mi_Inst;
-
- public cTransform(Editor3D i_Inst)
- {
- mi_Inst = i_Inst;
- }
-
- public void SetCoefficients(cMouse i_Mouse)
- {
- md_Rho = i_Mouse.md_Rho; // Distance of viewer (zoom)
- double d_Theta = i_Mouse.md_Theta * Math.PI / 180.0; // Height of viewer (elevation)
- double d_Phi = (i_Mouse.md_Phi -180.0) * Math.PI / 180.0; // Rotation around center (-Pi ... +Pi)
-
- // Speed optimization: precalculate factors
- md_sf = Math.Sin(d_Phi);
- md_cf = Math.Cos(d_Phi);
- md_st = Math.Sin(d_Theta); // Theta = 0...pi --> st = 0 .. 1 .. 0
- md_ct = Math.Cos(d_Theta); // Theta = 0...pi --> ct = 1 .. 0 .. -1
-
- CalcZoom();
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
- }
-
- ///
- /// The initial size is needed to calculate the user resizing factor.
- /// To assure that it is correct it must be set when the control has already been created.
- /// Then it will be the size that was defined in Visual Studio Form Designer.
- ///
- public void SetInitialSize(Size k_Size)
- {
- mk_InitialSize = k_Size;
- SetSize(k_Size);
- }
-
- ///
- /// The control has been resized.
- /// This may be called with an invalid size before the control is created!
- ///
- public void SetSize(Size k_Size) // Control.ClientSize
- {
- if (mk_InitialSize == Size.Empty)
- return;
-
- double d_Width = k_Size.Width * 0.0254 / 96.0; // 0.0254 meter = 1 inch. Screen has 96 DPI
- double d_Height = k_Size.Height * 0.0254 / 96.0;
-
- // linear transformation coefficients
- md_FactX = k_Size.Width / d_Width;
- md_FactY = -k_Size.Height / d_Height;
-
- md_OffsX = md_FactX * d_Width / 2.0;
- md_OffsY = -md_FactY * d_Height / 2.0;
-
- // -----------------------------------
-
- double d_ResizeX = (double)k_Size.Width / mk_InitialSize.Width;
- double d_ResizeY = (double)k_Size.Height / mk_InitialSize.Height;
- md_Resize = Math.Min(d_ResizeX, d_ResizeY);
-
- md_FactX *= md_Resize;
- md_FactY *= md_Resize;
-
- CalcZoom();
- mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
- }
-
- // Required for correct painting order of polygons (always from back to front)
- public double ProjectXY(double X, double Y, double Z = 0.0)
- {
- return X * md_cf + Y * md_sf + Z * md_ct;
- }
-
- // Used to convert mouse movements back into the 3D space depending on the current rotation angle
- public double ReverseProject(double X, double Y, double Z)
- {
- if (mi_Inst.AxisX.Mirror) X = -X;
- if (mi_Inst.AxisY.Mirror) Y = -Y;
- if (mi_Inst.AxisZ.Mirror) Z = -Z;
-
- // If Theta has the correct range from 10 to 170 degree --> Sinus(Theta) will never become zero.
- // This can only happen if VALUES_THETA has been manipulated to invalid Min/Max values.
- double d_Divide = Math.Max(0.1, md_st);
- return (-X * md_sf + Y * md_cf + Z / d_Divide) / md_Zoom;
- }
-
- ///
- /// This approximates a zoom factor that depends on Rho and the resize window factor.
- /// Used to adapt the size of lines, shapes and selected points.
- ///
- private void CalcZoom()
- {
- md_Zoom = md_Resize * (1800.0 / (md_Rho + 300));
- }
-
- // Performs projection. Calculates 2D screen coordinates from 3D point.
- public cPoint2D Project3D(cPoint3D i_Point3D, eMirror e_Mirror)
- {
- double X = i_Point3D.X;
- double Y = i_Point3D.Y;
- double Z = i_Point3D.Z;
-
- // Mirror axis values increasing / decreasing
- if (mi_Inst.AxisX.Mirror && (e_Mirror & eMirror.X) > 0) X = mi_Inst.mi_Bounds.X.Max - (X - mi_Inst.mi_Bounds.X.Min);
- if (mi_Inst.AxisY.Mirror && (e_Mirror & eMirror.Y) > 0) Y = mi_Inst.mi_Bounds.Y.Max - (Y - mi_Inst.mi_Bounds.Y.Min);
- if (mi_Inst.AxisZ.Mirror && (e_Mirror & eMirror.Z) > 0) Z = mi_Inst.mi_Bounds.Z.Max - (Z - mi_Inst.mi_Bounds.Z.Min);
-
- X = (X - mi_Center3D.X) * md_NormalizeX;
- Y = (Y - mi_Center3D.Y) * md_NormalizeY;
- Z = (Z - mi_Center3D.Z) * md_NormalizeZ;
-
- // 3D coordinates with center point in the middle of the screen
- // X positive to the right, X negative to the left
- // Y positive to the top, Y negative to the bottom
- double xn = -md_sf * X + md_cf * Y;
- double yn = -md_cf * md_ct * X - md_sf * md_ct * Y + md_st * Z;
- double zn = -md_cf * md_st * X - md_sf * md_st * Y - md_ct * Z + md_Rho;
-
- zn = Math.Max(zn, 0.01); // avoid division by zero
-
- // Thales' theorem
- cPoint2D i_Point2D = new cPoint2D(xn * DISTANCE / zn, yn * DISTANCE / zn);
-
- i_Point2D.md_X = i_Point2D.md_X * md_FactX + md_OffsX;
- i_Point2D.md_Y = i_Point2D.md_Y * md_FactY + md_OffsY;
- return i_Point2D;
- }
- }
-
- #endregion
-
- #region cDefault
-
- ///
- /// Stores defauls for Rho, Theta, Phi
- ///
- private class cDefault
- {
- public readonly double Min;
- public readonly double Max;
- public readonly double Default;
- public readonly double MouseFactor;
-
- public cDefault(double d_Min, double d_Max, double d_Default, double d_MouseFactor)
- {
- Min = d_Min;
- Max = d_Max;
- Default = d_Default;
- MouseFactor = d_MouseFactor;
- }
- }
-
- #endregion
-
- // Limits and default values for mouse actions and trackbars.
- // ATTENTION: It is strongly recommended not to change the MIN, MAX values.
- // The mouse factor defines how much mouse movement you need for a change.
- // A movement of mouse by approx 1000 pixels on the screen results in getting from Min to Max or vice versa.
- static readonly cDefault VALUES_RHO = new cDefault(300, 1800, 1350, 2 );
- static readonly cDefault VALUES_THETA = new cDefault( 10, 170, 70, 0.25); // degree
- static readonly cDefault VALUES_PHI = new cDefault( 0, 360, 230, 0.4 ); // degree (continuous rotation)
-
- // The coordinate axes are 10 % longer than the bounds of the X,Y,Z values
- const double AXIS_EXCESS = 0.1;
-
- // For any strange reason the graph is not centered vertically
- const int VERT_OFFSET = -30;
-
- // The maximum distance between mouse pointer and a 2D point to display the tooltip
- const int TOOLTIP_RADIUS = 6;
-
- // The maximum distance between mouse pointer and a 2D point to allow a match when selecting a 3D object.
- const int SELECT_RADIUS = 3;
-
- // Calculate 3-dimensional Z value from X,Y values
- public delegate double delRendererFunction(double X, double Y);
-
- // IMPORTANT: Read the detailed comment of function SelectionCallback() at the end of this class.
- public delegate eInvalidate delSelectHandler(eSelEvent e_Event, Keys e_Modifiers, int s32_DeltaX, int s32_DeltaY, cObject3D i_Object);
-
- Pen[] mi_BorderPens = new Pen[2];
- SolidBrush mi_TopLegendBrush = null;
- eRaster me_Raster = eRaster.Labels;
- cAxis[] mi_Axis = new cAxis[3];
- cMouse mi_Mouse = new cMouse();
- List mi_MessageData = new List();
- eRecalculate me_Recalculate = eRecalculate.Nothing;
- eNormalize me_Normalize = eNormalize.Separate;
- eLegendPos me_LegendPos = eLegendPos.BottomLeft;
- List mi_AxisLines = new List(); // 0, 3, or 45 axis lines of coordinate system
- List mi_UserObjects = new List(); // Draw objects from the user (cLine, cShape, cPolygon)
- List mi_AllObjects = new List(); // mi_UserObjects + mi_AxisLines
- cQuadrant mi_Quadrant = new cQuadrant();
- Dictionary mi_UserInputs = new Dictionary();
- cUndoImpl mi_UndoBuffer;
- cTransform mi_Transform;
- cBounds mi_Bounds;
- cTooltip mi_Tooltip;
- cSelection mi_Selection;
- cObject3D mi_DragObject;
-
- #region Properties
-
- ///
- /// See comment of enum eTooltip.
- /// This property can also be set in the Visual Studio Designer
- ///
- public eTooltip TooltipMode
- {
- get { return mi_Tooltip.Mode; }
- set { mi_Tooltip.Mode = value; }
- }
-
- ///
- /// See comment of enum eLegendPos.
- /// This property can also be set in the Visual Studio Designer
- ///
- public eLegendPos LegendPos
- {
- get { return me_LegendPos; }
- set { me_LegendPos = value; }
- }
-
- ///
- /// See comment of enum eNormalize.
- /// This change will become visible the next time you call Invalidate()
- ///
- public eNormalize Normalize
- {
- get { return me_Normalize; }
- set
- {
- if (me_Normalize != value)
- {
- me_Normalize = value;
- me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
- }
- }
- }
-
- ///
- /// See comment of enum eRaster
- /// This property can also be set in the Visual Studio Designer
- /// This change will become visible the next time you call Invalidate()
- ///
- public eRaster Raster
- {
- set
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- if (me_Raster != value)
- {
- me_Raster = value;
- me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
- }
- }
- get
- {
- return me_Raster;
- }
- }
-
- ///
- /// Sets the border color when the 3D Editor does not have the keyboard focus
- /// Setting BorderColor = Color.Empty turns off the border
- /// This change will become visible the next time you call Invalidate()
- /// This property can also be set in the Visual Studio Designer
- ///
- public Color BorderColorNormal
- {
- set
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- if (value.A > 0) mi_BorderPens[0] = new Pen(value, 1);
- else mi_BorderPens[0] = null; // transparent color
- }
- get
- {
- if (mi_BorderPens[0] != null) return mi_BorderPens[0].Color;
- else return Color.Empty;
- }
- }
-
- ///
- /// Sets the border color when the 3D Editor has the keyboard focus
- /// Setting BorderColorFocus = Color.Empty turns off the highlighting on focus.
- /// This change will become visible the next time you call Invalidate()
- /// This property can also be set in the Visual Studio Designer
- ///
- public Color BorderColorFocus
- {
- set
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- if (value.A > 0) mi_BorderPens[1] = new Pen(value, 1);
- else mi_BorderPens[1] = mi_BorderPens[0];
- }
- get
- {
- if (mi_BorderPens[1] != null) return mi_BorderPens[1].Color;
- else return BorderColorNormal;
- }
- }
-
- ///
- /// Show a legend with Rotation, Elevation and Distance at the top left
- /// Setting LegendColor = Color.Empty turns off the top legend
- /// This property can also be set in the Visual Studio Designer
- /// This change will become visible the next time you call Invalidate()
- ///
- public Color TopLegendColor
- {
- set
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- mi_TopLegendBrush = new SolidBrush(value);
- }
- get
- {
- if (mi_TopLegendBrush != null) return mi_TopLegendBrush.Color;
- else return Color.Empty;
- }
- }
-
- ///
- /// returns the total count of loaded draw objects (lines, shapes and polygons)
- ///
- [Browsable(false)]
- public String ObjectStatistics
- {
- get
- {
- int s32_Lines = 0;
- int s32_Shapes = 0;
- int s32_Polygons = 0;
- foreach (cDrawObj i_Obj in mi_UserObjects)
- {
- if (i_Obj is cLine) s32_Lines ++;
- if (i_Obj is cShape) s32_Shapes ++;
- if (i_Obj is cPolygon) s32_Polygons ++;
- }
- StringBuilder i_Out = new StringBuilder();
- if (s32_Lines > 0) i_Out.Append(s32_Lines + " Lines, ");
- if (s32_Shapes > 0) i_Out.Append(s32_Shapes + " Shapes, ");
- if (s32_Polygons > 0) i_Out.Append(s32_Polygons + " Polygons, ");
- return i_Out.ToString().TrimEnd(' ', ',');
- }
- }
-
- ///
- /// See comments for class cAxis
- /// The properties of the class cAxis can be expanded in the Visual Studio designer
- ///
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public cAxis AxisX
- {
- get { return mi_Axis[(int)eCoord.X]; }
- }
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public cAxis AxisY
- {
- get { return mi_Axis[(int)eCoord.Y]; }
- }
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public cAxis AxisZ
- {
- get { return mi_Axis[(int)eCoord.Z]; }
- }
-
- ///
- /// This property controls if and how the user can select draw objects / points
- /// The properties of the class cSelection can be expanded in the Visual Studio designer
- ///
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public cSelection Selection
- {
- get { return mi_Selection; }
- }
-
- ///
- /// This property contains the public methods for the Undo / Redo buffer
- /// The property 'Enabled' can be expanded in the Visual Studio designer
- ///
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public cUndoBuffer UndoBuffer
- {
- get { return mi_UndoBuffer; }
- }
-
- #endregion
-
- ///
- /// b_ResetOffset = true --> reset the offset that the user has created with SHIFT + moving the 3D object
- /// This change will become visible the next time you call Invalidate()
- ///
- public void SetCoefficients(double d_Rho, double d_Theta, double d_Phi, bool b_ResetOffset = true)
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- mi_Mouse.SetRho (d_Rho);
- mi_Mouse.SetTheta(d_Theta);
- mi_Mouse.SetPhi (d_Phi);
-
- if (b_ResetOffset)
- {
- mi_Mouse.mk_OffMove.X = 0;
- mi_Mouse.mk_OffMove.Y = 0;
- }
-
- mi_Transform.SetCoefficients(mi_Mouse);
- }
-
- ///
- /// Convert mouse movement in 2D space back into the 3D space depending on the current rotation angle and Min/Max values.
- ///
- public cPoint3D ReverseProject(int s32_MouseX, int s32_MouseY)
- {
- double d_FactX = mi_Transform.ReverseProject(mi_Bounds.X.Range, 0.0, 0.0);
- double d_FactY = mi_Transform.ReverseProject(0.0, mi_Bounds.Y.Range, 0.0);
- double d_FactZ = mi_Transform.ReverseProject(0.0, 0.0, mi_Bounds.Z.Range);
-
- return new cPoint3D(d_FactX * s32_MouseX / 300.0,
- d_FactY * s32_MouseX / 300.0,
- d_FactZ * s32_MouseY / 300.0);
- }
-
- ///
- /// Trackbars are optional for user interaction.
- /// If this function is never called thetrackbars are not used.
- ///
- public void AssignTrackBars(TrackBar i_Rho, TrackBar i_Theta, TrackBar i_Phi)
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- mi_Mouse.AssignTrackbar(eMouseAction.Rho, i_Rho, new EventHandler(OnTrackbarScroll));
- mi_Mouse.AssignTrackbar(eMouseAction.Theta, i_Theta, new EventHandler(OnTrackbarScroll));
- mi_Mouse.AssignTrackbar(eMouseAction.Phi, i_Phi, new EventHandler(OnTrackbarScroll));
- }
-
- ///
- /// Load one of the three pre-defined input control patterns
- ///
- public void SetUserInputs(eMouseCtrl e_MouseCtrl)
- {
- List i_Inputs = new List();
-
- switch (e_MouseCtrl)
- {
- case eMouseCtrl.L_Theta_R_Phi:
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.None, eMouseAction.Theta));
- i_Inputs.Add(new cUserInput(MouseButtons.Right, Keys.None, eMouseAction.Phi));
- break;
- case eMouseCtrl.L_Theta_L_Phi:
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.None, eMouseAction.ThetaAndPhi));
- break;
- case eMouseCtrl.M_Theta_M_Phi:
- i_Inputs.Add(new cUserInput(MouseButtons.Middle, Keys.None, eMouseAction.ThetaAndPhi));
- break;
- }
-
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Control, eMouseAction.Rho));
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Shift, eMouseAction.Move));
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Alt, eMouseAction.SelectObj));
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Alt | Keys.Control, eMouseAction.Callback));
- i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Alt | Keys.Shift, eMouseAction.Callback));
-
- SetUserInputs(i_Inputs.ToArray());
- }
-
- ///
- /// Load fully user defined input control patterns.
- /// Each user input must define a unique combination of mouse button and modifier key(s).
- ///
- public void SetUserInputs(cUserInput[] i_Inputs)
- {
- mi_UserInputs.Clear();
- foreach (cUserInput i_Input in i_Inputs)
- {
- // throws if same UID has already been added
- mi_UserInputs.Add(i_Input.UID, i_Input);
- }
- }
-
- // ==================================================================================
-
- ///
- /// Constructor
- ///
- public Editor3D()
- {
+ {
+ cShape3D i_Shape3D = new cShape3D(-1, -1, i_Point, e_Shape, s32_Radius, i_Brush, o_Tag);
+ mi_Shapes3D.Add(i_Shape3D);
+ return i_Shape3D;
+ }
+
+ // =============================================================================
+
+ ///
+ /// Called from AddRenderData()
+ ///
+ public override void AddDrawObjects(Editor3D i_Inst)
+ {
+ foreach (cShape3D i_Shape3D in mi_Shapes3D)
+ {
+ i_Inst.AddDrawObject(new cShape(i_Shape3D, mi_ColorScheme));
+ }
+ }
+ }
+
+ #endregion cScatterData
+
+ #region cLineData
+
+ public class cLineData : cRenderData
+ {
+ private List mi_Lines3D = new List();
+ private cColorScheme mi_ColorScheme;
+
+ public cLine3D[] AllLines
+ {
+ get { return mi_Lines3D.ToArray(); }
+ }
+
+ public cColorScheme ColorScheme
+ {
+ get { return mi_ColorScheme; }
+ }
+
+ ///
+ /// Constructor If you use only solid lines and specify a valid Pen, you can pass
+ /// i_ColorScheme == null here
+ ///
+ public cLineData(cColorScheme i_ColorScheme)
+ {
+ mi_ColorScheme = i_ColorScheme;
+ }
+
+ ///
+ /// Add a line which will be drawn entirely in one color.
+ ///
+ public cLine3D AddSolidLine(cPoint3D i_Start, cPoint3D i_End, int s32_Width, Pen i_Pen, Object o_Tag = null)
+ {
+ CheckPenMutable(i_Pen, mi_ColorScheme);
+
+ cLine3D i_Line3D = new cLine3D(i_Start, i_End, s32_Width, i_Pen, 1, o_Tag);
+ mi_Lines3D.Add(i_Line3D);
+ return i_Line3D;
+ }
+
+ ///
+ /// Add a line which will appear with multiple colors of the ColorScheme by drawing it
+ /// in multiple parts. If s32_Parts = 50, the line is rendered in 50 parts where each
+ /// part has it's own color depending on the Z coordinate.
+ ///
+ public cLine3D AddMultiColorLine(int s32_Parts, cPoint3D i_Start, cPoint3D i_End, int s32_Width, Pen i_Pen, Object o_Tag = null)
+ {
+ if (s32_Parts < 3)
+ throw new ArgumentException("Multi color lines require at least 3 parts");
+
+ if (mi_ColorScheme == null)
+ throw new Exception("To create a multi-color line you must specify a ColorScheme");
+
+ CheckPenMutable(i_Pen, mi_ColorScheme);
+
+ cLine3D i_Line3D = new cLine3D(i_Start, i_End, s32_Width, i_Pen, s32_Parts, o_Tag);
+ mi_Lines3D.Add(i_Line3D);
+ return i_Line3D;
+ }
+
+ ///
+ /// Creates connected lines from the points in the given order
+ ///
+ public cLine3D[] AddConnectedLines(List i_Points, int s32_Width, Pen i_Pen)
+ {
+ CheckPenMutable(i_Pen, mi_ColorScheme);
+
+ List i_NewLines = new List();
+
+ cPoint3D i_Prev = null;
+ for (int i = 0; i < i_Points.Count; i++)
+ {
+ cPoint3D i_Point = i_Points[i];
+ if (i_Prev != null)
+ {
+ cLine3D i_Line3D = new cLine3D(i_Prev, i_Point, s32_Width, i_Pen, 1);
+ i_NewLines.Add(i_Line3D);
+ mi_Lines3D.Add(i_Line3D);
+ }
+ i_Prev = i_Point;
+ }
+ return i_NewLines.ToArray();
+ }
+
+ // =============================================================================
+
+ ///
+ /// Called from AddRenderData()
+ ///
+ public override void AddDrawObjects(Editor3D i_Inst)
+ {
+ foreach (cLine3D i_Line3D in mi_Lines3D)
+ {
+ i_Inst.AddDrawObject(new cLine(i_Line3D, mi_ColorScheme));
+ }
+ }
+ }
+
+ #endregion cLineData
+
+ #region cPolygonData
+
+ public class cPolygonData : cRenderData
+ {
+ private bool mb_Fill;
+ private Pen mi_Pen;
+ private cColorScheme mi_ColorScheme;
+ private List mi_Polygons3D = new List();
+
+ public cPolygon3D[] AllPolygons
+ {
+ get { return mi_Polygons3D.ToArray(); }
+ }
+
+ public cColorScheme ColorScheme
+ {
+ get { return mi_ColorScheme; }
+ }
+
+ ///
+ /// Fill Mode: ------------ Polygons are filled with a color from the ColorScheme. If
+ /// you want only one color, set a ColorScheme which contains only one color. The Pen is
+ /// used to draw the thin lines between the polygons (mostly black, 1 pixel) If Pen is
+ /// null, no lines are drawn.
+ ///
+ /// Line Mode: ------------ Only the border lines of the polygons are drawn. The Pen is
+ /// used to draw these lines. The Pen's color and width will be modified.
+ ///
+ public cPolygonData(ePolygonMode e_Mode, Pen i_Pen, cColorScheme i_ColorScheme)
+ {
+ if (e_Mode == ePolygonMode.Lines)
+ {
+ if (i_Pen == null)
+ throw new ArgumentException("In Line mode you must specify a Line Pen");
+
+ CheckPenMutable(i_Pen, i_ColorScheme);
+ }
+
+ mb_Fill = e_Mode == ePolygonMode.Fill;
+ mi_Pen = i_Pen;
+ mi_ColorScheme = i_ColorScheme;
+ }
+
+ ///
+ /// In contrast to other drawing libraries (like WPF or Direct3D) you can add polygons
+ /// of any dimension here. A polygon can have any amount of corners (minimum 3). The
+ /// Brush can be specified in Fill mode. To use a Brush from the ColorScheme set i_Brush
+ /// = null.
+ ///
+ public cPolygon3D AddPolygon(Brush i_Brush, params cPoint3D[] i_Points3D)
+ {
+ cPolygon3D i_Polygon3D = new cPolygon3D(-1, -1, i_Brush, i_Points3D);
+ mi_Polygons3D.Add(i_Polygon3D);
+ return i_Polygon3D;
+ }
+
+ // =============================================================================
+
+ ///
+ /// Called from AddRenderData()
+ ///
+ public override void AddDrawObjects(Editor3D i_Inst)
+ {
+ foreach (cPolygon3D i_Polygon3D in mi_Polygons3D)
+ {
+ i_Inst.AddDrawObject(new cPolygon(mb_Fill, i_Polygon3D, mi_Pen, mi_ColorScheme));
+ }
+ }
+ }
+
+ #endregion cPolygonData
+
+ #region cMessgData
+
+ public class cMessgData
+ {
+ private String ms_Text;
+ private Brush mi_Brush;
+ private int ms32_PosX;
+ private int ms32_PosY;
+ private Font mi_Font;
+ private SizeF mk_Size;
+
+ ///
+ /// Here you can change the text without loading all the render objects again. The
+ /// change will become visible the next time you call Invalidate()
+ ///
+ public String Text
+ {
+ set
+ {
+ ms_Text = value;
+ mk_Size = SizeF.Empty;
+ }
+ }
+
+ ///
+ /// Here you can change the text color without loading all the render objects again. The
+ /// change will become visible the next time you call Invalidate()
+ ///
+ public Color TextColor
+ {
+ set { mi_Brush = new SolidBrush(value); }
+ }
+
+ ///
+ /// If X is negative, it is displayed right aligned at X pixels from the right If Y is
+ /// negative, it is displayed bottom aligned at Y pixels from the bottom
+ ///
+ public cMessgData(String s_Text, int X, int Y, Color c_Color,
+ FontStyle e_FontStyle = FontStyle.Bold,
+ int s32_FontSize = 9,
+ String s_FontFace = "Tahoma")
+ {
+ ms_Text = s_Text;
+ ms32_PosX = X;
+ ms32_PosY = Y;
+ mi_Brush = new SolidBrush(c_Color);
+ mi_Font = new Font(s_FontFace, s32_FontSize, e_FontStyle);
+ }
+
+ public void Draw(Graphics i_Graph, Rectangle k_Client)
+ {
+ if (String.IsNullOrEmpty(ms_Text))
+ return;
+
+ float X = ms32_PosX;
+ float Y = ms32_PosY;
+
+ if (X < 0 || Y < 0)
+ {
+ // Speed optimization: Measure the size only once.
+ if (mk_Size.IsEmpty)
+ mk_Size = i_Graph.MeasureString(ms_Text, mi_Font);
+
+ if (X < 0) X += k_Client.Width - mk_Size.Width;
+ if (Y < 0) Y += k_Client.Height - mk_Size.Height;
+ }
+
+ i_Graph.DrawString(ms_Text, mi_Font, mi_Brush, X, Y);
+ }
+ }
+
+ #endregion cMessgData
+
+ // ================= PRIVATE =================
+
+ #region cPoint2D
+
+ ///
+ /// This class represents a point in the 2D space, in pixels.
+ ///
+ private class cPoint2D
+ {
+ public double md_X;
+ public double md_Y;
+
+ public cPoint2D()
+ {
+ }
+
+ public cPoint2D(double X, double Y)
+ {
+ md_X = X;
+ md_Y = Y;
+ }
+
+ public cPoint2D Clone()
+ {
+ return new cPoint2D(md_X, md_Y);
+ }
+
+ public PointF Coord
+ {
+ get { return new PointF((float)md_X, (float)md_Y); }
+ }
+
+ public bool IsValid
+ {
+ get
+ {
+ // The screen will always be smaller than 9999 pixels
+ return (!Double.IsNaN(md_X) && Math.Abs(md_X) < 9999.9 &&
+ !Double.IsNaN(md_Y) && Math.Abs(md_Y) < 9999.9);
+ }
+ }
+
+ ///
+ /// Use the good old Pythagoras to calculate the pixel distance between this point and
+ /// X, Y.
+ ///
+ public int CalcDistanceTo(int X, int Y)
+ {
+ int s32_DiffX = (int)md_X - X;
+ int s32_DiffY = (int)md_Y - Y;
+ return (int)Math.Sqrt(s32_DiffX * s32_DiffX + s32_DiffY * s32_DiffY);
+ }
+
+ // For debugging in Visual Studio
+ public override string ToString()
+ {
+ return String.Format("cPoint2D (X={0}, Y={1})", FormatDouble(md_X), FormatDouble(md_Y));
+ }
+ }
+
+ #endregion cPoint2D
+
+ #region cPoint
+
+ ///
+ /// This class contains the 3D point and it's projection into the 2D space.
+ ///
+ private class cPoint
+ {
+ public cPoint3D mi_P3D;
+ public cPoint2D mi_P2D;
+ public int ms32_RadiusTip; // if 0 --> no tooltip
+
+ public cPoint(double d_X, double d_Y, double d_Z)
+ {
+ mi_P3D = new cPoint3D(d_X, d_Y, d_Z);
+ mi_P2D = new cPoint2D();
+ }
+
+ ///
+ /// The radius defines at which distance of the mouse from the 2D point the tooltip pops
+ /// up. Radius = 0 --> no tooltip
+ ///
+ public cPoint(cPoint3D i_Point3D, int s32_RadiusTip)
+ {
+ mi_P3D = i_Point3D;
+ mi_P2D = new cPoint2D();
+
+ if (s32_RadiusTip > 0)
+ ms32_RadiusTip = s32_RadiusTip + TOOLTIP_RADIUS;
+ }
+
+ ///
+ /// Projects the 3D coordinates into the 2D space (pixels on the screen).
+ ///
+ public void Project3D(Editor3D i_Inst, eMirror e_Mirror)
+ {
+ mi_P2D = i_Inst.mi_Transform.Project3D(mi_P3D, e_Mirror);
+ }
+
+ ///
+ /// For Debugging in Visual Studio
+ ///
+ public override string ToString()
+ {
+ return String.Format("cPoint [{0} = {1}]", mi_P3D, mi_P2D);
+ }
+ }
+
+ #endregion cPoint
+
+ #region cTooltip
+
+ private class cTooltip
+ {
+ private Editor3D mi_Inst = null;
+ private eTooltip me_Mode = eTooltip.All;
+ private ToolTip mi_Tooltip = new ToolTip();
+ private List mi_Points = new List();
+ private cPoint mi_Last = null;
+
+ public eTooltip Mode
+ {
+ get { return me_Mode; }
+ set { me_Mode = value; }
+ }
+
+ // Constructor
+ public cTooltip(Editor3D i_Inst)
+ {
+ mi_Inst = i_Inst;
+
+ mi_Tooltip.AutoPopDelay = 30000; // The maximum that Windows allows are 32.767 seconds (0x7FFF milliseconds)
+ mi_Tooltip.InitialDelay = 50;
+ mi_Tooltip.ReshowDelay = 50;
+ }
+
+ public void Clear()
+ {
+ mi_Points.Clear();
+ mi_Last = null;
+ Hide();
+ }
+
+ public void AddPoint(cPoint i_Point)
+ {
+ // ATTENTION adding a && !mi_Points.Contains(i_Point) here would make this function
+ // 40 times slower! The more points are already in mi_Points, the slower it would
+ // become. Several points will be added multiple times here, but this does not
+ // affect the functioning of the tooltip.
+ if (i_Point != null && i_Point.ms32_RadiusTip > 0)
+ mi_Points.Add(i_Point);
+ }
+
+ public void Hide()
+ {
+ mi_Tooltip.Hide(mi_Inst);
+ }
+
+ public void OnMouseMove(MouseEventArgs e)
+ {
+ if (me_Mode == eTooltip.Off)
+ {
+ Hide();
+ return;
+ }
+
+ int s32_MouseX = e.X - mi_Inst.mi_Mouse.mk_OffMove.X - mi_Inst.mi_Mouse.mk_OffCoord.X;
+ int s32_MouseY = e.Y - mi_Inst.mi_Mouse.mk_OffMove.Y - mi_Inst.mi_Mouse.mk_OffCoord.Y;
+
+ int s32_MinDist = int.MaxValue;
+ cPoint i_Nearest = null;
+ foreach (cPoint i_Point in mi_Points)
+ {
+ int s32_Dist = i_Point.mi_P2D.CalcDistanceTo(s32_MouseX, s32_MouseY);
+ if (s32_Dist < s32_MinDist)
+ {
+ s32_MinDist = s32_Dist;
+ i_Nearest = i_Point;
+ }
+ }
+
+ if (i_Nearest != null && s32_MinDist < i_Nearest.ms32_RadiusTip)
+ {
+ if (mi_Last == i_Nearest)
+ return; // The mouse is still over the same point
+
+ mi_Last = i_Nearest;
+
+ String s_TT = "";
+ if ((me_Mode & eTooltip.Coord) > 0)
+ s_TT = String.Format("X = {0}\nY = {1}\nZ = {2}\n", FormatDouble(i_Nearest.mi_P3D.X),
+ FormatDouble(i_Nearest.mi_P3D.Y),
+ FormatDouble(i_Nearest.mi_P3D.Z));
+
+ if ((me_Mode & eTooltip.UserText) > 0 && i_Nearest.mi_P3D.Tooltip != null)
+ s_TT += i_Nearest.mi_P3D.Tooltip;
+
+ s_TT = s_TT.Trim();
+ if (s_TT.Length > 0)
+ {
+ mi_Tooltip.Show(s_TT, mi_Inst, e.X + 10, e.Y + 10);
+ return;
+ }
+ }
+
+ mi_Last = null;
+ Hide();
+ }
+ }
+
+ #endregion cTooltip
+
+ #region cBackup
+
+ private class cBackup
+ {
+ public Dictionary mi_Changes = new Dictionary();
+
+ public void StoreProperty(eUndoProp e_Property, Object o_OldValue, Object o_NewValue)
+ {
+ Object[] o_Values;
+ if (mi_Changes.TryGetValue(e_Property, out o_Values))
+ {
+ // The property has changed multiple times (dragging) --> store only the last value
+ o_Values[1] = o_NewValue;
+ }
+ else
+ {
+ // The property has changed for the first time
+ mi_Changes.Add(e_Property, new Object[] { o_OldValue, o_NewValue });
+ }
+ }
+
+ public void Restore(cObject3D i_Object3D, bool b_Undo)
+ {
+ foreach (KeyValuePair i_Pair in mi_Changes)
+ {
+ if (b_Undo) i_Object3D.Restore(i_Pair.Key, i_Pair.Value[0]); // Undo: old value
+ else i_Object3D.Restore(i_Pair.Key, i_Pair.Value[1]); // Redo: new value
+ }
+ }
+
+ // for debugging in Visual Studio
+ public override string ToString()
+ {
+ StringBuilder i_Dbg = new StringBuilder();
+ foreach (eUndoProp e_Property in mi_Changes.Keys)
+ {
+ if (i_Dbg.Length > 0) i_Dbg.Append(", ");
+ i_Dbg.Append(e_Property);
+ }
+ return "cBackup Properties: " + i_Dbg.ToString();
+ }
+ }
+
+ #endregion cBackup
+
+ #region cUndoEntry
+
+ private class cUndoEntry
+ {
+ public Dictionary mi_Backups = new Dictionary();
+ public List mi_Added = new List();
+ public List mi_Removed = new List();
+
+ public bool IsEmpty
+ {
+ get { return mi_Backups.Count == 0 && mi_Added.Count == 0 && mi_Removed.Count == 0; }
+ }
+
+ // for debugging in Visual Studio
+ public override string ToString()
+ {
+ return String.Format("Backups: {0}, Added: {1}, Removed: {2}", (mi_Backups == null) ? 0 : mi_Backups.Count,
+ (mi_Added == null) ? 0 : mi_Added.Count,
+ (mi_Removed == null) ? 0 : mi_Removed.Count);
+ }
+ }
+
+ #endregion cUndoEntry
+
+ #region cUndoImpl
+
+ ///
+ /// This private class stores the changes after each edit operation that the user has made.
+ ///
+ private class cUndoImpl : cUndoBuffer
+ {
+ private Editor3D mi_Inst;
+ private int ms32_UndoIdx = 0; // Points to the next Undo item in mi_UndoList
+ private int ms32_RedoIdx = 0; // Points to the next Redo item in mi_UndoList
+ private bool mb_Enabled = false; // The Undo Buffer is by default disabled
+ private bool mb_DoInit = false; // After Clear() execute Init() once.
+ private bool mb_Pending = false; // User actions are pending that have not yet been stored
+ private cUndoEntry mi_CurUndo = new cUndoEntry();
+ private List mi_UndoList = new List();
+
+ ///
+ /// Do not enable the Undo buffer if you don't need the Undo functionality. By default
+ /// it is disabled.
+ ///
+ public override bool Enabled
+ {
+ get { return mb_Enabled; }
+ set
+ {
+ Clear();
+ mb_Enabled = value;
+ }
+ }
+
+ ///
+ /// Constructor
+ ///
+ public cUndoImpl(Editor3D i_Inst)
+ {
+ mi_Inst = i_Inst;
+ }
+
+ public override void Clear()
+ {
+ ms32_UndoIdx = 0;
+ ms32_RedoIdx = 0;
+ mb_DoInit = true;
+ mb_Pending = false;
+ mi_CurUndo = new cUndoEntry();
+ mi_UndoList.Clear();
+ }
+
+ ///
+ /// This must only be called from Editor3D.Render() ! The first time calling
+ /// Invalidate() after Clear() --> clear all draw objets that have been added.
+ /// Otherwise all added objects would be stored. (This class stores only user changes.)
+ ///
+ public void Init()
+ {
+ if (mb_DoInit)
+ {
+ Clear();
+ mb_DoInit = false;
+ }
+ }
+
+ ///
+ /// Called when any public property of a 3D object was changed (e.g. X,Y,Z coordinates
+ /// of a cPoint3D). These changes will be stored when Store() is called.
+ ///
+ public void Backup(cObject3D i_Object3D, eUndoProp e_Property, Object o_OldValue, Object o_NewValue)
+ {
+ if (!mb_Enabled)
+ return;
+
+ cBackup i_Backup;
+ if (!mi_CurUndo.mi_Backups.TryGetValue(i_Object3D, out i_Backup))
+ {
+ i_Backup = new cBackup();
+ mi_CurUndo.mi_Backups.Add(i_Object3D, i_Backup);
+ }
+
+ i_Backup.StoreProperty(e_Property, o_OldValue, o_NewValue);
+ mb_Pending = true;
+ }
+
+ ///
+ /// Called when the user removes a draw object. These changes will be stored when
+ /// Store() is called.
+ ///
+ public void DrawObjectRemoved(cDrawObj i_DrawObject)
+ {
+ if (!mb_Enabled)
+ return;
+
+ mi_CurUndo.mi_Removed.Add(i_DrawObject);
+ mb_Pending = true;
+ }
+
+ ///
+ /// Called when the user adds a draw object. These changes will be stored when Store()
+ /// is called.
+ ///
+ public void DrawObjectAdded(cDrawObj i_DrawObject)
+ {
+ if (!mb_Enabled)
+ return;
+
+ mi_CurUndo.mi_Added.Add(i_DrawObject);
+ mb_Pending = true;
+ }
+
+ ///
+ /// Store the changes on user objects in the Undo buffer on mouse-up.
+ ///
+ public void Store()
+ {
+ if (!mb_Enabled || !mb_Pending || mi_CurUndo.IsEmpty)
+ return;
+
+ // Remove any entries in the Undo Buffer behind the current index
+ int s32_Remove = mi_UndoList.Count - ms32_UndoIdx - 1;
+ if (s32_Remove > 0)
+ mi_UndoList.RemoveRange(ms32_UndoIdx + 1, s32_Remove);
+
+ mi_UndoList.Add(mi_CurUndo);
+ mi_CurUndo = new cUndoEntry();
+
+ ms32_UndoIdx = mi_UndoList.Count - 1;
+ ms32_RedoIdx = mi_UndoList.Count - 1;
+ mb_Pending = false;
+ }
+
+ ///
+ /// This is also called when hitting CTRL + Z while the 3D Editor has the keyboard focus
+ ///
+ public override bool Undo()
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+
+ if (!mb_Enabled || mi_UndoList.Count == 0 || ms32_UndoIdx < 0)
+ return false;
+
+ Restore(true, ms32_UndoIdx);
+
+ ms32_RedoIdx = ms32_UndoIdx;
+ ms32_UndoIdx--;
+ return true;
+ }
+
+ ///
+ /// This is also called when hitting CTRL + Y while the 3D Editor has the keyboard focus
+ ///
+ public override bool Redo()
+ {
+ Debug.Assert(!mi_Inst.InvokeRequired); // Call only from GUI thread
+
+ if (!mb_Enabled || mi_UndoList.Count == 0 || ms32_RedoIdx >= mi_UndoList.Count)
+ return false;
+
+ Restore(false, ms32_RedoIdx);
+
+ ms32_UndoIdx = ms32_RedoIdx;
+ ms32_RedoIdx++;
+ return true;
+ }
+
+ private void Restore(bool b_Undo, int s32_Index)
+ {
+ s32_Index = Math.Max(s32_Index, 0);
+ s32_Index = Math.Min(s32_Index, mi_UndoList.Count - 1);
+
+ cUndoEntry i_UndoItem = mi_UndoList[s32_Index];
+
+ foreach (cDrawObj i_Removed in i_UndoItem.mi_Removed)
+ {
+ if (b_Undo) mi_Inst.mi_UserObjects.Add(i_Removed);
+ else mi_Inst.mi_UserObjects.Remove(i_Removed);
+
+ mi_Inst.me_Recalculate |= eRecalculate.AddRemove | eRecalculate.Objects | eRecalculate.CoordSystem;
+ }
+
+ foreach (cDrawObj i_Added in i_UndoItem.mi_Added)
+ {
+ if (b_Undo) mi_Inst.mi_UserObjects.Remove(i_Added);
+ else mi_Inst.mi_UserObjects.Add(i_Added);
+
+ mi_Inst.me_Recalculate |= eRecalculate.AddRemove | eRecalculate.Objects | eRecalculate.CoordSystem;
+ }
+
+ foreach (KeyValuePair i_Pair in i_UndoItem.mi_Backups)
+ {
+ i_Pair.Value.Restore(i_Pair.Key, b_Undo);
+
+ mi_Inst.me_Recalculate |= eRecalculate.Objects | eRecalculate.CoordSystem;
+ }
+
+ mi_Inst.Invalidate();
+ }
+ }
+
+ #endregion cUndoImpl
+
+ // ----- DrawObjects -----
+
+ #region cDrawObj
+
+ ///
+ /// Base class for cLine, cShape, cPolygon
+ ///
+ private abstract class cDrawObj : IComparable
+ {
+ public Editor3D mi_Inst;
+ public SmoothingMode me_SmoothMode;
+ public cPoint[] mi_Points;
+ public cObject3D mi_Object3D; // This is a cLine3D, cShape3D or cPolygon3D
+ public double md_Sort; // sorting is important. Always draw first back, then front objects.
+ public bool mb_IsAxis; // This is a line from the coordinate system
+ protected double md_AvrgZ; // 3D center of the Z coordinates of all points in this object
+ protected bool mb_IsValid;
+ protected eMirror me_Mirror;
+
+ // --------------------------------------------------
+
+ ///
+ /// Set after conversion 3D --> 2D. If invalid coordinates are found (NaN or >
+ /// 9999) this returns false. If projection results in lines of thousands of pixels
+ /// length, the drawing will become extremely slow. Do not draw lines or polygons
+ /// outside the screen area.
+ ///
+ public bool IsValid
+ {
+ get { return mb_IsValid; }
+ }
+
+ ///
+ /// The object is selected
+ ///
+ public bool Selected
+ {
+ get { return mi_Object3D.Selected; }
+ set { mi_Object3D.Selected = value; }
+ }
+
+ ///
+ /// The type of this object
+ ///
+ public virtual eSelType SelType
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ ///
+ /// Calculates colors from color scheme
+ ///
+ public virtual void ProcessColors()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Calculates the 2D screen coordinates for each 3D point.
+ ///
+ public virtual void Project3D()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Draw into Graphics
+ ///
+ public virtual void Render(Graphics i_Graph)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Check if a user click at mouse point X, Y matches this draw object
+ ///
+ public virtual cObject3D MatchesPoint2D(int X, int Y)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Uses the center of a draw object (e.g. the middle of a line) to calculate in which
+ /// order the draw objects are rendered. This is called for all user defined objects,
+ /// but not for the coordinate system which has it's own logic.
+ ///
+ public void CalcSortOrder()
+ {
+ Debug.Assert(!mb_IsAxis); // axes have their own value for md_Sort
+
+ double d_AvrgX = 0.0;
+ double d_AvrgY = 0.0;
+ md_AvrgZ = 0.0;
+
+ foreach (cPoint i_Point in mi_Points)
+ {
+ d_AvrgX += i_Point.mi_P3D.X;
+ d_AvrgY += i_Point.mi_P3D.Y;
+ md_AvrgZ += i_Point.mi_P3D.Z;
+ }
+
+ d_AvrgX /= mi_Points.Length;
+ d_AvrgY /= mi_Points.Length;
+ md_AvrgZ /= mi_Points.Length;
+
+ if (mi_Object3D.Row > -1) // only Polygons and Scatter circles created by cSurfaceData
+ {
+ int X = mi_Object3D.Column + 1;
+ int Y = mi_Object3D.Row + 1;
+
+ // Mirror axis values increasing / decreasing
+ if (mi_Inst.AxisX.Mirror && (me_Mirror & eMirror.X) > 0) X = 5000 - mi_Object3D.Column;
+ if (mi_Inst.AxisY.Mirror && (me_Mirror & eMirror.Y) > 0) Y = 5000 - mi_Object3D.Row;
+
+ // In case of a surface grid the Z value must be ignored because sorting is
+ // ALWAYS based on the position in the grid. Using the Z value here may even
+ // result in wrong sort order.
+ md_Sort = mi_Inst.mi_Transform.ProjectXY(X, Y);
+ }
+ else
+ {
+ double X = d_AvrgX;
+ double Y = d_AvrgY;
+ double Z = md_AvrgZ;
+
+ // Mirror axis values increasing / decreasing
+ if (mi_Inst.AxisX.Mirror && (me_Mirror & eMirror.X) > 0) X = mi_Inst.mi_Bounds.X.Max - (X - mi_Inst.mi_Bounds.X.Min);
+ if (mi_Inst.AxisY.Mirror && (me_Mirror & eMirror.Y) > 0) Y = mi_Inst.mi_Bounds.Y.Max - (Y - mi_Inst.mi_Bounds.Y.Min);
+ if (mi_Inst.AxisZ.Mirror && (me_Mirror & eMirror.Z) > 0) Z = mi_Inst.mi_Bounds.Z.Max - (Z - mi_Inst.mi_Bounds.Z.Min);
+
+ // In case of any other 3D object the Z value must also be included in the
+ // calculation to avoid artifacts. Demo Sphere shows that the Z value is
+ // required if you move Theta to an extreme.
+ X = (X - mi_Inst.mi_Transform.mi_Center3D.X) * mi_Inst.mi_Transform.md_NormalizeX;
+ Y = (Y - mi_Inst.mi_Transform.mi_Center3D.Y) * mi_Inst.mi_Transform.md_NormalizeY;
+ Z = (Z - mi_Inst.mi_Transform.mi_Center3D.Z) * mi_Inst.mi_Transform.md_NormalizeZ;
+
+ md_Sort = mi_Inst.mi_Transform.ProjectXY(X, Y, Z);
+ }
+ }
+
+ ///
+ /// Used for sorting all DrawObjects from back to front
+ ///
+ int IComparable.CompareTo(Object o_Comp)
+ {
+ return md_Sort.CompareTo(((cDrawObj)o_Comp).md_Sort);
+ }
+ }
+
+ #endregion cDrawObj
+
+ #region cLine
+
+ private class cLine : cDrawObj
+ {
+ private cLine3D mi_Line3D; // object passed to and from the user
+ private Pen mi_Pen;
+ private Brush mi_Brush; // assigned to Pen
+ private float mf_LineWidth; // Linewidth with zoom factor
+ private float mf_SelSize; // size of selection points
+
+ // -------- coordinate axes --------
+ public double md_Angle; // needed to calculate current rotation quadrant of coordinate axis
+
+ public eCoord me_Line = eCoord.Invalid; // main coordinate in coordinate direction
+ public eCoord me_Offset = eCoord.Invalid; // secondary coordinate in coordinate direction
+ public String ms_Label; // Label for axis
+
+ // ---------- multicolor -----------
+ private cPoint[] mi_ColorPoints; // all points on the line that are drawn separately
+
+ private Brush[] mi_ColorBrushes; // all Brushes which are assigned to the Pen
+ private cColorScheme mi_ColorScheme;
+
+ public override eSelType SelType
+ {
+ get { return eSelType.Line; }
+ }
+
+ ///
+ /// Constructor 1 for coordinate system. LineWidth is always 1.
+ ///
+ public cLine(Editor3D i_Inst, eCoord e_Line, eCoord e_Offset, eMirror e_Mirror)
+ {
+ if (e_Line == e_Offset) mi_Pen = i_Inst.mi_Axis[(int)e_Line].AxisPen; // Main axis
+ else mi_Pen = i_Inst.mi_Axis[(int)e_Offset].RasterPen; // Raster line
+
+ mi_Inst = i_Inst;
+ me_Line = e_Line;
+ me_Offset = e_Offset;
+ mi_Brush = mi_Pen.Brush;
+ mi_Points = new cPoint[2];
+ mi_Points[0] = new cPoint(0, 0, 0);
+ mi_Points[1] = new cPoint(0, 0, 0);
+ mb_IsAxis = true;
+ me_Mirror = e_Mirror;
+ me_SmoothMode = SmoothingMode.AntiAlias;
+ }
+
+ ///
+ /// Constructor 2 for user lines if i_Line3D.Pen == null --> Pen from ColorScheme is
+ /// used if i_Line3D.Pen != null --> ColorScheme will be ignored, even if a
+ /// ColorScheme is specified An indvidual LineWidth can be defined in each cLine3D.
+ ///
+ public cLine(cLine3D i_Line3D, cColorScheme i_ColorScheme)
+ {
+ if (i_Line3D.Pen == null && i_ColorScheme == null)
+ throw new ArgumentException("You must specify a Pen or a ColorScheme");
+
+ mi_ColorScheme = i_ColorScheme;
+ mi_Line3D = i_Line3D;
+ mi_Object3D = i_Line3D;
+ mi_Pen = i_Line3D.Pen; // get user's Pen or null
+ mi_Points = new cPoint[2];
+ mi_Points[0] = new cPoint(i_Line3D.Points[0], 1);
+ mi_Points[1] = new cPoint(i_Line3D.Points[1], 1);
+ mb_IsAxis = false;
+ me_Mirror = eMirror.All;
+ me_SmoothMode = SmoothingMode.AntiAlias;
+
+ if (mi_Pen != null)
+ {
+ // The original Brush must be stored separately because the same Pen may be used
+ // for multiple instances of cLine Changing the color of one line would affect
+ // all the others.
+ mi_Brush = mi_Pen.Brush;
+ }
+ else
+ {
+ mi_Pen = new Pen(Brushes.Black); // color and width will be changed below
+ mi_Brush = null; // Brush will be taken from colorscheme
+ }
+ mi_Pen.StartCap = LineCap.Round;
+ mi_Pen.EndCap = LineCap.Round;
+
+ // ---------- multi color ---------
+
+ if (i_Line3D.ColorParts > 1)
+ {
+ mi_ColorBrushes = new Brush[i_Line3D.ColorParts];
+ mi_ColorPoints = new cPoint[i_Line3D.ColorParts];
+ }
+ }
+
+ public override void ProcessColors()
+ {
+ Debug.Assert(!mb_IsAxis); // axes have their own color
+
+ // If the user has changed the Pen --> use the new Pen and it's Brush
+ if (mi_Line3D.Pen != null)
+ {
+ mi_Pen = mi_Line3D.Pen;
+ mi_Brush = mi_Pen.Brush;
+
+ mi_Pen.StartCap = LineCap.Round;
+ mi_Pen.EndCap = LineCap.Round;
+ }
+
+ if (mi_ColorPoints != null) // multicolor line
+ {
+ double d_X = mi_Points[0].mi_P3D.X;
+ double d_Y = mi_Points[0].mi_P3D.Y;
+ double d_Z = mi_Points[0].mi_P3D.Z;
+
+ double d_DeltaX = (mi_Points[1].mi_P3D.X - d_X) / mi_ColorPoints.Length;
+ double d_DeltaY = (mi_Points[1].mi_P3D.Y - d_Y) / mi_ColorPoints.Length;
+ double d_DeltaZ = (mi_Points[1].mi_P3D.Z - d_Z) / mi_ColorPoints.Length;
+
+ mi_ColorPoints[0] = mi_Points[0]; // Set Start Point
+
+ cPoint i_Prev = mi_Points[0];
+ for (int i = 1; i < mi_ColorPoints.Length; i++)
+ {
+ d_X += d_DeltaX;
+ d_Y += d_DeltaY;
+ d_Z += d_DeltaZ;
+
+ cPoint i_Point = new cPoint(new cPoint3D(d_X, d_Y, d_Z), 0);
+
+ double d_AvrgZ = (i_Prev.mi_P3D.Z + i_Point.mi_P3D.Z) / 2.0;
+ double d_FactorZ = mi_Inst.mi_Bounds.CalcFactorZ(d_AvrgZ);
+ int s32_Index = mi_ColorScheme.CalcIndex(d_FactorZ);
+ mi_ColorBrushes[i] = mi_ColorScheme.GetBrush(s32_Index);
+ mi_ColorPoints[i] = i_Point;
+
+ i_Prev = i_Point;
+ }
+
+ mi_ColorPoints[mi_ColorPoints.Length - 1] = mi_Points[1]; // Set End Point
+ }
+ else // solid line
+ {
+ if (mi_ColorScheme != null)
+ {
+ // Load missing Pen from ColorScheme which the user has not specified.
+ double d_FactorZ = mi_Inst.mi_Bounds.CalcFactorZ(md_AvrgZ);
+ int s32_Index = mi_ColorScheme.CalcIndex(d_FactorZ);
+ mi_Brush = mi_ColorScheme.GetBrush(s32_Index);
+ }
+ }
+ }
+
+ public override void Project3D()
+ {
+ cPoint[] i_PointArr = (mi_ColorPoints != null) ? mi_ColorPoints : mi_Points;
+ foreach (cPoint i_Point in i_PointArr)
+ {
+ i_Point.Project3D(mi_Inst, me_Mirror);
+ }
+
+ mb_IsValid = mi_Points[0].mi_P2D.IsValid && mi_Points[1].mi_P2D.IsValid;
+
+ if (!mb_IsAxis)
+ {
+ mf_LineWidth = (float)(mi_Line3D.Width * mi_Inst.mi_Transform.md_Zoom);
+ // Diameter of circle for selected points
+ mf_SelSize = (float)(Math.Max(6, mi_Line3D.Width * 2) * mi_Inst.mi_Transform.md_Zoom);
+ }
+ }
+
+ public override void Render(Graphics i_Graph)
+ {
+ // b_LineSel depends on the selection of the line only. It does not matter if an end
+ // point is selected or not.
+ bool b_LineSel = !mb_IsAxis && Selected && mi_Inst.mi_Selection.HighlightPen != null;
+
+ if (mi_ColorPoints == null || b_LineSel) // draw solid line
+ {
+ Pen i_DrawPen;
+ if (b_LineSel)
+ {
+ i_DrawPen = mi_Inst.mi_Selection.HighlightPen;
+ }
+ else
+ {
+ i_DrawPen = mi_Pen;
+ i_DrawPen.Brush = mi_Brush; // mi_Brush = null for multicolor lines!
+ }
+
+ // Axis lines have always 1 pixel width
+ if (!mb_IsAxis)
+ i_DrawPen.Width = mf_LineWidth;
+
+ i_Graph.DrawLine(i_DrawPen, mi_Points[0].mi_P2D.Coord, mi_Points[1].mi_P2D.Coord);
+ }
+ else // multi color
+ {
+ mi_Pen.Width = mf_LineWidth;
+
+ cPoint i_Prev = mi_ColorPoints[0];
+ for (int i = 1; i < mi_ColorPoints.Length; i++)
+ {
+ cPoint i_Point = mi_ColorPoints[i];
+ mi_Pen.Brush = mi_ColorBrushes[i];
+ i_Graph.DrawLine(mi_Pen, i_Prev.mi_P2D.Coord, i_Point.mi_P2D.Coord);
+
+ i_Prev = i_Point;
+ }
+ }
+
+ // Draw circle for selected points
+ foreach (cPoint i_Point in mi_Points)
+ {
+ if (!i_Point.mi_P3D.Selected)
+ continue;
+
+ float X = (float)i_Point.mi_P2D.md_X - mf_SelSize / 2.0f;
+ float Y = (float)i_Point.mi_P2D.md_Y - mf_SelSize / 2.0f;
+ i_Graph.FillEllipse(mi_Inst.mi_Selection.HighlightBrush, X, Y, mf_SelSize, mf_SelSize);
+ }
+ }
+
+ ///
+ /// Check if a user click at X, Y matches this draw object
+ ///
+ public override cObject3D MatchesPoint2D(int X, int Y)
+ {
+ if (mb_IsAxis)
+ return null; // do not allow to select axis lines
+
+ int s32_MaxDist = Math.Max(1, (int)mi_Pen.Width / 2) + SELECT_RADIUS;
+
+ if (mi_Inst.Selection.SinglePoints)
+ {
+ foreach (cPoint i_Point in mi_Points)
+ {
+ if (i_Point.mi_P2D.CalcDistanceTo(X, Y) <= s32_MaxDist)
+ return i_Point.mi_P3D;
+ }
+ }
+ else // select entire line
+ {
+ if (IsPointOnLine(mi_Points[0].mi_P2D, mi_Points[1].mi_P2D, X, Y, s32_MaxDist))
+ return mi_Line3D;
+ }
+
+ return null;
+ }
+
+ // ---------------- Coord System ---------------
+
+ ///
+ /// Used while creating coordinate system Check if 2 lines have the same coordinates.
+ ///
+ public bool CoordEquals(cLine i_Line)
+ {
+ return mi_Points[0].mi_P3D.CoordEquals(i_Line.mi_Points[0].mi_P3D) &&
+ mi_Points[1].mi_P3D.CoordEquals(i_Line.mi_Points[1].mi_P3D);
+ }
+
+ ///
+ /// Used while creating coordinate system Calculate the angle of the 3 main axes on the
+ /// screen in a range from 0 to 360 degree.
+ ///
+ public void CalcAngle2D()
+ {
+ double d_DX = mi_Points[1].mi_P2D.md_X - mi_Points[0].mi_P2D.md_X;
+ double d_DY = mi_Points[1].mi_P2D.md_Y - mi_Points[0].mi_P2D.md_Y;
+ md_Angle = Math.Atan2(d_DY, d_DX) * 180.0 / Math.PI;
+ if (md_Angle < 0.0) md_Angle += 360.0;
+ }
+
+ // For debugging in Visual Studio
+ public override string ToString()
+ {
+ String s_Dbg = String.Format("cLine from {0} to {1}", mi_Points[0], mi_Points[1]);
+ if (mb_IsAxis)
+ s_Dbg += String.Format(" (Axis {0}, {1})", me_Line, me_Offset);
+
+ return s_Dbg;
+ }
+ }
+
+ #endregion cLine
+
+ #region cShape
+
+ private class cShape : cDrawObj
+ {
+ private float mf_Radius; // radius of shape adapted with Zoom factor
+ private float mf_Diameter; // diameter of shape adapted with Zoom factor
+ private PointF mk_TopLeft; // top left corner in screen coordinates for all types of shapes
+ private PointF[] mk_Polygon; // used for triangles or any future user objects
+ private Brush mi_Brush;
+ private cShape3D mi_Shape3D;
+ private cColorScheme mi_ColorScheme;
+
+ public override eSelType SelType
+ {
+ get { return eSelType.Shape; }
+ }
+
+ ///
+ /// Constructor
+ ///
+ public cShape(cShape3D i_Shape3D, cColorScheme i_ColorScheme)
+ {
+ if (i_Shape3D.Brush == null && i_ColorScheme == null)
+ throw new ArgumentException("You must specify a Brush or a ColorScheme");
+
+ mi_Shape3D = i_Shape3D;
+ mi_Object3D = i_Shape3D;
+ mi_Points = new cPoint[1];
+ mi_Points[0] = new cPoint(i_Shape3D.Points[0], i_Shape3D.Radius);
+ mi_ColorScheme = i_ColorScheme;
+ me_SmoothMode = SmoothingMode.AntiAlias;
+ me_Mirror = eMirror.All;
+ }
+
+ public override void ProcessColors()
+ {
+ // If the user has specified an individual brush for this Shape --> always use it
+ mi_Brush = mi_Shape3D.Brush;
+
+ // Otherwise use Brush from ColorScheme
+ if (mi_Brush == null)
+ {
+ double d_FactorZ = mi_Inst.mi_Bounds.CalcFactorZ(md_AvrgZ);
+ int s32_Index = mi_ColorScheme.CalcIndex(d_FactorZ);
+ mi_Brush = mi_ColorScheme.GetBrush(s32_Index);
+ }
+ }
+
+ public override void Project3D()
+ {
+ mi_Points[0].Project3D(mi_Inst, me_Mirror);
+
+ mb_IsValid = mi_Points[0].mi_P2D.IsValid;
+ mf_Radius = (float)(mi_Shape3D.Radius * mi_Inst.mi_Transform.md_Zoom);
+ mf_Diameter = mf_Radius * 2.0f;
+
+ // Move coordinate from center to upper left corner of circle
+ mk_TopLeft = mi_Points[0].mi_P2D.Coord;
+ mk_TopLeft.X -= mf_Radius;
+ mk_TopLeft.Y -= mf_Radius;
+
+ switch (mi_Shape3D.Shape)
+ {
+ case eScatterShape.Triangle:
+ mk_Polygon = new PointF[3];
+ // top center
+ mk_Polygon[0].X = mk_TopLeft.X + mf_Radius;
+ mk_Polygon[0].Y = mk_TopLeft.Y;
+ // bottom left
+ mk_Polygon[1].X = mk_TopLeft.X;
+ mk_Polygon[1].Y = mk_TopLeft.Y + mf_Diameter;
+ // bottom right
+ mk_Polygon[2].X = mk_TopLeft.X + mf_Diameter;
+ mk_Polygon[2].Y = mk_TopLeft.Y + mf_Diameter;
+ break;
+
+ // case eScatterShape.Star: Here you can implement your own shapes break;
+ }
+ }
+
+ public override void Render(Graphics i_Graph)
+ {
+ bool b_DrawSel = Selected && mi_Inst.mi_Selection.HighlightBrush != null;
+ Brush i_DrawBrush = b_DrawSel ? mi_Inst.mi_Selection.HighlightBrush : mi_Brush;
+
+ switch (mi_Shape3D.Shape)
+ {
+ case eScatterShape.Circle:
+ i_Graph.FillEllipse(i_DrawBrush, mk_TopLeft.X, mk_TopLeft.Y, mf_Diameter, mf_Diameter);
+ break;
+
+ case eScatterShape.Square:
+ i_Graph.FillRectangle(i_DrawBrush, mk_TopLeft.X, mk_TopLeft.Y, mf_Diameter, mf_Diameter);
+ break;
+
+ default:
+ i_Graph.FillPolygon(i_DrawBrush, mk_Polygon);
+ break;
+ }
+ }
+
+ ///
+ /// Check if a user click at X, Y matches this draw object
+ ///
+ public override cObject3D MatchesPoint2D(int X, int Y)
+ {
+ int s32_MaxDist = (int)mf_Radius + SELECT_RADIUS;
+
+ if (mi_Points[0].mi_P2D.CalcDistanceTo(X, Y) <= s32_MaxDist)
+ return mi_Shape3D;
+
+ return null;
+ }
+
+ ///
+ /// For Debugging in Visual Studio
+ ///
+ public override string ToString()
+ {
+ return String.Format("cShape {0} at {1}, Diameter {2}", mi_Shape3D.Shape, mi_Points[0], FormatDouble(mf_Diameter));
+ }
+ }
+
+ #endregion cShape
+
+ #region cPolygon
+
+ private class cPolygon : cDrawObj
+ {
+ private bool mb_Fill; // Fill / Line mode
+ private float mf_SelSize; // size of selection points
+ private PointF[] mk_Screen; // the 2D polygon corner points in screen coordinates
+ private int ms32_OrgWidth; // original line width for Line Pen
+ private float mf_LineWidth; // zoomed line width for Line Pen
+ private Pen mi_LinePen; // used in Line mode
+ private Pen mi_BorderPen; // used in Fill mode (not zoomed)
+ private Brush mi_Brush; // used in Fill mode
+ private cPolygon3D mi_Polygon3D;
+ private cColorScheme mi_ColorScheme;
+
+ public override eSelType SelType
+ {
+ get { return eSelType.Polygon; }
+ }
+
+ ///
+ /// Constructor
+ ///
+ public cPolygon(bool b_Fill, cPolygon3D i_Polygon3D, Pen i_Pen, cColorScheme i_ColorScheme)
+ {
+ mi_Points = new cPoint[i_Polygon3D.Points.Length];
+ for (int i = 0; i < mi_Points.Length; i++)
+ {
+ mi_Points[i] = new cPoint(i_Polygon3D.Points[i], 1);
+ }
+
+ mb_Fill = b_Fill;
+ mi_Polygon3D = i_Polygon3D;
+ mi_Object3D = i_Polygon3D;
+ mi_ColorScheme = i_ColorScheme;
+ mk_Screen = new PointF[mi_Points.Length];
+ me_Mirror = eMirror.All;
+
+ if (b_Fill) mi_BorderPen = i_Pen;
+ else mi_LinePen = i_Pen;
+
+ if (i_Pen != null)
+ ms32_OrgWidth = (int)i_Pen.Width;
+
+ // Drawing polygon border lines with antialias makes them very thick and black. Do
+ // not smooth the lines when the polygons are filled with color and the lines are
+ // only separators. But use smooth mode if only the lines are drawn.
+ me_SmoothMode = (b_Fill) ? SmoothingMode.None : SmoothingMode.AntiAlias;
+ }
+
+ public override void ProcessColors()
+ {
+ // If the user has specified an individual brush for this Polygon --> always use it
+ if (mi_Polygon3D.Brush != null)
+ {
+ mi_Brush = mi_Polygon3D.Brush;
+ }
+ else if (mi_ColorScheme != null) // ColorScheme is never null in Fill mode
+ {
+ double d_FactorZ = mi_Inst.mi_Bounds.CalcFactorZ(md_AvrgZ);
+ int s32_Index = mi_ColorScheme.CalcIndex(d_FactorZ);
+ mi_Brush = mi_ColorScheme.GetBrush(s32_Index); // used for Fill and assigned to LinePen
+ }
+ }
+
+ public override void Project3D()
+ {
+ foreach (cPoint i_Point in mi_Points)
+ {
+ i_Point.Project3D(mi_Inst, me_Mirror);
+ }
+
+ // Line width for Line mode
+ mf_LineWidth = (float)(ms32_OrgWidth * mi_Inst.mi_Transform.md_Zoom);
+
+ // Diameter of circle for selected points
+ mf_SelSize = (float)(Math.Max(6, ms32_OrgWidth * 2) * mi_Inst.mi_Transform.md_Zoom);
+
+ mb_IsValid = true;
+ for (int i = 0; i < mi_Points.Length; i++)
+ {
+ if (mi_Points[i].mi_P2D.IsValid)
+ mk_Screen[i] = mi_Points[i].mi_P2D.Coord;
+ else
+ mb_IsValid = false;
+ }
+ }
+
+ public override void Render(Graphics i_Graph)
+ {
+ // Fill polygon with solid color
+ if (mb_Fill)
+ {
+ Brush i_FillBrush = mi_Brush;
+ if (Selected && mi_Inst.mi_Selection.HighlightBrush != null)
+ i_FillBrush = mi_Inst.mi_Selection.HighlightBrush;
+
+ i_Graph.FillPolygon(i_FillBrush, mk_Screen);
+ }
+
+ Pen i_DrawPen;
+ if (mb_Fill)
+ {
+ i_DrawPen = mi_BorderPen;
+ }
+ else // Line mode
+ {
+ i_DrawPen = mi_LinePen;
+ i_DrawPen.Width = mf_LineWidth;
+
+ if (mi_Brush != null)
+ i_DrawPen.Brush = mi_Brush;
+ }
+
+ // Fill mode --> draw thin black border lines around the polygons Line mode --> draw
+ // thicker lines around transparent polygons
+ if (i_DrawPen != null)
+ {
+ // ATTENTION: Graphics.DrawPolygon() with a Pen > 1 pixel is buggy in the .NET
+ // framework (artifacts)! The lines must be drawn one by one manually here.
+ int T = mk_Screen.Length - 1;
+ for (int F = 0; F < mk_Screen.Length; F++)
+ {
+ i_Graph.DrawLine(i_DrawPen, mk_Screen[F], mk_Screen[T]);
+ T = F;
+ }
+ }
+
+ // Draw selected points
+ foreach (cPoint i_Point in mi_Points)
+ {
+ if (!i_Point.mi_P3D.Selected)
+ continue;
+
+ float X = (float)i_Point.mi_P2D.md_X - mf_SelSize / 2.0f;
+ float Y = (float)i_Point.mi_P2D.md_Y - mf_SelSize / 2.0f;
+ i_Graph.FillEllipse(mi_Inst.mi_Selection.HighlightBrush, X, Y, mf_SelSize, mf_SelSize);
+ }
+ }
+
+ ///
+ /// Check if a user click at X, Y matches this draw object
+ ///
+ public override cObject3D MatchesPoint2D(int X, int Y)
+ {
+ // ATTENTION: Selecting entire polygons makes only sense with eSurfaceMode.Fill In
+ // line mode polygons are transparent and a click into the polygon would go to the background.
+ if (!mb_Fill || mi_Inst.mi_Selection.SinglePoints)
+ {
+ int s32_MaxDist = (int)mf_SelSize / 2 + SELECT_RADIUS;
+ foreach (cPoint i_Point in mi_Points)
+ {
+ if (i_Point.mi_P2D.CalcDistanceTo(X, Y) <= s32_MaxDist)
+ return i_Point.mi_P3D;
+ }
+ }
+ else // select entire polygon
+ {
+ // Detect if the point is inside the polygon. Here SELECT_RADIUS is ignored. But
+ // the user must only click into the middle of the polygon, which is easier than
+ // clicking a thin line.
+ bool b_Result = false;
+ int k = mi_Points.Length - 1;
+ for (int i = 0; i < mi_Points.Length; i++)
+ {
+ cPoint2D i_Point1 = mi_Points[i].mi_P2D;
+ cPoint2D i_Point2 = mi_Points[k].mi_P2D;
+
+ if (i_Point1.md_Y < Y && i_Point2.md_Y >= Y ||
+ i_Point2.md_Y < Y && i_Point1.md_Y >= Y)
+ {
+ if (i_Point1.md_X + (Y - i_Point1.md_Y) /
+ (i_Point2.md_Y - i_Point1.md_Y) *
+ (i_Point2.md_X - i_Point1.md_X) < X)
+ {
+ b_Result = !b_Result;
+ }
+ }
+ k = i;
+ }
+ if (b_Result)
+ return mi_Polygon3D;
+ }
+ return null;
+ }
+
+ ///
+ /// For debugging in Visual Studio
+ ///
+ public override string ToString()
+ {
+ return String.Format("cPolygon ({0} points)", mk_Screen.Length);
+ }
+ }
+
+ #endregion cPolygon
+
+ // ----- Math Stuff ------
+
+ #region cMouse
+
+ private class cMouse
+ {
+ public eMouseAction me_Action; // left mouse button action
+ public Point mk_LastPos; // last mouse location
+ public Point mk_OffMove; // Mouse offset after moving the graph with the mouse
+ public Point mk_OffCoord; // Offset caused by labels in coordinate system
+ public TrackBar mi_TrackRho; // Rho trackbar (optional)
+ public TrackBar mi_TrackTheta; // Theta trackbar (optional)
+ public TrackBar mi_TrackPhi; // Phi trackbar (optional)
+ public double md_Rho = VALUES_RHO.Default;
+ public double md_Theta = VALUES_THETA.Default;
+ public double md_Phi = VALUES_PHI.Default;
+
+ public void AssignTrackbar(eMouseAction e_Trackbar, TrackBar i_Trackbar, EventHandler i_OnScroll)
+ {
+ if (i_Trackbar == null)
+ return;
+
+ cDefault i_Default = null;
+ switch (e_Trackbar)
+ {
+ case eMouseAction.Rho:
+ i_Default = VALUES_RHO;
+ mi_TrackRho = i_Trackbar;
+ break;
+
+ case eMouseAction.Theta:
+ i_Default = VALUES_THETA;
+ mi_TrackTheta = i_Trackbar;
+ break;
+
+ case eMouseAction.Phi:
+ i_Default = VALUES_PHI;
+ mi_TrackPhi = i_Trackbar;
+ break;
+ }
+
+ i_Trackbar.Minimum = (int)i_Default.Min;
+ i_Trackbar.Maximum = (int)i_Default.Max;
+ i_Trackbar.Value = (int)i_Default.Default;
+ i_Trackbar.Scroll += i_OnScroll;
+ }
+
+ ///
+ /// User has moved the TrackBar
+ ///
+ public void OnTrackBarScroll()
+ {
+ if (mi_TrackRho != null) md_Rho = mi_TrackRho.Value;
+ if (mi_TrackTheta != null) md_Theta = mi_TrackTheta.Value;
+ if (mi_TrackPhi != null) md_Phi = mi_TrackPhi.Value;
+ }
+
+ public bool OnMouseWheel(int s32_Delta)
+ {
+ if (me_Action != eMouseAction.None)
+ return false;
+
+ me_Action = eMouseAction.Rho;
+ OnMouseMove(0, s32_Delta / 10);
+ me_Action = eMouseAction.None;
+ return true;
+ }
+
+ ///
+ /// User has dragged the mouse over the 3D control
+ ///
+ public void OnMouseMove(int s32_DiffX, int s32_DiffY)
+ {
+ if (me_Action == eMouseAction.Rho)
+ {
+ md_Rho += s32_DiffY * VALUES_RHO.MouseFactor;
+ SetRho(md_Rho);
+ }
+ if (me_Action == eMouseAction.Theta || me_Action == eMouseAction.ThetaAndPhi)
+ {
+ md_Theta -= s32_DiffY * VALUES_THETA.MouseFactor;
+ SetTheta(md_Theta);
+ }
+ if (me_Action == eMouseAction.Phi || me_Action == eMouseAction.ThetaAndPhi)
+ {
+ md_Phi -= s32_DiffX * VALUES_PHI.MouseFactor;
+ SetPhi(md_Phi);
+ }
+ }
+
+ public void SetRho(double d_Rho)
+ {
+ md_Rho = d_Rho;
+ md_Rho = Math.Max(md_Rho, VALUES_RHO.Min);
+ md_Rho = Math.Min(md_Rho, VALUES_RHO.Max);
+ if (mi_TrackRho != null)
+ mi_TrackRho.Value = (int)md_Rho;
+ }
+
+ public void SetTheta(double d_Theta)
+ {
+ md_Theta = d_Theta;
+ md_Theta = Math.Max(md_Theta, VALUES_THETA.Min);
+ md_Theta = Math.Min(md_Theta, VALUES_THETA.Max);
+ if (mi_TrackTheta != null)
+ mi_TrackTheta.Value = (int)md_Theta;
+ }
+
+ public void SetPhi(double d_Phi)
+ {
+ md_Phi = d_Phi;
+ while (md_Phi > 360.0) md_Phi -= 360.0; // continuous rotation
+ while (md_Phi < 0.0) md_Phi += 360.0; // continuous rotation
+ if (mi_TrackPhi != null)
+ mi_TrackPhi.Value = (int)md_Phi;
+ }
+ }
+
+ #endregion cMouse
+
+ #region cRange
+
+ private class cRange
+ {
+ private double md_Min, md_Max;
+
+ public double Min
+ {
+ get { return md_Min; }
+ }
+
+ public double Max
+ {
+ get { return md_Max; }
+ }
+
+ public double Range
+ {
+ get { return md_Max - md_Min; }
+ }
+
+ ///
+ /// Constructor
+ ///
+ public cRange(double d_Min, double d_Max, bool b_IncludeZero, eRaster e_Raster)
+ {
+ md_Min = d_Min;
+ md_Max = d_Max;
+
+ if (md_Max == md_Min)
+ {
+ md_Min -= 1.0;
+ md_Max += 1.0;
+ }
+
+ if (e_Raster == eRaster.Off)
+ return;
+
+ if (b_IncludeZero)
+ {
+ md_Min = Math.Min(0.0, md_Min);
+ md_Max = Math.Max(0.0, md_Max);
+ }
+
+ // Add 10 % excess to all axes
+ if (md_Min < 0.0 || (md_Min > 0.0 && !b_IncludeZero)) md_Min -= Math.Abs(md_Min) * AXIS_EXCESS;
+ if (md_Max > 0.0 || (md_Max < 0.0 && !b_IncludeZero)) md_Max += Math.Abs(md_Max) * AXIS_EXCESS;
+ }
+ }
+
+ #endregion cRange
+
+ #region cBounds
+
+ private class cBounds
+ {
+ private Editor3D mi_Inst;
+ private cRange mi_RangeX;
+ private cRange mi_RangeY;
+ private cRange mi_RangeZ;
+ private double md_MinX, md_MaxX, md_MinY, md_MaxY, md_MinZ, md_MaxZ;
+
+ public cRange X
+ {
+ get { return mi_RangeX; }
+ }
+
+ public cRange Y
+ {
+ get { return mi_RangeY; }
+ }
+
+ public cRange Z
+ {
+ get { return mi_RangeZ; }
+ }
+
+ ///
+ /// Constructor
+ ///
+ public cBounds(Editor3D i_Inst)
+ {
+ mi_Inst = i_Inst;
+ }
+
+ ///
+ /// Also assigns mi_Inst to all draw objects
+ ///
+ public void Calculate()
+ {
+ md_MinX = double.PositiveInfinity;
+ md_MaxX = double.NegativeInfinity;
+ md_MinY = double.PositiveInfinity;
+ md_MaxY = double.NegativeInfinity;
+ md_MinZ = double.PositiveInfinity;
+ md_MaxZ = double.NegativeInfinity;
+
+ foreach (cDrawObj i_DrawObj in mi_Inst.mi_UserObjects)
+ {
+ i_DrawObj.mi_Inst = mi_Inst;
+ i_DrawObj.mi_Object3D.mi_Inst = mi_Inst;
+
+ foreach (cPoint i_Point in i_DrawObj.mi_Points)
+ {
+ cPoint3D i_Point3D = i_Point.mi_P3D;
+ i_Point3D.mi_Inst = mi_Inst;
+
+ md_MinX = Math.Min(md_MinX, i_Point3D.X);
+ md_MaxX = Math.Max(md_MaxX, i_Point3D.X);
+ md_MinY = Math.Min(md_MinY, i_Point3D.Y);
+ md_MaxY = Math.Max(md_MaxY, i_Point3D.Y);
+ md_MinZ = Math.Min(md_MinZ, i_Point3D.Z);
+ md_MaxZ = Math.Max(md_MaxZ, i_Point3D.Z);
+ }
+ }
+
+ mi_RangeX = new cRange(md_MinX, md_MaxX, mi_Inst.AxisX.IncludeZero, mi_Inst.me_Raster);
+ mi_RangeY = new cRange(md_MinY, md_MaxY, mi_Inst.AxisY.IncludeZero, mi_Inst.me_Raster);
+ mi_RangeZ = new cRange(md_MinZ, md_MaxZ, mi_Inst.AxisZ.IncludeZero, mi_Inst.me_Raster);
+ }
+
+ ///
+ /// Used to get the color from the ColorScheme
+ ///
+ public double CalcFactorZ(double d_Value)
+ {
+ return (d_Value - md_MinZ) / (md_MaxZ - md_MinZ);
+ }
+ }
+
+ #endregion cBounds
+
+ #region cQuadrant
+
+ private class cQuadrant
+ {
+ public double md_SortXY; // Sort order of raster in area XY (red)
+ public double md_SortXZ; // Sort order of X axis and raster in area XZ (blue)
+ public double md_SortYZ; // Sort order of Y axis and raster in area YZ (green)
+ public int ms32_Quadrant;
+ public bool mb_BottomView;
+
+ public void Calculate(double d_Phi, cLine i_AxisX, cLine i_AxisY, cLine i_AxisZ)
+ {
+ // Split rotation into 4 sections (0...3) which increment every 90° starting at 45°
+ int s32_Section45 = (int)d_Phi + 45;
+ if (s32_Section45 > 360) s32_Section45 -= 360;
+ s32_Section45 = Math.Min(3, s32_Section45 / 90);
+
+ // Theta elevation lets the camera watch the graph from the top or bottom
+ switch (s32_Section45)
+ {
+ case 0: mb_BottomView = i_AxisX.md_Angle < 180.0; break;
+ case 1: mb_BottomView = i_AxisY.md_Angle < 180.0; break;
+ case 2: mb_BottomView = i_AxisX.md_Angle > 180.0; break;
+ case 3: mb_BottomView = i_AxisY.md_Angle > 180.0; break;
+ }
+
+ // The quadrant changes when the 2D transformed Z axis is in line with the X or Y axis
+ if (mb_BottomView)
+ {
+ switch (s32_Section45)
+ {
+ case 0: ms32_Quadrant = i_AxisX.md_Angle + 180.0 < i_AxisZ.md_Angle ? 1 : 0; break;
+ case 1: ms32_Quadrant = i_AxisY.md_Angle + 180.0 < i_AxisZ.md_Angle ? 2 : 1; break;
+ case 2: ms32_Quadrant = i_AxisX.md_Angle < i_AxisZ.md_Angle ? 3 : 2; break;
+ case 3: ms32_Quadrant = i_AxisY.md_Angle < i_AxisZ.md_Angle ? 0 : 3; break;
+ }
+ }
+ else // Top View
+ {
+ switch (s32_Section45)
+ {
+ case 0: ms32_Quadrant = i_AxisX.md_Angle > i_AxisZ.md_Angle ? 1 : 0; break;
+ case 1: ms32_Quadrant = i_AxisY.md_Angle > i_AxisZ.md_Angle ? 2 : 1; break;
+ case 2: ms32_Quadrant = i_AxisX.md_Angle + 180.0 > i_AxisZ.md_Angle ? 3 : 2; break;
+ case 3: ms32_Quadrant = i_AxisY.md_Angle + 180.0 > i_AxisZ.md_Angle ? 0 : 3; break;
+ }
+ }
+
+ md_SortXY = (mb_BottomView) ? 99999.9 : -99999.9;
+ md_SortXZ = (ms32_Quadrant == 1 || ms32_Quadrant == 2) ? 99999.9 : -99999.9;
+ md_SortYZ = (ms32_Quadrant == 0 || ms32_Quadrant == 1) ? 99999.9 : -99999.9;
+
+ i_AxisX.md_Sort = md_SortXZ;
+ i_AxisY.md_Sort = md_SortYZ;
+ i_AxisZ.md_Sort = (ms32_Quadrant == 3) ? -99999.9 : 99999.9;
+
+ // Debug.WriteLine(String.Format("Section: {0} Quadrant: {1}", s32_Section45, ms32_Quadrant));
+ }
+ }
+
+ #endregion cQuadrant
+
+ #region cTransform
+
+ private class cTransform
+ {
+ // Camera distance. Smaller values result in ugly stretched egdes when rotating.
+ private const double DISTANCE = 0.55;
+
+ private double md_sf; // sf = sinus fi
+ private double md_st; // st = sinus theta
+ private double md_cf; // cf = cosinus fi
+ private double md_ct; // ct = cosinus theta
+ private double md_Rho;
+
+ // ----------------
+ private double md_FactX;
+
+ private double md_OffsX;
+ private double md_FactY;
+ private double md_OffsY;
+ private double md_Resize = 1.0;
+
+ // ----------------
+ public cPoint3D mi_Center3D = new cPoint3D(0, 0, 0);
+
+ public double md_NormalizeX;
+ public double md_NormalizeY;
+ public double md_NormalizeZ;
+ public double md_Zoom;
+
+ // ----------------
+ private Size mk_InitialSize = Size.Empty;
+
+ private Editor3D mi_Inst;
+
+ public cTransform(Editor3D i_Inst)
+ {
+ mi_Inst = i_Inst;
+ }
+
+ public void SetCoefficients(cMouse i_Mouse)
+ {
+ md_Rho = i_Mouse.md_Rho; // Distance of viewer (zoom)
+ double d_Theta = i_Mouse.md_Theta * Math.PI / 180.0; // Height of viewer (elevation)
+ double d_Phi = (i_Mouse.md_Phi - 180.0) * Math.PI / 180.0; // Rotation around center (-Pi ... +Pi)
+
+ // Speed optimization: precalculate factors
+ md_sf = Math.Sin(d_Phi);
+ md_cf = Math.Cos(d_Phi);
+ md_st = Math.Sin(d_Theta); // Theta = 0...pi --> st = 0 .. 1 .. 0
+ md_ct = Math.Cos(d_Theta); // Theta = 0...pi --> ct = 1 .. 0 .. -1
+
+ CalcZoom();
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+
+ ///
+ /// The initial size is needed to calculate the user resizing factor. To assure that it
+ /// is correct it must be set when the control has already been created. Then it will be
+ /// the size that was defined in Visual Studio Form Designer.
+ ///
+ public void SetInitialSize(Size k_Size)
+ {
+ mk_InitialSize = k_Size;
+ SetSize(k_Size);
+ }
+
+ ///
+ /// The control has been resized. This may be called with an invalid size before the
+ /// control is created!
+ ///
+ public void SetSize(Size k_Size) // Control.ClientSize
+ {
+ if (mk_InitialSize == Size.Empty)
+ return;
+
+ double d_Width = k_Size.Width * 0.0254 / 96.0; // 0.0254 meter = 1 inch. Screen has 96 DPI
+ double d_Height = k_Size.Height * 0.0254 / 96.0;
+
+ // linear transformation coefficients
+ md_FactX = k_Size.Width / d_Width;
+ md_FactY = -k_Size.Height / d_Height;
+
+ md_OffsX = md_FactX * d_Width / 2.0;
+ md_OffsY = -md_FactY * d_Height / 2.0;
+
+ // -----------------------------------
+
+ double d_ResizeX = (double)k_Size.Width / mk_InitialSize.Width;
+ double d_ResizeY = (double)k_Size.Height / mk_InitialSize.Height;
+ md_Resize = Math.Min(d_ResizeX, d_ResizeY);
+
+ md_FactX *= md_Resize;
+ md_FactY *= md_Resize;
+
+ CalcZoom();
+ mi_Inst.me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+
+ // Required for correct painting order of polygons (always from back to front)
+ public double ProjectXY(double X, double Y, double Z = 0.0)
+ {
+ return X * md_cf + Y * md_sf + Z * md_ct;
+ }
+
+ // Used to convert mouse movements back into the 3D space depending on the current
+ // rotation angle
+ public double ReverseProject(double X, double Y, double Z)
+ {
+ if (mi_Inst.AxisX.Mirror) X = -X;
+ if (mi_Inst.AxisY.Mirror) Y = -Y;
+ if (mi_Inst.AxisZ.Mirror) Z = -Z;
+
+ // If Theta has the correct range from 10 to 170 degree --> Sinus(Theta) will never
+ // become zero. This can only happen if VALUES_THETA has been manipulated to invalid
+ // Min/Max values.
+ double d_Divide = Math.Max(0.1, md_st);
+ return (-X * md_sf + Y * md_cf + Z / d_Divide) / md_Zoom;
+ }
+
+ ///
+ /// This approximates a zoom factor that depends on Rho and the resize window factor.
+ /// Used to adapt the size of lines, shapes and selected points.
+ ///
+ private void CalcZoom()
+ {
+ md_Zoom = md_Resize * (1800.0 / (md_Rho + 300));
+ }
+
+ // Performs projection. Calculates 2D screen coordinates from 3D point.
+ public cPoint2D Project3D(cPoint3D i_Point3D, eMirror e_Mirror)
+ {
+ double X = i_Point3D.X;
+ double Y = i_Point3D.Y;
+ double Z = i_Point3D.Z;
+
+ // Mirror axis values increasing / decreasing
+ if (mi_Inst.AxisX.Mirror && (e_Mirror & eMirror.X) > 0) X = mi_Inst.mi_Bounds.X.Max - (X - mi_Inst.mi_Bounds.X.Min);
+ if (mi_Inst.AxisY.Mirror && (e_Mirror & eMirror.Y) > 0) Y = mi_Inst.mi_Bounds.Y.Max - (Y - mi_Inst.mi_Bounds.Y.Min);
+ if (mi_Inst.AxisZ.Mirror && (e_Mirror & eMirror.Z) > 0) Z = mi_Inst.mi_Bounds.Z.Max - (Z - mi_Inst.mi_Bounds.Z.Min);
+
+ X = (X - mi_Center3D.X) * md_NormalizeX;
+ Y = (Y - mi_Center3D.Y) * md_NormalizeY;
+ Z = (Z - mi_Center3D.Z) * md_NormalizeZ;
+
+ // 3D coordinates with center point in the middle of the screen X positive to the
+ // right, X negative to the left Y positive to the top, Y negative to the bottom
+ double xn = -md_sf * X + md_cf * Y;
+ double yn = -md_cf * md_ct * X - md_sf * md_ct * Y + md_st * Z;
+ double zn = -md_cf * md_st * X - md_sf * md_st * Y - md_ct * Z + md_Rho;
+
+ zn = Math.Max(zn, 0.01); // avoid division by zero
+
+ // Thales' theorem
+ cPoint2D i_Point2D = new cPoint2D(xn * DISTANCE / zn, yn * DISTANCE / zn);
+
+ i_Point2D.md_X = i_Point2D.md_X * md_FactX + md_OffsX;
+ i_Point2D.md_Y = i_Point2D.md_Y * md_FactY + md_OffsY;
+ return i_Point2D;
+ }
+ }
+
+ #endregion cTransform
+
+ #region cDefault
+
+ ///
+ /// Stores defauls for Rho, Theta, Phi
+ ///
+ private class cDefault
+ {
+ public readonly double Min;
+ public readonly double Max;
+ public readonly double Default;
+ public readonly double MouseFactor;
+
+ public cDefault(double d_Min, double d_Max, double d_Default, double d_MouseFactor)
+ {
+ Min = d_Min;
+ Max = d_Max;
+ Default = d_Default;
+ MouseFactor = d_MouseFactor;
+ }
+ }
+
+ #endregion cDefault
+
+ // Limits and default values for mouse actions and trackbars.
+ // ATTENTION: It is strongly recommended not to change the MIN, MAX values. The mouse factor
+ // defines how much mouse movement you need for a change. A movement of mouse by approx 1000
+ // pixels on the screen results in getting from Min to Max or vice versa.
+ private static readonly cDefault VALUES_RHO = new cDefault(300, 1800, 1350, 2);
+
+ private static readonly cDefault VALUES_THETA = new cDefault(10, 170, 70, 0.25); // degree
+ private static readonly cDefault VALUES_PHI = new cDefault(0, 360, 230, 0.4); // degree (continuous rotation)
+
+ // The coordinate axes are 10 % longer than the bounds of the X,Y,Z values
+ private const double AXIS_EXCESS = 0.1;
+
+ // For any strange reason the graph is not centered vertically
+ private const int VERT_OFFSET = -30;
+
+ // The maximum distance between mouse pointer and a 2D point to display the tooltip
+ private const int TOOLTIP_RADIUS = 6;
+
+ // The maximum distance between mouse pointer and a 2D point to allow a match when selecting
+ // a 3D object.
+ private const int SELECT_RADIUS = 3;
+
+ // Calculate 3-dimensional Z value from X,Y values
+ public delegate double delRendererFunction(double X, double Y);
+
+ // IMPORTANT: Read the detailed comment of function SelectionCallback() at the end of this class.
+ public delegate eInvalidate delSelectHandler(eSelEvent e_Event, Keys e_Modifiers, int s32_DeltaX, int s32_DeltaY, cObject3D i_Object);
+
+ private Pen[] mi_BorderPens = new Pen[2];
+ private SolidBrush mi_TopLegendBrush = null;
+ private eRaster me_Raster = eRaster.Labels;
+ private cAxis[] mi_Axis = new cAxis[3];
+ private cMouse mi_Mouse = new cMouse();
+ private List mi_MessageData = new List();
+ private eRecalculate me_Recalculate = eRecalculate.Nothing;
+ private eNormalize me_Normalize = eNormalize.Separate;
+ private eLegendPos me_LegendPos = eLegendPos.BottomLeft;
+ private List mi_AxisLines = new List(); // 0, 3, or 45 axis lines of coordinate system
+ private List mi_UserObjects = new List(); // Draw objects from the user (cLine, cShape, cPolygon)
+ private List mi_AllObjects = new List(); // mi_UserObjects + mi_AxisLines
+ private cQuadrant mi_Quadrant = new cQuadrant();
+ private Dictionary mi_UserInputs = new Dictionary();
+ private cUndoImpl mi_UndoBuffer;
+ private cTransform mi_Transform;
+ private cBounds mi_Bounds;
+ private cTooltip mi_Tooltip;
+ private cSelection mi_Selection;
+ private cObject3D mi_DragObject;
+
+ #region Properties
+
+ ///
+ /// See comment of enum eTooltip. This property can also be set in the Visual Studio Designer
+ ///
+ public eTooltip TooltipMode
+ {
+ get { return mi_Tooltip.Mode; }
+ set { mi_Tooltip.Mode = value; }
+ }
+
+ ///
+ /// See comment of enum eLegendPos. This property can also be set in the Visual Studio Designer
+ ///
+ public eLegendPos LegendPos
+ {
+ get { return me_LegendPos; }
+ set { me_LegendPos = value; }
+ }
+
+ ///
+ /// See comment of enum eNormalize. This change will become visible the next time you call Invalidate()
+ ///
+ public eNormalize Normalize
+ {
+ get { return me_Normalize; }
+ set
+ {
+ if (me_Normalize != value)
+ {
+ me_Normalize = value;
+ me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+ }
+ }
+
+ ///
+ /// See comment of enum eRaster This property can also be set in the Visual Studio Designer
+ /// This change will become visible the next time you call Invalidate()
+ ///
+ public eRaster Raster
+ {
+ set
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ if (me_Raster != value)
+ {
+ me_Raster = value;
+ me_Recalculate |= eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+ }
+ get
+ {
+ return me_Raster;
+ }
+ }
+
+ ///
+ /// Sets the border color when the 3D Editor does not have the keyboard focus Setting
+ /// BorderColor = Color.Empty turns off the border This change will become visible the next
+ /// time you call Invalidate() This property can also be set in the Visual Studio Designer
+ ///
+ public Color BorderColorNormal
+ {
+ set
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ if (value.A > 0) mi_BorderPens[0] = new Pen(value, 1);
+ else mi_BorderPens[0] = null; // transparent color
+ }
+ get
+ {
+ if (mi_BorderPens[0] != null) return mi_BorderPens[0].Color;
+ else return Color.Empty;
+ }
+ }
+
+ ///
+ /// Sets the border color when the 3D Editor has the keyboard focus Setting BorderColorFocus
+ /// = Color.Empty turns off the highlighting on focus. This change will become visible the
+ /// next time you call Invalidate() This property can also be set in the Visual Studio Designer
+ ///
+ public Color BorderColorFocus
+ {
+ set
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ if (value.A > 0) mi_BorderPens[1] = new Pen(value, 1);
+ else mi_BorderPens[1] = mi_BorderPens[0];
+ }
+ get
+ {
+ if (mi_BorderPens[1] != null) return mi_BorderPens[1].Color;
+ else return BorderColorNormal;
+ }
+ }
+
+ ///
+ /// Show a legend with Rotation, Elevation and Distance at the top left Setting LegendColor
+ /// = Color.Empty turns off the top legend This property can also be set in the Visual
+ /// Studio Designer This change will become visible the next time you call Invalidate()
+ ///
+ public Color TopLegendColor
+ {
+ set
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ mi_TopLegendBrush = new SolidBrush(value);
+ }
+ get
+ {
+ if (mi_TopLegendBrush != null) return mi_TopLegendBrush.Color;
+ else return Color.Empty;
+ }
+ }
+
+ ///
+ /// returns the total count of loaded draw objects (lines, shapes and polygons)
+ ///
+ [Browsable(false)]
+ public String ObjectStatistics
+ {
+ get
+ {
+ int s32_Lines = 0;
+ int s32_Shapes = 0;
+ int s32_Polygons = 0;
+ foreach (cDrawObj i_Obj in mi_UserObjects)
+ {
+ if (i_Obj is cLine) s32_Lines++;
+ if (i_Obj is cShape) s32_Shapes++;
+ if (i_Obj is cPolygon) s32_Polygons++;
+ }
+ StringBuilder i_Out = new StringBuilder();
+ if (s32_Lines > 0) i_Out.Append(s32_Lines + " Lines, ");
+ if (s32_Shapes > 0) i_Out.Append(s32_Shapes + " Shapes, ");
+ if (s32_Polygons > 0) i_Out.Append(s32_Polygons + " Polygons, ");
+ return i_Out.ToString().TrimEnd(' ', ',');
+ }
+ }
+
+ ///
+ /// See comments for class cAxis The properties of the class cAxis can be expanded in the
+ /// Visual Studio designer
+ ///
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public cAxis AxisX
+ {
+ get { return mi_Axis[(int)eCoord.X]; }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public cAxis AxisY
+ {
+ get { return mi_Axis[(int)eCoord.Y]; }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public cAxis AxisZ
+ {
+ get { return mi_Axis[(int)eCoord.Z]; }
+ }
+
+ ///
+ /// This property controls if and how the user can select draw objects / points The
+ /// properties of the class cSelection can be expanded in the Visual Studio designer
+ ///
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public cSelection Selection
+ {
+ get { return mi_Selection; }
+ }
+
+ ///
+ /// This property contains the public methods for the Undo / Redo buffer The property
+ /// 'Enabled' can be expanded in the Visual Studio designer
+ ///
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public cUndoBuffer UndoBuffer
+ {
+ get { return mi_UndoBuffer; }
+ }
+
+ #endregion Properties
+
+ ///
+ /// b_ResetOffset = true --> reset the offset that the user has created with SHIFT +
+ /// moving the 3D object This change will become visible the next time you call Invalidate()
+ ///
+ public void SetCoefficients(double d_Rho, double d_Theta, double d_Phi, bool b_ResetOffset = true)
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ mi_Mouse.SetRho(d_Rho);
+ mi_Mouse.SetTheta(d_Theta);
+ mi_Mouse.SetPhi(d_Phi);
+
+ if (b_ResetOffset)
+ {
+ mi_Mouse.mk_OffMove.X = 0;
+ mi_Mouse.mk_OffMove.Y = 0;
+ }
+
+ mi_Transform.SetCoefficients(mi_Mouse);
+ }
+
+ ///
+ /// Convert mouse movement in 2D space back into the 3D space depending on the current
+ /// rotation angle and Min/Max values.
+ ///
+ public cPoint3D ReverseProject(int s32_MouseX, int s32_MouseY)
+ {
+ double d_FactX = mi_Transform.ReverseProject(mi_Bounds.X.Range, 0.0, 0.0);
+ double d_FactY = mi_Transform.ReverseProject(0.0, mi_Bounds.Y.Range, 0.0);
+ double d_FactZ = mi_Transform.ReverseProject(0.0, 0.0, mi_Bounds.Z.Range);
+
+ return new cPoint3D(d_FactX * s32_MouseX / 300.0,
+ d_FactY * s32_MouseX / 300.0,
+ d_FactZ * s32_MouseY / 300.0);
+ }
+
+ ///
+ /// Trackbars are optional for user interaction. If this function is never called
+ /// thetrackbars are not used.
+ ///
+ public void AssignTrackBars(TrackBar i_Rho, TrackBar i_Theta, TrackBar i_Phi)
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ mi_Mouse.AssignTrackbar(eMouseAction.Rho, i_Rho, new EventHandler(OnTrackbarScroll));
+ mi_Mouse.AssignTrackbar(eMouseAction.Theta, i_Theta, new EventHandler(OnTrackbarScroll));
+ mi_Mouse.AssignTrackbar(eMouseAction.Phi, i_Phi, new EventHandler(OnTrackbarScroll));
+ }
+
+ ///
+ /// Load one of the three pre-defined input control patterns
+ ///
+ public void SetUserInputs(eMouseCtrl e_MouseCtrl)
+ {
+ List i_Inputs = new List();
+
+ switch (e_MouseCtrl)
+ {
+ case eMouseCtrl.L_Theta_R_Phi:
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.None, eMouseAction.Theta));
+ i_Inputs.Add(new cUserInput(MouseButtons.Right, Keys.None, eMouseAction.Phi));
+ break;
+
+ case eMouseCtrl.L_Theta_L_Phi:
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.None, eMouseAction.ThetaAndPhi));
+ break;
+
+ case eMouseCtrl.M_Theta_M_Phi:
+ i_Inputs.Add(new cUserInput(MouseButtons.Middle, Keys.None, eMouseAction.ThetaAndPhi));
+ break;
+ }
+
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Control, eMouseAction.Rho));
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Shift, eMouseAction.Move));
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Alt, eMouseAction.SelectObj));
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Alt | Keys.Control, eMouseAction.Callback));
+ i_Inputs.Add(new cUserInput(MouseButtons.Left, Keys.Alt | Keys.Shift, eMouseAction.Callback));
+
+ SetUserInputs(i_Inputs.ToArray());
+ }
+
+ ///
+ /// Load fully user defined input control patterns. Each user input must define a unique
+ /// combination of mouse button and modifier key(s).
+ ///
+ public void SetUserInputs(cUserInput[] i_Inputs)
+ {
+ mi_UserInputs.Clear();
+ foreach (cUserInput i_Input in i_Inputs)
+ {
+ // throws if same UID has already been added
+ mi_UserInputs.Add(i_Input.UID, i_Input);
+ }
+ }
+
+ // ==================================================================================
+
+ ///
+ /// Constructor
+ ///
+ public Editor3D()
+ {
this.DoubleBuffered = true; // 启用双缓冲
- // avoid flicker
- SetStyle(ControlStyles.AllPaintingInWmPaint, true);
- SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
-
- mi_Bounds = new cBounds (this);
- mi_Transform = new cTransform(this);
- mi_Tooltip = new cTooltip (this);
- mi_Selection = new cSelection(this);
- mi_UndoBuffer = new cUndoImpl (this);
-
- // Load the default colors
- BackColor = Color.White;
-
- mi_Axis[(int)eCoord.X] = new cAxis(this, eCoord.X, Color.DarkBlue);
- mi_Axis[(int)eCoord.Y] = new cAxis(this, eCoord.Y, Color.DarkGreen);
- mi_Axis[(int)eCoord.Z] = new cAxis(this, eCoord.Z, Color.DarkRed);
-
- mi_BorderPens[0] = new Pen (Color.FromArgb(255, 0xB4, 0xB4, 0xB4), 1); // normal border: bright gray
- mi_BorderPens[1] = new Pen (Color.FromArgb(255, 0x33, 0x99, 0xFF), 1); // focused border: bright cyan
- mi_TopLegendBrush = new SolidBrush(Color.FromArgb(255, 0xC8, 0xC8, 0x96)); // beige
-
- mi_Transform.SetCoefficients(mi_Mouse);
- SetUserInputs(eMouseCtrl.L_Theta_R_Phi);
- }
-
- // ==================================================================================
-
- ///
- /// Removes all content from the control.
- /// This change will become visible the next time you call Invalidate()
- ///
- public void Clear()
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- mi_MessageData.Clear();
- mi_UserObjects.Clear();
- mi_AxisLines .Clear();
- mi_AllObjects .Clear();
- mi_UndoBuffer .Clear();
- AxisX.Reset();
- AxisY.Reset();
- AxisZ.Reset();
- mi_Mouse.mk_OffMove = Point.Empty;
- mi_Mouse.mk_OffCoord = Point.Empty;
- me_Recalculate = eRecalculate.Nothing;
- }
-
- ///
- /// Adds a message to be shown, even if no 3D data is loaded.
- /// Messages which are null are allowed, they will be skipped.
- /// This change will become visible the next time you call Invalidate()
- ///
- public void AddMessageData(params cMessgData[] i_Messages)
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- foreach (cMessgData i_Mesg in i_Messages)
- {
- if (i_Mesg != null)
- mi_MessageData.Add(i_Mesg);
- }
- }
-
- ///
- /// Here you can add cSurfaceData, cScatterData, cPolygonData
- /// RenderData which are null are allowed, they will be skipped.
- /// This change will become visible the next time you call Invalidate()
- ///
- public void AddRenderData(params cRenderData[] i_Render)
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- foreach (cRenderData i_Data in i_Render)
- {
- if (i_Data != null)
- {
- i_Data.AddDrawObjects(this);
- me_Recalculate |= eRecalculate.AddRemove | eRecalculate.CoordSystem | eRecalculate.Objects;
- }
- }
- }
-
- ///
- /// This is called from cRenderData.AddDrawObjects()
- ///
- private void AddDrawObject(cDrawObj i_Obj)
- {
- mi_UserObjects.Add(i_Obj);
- mi_UndoBuffer.DrawObjectAdded(i_Obj);
- }
-
- ///
- /// Removes one or multiple 3D objects: cLine3D, cShape3D or cPolygon3D.
- /// ATTENTION: It is not possible to remove a cPoint3D which is part of one or multiple Lines/Shapes/Polygons
- /// This change will become visible the next time you call Invalidate()
- ///
- public void RemoveObjects(params cObject3D[] i_Objects)
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
-
- foreach (cObject3D i_Object3D in i_Objects)
- {
- if (i_Object3D is cPoint3D)
- throw new ArgumentException("You cannot remove a single point. Remove the 3D object that contains the point instead.");
-
- int s32_Found = -1;
- for (int D=0; D
- /// Search the object at the coordinate X, Y relative to to the upper left corner of the control.
- /// b_OnlyCanSelect = true --> return only objects that can be selected by the user
- /// b_OnlyCanSelect = false --> return any object at the given location
- ///
- public cObject3D FindObjectAt(int X, int Y, bool b_OnlyCanSelect)
- {
- X -= (mi_Mouse.mk_OffMove.X + mi_Mouse.mk_OffCoord.X);
- Y -= (mi_Mouse.mk_OffMove.Y + mi_Mouse.mk_OffCoord.Y);
-
- // Search in reverse order. Last drawn objects are in foreground.
- // ATTENTION: mi_UserObjects cannot be used here because it is not sorted --> background polygons would be found.
- for (int i=mi_AllObjects.Count -1; i>=0; i--)
- {
- cDrawObj i_Draw = mi_AllObjects[i];
-
- if (i_Draw.mi_Object3D == null)
- continue; // a coordinate system line
-
- if (b_OnlyCanSelect && !i_Draw.mi_Object3D.CanSelect)
- continue; // user selection disabled for this object
-
- // MatchesPoint2D() returns a cPoint3D, cLine3D, cShape3D or cPolygon3D.
- cObject3D i_Found = i_Draw.MatchesPoint2D(X, Y);
- if (i_Found != null)
- return i_Found;
- }
- return null;
- }
-
- // ========================================== PRIVATE ============================================
-
- ///
- /// This function normalizes the 3D ranges for the X,Y,Z coordinates.
- /// Otherwise a 3D range of X,Y from -10 to +10 will appear much smaller than a range from -100 to +100.
- /// It adapts the values so that rotation (phi) goes through the center of the X, Y pane.
- ///
- private void NormalizeRanges()
- {
- double d_RangeX = mi_Bounds.X.Range;
- double d_RangeY = mi_Bounds.Y.Range;
- double d_RangeZ = mi_Bounds.Z.Range;
-
- switch (me_Normalize)
- {
- case eNormalize.MaintainXY:
- double d_RangeXY = (d_RangeX + d_RangeY) / 2.0; // average
- d_RangeX = d_RangeXY;
- d_RangeY = d_RangeXY;
- break;
-
- case eNormalize.MaintainXYZ:
- double d_RangeXYZ = (d_RangeX + d_RangeY + d_RangeZ) / 3.0; // average
- d_RangeX = d_RangeXYZ;
- d_RangeY = d_RangeXYZ;
- d_RangeZ = d_RangeXYZ;
- break;
- }
-
- mi_Transform.md_NormalizeX = 250.0 / d_RangeX; // Ranges will never be zero.
- mi_Transform.md_NormalizeY = 250.0 / d_RangeY;
- mi_Transform.md_NormalizeZ = 250.0 / d_RangeZ;
-
- mi_Transform.mi_Center3D.X = (mi_Bounds.X.Max + mi_Bounds.X.Min) / 2.0; // average
- mi_Transform.mi_Center3D.Y = (mi_Bounds.Y.Max + mi_Bounds.Y.Min) / 2.0;
- mi_Transform.mi_Center3D.Z = (mi_Bounds.Z.Max + mi_Bounds.Z.Min) / 2.0;
- }
-
- ///
- /// Fills mi_AxisLines with 3 main axis and 42 raster lines
- ///
- private void CreateCoordinateSystem(Graphics i_Graph)
- {
- mi_Mouse.mk_OffCoord = new Point(0, VERT_OFFSET);
- mi_AxisLines.Clear();
-
- if (me_Raster == eRaster.Off)
- return;
-
- cLine i_MainAxisX = new cLine(this, eCoord.X, eCoord.X, eMirror.Z);
- mi_AxisLines.Add(i_MainAxisX);
-
- i_MainAxisX.mi_Points[0].mi_P3D.X = mi_Bounds.X.Min;
- i_MainAxisX.mi_Points[1].mi_P3D.X = mi_Bounds.X.Max;
- // ------------
- i_MainAxisX.mi_Points[0].mi_P3D.Y = mi_Bounds.Y.Min; // X axis at minimum Y position
- i_MainAxisX.mi_Points[1].mi_P3D.Y = mi_Bounds.Y.Min; // X axis at minimum Y position
-
- // ---------------------------------------------------
-
- cLine i_MainAxisY = new cLine(this, eCoord.Y, eCoord.Y, eMirror.Z);
- mi_AxisLines.Add(i_MainAxisY);
-
- i_MainAxisY.mi_Points[0].mi_P3D.Y = mi_Bounds.Y.Min;
- i_MainAxisY.mi_Points[1].mi_P3D.Y = mi_Bounds.Y.Max;
- // ------------
- i_MainAxisY.mi_Points[0].mi_P3D.X = mi_Bounds.X.Min; // Y axis at minimum X position
- i_MainAxisY.mi_Points[1].mi_P3D.X = mi_Bounds.X.Min; // Y axis at minimum X position
- // ------------
- if (mi_Bounds.Z.Min < 0.0 && mi_Bounds.Z.Max > 0.0)
- i_MainAxisY.ms_Label = "0"; // label for Z value zero (red)
-
- // ---------------------------------------------------
-
- cLine i_MainAxisZ = new cLine(this, eCoord.Z, eCoord.Z, eMirror.None);
- mi_AxisLines.Add(i_MainAxisZ);
-
- i_MainAxisZ.mi_Points[0].mi_P3D.Z = mi_Bounds.Z.Min;
- i_MainAxisZ.mi_Points[1].mi_P3D.Z = mi_Bounds.Z.Max;
- // ------------
- i_MainAxisZ.mi_Points[0].mi_P3D.X = mi_Bounds.X.Min; // Z axis start at minimum X position
- i_MainAxisZ.mi_Points[1].mi_P3D.X = mi_Bounds.X.Min; // Z axis start at minimum X position
- i_MainAxisZ.mi_Points[0].mi_P3D.Y = mi_Bounds.Y.Min; // Z axis start at minimum Y position
- i_MainAxisZ.mi_Points[1].mi_P3D.Y = mi_Bounds.Y.Min; // Z axis start at minimum Y position
-
- // ---------------------------------------------------
-
- foreach (cLine i_Axis in mi_AxisLines)
- {
- i_Axis.Project3D();
- i_Axis.CalcAngle2D(); // required to calculate the quadrant
- }
-
- // Calculate currently visible quadrant
- mi_Quadrant.Calculate(mi_Mouse.md_Phi, i_MainAxisX, i_MainAxisY, i_MainAxisZ);
-
- // Add raster lines in 6 different directions
- if (me_Raster >= eRaster.Raster)
- {
- for (int A=0; A<6; A++) // iterate Axes X,Y,Z twice
- {
- int F = A;
- int S = A;
-
- // Combine X+Y, Y+Z, Z+X, Y+X, Z+Y, X+Z
- if (A >= 3) F ++;
- else S ++;
-
- eCoord e_First = (eCoord)(F % 3);
- eCoord e_Second = (eCoord)(S % 3);
-
- // Define which mirror operations are allowed for this raster line
- eMirror e_Mirror = eMirror.None;
- if (e_Second == eCoord.X) e_Mirror = eMirror.X;
- if (e_Second == eCoord.Y) e_Mirror = eMirror.Y;
- if (e_Second == eCoord.Z) e_Mirror = eMirror.Z;
-
- cLine i_FirstLine = mi_AxisLines[(int)e_First]; // Main axis
- cLine i_SecndLine = mi_AxisLines[(int)e_Second]; // Main axis
-
- double d_SecndStart = i_SecndLine.mi_Points[0].mi_P3D.GetValue(e_Second);
- double d_SecndEnd = i_SecndLine.mi_Points[1].mi_P3D.GetValue(e_Second);
-
- // Distance between raster lines
- double d_Interval = CalculateInterval(d_SecndEnd - d_SecndStart);
-
- int s32_Start = (int)(d_SecndStart / d_Interval) - 1;
- int s32_End = (int)(d_SecndEnd / d_Interval) + 1;
-
- for (int L=s32_Start; L d_SecndEnd)
- continue;
-
- cLine i_Raster = new cLine(this, e_First, e_Second, e_Mirror);
-
- i_Raster.mi_Points[0].mi_P3D = i_FirstLine.mi_Points[0].mi_P3D.Clone();
- i_Raster.mi_Points[1].mi_P3D = i_FirstLine.mi_Points[1].mi_P3D.Clone();
-
- i_Raster.mi_Points[0].mi_P3D.SetValue(e_Second, d_Offset);
- i_Raster.mi_Points[1].mi_P3D.SetValue(e_Second, d_Offset);
-
- i_Raster.ms_Label = FormatDouble(d_Offset);
-
- // Do not draw the raster line at value zero if it equals the main axis and the axis is not mirrored
- if (L == 0 && !mi_Axis[(int)e_Second].Mirror && i_Raster.CoordEquals(mi_AxisLines[(int)e_First]))
- continue;
-
- if ((e_First == eCoord.X && e_Second == eCoord.Z) || // Blue
- (e_First == eCoord.Z && e_Second == eCoord.X))
- {
- i_Raster.md_Sort = mi_Quadrant.md_SortXZ;
- }
- else if ((e_First == eCoord.Z && e_Second == eCoord.Y) || // Green
- (e_First == eCoord.Y && e_Second == eCoord.Z))
- {
- i_Raster.md_Sort = mi_Quadrant.md_SortYZ;
- }
- else // X + Y Red
- {
- i_Raster.md_Sort = mi_Quadrant.md_SortXY;
-
- // Special case: XY raster lines must be shifted down to negative end of Z axis
- i_Raster.mi_Points[0].mi_P3D.Z = i_MainAxisZ.mi_Points[0].mi_P3D.Z;
- i_Raster.mi_Points[1].mi_P3D.Z = i_MainAxisZ.mi_Points[0].mi_P3D.Z;
- }
-
- i_Raster.Project3D();
- mi_AxisLines.Add(i_Raster);
- } // for (L)
- } // for (A)
- } // if (Raster)
-
- // Remove the green and blue main axes if Z value zero is outside the visible range
- if (mi_Bounds.Z.Min > 0.0 || mi_Bounds.Z.Max < 0.0)
- {
- mi_AxisLines.Remove(i_MainAxisX);
- mi_AxisLines.Remove(i_MainAxisY);
- }
-
- // Move the graph to the left when labels are enabled
- if (me_Raster == eRaster.Labels)
- {
- int s32_LabelWidth = 0;
- foreach (cLine i_Line in mi_AxisLines)
- {
- if (i_Line.me_Line == eCoord.Y && i_Line.me_Offset == eCoord.Z)
- {
- SizeF k_Size = i_Graph.MeasureString(i_Line.ms_Label, Font);
- s32_LabelWidth = Math.Max(s32_LabelWidth, (int)k_Size.Width);
- }
- }
- mi_Mouse.mk_OffCoord.X -= s32_LabelWidth / 2;
- }
- }
-
- ///
- /// Makes a color brigther
- ///
- private static Color BrightenColor(Color c_Color)
- {
- int s32_Red = c_Color.R + (255 - c_Color.R) / 2;
- int s32_Green = c_Color.G + (255 - c_Color.G) / 2;
- int s32_Blue = c_Color.B + (255 - c_Color.B) / 2;
-
- return Color.FromArgb(255, s32_Red, s32_Green, s32_Blue);
- }
-
- ///
- /// returns intervals of 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, etc...
- /// The count of intervals which fit into the range is always between 5 and 10
- ///
- private static double CalculateInterval(double d_Range)
- {
- double d_Factor = Math.Pow(10.0, Math.Floor(Math.Log10(d_Range)));
- if (d_Range / d_Factor >= 5.0)
- return d_Factor;
- else if (d_Range / (d_Factor / 2.0) >= 5.0)
- return d_Factor / 2.0;
- else
- return d_Factor / 5.0;
- }
-
- // md_Label = 123.000 --> display "123"
- // md_Label = 15.700 --> display "15.7"
- // md_Label = 4.260 --> display "4.26"
- // md_Label = 0.834 --> display "0.834"
- public static String FormatDouble(double d_Label)
- {
- return d_Label.ToString("0.000", CultureInfo.InvariantCulture).TrimEnd('0').TrimEnd('.');
- }
-
- ///
- /// For debugging drawing speed.
- /// ATTENTION: The first time the delays are wrong because of JIT compilation delays.
- ///
- private static void FormatStopwatch(String s_Measure, Stopwatch i_Watch, StringBuilder i_Debug)
- {
- double d_Elapsed = (double)i_Watch.ElapsedTicks / TimeSpan.TicksPerMillisecond;
- i_Debug.AppendLine(s_Measure.PadRight(17) + d_Elapsed.ToString("0.000") + " ms");
- i_Watch.Restart();
- }
-
- ///
- /// Checks if the 2D point X,Y lies on the line between i_Start and i_End within s32_MaxDist
- /// All coordinates in pixels.
- ///
- private static bool IsPointOnLine(cPoint2D i_Start, cPoint2D i_End, int X, int Y, int s32_MaxDist)
- {
- double d_DeltaX = i_End.md_X - i_Start.md_X;
- double d_DeltaY = i_End.md_Y - i_Start.md_Y;
-
- int s32_StepsX = Math.Abs((int)d_DeltaX);
- int s32_StepsY = Math.Abs((int)d_DeltaY);
- int s32_Steps = Math.Max(s32_StepsX, s32_StepsY);
-
- d_DeltaX /= s32_Steps;
- d_DeltaY /= s32_Steps;
-
- cPoint2D i_Point = i_Start.Clone();
- for (int S=0; S<=s32_Steps; S++)
- {
- if (i_Point.CalcDistanceTo(X, Y) <= s32_MaxDist)
- return true;
-
- i_Point.md_X += d_DeltaX;
- i_Point.md_Y += d_DeltaY;
- }
- return false;
- }
-
- // =================================== DRAWING =====================================
-
- protected override void OnPaintBackground(PaintEventArgs e)
- {
- // Background is painted flickerless in OnPaint()
- }
-
- ///
- /// This is invoked by Invalidate() when the GUI thread becomes idle.
- ///
- protected override void OnPaint(PaintEventArgs e)
- {
+ // avoid flicker
+ SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+ SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+
+ mi_Bounds = new cBounds(this);
+ mi_Transform = new cTransform(this);
+ mi_Tooltip = new cTooltip(this);
+ mi_Selection = new cSelection(this);
+ mi_UndoBuffer = new cUndoImpl(this);
+
+ // Load the default colors
+ BackColor = Color.White;
+
+ mi_Axis[(int)eCoord.X] = new cAxis(this, eCoord.X, Color.DarkBlue);
+ mi_Axis[(int)eCoord.Y] = new cAxis(this, eCoord.Y, Color.DarkGreen);
+ mi_Axis[(int)eCoord.Z] = new cAxis(this, eCoord.Z, Color.DarkRed);
+
+ mi_BorderPens[0] = new Pen(Color.FromArgb(255, 0xB4, 0xB4, 0xB4), 1); // normal border: bright gray
+ mi_BorderPens[1] = new Pen(Color.FromArgb(255, 0x33, 0x99, 0xFF), 1); // focused border: bright cyan
+ mi_TopLegendBrush = new SolidBrush(Color.FromArgb(255, 0xC8, 0xC8, 0x96)); // beige
+
+ mi_Transform.SetCoefficients(mi_Mouse);
+ SetUserInputs(eMouseCtrl.L_Theta_R_Phi);
+ }
+
+ // ==================================================================================
+
+ ///
+ /// Removes all content from the control. This change will become visible the next time you
+ /// call Invalidate()
+ ///
+ public void Clear()
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ mi_MessageData.Clear();
+ mi_UserObjects.Clear();
+ mi_AxisLines.Clear();
+ mi_AllObjects.Clear();
+ mi_UndoBuffer.Clear();
+ AxisX.Reset();
+ AxisY.Reset();
+ AxisZ.Reset();
+ mi_Mouse.mk_OffMove = Point.Empty;
+ mi_Mouse.mk_OffCoord = Point.Empty;
+ me_Recalculate = eRecalculate.Nothing;
+ }
+
+ ///
+ /// Adds a message to be shown, even if no 3D data is loaded. Messages which are null are
+ /// allowed, they will be skipped. This change will become visible the next time you call Invalidate()
+ ///
+ public void AddMessageData(params cMessgData[] i_Messages)
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ foreach (cMessgData i_Mesg in i_Messages)
+ {
+ if (i_Mesg != null)
+ mi_MessageData.Add(i_Mesg);
+ }
+ }
+
+ ///
+ /// Here you can add cSurfaceData, cScatterData, cPolygonData RenderData which are null are
+ /// allowed, they will be skipped. This change will become visible the next time you call Invalidate()
+ ///
+ public void AddRenderData(params cRenderData[] i_Render)
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ foreach (cRenderData i_Data in i_Render)
+ {
+ if (i_Data != null)
+ {
+ i_Data.AddDrawObjects(this);
+ me_Recalculate |= eRecalculate.AddRemove | eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+ }
+ }
+
+ ///
+ /// This is called from cRenderData.AddDrawObjects()
+ ///
+ private void AddDrawObject(cDrawObj i_Obj)
+ {
+ mi_UserObjects.Add(i_Obj);
+ mi_UndoBuffer.DrawObjectAdded(i_Obj);
+ }
+
+ ///
+ /// Removes one or multiple 3D objects: cLine3D, cShape3D or cPolygon3D.
+ /// ATTENTION: It is not possible to remove a cPoint3D which is part of one or multiple Lines/Shapes/Polygons
+ /// This change will become visible the next time you call Invalidate()
+ ///
+ public void RemoveObjects(params cObject3D[] i_Objects)
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
+
+ foreach (cObject3D i_Object3D in i_Objects)
+ {
+ if (i_Object3D is cPoint3D)
+ throw new ArgumentException("You cannot remove a single point. Remove the 3D object that contains the point instead.");
+
+ int s32_Found = -1;
+ for (int D = 0; D < mi_UserObjects.Count; D++)
+ {
+ if (mi_UserObjects[D].mi_Object3D == i_Object3D)
+ {
+ s32_Found = D;
+ break;
+ }
+ }
+
+ if (s32_Found < 0)
+ {
+ Debug.Assert(false, "There is something wrong in your code. You are removing an object that does not exist.");
+ }
+ else
+ {
+ cDrawObj i_DrawObj = mi_UserObjects[s32_Found];
+ mi_UserObjects.RemoveAt(s32_Found);
+ mi_UndoBuffer.DrawObjectRemoved(i_DrawObj);
+ me_Recalculate |= eRecalculate.AddRemove | eRecalculate.CoordSystem | eRecalculate.Objects;
+ }
+ }
+
+ mi_UndoBuffer.Store();
+ }
+
+ ///
+ /// Search the object at the coordinate X, Y relative to to the upper left corner of the
+ /// control. b_OnlyCanSelect = true --> return only objects that can be selected by the
+ /// user b_OnlyCanSelect = false --> return any object at the given location
+ ///
+ public cObject3D FindObjectAt(int X, int Y, bool b_OnlyCanSelect)
+ {
+ X -= (mi_Mouse.mk_OffMove.X + mi_Mouse.mk_OffCoord.X);
+ Y -= (mi_Mouse.mk_OffMove.Y + mi_Mouse.mk_OffCoord.Y);
+
+ // Search in reverse order. Last drawn objects are in foreground.
+ // ATTENTION: mi_UserObjects cannot be used here because it is not sorted --> background
+ // polygons would be found.
+ for (int i = mi_AllObjects.Count - 1; i >= 0; i--)
+ {
+ cDrawObj i_Draw = mi_AllObjects[i];
+
+ if (i_Draw.mi_Object3D == null)
+ continue; // a coordinate system line
+
+ if (b_OnlyCanSelect && !i_Draw.mi_Object3D.CanSelect)
+ continue; // user selection disabled for this object
+
+ // MatchesPoint2D() returns a cPoint3D, cLine3D, cShape3D or cPolygon3D.
+ cObject3D i_Found = i_Draw.MatchesPoint2D(X, Y);
+ if (i_Found != null)
+ return i_Found;
+ }
+ return null;
+ }
+
+ // ========================================== PRIVATE ============================================
+
+ ///
+ /// This function normalizes the 3D ranges for the X,Y,Z coordinates. Otherwise a 3D range
+ /// of X,Y from -10 to +10 will appear much smaller than a range from -100 to +100. It
+ /// adapts the values so that rotation (phi) goes through the center of the X, Y pane.
+ ///
+ private void NormalizeRanges()
+ {
+ double d_RangeX = mi_Bounds.X.Range;
+ double d_RangeY = mi_Bounds.Y.Range;
+ double d_RangeZ = mi_Bounds.Z.Range;
+
+ switch (me_Normalize)
+ {
+ case eNormalize.MaintainXY:
+ double d_RangeXY = (d_RangeX + d_RangeY) / 2.0; // average
+ d_RangeX = d_RangeXY;
+ d_RangeY = d_RangeXY;
+ break;
+
+ case eNormalize.MaintainXYZ:
+ double d_RangeXYZ = (d_RangeX + d_RangeY + d_RangeZ) / 3.0; // average
+ d_RangeX = d_RangeXYZ;
+ d_RangeY = d_RangeXYZ;
+ d_RangeZ = d_RangeXYZ;
+ break;
+ }
+
+ mi_Transform.md_NormalizeX = 250.0 / d_RangeX; // Ranges will never be zero.
+ mi_Transform.md_NormalizeY = 250.0 / d_RangeY;
+ mi_Transform.md_NormalizeZ = 250.0 / d_RangeZ;
+
+ mi_Transform.mi_Center3D.X = (mi_Bounds.X.Max + mi_Bounds.X.Min) / 2.0; // average
+ mi_Transform.mi_Center3D.Y = (mi_Bounds.Y.Max + mi_Bounds.Y.Min) / 2.0;
+ mi_Transform.mi_Center3D.Z = (mi_Bounds.Z.Max + mi_Bounds.Z.Min) / 2.0;
+ }
+
+ ///
+ /// Fills mi_AxisLines with 3 main axis and 42 raster lines
+ ///
+ private void CreateCoordinateSystem(Graphics i_Graph)
+ {
+ mi_Mouse.mk_OffCoord = new Point(0, VERT_OFFSET);
+ mi_AxisLines.Clear();
+
+ if (me_Raster == eRaster.Off)
+ return;
+
+ cLine i_MainAxisX = new cLine(this, eCoord.X, eCoord.X, eMirror.Z);
+ mi_AxisLines.Add(i_MainAxisX);
+
+ i_MainAxisX.mi_Points[0].mi_P3D.X = mi_Bounds.X.Min;
+ i_MainAxisX.mi_Points[1].mi_P3D.X = mi_Bounds.X.Max;
+ // ------------
+ i_MainAxisX.mi_Points[0].mi_P3D.Y = mi_Bounds.Y.Min; // X axis at minimum Y position
+ i_MainAxisX.mi_Points[1].mi_P3D.Y = mi_Bounds.Y.Min; // X axis at minimum Y position
+
+ // ---------------------------------------------------
+
+ cLine i_MainAxisY = new cLine(this, eCoord.Y, eCoord.Y, eMirror.Z);
+ mi_AxisLines.Add(i_MainAxisY);
+
+ i_MainAxisY.mi_Points[0].mi_P3D.Y = mi_Bounds.Y.Min;
+ i_MainAxisY.mi_Points[1].mi_P3D.Y = mi_Bounds.Y.Max;
+ // ------------
+ i_MainAxisY.mi_Points[0].mi_P3D.X = mi_Bounds.X.Min; // Y axis at minimum X position
+ i_MainAxisY.mi_Points[1].mi_P3D.X = mi_Bounds.X.Min; // Y axis at minimum X position
+ // ------------
+ if (mi_Bounds.Z.Min < 0.0 && mi_Bounds.Z.Max > 0.0)
+ i_MainAxisY.ms_Label = "0"; // label for Z value zero (red)
+
+ // ---------------------------------------------------
+
+ cLine i_MainAxisZ = new cLine(this, eCoord.Z, eCoord.Z, eMirror.None);
+ mi_AxisLines.Add(i_MainAxisZ);
+
+ i_MainAxisZ.mi_Points[0].mi_P3D.Z = mi_Bounds.Z.Min;
+ i_MainAxisZ.mi_Points[1].mi_P3D.Z = mi_Bounds.Z.Max;
+ // ------------
+ i_MainAxisZ.mi_Points[0].mi_P3D.X = mi_Bounds.X.Min; // Z axis start at minimum X position
+ i_MainAxisZ.mi_Points[1].mi_P3D.X = mi_Bounds.X.Min; // Z axis start at minimum X position
+ i_MainAxisZ.mi_Points[0].mi_P3D.Y = mi_Bounds.Y.Min; // Z axis start at minimum Y position
+ i_MainAxisZ.mi_Points[1].mi_P3D.Y = mi_Bounds.Y.Min; // Z axis start at minimum Y position
+
+ // ---------------------------------------------------
+
+ foreach (cLine i_Axis in mi_AxisLines)
+ {
+ i_Axis.Project3D();
+ i_Axis.CalcAngle2D(); // required to calculate the quadrant
+ }
+
+ // Calculate currently visible quadrant
+ mi_Quadrant.Calculate(mi_Mouse.md_Phi, i_MainAxisX, i_MainAxisY, i_MainAxisZ);
+
+ // Add raster lines in 6 different directions
+ if (me_Raster >= eRaster.Raster)
+ {
+ for (int A = 0; A < 6; A++) // iterate Axes X,Y,Z twice
+ {
+ int F = A;
+ int S = A;
+
+ // Combine X+Y, Y+Z, Z+X, Y+X, Z+Y, X+Z
+ if (A >= 3) F++;
+ else S++;
+
+ eCoord e_First = (eCoord)(F % 3);
+ eCoord e_Second = (eCoord)(S % 3);
+
+ // Define which mirror operations are allowed for this raster line
+ eMirror e_Mirror = eMirror.None;
+ if (e_Second == eCoord.X) e_Mirror = eMirror.X;
+ if (e_Second == eCoord.Y) e_Mirror = eMirror.Y;
+ if (e_Second == eCoord.Z) e_Mirror = eMirror.Z;
+
+ cLine i_FirstLine = mi_AxisLines[(int)e_First]; // Main axis
+ cLine i_SecndLine = mi_AxisLines[(int)e_Second]; // Main axis
+
+ double d_SecndStart = i_SecndLine.mi_Points[0].mi_P3D.GetValue(e_Second);
+ double d_SecndEnd = i_SecndLine.mi_Points[1].mi_P3D.GetValue(e_Second);
+
+ // Distance between raster lines
+ double d_Interval = CalculateInterval(d_SecndEnd - d_SecndStart);
+
+ int s32_Start = (int)(d_SecndStart / d_Interval) - 1;
+ int s32_End = (int)(d_SecndEnd / d_Interval) + 1;
+
+ for (int L = s32_Start; L < s32_End; L++) // iterate raster lines
+ {
+ double d_Offset = d_Interval * L;
+
+ if (d_Offset < d_SecndStart || d_Offset > d_SecndEnd)
+ continue;
+
+ cLine i_Raster = new cLine(this, e_First, e_Second, e_Mirror);
+
+ i_Raster.mi_Points[0].mi_P3D = i_FirstLine.mi_Points[0].mi_P3D.Clone();
+ i_Raster.mi_Points[1].mi_P3D = i_FirstLine.mi_Points[1].mi_P3D.Clone();
+
+ i_Raster.mi_Points[0].mi_P3D.SetValue(e_Second, d_Offset);
+ i_Raster.mi_Points[1].mi_P3D.SetValue(e_Second, d_Offset);
+
+ i_Raster.ms_Label = FormatDouble(d_Offset);
+
+ // Do not draw the raster line at value zero if it equals the main axis and
+ // the axis is not mirrored
+ if (L == 0 && !mi_Axis[(int)e_Second].Mirror && i_Raster.CoordEquals(mi_AxisLines[(int)e_First]))
+ continue;
+
+ if ((e_First == eCoord.X && e_Second == eCoord.Z) || // Blue
+ (e_First == eCoord.Z && e_Second == eCoord.X))
+ {
+ i_Raster.md_Sort = mi_Quadrant.md_SortXZ;
+ }
+ else if ((e_First == eCoord.Z && e_Second == eCoord.Y) || // Green
+ (e_First == eCoord.Y && e_Second == eCoord.Z))
+ {
+ i_Raster.md_Sort = mi_Quadrant.md_SortYZ;
+ }
+ else // X + Y Red
+ {
+ i_Raster.md_Sort = mi_Quadrant.md_SortXY;
+
+ // Special case: XY raster lines must be shifted down to negative end of
+ // Z axis
+ i_Raster.mi_Points[0].mi_P3D.Z = i_MainAxisZ.mi_Points[0].mi_P3D.Z;
+ i_Raster.mi_Points[1].mi_P3D.Z = i_MainAxisZ.mi_Points[0].mi_P3D.Z;
+ }
+
+ i_Raster.Project3D();
+ mi_AxisLines.Add(i_Raster);
+ } // for (L)
+ } // for (A)
+ } // if (Raster)
+
+ // Remove the green and blue main axes if Z value zero is outside the visible range
+ if (mi_Bounds.Z.Min > 0.0 || mi_Bounds.Z.Max < 0.0)
+ {
+ mi_AxisLines.Remove(i_MainAxisX);
+ mi_AxisLines.Remove(i_MainAxisY);
+ }
+
+ // Move the graph to the left when labels are enabled
+ if (me_Raster == eRaster.Labels)
+ {
+ int s32_LabelWidth = 0;
+ foreach (cLine i_Line in mi_AxisLines)
+ {
+ if (i_Line.me_Line == eCoord.Y && i_Line.me_Offset == eCoord.Z)
+ {
+ SizeF k_Size = i_Graph.MeasureString(i_Line.ms_Label, Font);
+ s32_LabelWidth = Math.Max(s32_LabelWidth, (int)k_Size.Width);
+ }
+ }
+ mi_Mouse.mk_OffCoord.X -= s32_LabelWidth / 2;
+ }
+ }
+
+ ///
+ /// Makes a color brigther
+ ///
+ private static Color BrightenColor(Color c_Color)
+ {
+ int s32_Red = c_Color.R + (255 - c_Color.R) / 2;
+ int s32_Green = c_Color.G + (255 - c_Color.G) / 2;
+ int s32_Blue = c_Color.B + (255 - c_Color.B) / 2;
+
+ return Color.FromArgb(255, s32_Red, s32_Green, s32_Blue);
+ }
+
+ ///
+ /// returns intervals of 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, etc... The count of intervals
+ /// which fit into the range is always between 5 and 10
+ ///
+ private static double CalculateInterval(double d_Range)
+ {
+ double d_Factor = Math.Pow(10.0, Math.Floor(Math.Log10(d_Range)));
+ if (d_Range / d_Factor >= 5.0)
+ return d_Factor;
+ else if (d_Range / (d_Factor / 2.0) >= 5.0)
+ return d_Factor / 2.0;
+ else
+ return d_Factor / 5.0;
+ }
+
+ // md_Label = 123.000 --> display "123" md_Label = 15.700 --> display "15.7" md_Label =
+ // 4.260 --> display "4.26" md_Label = 0.834 --> display "0.834"
+ public static String FormatDouble(double d_Label)
+ {
+ return d_Label.ToString("0.000", CultureInfo.InvariantCulture).TrimEnd('0').TrimEnd('.');
+ }
+
+ ///
+ /// For debugging drawing speed.
+ /// ATTENTION: The first time the delays are wrong because of JIT compilation delays.
+ ///
+ private static void FormatStopwatch(String s_Measure, Stopwatch i_Watch, StringBuilder i_Debug)
+ {
+ double d_Elapsed = (double)i_Watch.ElapsedTicks / TimeSpan.TicksPerMillisecond;
+ i_Debug.AppendLine(s_Measure.PadRight(17) + d_Elapsed.ToString("0.000") + " ms");
+ i_Watch.Restart();
+ }
+
+ ///
+ /// Checks if the 2D point X,Y lies on the line between i_Start and i_End within s32_MaxDist
+ /// All coordinates in pixels.
+ ///
+ private static bool IsPointOnLine(cPoint2D i_Start, cPoint2D i_End, int X, int Y, int s32_MaxDist)
+ {
+ double d_DeltaX = i_End.md_X - i_Start.md_X;
+ double d_DeltaY = i_End.md_Y - i_Start.md_Y;
+
+ int s32_StepsX = Math.Abs((int)d_DeltaX);
+ int s32_StepsY = Math.Abs((int)d_DeltaY);
+ int s32_Steps = Math.Max(s32_StepsX, s32_StepsY);
+
+ d_DeltaX /= s32_Steps;
+ d_DeltaY /= s32_Steps;
+
+ cPoint2D i_Point = i_Start.Clone();
+ for (int S = 0; S <= s32_Steps; S++)
+ {
+ if (i_Point.CalcDistanceTo(X, Y) <= s32_MaxDist)
+ return true;
+
+ i_Point.md_X += d_DeltaX;
+ i_Point.md_Y += d_DeltaY;
+ }
+ return false;
+ }
+
+ // =================================== DRAWING =====================================
+
+ protected override void OnPaintBackground(PaintEventArgs e)
+ {
+ // Background is painted flickerless in OnPaint()
+ }
+
+ ///
+ /// This is invoked by Invalidate() when the GUI thread becomes idle.
+ ///
+ protected override void OnPaint(PaintEventArgs e)
+ {
base.OnPaint(e);
// Stupidly the .NET framework draws a red cross if any exception occurres in OnPaint()
try
- {
- Render(e.Graphics);
- }
- catch (Exception Ex)
- {
- e.Graphics.ResetTransform();
- e.Graphics.Clear(Color.DarkRed);
- e.Graphics.DrawString(Ex.Message + "\n" + Ex.StackTrace, new Font("Verdana", 8, FontStyle.Bold), Brushes.White, 10, 10);
- return;
- }
+ {
+ Render(e.Graphics);
+ }
+ catch (Exception Ex)
+ {
+ e.Graphics.ResetTransform();
+ e.Graphics.Clear(Color.DarkRed);
+ e.Graphics.DrawString(Ex.Message + "\n" + Ex.StackTrace, new Font("Verdana", 8, FontStyle.Bold), Brushes.White, 10, 10);
+ return;
+ }
- DrawBorder(e.Graphics);
- }
+ DrawBorder(e.Graphics);
+ }
- public Bitmap GetScreenshot()
- {
- Debug.Assert(!InvokeRequired); // Call only from GUI thread
+ public Bitmap GetScreenshot()
+ {
+ Debug.Assert(!InvokeRequired); // Call only from GUI thread
- Bitmap i_Bmp = new Bitmap(ClientSize.Width, ClientSize.Height);
- using (Graphics i_Graph = Graphics.FromImage(i_Bmp))
- {
- Render(i_Graph);
- }
- return i_Bmp;
- }
+ Bitmap i_Bmp = new Bitmap(ClientSize.Width, ClientSize.Height);
+ using (Graphics i_Graph = Graphics.FromImage(i_Bmp))
+ {
+ Render(i_Graph);
+ }
+ return i_Bmp;
+ }
- private void Render(Graphics i_Graph)
- {
- i_Graph.Clear(BackColor);
+ private void Render(Graphics i_Graph)
+ {
+ i_Graph.Clear(BackColor);
- foreach (cMessgData i_Mesg in mi_MessageData)
- {
- i_Mesg.Draw(i_Graph, ClientRectangle);
- }
+ foreach (cMessgData i_Mesg in mi_MessageData)
+ {
+ i_Mesg.Draw(i_Graph, ClientRectangle);
+ }
- // If there are no 3D objects --> only show the messages. Do not show an empty coordinate system.
- if (mi_UserObjects.Count == 0)
- return;
+ // If there are no 3D objects --> only show the messages. Do not show an empty
+ // coordinate system.
+ if (mi_UserObjects.Count == 0)
+ return;
- mi_UndoBuffer.Init();
+ mi_UndoBuffer.Init();
- #if DEBUG_SPEED
+#if DEBUG_SPEED
StringBuilder i_Debug = new StringBuilder();
Stopwatch i_Watch = new Stopwatch();
i_Debug.Append("--------------------------\n");
i_Watch.Start();
- #endif
+#endif
- // Speed optimization: Add points to tooltip only if required
- if ((me_Recalculate & eRecalculate.AddRemove) > 0)
- {
- mi_Tooltip.Clear();
- foreach (cDrawObj i_Object in mi_UserObjects)
- {
- foreach (cPoint i_Point in i_Object.mi_Points)
- {
- mi_Tooltip.AddPoint(i_Point);
- }
- }
+ // Speed optimization: Add points to tooltip only if required
+ if ((me_Recalculate & eRecalculate.AddRemove) > 0)
+ {
+ mi_Tooltip.Clear();
+ foreach (cDrawObj i_Object in mi_UserObjects)
+ {
+ foreach (cPoint i_Point in i_Object.mi_Points)
+ {
+ mi_Tooltip.AddPoint(i_Point);
+ }
+ }
- #if DEBUG_SPEED
+#if DEBUG_SPEED
FormatStopwatch("Add Tooltip: ", i_Watch, i_Debug);
- #endif
- }
+#endif
+ }
- // Speed optimization: Calculate coordinate system only if required
- if ((me_Recalculate & eRecalculate.CoordSystem) > 0)
- {
- // Calculate Min/Max for all UserObjects, assign mi_Inst to all user objects.
- mi_Bounds.Calculate();
+ // Speed optimization: Calculate coordinate system only if required
+ if ((me_Recalculate & eRecalculate.CoordSystem) > 0)
+ {
+ // Calculate Min/Max for all UserObjects, assign mi_Inst to all user objects.
+ mi_Bounds.Calculate();
- // Calculate factors for transformation
- NormalizeRanges();
+ // Calculate factors for transformation
+ NormalizeRanges();
- // Fills mi_AxisLines with 3 main axis and 42 raster lines
- CreateCoordinateSystem(i_Graph);
+ // Fills mi_AxisLines with 3 main axis and 42 raster lines
+ CreateCoordinateSystem(i_Graph);
- #if DEBUG_SPEED
+#if DEBUG_SPEED
FormatStopwatch("Coord System: ", i_Watch, i_Debug);
- #endif
- }
+#endif
+ }
- // Speed optimization: Calculate 3D objects only if required
- if ((me_Recalculate & eRecalculate.Objects) > 0)
- {
- foreach (cDrawObj i_Object in mi_UserObjects)
- {
- // This must not be called for axes
- i_Object.CalcSortOrder();
+ // Speed optimization: Calculate 3D objects only if required
+ if ((me_Recalculate & eRecalculate.Objects) > 0)
+ {
+ foreach (cDrawObj i_Object in mi_UserObjects)
+ {
+ // This must not be called for axes
+ i_Object.CalcSortOrder();
- // This must not be called for axes
- // reload Pens, Brushes from User objects or from ColorScheme
- i_Object.ProcessColors();
+ // This must not be called for axes reload Pens, Brushes from User objects or
+ // from ColorScheme
+ i_Object.ProcessColors();
- // Project 3D --> 2D, calculate line width, shape radius,...
- i_Object.Project3D();
- }
+ // Project 3D --> 2D, calculate line width, shape radius,...
+ i_Object.Project3D();
+ }
- #if DEBUG_SPEED
+#if DEBUG_SPEED
FormatStopwatch("Prepare Objects: ", i_Watch, i_Debug);
- #endif
- }
+#endif
+ }
- // Speed optimization: Merge lists only if at least one of them has changed
- if ((me_Recalculate & (eRecalculate.AddRemove | eRecalculate.CoordSystem)) > 0)
- {
- mi_AllObjects.Clear();
- mi_AllObjects.AddRange(mi_AxisLines);
- mi_AllObjects.AddRange(mi_UserObjects);
+ // Speed optimization: Merge lists only if at least one of them has changed
+ if ((me_Recalculate & (eRecalculate.AddRemove | eRecalculate.CoordSystem)) > 0)
+ {
+ mi_AllObjects.Clear();
+ mi_AllObjects.AddRange(mi_AxisLines);
+ mi_AllObjects.AddRange(mi_UserObjects);
- #if DEBUG_SPEED
+#if DEBUG_SPEED
FormatStopwatch("Merge Lists: ", i_Watch, i_Debug);
- #endif
- }
+#endif
+ }
- // Speed optimization: sort draw objects only if required
- if ((me_Recalculate & (eRecalculate.AddRemove | eRecalculate.CoordSystem | eRecalculate.Objects)) > 0)
- {
- // Sort draw order from background to foreground
- mi_AllObjects.Sort();
+ // Speed optimization: sort draw objects only if required
+ if ((me_Recalculate & (eRecalculate.AddRemove | eRecalculate.CoordSystem | eRecalculate.Objects)) > 0)
+ {
+ // Sort draw order from background to foreground
+ mi_AllObjects.Sort();
- #if DEBUG_SPEED
+#if DEBUG_SPEED
FormatStopwatch("Sort List: ", i_Watch, i_Debug);
- #endif
- }
+#endif
+ }
- me_Recalculate = eRecalculate.Nothing;
+ me_Recalculate = eRecalculate.Nothing;
- // ---------------------------------------------------
+ // ---------------------------------------------------
- // Draw axis legends in bottom left corner
- if (me_LegendPos == eLegendPos.BottomLeft)
- {
- int X = 4;
- int Y = ClientSize.Height - Font.Height - 4;
- for (int i=2; i>=0; i--)
- {
- if (String.IsNullOrEmpty(mi_Axis[i].LegendText))
- continue;
+ // Draw axis legends in bottom left corner
+ if (me_LegendPos == eLegendPos.BottomLeft)
+ {
+ int X = 4;
+ int Y = ClientSize.Height - Font.Height - 4;
+ for (int i = 2; i >= 0; i--)
+ {
+ if (String.IsNullOrEmpty(mi_Axis[i].LegendText))
+ continue;
- String s_Disp = String.Format("{0}: {1}", (eCoord)i, mi_Axis[i].LegendText);
- i_Graph.DrawString(s_Disp, Font, mi_Axis[i].LegendBrush, X, Y);
- Y -= Font.Height;
- }
- }
+ String s_Disp = String.Format("{0}: {1}", (eCoord)i, mi_Axis[i].LegendText);
+ i_Graph.DrawString(s_Disp, Font, mi_Axis[i].LegendBrush, X, Y);
+ Y -= Font.Height;
+ }
+ }
- // Draw rotation legends at top
- if (mi_TopLegendBrush != null)
- {
- String[] s_Legend = new String[] { "Rotation:", "Elevation:", "Distance:" };
- String[] s_Value = new String[] { String.Format("{0:+#;-#;0}°", (int)mi_Mouse.md_Phi),
- String.Format("{0:+#;-#;0}°", (int)mi_Mouse.md_Theta),
- String.Format("{0}", (int)mi_Mouse.md_Rho) };
+ // Draw rotation legends at top
+ if (mi_TopLegendBrush != null)
+ {
+ String[] s_Legend = new String[] { "Rotation:", "Elevation:", "Distance:" };
+ String[] s_Value = new String[] { String.Format("{0:+#;-#;0}°", (int)mi_Mouse.md_Phi),
+ String.Format("{0:+#;-#;0}°", (int)mi_Mouse.md_Theta),
+ String.Format("{0}", (int)mi_Mouse.md_Rho) };
- SizeF k_Size = i_Graph.MeasureString(s_Legend[1], Font); // measure the widest string
- int X = 4;
- int Y = 3;
- for (int i=0; i<3; i++)
- {
- i_Graph.DrawString(s_Legend[i], Font, mi_TopLegendBrush, X, Y);
- i_Graph.DrawString(s_Value [i], Font, mi_TopLegendBrush, X + k_Size.Width, Y);
- Y += Font.Height;
- }
- }
+ SizeF k_Size = i_Graph.MeasureString(s_Legend[1], Font); // measure the widest string
+ int X = 4;
+ int Y = 3;
+ for (int i = 0; i < 3; i++)
+ {
+ i_Graph.DrawString(s_Legend[i], Font, mi_TopLegendBrush, X, Y);
+ i_Graph.DrawString(s_Value[i], Font, mi_TopLegendBrush, X + k_Size.Width, Y);
+ Y += Font.Height;
+ }
+ }
- // ---------------------------------------------------
+ // ---------------------------------------------------
- // Set X, Y offset which user has set by mouse dragging with SHIFT key pressed
- i_Graph.TranslateTransform(mi_Mouse.mk_OffMove.X + mi_Mouse.mk_OffCoord.X,
- mi_Mouse.mk_OffMove.Y + mi_Mouse.mk_OffCoord.Y);
+ // Set X, Y offset which user has set by mouse dragging with SHIFT key pressed
+ i_Graph.TranslateTransform(mi_Mouse.mk_OffMove.X + mi_Mouse.mk_OffCoord.X,
+ mi_Mouse.mk_OffMove.Y + mi_Mouse.mk_OffCoord.Y);
- SmoothingMode e_Smooth = SmoothingMode.Invalid;
+ SmoothingMode e_Smooth = SmoothingMode.Invalid;
- foreach (cDrawObj i_DrawObj in mi_AllObjects)
- {
- if (!i_DrawObj.IsValid)
- continue; // avoid overflow exception or hanging
+ foreach (cDrawObj i_DrawObj in mi_AllObjects)
+ {
+ if (!i_DrawObj.IsValid)
+ continue; // avoid overflow exception or hanging
- if (e_Smooth != i_DrawObj.me_SmoothMode) // avoid unneccessary calls into GDI+ (speed optimization)
- {
- e_Smooth = i_DrawObj.me_SmoothMode;
- i_Graph.SmoothingMode = i_DrawObj.me_SmoothMode;
- }
+ if (e_Smooth != i_DrawObj.me_SmoothMode) // avoid unneccessary calls into GDI+ (speed optimization)
+ {
+ e_Smooth = i_DrawObj.me_SmoothMode;
+ i_Graph.SmoothingMode = i_DrawObj.me_SmoothMode;
+ }
- // Draw Line, Shape, Polygon
- i_DrawObj.Render(i_Graph);
+ // Draw Line, Shape, Polygon
+ i_DrawObj.Render(i_Graph);
- // Draw labels and legends
- cLine i_Line = i_DrawObj as cLine;
- if (i_Line != null &&
- i_Line.me_Line != eCoord.Invalid &&
- mi_Quadrant.mb_BottomView == false && // no label in bottom view
- mi_Quadrant.ms32_Quadrant == 3) // showing labels makes sense only in quadrant 3
- {
- bool b_Legend = false;
+ // Draw labels and legends
+ cLine i_Line = i_DrawObj as cLine;
+ if (i_Line != null &&
+ i_Line.me_Line != eCoord.Invalid &&
+ mi_Quadrant.mb_BottomView == false && // no label in bottom view
+ mi_Quadrant.ms32_Quadrant == 3) // showing labels makes sense only in quadrant 3
+ {
+ bool b_Legend = false;
- // Draw axis legends at end of of main axis
- if (me_LegendPos == eLegendPos.AxisEnd && i_Line.me_Line == i_Line.me_Offset)
- {
- cAxis i_Axis = mi_Axis[(int)i_Line.me_Line];
- if (!String.IsNullOrEmpty(i_Axis.LegendText))
- {
- StringFormat i_Align = new StringFormat();
- PointF k_Pos = i_Line.mi_Points[1].mi_P2D.Coord;
- switch (i_Line.me_Line)
- {
- case eCoord.X:
- k_Pos.X += (float)mi_Transform.ProjectXY(5, -5);
- k_Pos.Y += (float)mi_Transform.ProjectXY(5, -Font.Height / 2 - 2);
- i_Align.Alignment = StringAlignment.Far;
- break;
- case eCoord.Y:
- k_Pos.X += 5;
- k_Pos.Y -= Font.Height / 2;
- break;
- case eCoord.Z:
- k_Pos.X -= 5;
- k_Pos.Y -= Font.Height + 5;
- break;
- }
+ // Draw axis legends at end of of main axis
+ if (me_LegendPos == eLegendPos.AxisEnd && i_Line.me_Line == i_Line.me_Offset)
+ {
+ cAxis i_Axis = mi_Axis[(int)i_Line.me_Line];
+ if (!String.IsNullOrEmpty(i_Axis.LegendText))
+ {
+ StringFormat i_Align = new StringFormat();
+ PointF k_Pos = i_Line.mi_Points[1].mi_P2D.Coord;
+ switch (i_Line.me_Line)
+ {
+ case eCoord.X:
+ k_Pos.X += (float)mi_Transform.ProjectXY(5, -5);
+ k_Pos.Y += (float)mi_Transform.ProjectXY(5, -Font.Height / 2 - 2);
+ i_Align.Alignment = StringAlignment.Far;
+ break;
- i_Graph.DrawString(i_Axis.LegendText, Font, i_Axis.LegendBrush, k_Pos, i_Align);
- b_Legend = true; // do not draw a label if a legend has already been drawn (see Demo Sphere)
- }
- }
-
- // Draw labels of raster lines
- if (me_Raster == eRaster.Labels && !b_Legend && !String.IsNullOrEmpty(i_Line.ms_Label))
- {
- Brush i_Brush = null;
- StringFormat i_Align = new StringFormat();
- PointF k_Pos = i_Line.mi_Points[1].mi_P2D.Coord;
+ case eCoord.Y:
+ k_Pos.X += 5;
+ k_Pos.Y -= Font.Height / 2;
+ break;
- if (i_Line.me_Line == eCoord.Y)
- {
- if (i_Line.me_Offset == eCoord.X)
- {
- k_Pos.X += (float)mi_Transform.ProjectXY(5, -5);
- k_Pos.Y += (float)mi_Transform.ProjectXY(-Font.Height / 2, 5);
- i_Brush = AxisX.LegendBrush;
- }
- else // Y (Main axis) and Z (Raster)
- {
- k_Pos.X += 5;
- k_Pos.Y -= Font.Height / 2;
- i_Brush = AxisZ.LegendBrush;
- }
- }
- else if (i_Line.me_Line == eCoord.X && i_Line.me_Offset == eCoord.Y)
- {
- k_Pos.X += (float)mi_Transform.ProjectXY(5, -5);
- k_Pos.Y += (float)mi_Transform.ProjectXY(5, -Font.Height / 2);
- i_Align.Alignment = StringAlignment.Far;
- i_Brush = AxisY.LegendBrush;
- }
+ case eCoord.Z:
+ k_Pos.X -= 5;
+ k_Pos.Y -= Font.Height + 5;
+ break;
+ }
- if (i_Brush != null)
- i_Graph.DrawString(i_Line.ms_Label, Font, i_Brush, k_Pos, i_Align);
- }
- } // if (Line != null)
- } // foreach (cDrawObj)
+ i_Graph.DrawString(i_Axis.LegendText, Font, i_Axis.LegendBrush, k_Pos, i_Align);
+ b_Legend = true; // do not draw a label if a legend has already been drawn (see Demo Sphere)
+ }
+ }
- #if DEBUG_SPEED
+ // Draw labels of raster lines
+ if (me_Raster == eRaster.Labels && !b_Legend && !String.IsNullOrEmpty(i_Line.ms_Label))
+ {
+ Brush i_Brush = null;
+ StringFormat i_Align = new StringFormat();
+ PointF k_Pos = i_Line.mi_Points[1].mi_P2D.Coord;
+
+ if (i_Line.me_Line == eCoord.Y)
+ {
+ if (i_Line.me_Offset == eCoord.X)
+ {
+ k_Pos.X += (float)mi_Transform.ProjectXY(5, -5);
+ k_Pos.Y += (float)mi_Transform.ProjectXY(-Font.Height / 2, 5);
+ i_Brush = AxisX.LegendBrush;
+ }
+ else // Y (Main axis) and Z (Raster)
+ {
+ k_Pos.X += 5;
+ k_Pos.Y -= Font.Height / 2;
+ i_Brush = AxisZ.LegendBrush;
+ }
+ }
+ else if (i_Line.me_Line == eCoord.X && i_Line.me_Offset == eCoord.Y)
+ {
+ k_Pos.X += (float)mi_Transform.ProjectXY(5, -5);
+ k_Pos.Y += (float)mi_Transform.ProjectXY(5, -Font.Height / 2);
+ i_Align.Alignment = StringAlignment.Far;
+ i_Brush = AxisY.LegendBrush;
+ }
+
+ if (i_Brush != null)
+ i_Graph.DrawString(i_Line.ms_Label, Font, i_Brush, k_Pos, i_Align);
+ }
+ } // if (Line != null)
+ } // foreach (cDrawObj)
+
+#if DEBUG_SPEED
FormatStopwatch("Render Objects: ", i_Watch, i_Debug);
Debug.Print(i_Debug.ToString().TrimEnd());
- #endif
- }
+#endif
+ }
- // ============================================================================
+ // ============================================================================
- protected override void OnCreateControl()
- {
- base.OnCreateControl();
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
- // This control draws it's own border. See DrawBorder()
- BorderStyle = BorderStyle.None;
+ // This control draws it's own border. See DrawBorder()
+ BorderStyle = BorderStyle.None;
- // This is the size of the control defined in Visual Studio Form Designer
- mi_Transform.SetInitialSize(ClientSize);
- }
+ // This is the size of the control defined in Visual Studio Form Designer
+ mi_Transform.SetInitialSize(ClientSize);
+ }
- protected override void OnSizeChanged(EventArgs e)
- {
- base.OnSizeChanged(e);
+ protected override void OnSizeChanged(EventArgs e)
+ {
+ base.OnSizeChanged(e);
- // This may be called with an invalid size before the control is created!
- mi_Transform.SetSize(ClientSize);
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- }
+ // This may be called with an invalid size before the control is created!
+ mi_Transform.SetSize(ClientSize);
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ }
- ///
- /// This is only called when the user moves the trackbar, not when TrackBar.Value is set programmatically.
- ///
- void OnTrackbarScroll(object sender, EventArgs e)
- {
- mi_Mouse.OnTrackBarScroll();
- mi_Transform.SetCoefficients(mi_Mouse);
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- }
+ ///
+ /// This is only called when the user moves the trackbar, not when TrackBar.Value is set programmatically.
+ ///
+ private void OnTrackbarScroll(object sender, EventArgs e)
+ {
+ mi_Mouse.OnTrackBarScroll();
+ mi_Transform.SetCoefficients(mi_Mouse);
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ }
- // --------------------------------------------
+ // --------------------------------------------
- protected override void OnGotFocus(EventArgs e)
- {
- base.OnGotFocus(e);
- DrawBorder(null);
- }
- protected override void OnLostFocus(EventArgs e)
- {
- base.OnLostFocus(e);
- DrawBorder(null);
- }
- ///
- /// Draw a one pixel border around the control which may change color when the control has the focus.
- ///
- private void DrawBorder(Graphics i_Graphics)
- {
- Pen i_Pen = mi_BorderPens[Focused ? 1 : 0];
- if (i_Pen != null)
- {
- BorderStyle = BorderStyle.None;
+ protected override void OnGotFocus(EventArgs e)
+ {
+ base.OnGotFocus(e);
+ DrawBorder(null);
+ }
- if (i_Graphics == null)
- i_Graphics = Graphics.FromHwnd(Handle);
+ protected override void OnLostFocus(EventArgs e)
+ {
+ base.OnLostFocus(e);
+ DrawBorder(null);
+ }
- i_Graphics.ResetTransform();
- Rectangle r_Rect = ClientRectangle;
- i_Graphics.DrawRectangle(i_Pen, r_Rect.X, r_Rect.Y, r_Rect.Width - 1, r_Rect.Height - 1);
- }
- }
+ ///
+ /// Draw a one pixel border around the control which may change color when the control has
+ /// the focus.
+ ///
+ private void DrawBorder(Graphics i_Graphics)
+ {
+ Pen i_Pen = mi_BorderPens[Focused ? 1 : 0];
+ if (i_Pen != null)
+ {
+ BorderStyle = BorderStyle.None;
- // ============================== MOUSE =====================================
+ if (i_Graphics == null)
+ i_Graphics = Graphics.FromHwnd(Handle);
- protected override void OnMouseDown(MouseEventArgs e)
- {
- base.OnMouseDown(e);
+ i_Graphics.ResetTransform();
+ Rectangle r_Rect = ClientRectangle;
+ i_Graphics.DrawRectangle(i_Pen, r_Rect.X, r_Rect.Y, r_Rect.Width - 1, r_Rect.Height - 1);
+ }
+ }
- mi_Tooltip.Hide();
- mi_Mouse.mk_LastPos = e.Location;
+ // ============================== MOUSE =====================================
- if (mi_AllObjects.Count == 0)
- return;
-
- int s32_UID = (int)Control.ModifierKeys | (int)e.Button;
-
- cUserInput i_Input;
- if (mi_UserInputs.TryGetValue(s32_UID, out i_Input))
- {
- switch (i_Input.Action)
- {
- case eMouseAction.SelectObj:
- case eMouseAction.Callback:
- OnSelMouseDown(e.X, e.Y, i_Input);
- break;
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ base.OnMouseDown(e);
- default:
- mi_Mouse.me_Action = i_Input.Action;
- Cursor = i_Input.Cursor;
- break;
- }
- }
- }
+ mi_Tooltip.Hide();
+ mi_Mouse.mk_LastPos = e.Location;
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
+ if (mi_AllObjects.Count == 0)
+ return;
- int s32_DeltaX = e.X - mi_Mouse.mk_LastPos.X;
- int s32_DeltaY = e.Y - mi_Mouse.mk_LastPos.Y;
- mi_Mouse.mk_LastPos = e.Location;
+ int s32_UID = (int)Control.ModifierKeys | (int)e.Button;
- switch (mi_Mouse.me_Action)
- {
- case eMouseAction.Move:
- mi_Tooltip.Hide();
- mi_Mouse.mk_OffMove.X += s32_DeltaX;
- mi_Mouse.mk_OffMove.Y += s32_DeltaY;
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- break;
+ cUserInput i_Input;
+ if (mi_UserInputs.TryGetValue(s32_UID, out i_Input))
+ {
+ switch (i_Input.Action)
+ {
+ case eMouseAction.SelectObj:
+ case eMouseAction.Callback:
+ OnSelMouseDown(e.X, e.Y, i_Input);
+ break;
- case eMouseAction.Rho:
- case eMouseAction.Theta:
- case eMouseAction.Phi:
- case eMouseAction.ThetaAndPhi:
- mi_Tooltip.Hide();
- mi_Mouse.OnMouseMove(s32_DeltaX, s32_DeltaY);
- mi_Transform.SetCoefficients(mi_Mouse);
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- break;
+ default:
+ mi_Mouse.me_Action = i_Input.Action;
+ Cursor = i_Input.Cursor;
+ break;
+ }
+ }
+ }
- case eMouseAction.SelectObj:
- case eMouseAction.Callback:
- int s32_UID = (int)Control.ModifierKeys | (int)e.Button;
- cUserInput i_Input;
- if (mi_UserInputs.TryGetValue(s32_UID, out i_Input))
- {
- if (i_Input.Action == mi_Mouse.me_Action)
- {
- // Mouse.Y coordinates have the zero point at top left
- // Editor3D coordinates have the zero point at bottom left --> negate Y
- SelectionCallback(eSelEvent.MouseDrag, i_Input.Modifiers, s32_DeltaX, -s32_DeltaY, mi_DragObject);
- }
- else
- {
- // The modifier keys have changed --> abort sending events to the callback
- OnMouseExit();
- }
- }
- break;
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
- case eMouseAction.None:
- mi_Tooltip.OnMouseMove(e);
- break;
- }
- }
+ int s32_DeltaX = e.X - mi_Mouse.mk_LastPos.X;
+ int s32_DeltaY = e.Y - mi_Mouse.mk_LastPos.Y;
+ mi_Mouse.mk_LastPos = e.Location;
- protected override void OnMouseUp(MouseEventArgs e)
- {
- base.OnMouseUp(e);
- OnMouseExit();
- }
- protected override void OnMouseLeave(EventArgs e)
- {
- base.OnMouseLeave(e);
- OnMouseExit();
- }
- private void OnMouseExit()
- {
- mi_Tooltip.Hide();
- Cursor = Cursors.Arrow;
+ switch (mi_Mouse.me_Action)
+ {
+ case eMouseAction.Move:
+ mi_Tooltip.Hide();
+ mi_Mouse.mk_OffMove.X += s32_DeltaX;
+ mi_Mouse.mk_OffMove.Y += s32_DeltaY;
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ break;
- switch (mi_Mouse.me_Action)
- {
- case eMouseAction.SelectObj:
- case eMouseAction.Callback:
- SelectionCallback(eSelEvent.MouseUp, Keys.None, 0, 0, mi_DragObject);
- break;
- }
+ case eMouseAction.Rho:
+ case eMouseAction.Theta:
+ case eMouseAction.Phi:
+ case eMouseAction.ThetaAndPhi:
+ mi_Tooltip.Hide();
+ mi_Mouse.OnMouseMove(s32_DeltaX, s32_DeltaY);
+ mi_Transform.SetCoefficients(mi_Mouse);
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ break;
- mi_DragObject = null;
- mi_Mouse.me_Action = eMouseAction.None;
+ case eMouseAction.SelectObj:
+ case eMouseAction.Callback:
+ int s32_UID = (int)Control.ModifierKeys | (int)e.Button;
+ cUserInput i_Input;
+ if (mi_UserInputs.TryGetValue(s32_UID, out i_Input))
+ {
+ if (i_Input.Action == mi_Mouse.me_Action)
+ {
+ // Mouse.Y coordinates have the zero point at top left Editor3D
+ // coordinates have the zero point at bottom left --> negate Y
+ SelectionCallback(eSelEvent.MouseDrag, i_Input.Modifiers, s32_DeltaX, -s32_DeltaY, mi_DragObject);
+ }
+ else
+ {
+ // The modifier keys have changed --> abort sending events to the callback
+ OnMouseExit();
+ }
+ }
+ break;
- // Store any pending user changes on mouse-up
- mi_UndoBuffer.Store();
- }
+ case eMouseAction.None:
+ mi_Tooltip.OnMouseMove(e);
+ break;
+ }
+ }
- protected override void OnMouseWheel(MouseEventArgs e)
- {
- base.OnMouseWheel(e);
- mi_Tooltip.Hide();
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ base.OnMouseUp(e);
+ OnMouseExit();
+ }
- if (mi_Mouse.OnMouseWheel(e.Delta))
- {
- mi_Transform.SetCoefficients(mi_Mouse);
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- }
- }
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ base.OnMouseLeave(e);
+ OnMouseExit();
+ }
- ///
- /// Select 3D object or call selection callback function
- ///
- private void OnSelMouseDown(int X, int Y, cUserInput i_Input)
- {
- if (!mi_Selection.Enabled)
- return;
+ private void OnMouseExit()
+ {
+ mi_Tooltip.Hide();
+ Cursor = Cursors.Arrow;
- cObject3D i_Found = FindObjectAt(X, Y, true);
+ switch (mi_Mouse.me_Action)
+ {
+ case eMouseAction.SelectObj:
+ case eMouseAction.Callback:
+ SelectionCallback(eSelEvent.MouseUp, Keys.None, 0, 0, mi_DragObject);
+ break;
+ }
- if (mi_Selection.Callback != null)
- {
- // Start dragging even if i_Found == null
- mi_DragObject = i_Found;
- mi_Mouse.me_Action = i_Input.Action;
- Cursor = i_Input.Cursor;
+ mi_DragObject = null;
+ mi_Mouse.me_Action = eMouseAction.None;
- SelectionCallback(eSelEvent.MouseDown, i_Input.Modifiers, 0, 0, mi_DragObject);
- return;
- }
+ // Store any pending user changes on mouse-up
+ mi_UndoBuffer.Store();
+ }
- // No callback assigned --> toggle selection of 3D object.
- if (i_Found != null && i_Input.Action == eMouseAction.SelectObj)
- {
- // Not multiselect --> remove all current selections
- if (!mi_Selection.MultiSelect)
- mi_Selection.DeSelectAll();
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ base.OnMouseWheel(e);
+ mi_Tooltip.Hide();
- i_Found.Selected = !i_Found.Selected; // toggle selection
+ if (mi_Mouse.OnMouseWheel(e.Delta))
+ {
+ mi_Transform.SetCoefficients(mi_Mouse);
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ }
+ }
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- }
- }
+ ///
+ /// Select 3D object or call selection callback function
+ ///
+ private void OnSelMouseDown(int X, int Y, cUserInput i_Input)
+ {
+ if (!mi_Selection.Enabled)
+ return;
- // ============================== KEYBOARD =====================================
+ cObject3D i_Found = FindObjectAt(X, Y, true);
- protected override void OnKeyDown(KeyEventArgs e)
- {
- base.OnKeyDown(e);
+ if (mi_Selection.Callback != null)
+ {
+ // Start dragging even if i_Found == null
+ mi_DragObject = i_Found;
+ mi_Mouse.me_Action = i_Input.Action;
+ Cursor = i_Input.Cursor;
- if (e.Control && !e.Alt && !e.Shift)
- {
- switch (e.KeyCode)
- {
- case Keys.Y: mi_UndoBuffer.Redo(); break; // CTRL + Y --> Redo
- case Keys.Z: mi_UndoBuffer.Undo(); break; // CTRL + Z --> Undo
- }
- }
- }
+ SelectionCallback(eSelEvent.MouseDown, i_Input.Modifiers, 0, 0, mi_DragObject);
+ return;
+ }
- // ========================== SELECTION CALLBACK ===============================
+ // No callback assigned --> toggle selection of 3D object.
+ if (i_Found != null && i_Input.Action == eMouseAction.SelectObj)
+ {
+ // Not multiselect --> remove all current selections
+ if (!mi_Selection.MultiSelect)
+ mi_Selection.DeSelectAll();
- ///
- /// Selection.Callback is called on the mouse events Down, Move and Up if cUserInput.Action = Callback or SelectObj
- /// The callback must never throw an exception.
- /// i_Object may be cPoint3D if Selection.SinglePoints = true
- /// i_Object may be cShape3D, cLine3D, cPolygon3D if Selection.SinglePoints = false
- /// i_Object may be null if the user has clicked a location without a 3D object.
- /// In this case the callback can call Selection.GetSelectedObjects() / GetSelectedPoints() to obtain the previous selections.
- /// The callback is responsible for selecting / deselecting the desired objects.
- /// If the callback does not change the selection status, the 3D object will never be selected / deselected.
- /// The callback is allowed to show a MessageBox to the user.
- ///
- private void SelectionCallback(eSelEvent e_Event, Keys e_Modifiers, int s32_DeltaX, int s32_DeltaY, cObject3D i_Object)
- {
- if (!mi_Selection.Enabled || mi_Selection.Callback == null)
- return;
- try
- {
- eInvalidate e_Invalidate = mi_Selection.Callback(e_Event, e_Modifiers, s32_DeltaX, s32_DeltaY, i_Object);
+ i_Found.Selected = !i_Found.Selected; // toggle selection
- if (e_Invalidate == eInvalidate.CoordSystem)
- me_Recalculate |= eRecalculate.CoordSystem;
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ }
+ }
- if (e_Invalidate != eInvalidate.NoChange)
- Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
- }
- catch (Exception Ex)
- {
- MessageBox.Show(TopLevelControl, "Your callback function has crashed:\n\n" + Ex.Message + "\n\n" + Ex.StackTrace,
- "Bug Alarm", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- }
+ // ============================== KEYBOARD =====================================
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+
+ if (e.Control && !e.Alt && !e.Shift)
+ {
+ switch (e.KeyCode)
+ {
+ case Keys.Y: mi_UndoBuffer.Redo(); break; // CTRL + Y --> Redo
+ case Keys.Z: mi_UndoBuffer.Undo(); break; // CTRL + Z --> Undo
+ }
+ }
+ }
+
+ // ========================== SELECTION CALLBACK ===============================
+
+ ///
+ /// Selection.Callback is called on the mouse events Down, Move and Up if cUserInput.Action
+ /// = Callback or SelectObj The callback must never throw an exception. i_Object may be
+ /// cPoint3D if Selection.SinglePoints = true i_Object may be cShape3D, cLine3D, cPolygon3D
+ /// if Selection.SinglePoints = false i_Object may be null if the user has clicked a
+ /// location without a 3D object. In this case the callback can call
+ /// Selection.GetSelectedObjects() / GetSelectedPoints() to obtain the previous selections.
+ /// The callback is responsible for selecting / deselecting the desired objects. If the
+ /// callback does not change the selection status, the 3D object will never be selected /
+ /// deselected. The callback is allowed to show a MessageBox to the user.
+ ///
+ private void SelectionCallback(eSelEvent e_Event, Keys e_Modifiers, int s32_DeltaX, int s32_DeltaY, cObject3D i_Object)
+ {
+ if (!mi_Selection.Enabled || mi_Selection.Callback == null)
+ return;
+ try
+ {
+ eInvalidate e_Invalidate = mi_Selection.Callback(e_Event, e_Modifiers, s32_DeltaX, s32_DeltaY, i_Object);
+
+ if (e_Invalidate == eInvalidate.CoordSystem)
+ me_Recalculate |= eRecalculate.CoordSystem;
+
+ if (e_Invalidate != eInvalidate.NoChange)
+ Invalidate(); // Windows will call OnPaint() when the GUI thread becomes idle.
+ }
+ catch (Exception Ex)
+ {
+ MessageBox.Show(TopLevelControl, "Your callback function has crashed:\n\n" + Ex.Message + "\n\n" + Ex.StackTrace,
+ "Bug Alarm", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/HexcalMC/Base/Editor3D/FunctionCompiler.cs b/HexcalMC/Base/Editor3D/FunctionCompiler.cs
index 7381fc5..0578537 100644
--- a/HexcalMC/Base/Editor3D/FunctionCompiler.cs
+++ b/HexcalMC/Base/Editor3D/FunctionCompiler.cs
@@ -1,115 +1,112 @@
-
-/*****************************************************************************
+/*****************************************************************************
This class has been written by Łukasz Światkowski
Cleaned up spaghetti code and improved by Elmü (elmue@gmx.de)
*****************************************************************************/
+using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Reflection;
-using System.Runtime.Serialization;
using System.Text;
-using Microsoft.CSharp;
-
using delRendererFunction = Plot3D.Editor3D.delRendererFunction;
namespace Plot3D
{
- public delegate double delCompiledFunction(params double[] x);
+ public delegate double delCompiledFunction(params double[] x);
- public static class FunctionCompiler
- {
- const String EVAL_CLASS =
- "using {1};\n"
- + "public class Eval\n"
- + "{{\n"
- + " public static double e {{ get {{ return System.Math.E; }} }}\n"
- + " public static double pi {{ get {{ return System.Math.PI; }} }}\n"
- // -------------------------------------------------------
- + " public static double abs (double x) {{ return System.Math.Abs(x); }}\n"
- + " public static double acos (double x) {{ return System.Math.Acos(x); }}\n"
- + " public static double asin (double x) {{ return System.Math.Asin(x); }}\n"
- + " public static double atan (double x) {{ return System.Math.Atan(x); }}\n"
- + " public static double atan2(double x, double y) {{ return System.Math.Atan2(x, y); }}\n"
- + " public static double ceil (double x) {{ return System.Math.Ceiling(x); }}\n"
- + " public static double cos (double x) {{ return System.Math.Cos(x); }}\n"
- + " public static double cosh (double x) {{ return System.Math.Cosh(x); }}\n"
- + " public static double exp (double x) {{ return System.Math.Exp(x); }}\n"
- + " public static double floor(double x) {{ return System.Math.Floor(x); }}\n"
- + " public static double log (double x) {{ return System.Math.Log(x); }}\n"
- + " public static double log2 (double x) {{ return System.Math.Log(x, 2.0); }}\n"
- + " public static double log10(double x) {{ return System.Math.Log10(x); }}\n"
- + " public static double max (double x, double y) {{ return System.Math.Max(x, y); }}\n"
- + " public static double min (double x, double y) {{ return System.Math.Min(x, y); }}\n"
- + " public static double pow (double x, double y) {{ return System.Math.Pow(x, y); }}\n"
- + " public static double round(double x) {{ return System.Math.Round(x); }}\n"
- + " public static double sign (double x) {{ return System.Math.Sign(x); }}\n"
- + " public static double sin (double x) {{ return System.Math.Sin(x); }}\n"
- + " public static double sinh (double x) {{ return System.Math.Sinh(x); }}\n"
- + " public static double sqrt (double x) {{ return System.Math.Sqrt(x); }}\n"
- + " public static double tan (double x) {{ return System.Math.Tan(x); }}\n"
- + " public static double tanh (double x) {{ return System.Math.Tanh(x); }}\n"
- // -------------------------------------------------------
- + " public static double __eval(params double[] __X)\n"
- + " {{\n"
- + " double x = __X[0];\n"
- + " double y = __X[1];\n"
- + " return {0};\n"
- + " }}\n"
- + " public static {2} __get()\n"
- + " {{\n"
- + " return __eval;\n"
- + " }}\n"
- + "}}";
+ public static class FunctionCompiler
+ {
+ private const String EVAL_CLASS =
+ "using {1};\n"
+ + "public class Eval\n"
+ + "{{\n"
+ + " public static double e {{ get {{ return System.Math.E; }} }}\n"
+ + " public static double pi {{ get {{ return System.Math.PI; }} }}\n"
+ // -------------------------------------------------------
+ + " public static double abs (double x) {{ return System.Math.Abs(x); }}\n"
+ + " public static double acos (double x) {{ return System.Math.Acos(x); }}\n"
+ + " public static double asin (double x) {{ return System.Math.Asin(x); }}\n"
+ + " public static double atan (double x) {{ return System.Math.Atan(x); }}\n"
+ + " public static double atan2(double x, double y) {{ return System.Math.Atan2(x, y); }}\n"
+ + " public static double ceil (double x) {{ return System.Math.Ceiling(x); }}\n"
+ + " public static double cos (double x) {{ return System.Math.Cos(x); }}\n"
+ + " public static double cosh (double x) {{ return System.Math.Cosh(x); }}\n"
+ + " public static double exp (double x) {{ return System.Math.Exp(x); }}\n"
+ + " public static double floor(double x) {{ return System.Math.Floor(x); }}\n"
+ + " public static double log (double x) {{ return System.Math.Log(x); }}\n"
+ + " public static double log2 (double x) {{ return System.Math.Log(x, 2.0); }}\n"
+ + " public static double log10(double x) {{ return System.Math.Log10(x); }}\n"
+ + " public static double max (double x, double y) {{ return System.Math.Max(x, y); }}\n"
+ + " public static double min (double x, double y) {{ return System.Math.Min(x, y); }}\n"
+ + " public static double pow (double x, double y) {{ return System.Math.Pow(x, y); }}\n"
+ + " public static double round(double x) {{ return System.Math.Round(x); }}\n"
+ + " public static double sign (double x) {{ return System.Math.Sign(x); }}\n"
+ + " public static double sin (double x) {{ return System.Math.Sin(x); }}\n"
+ + " public static double sinh (double x) {{ return System.Math.Sinh(x); }}\n"
+ + " public static double sqrt (double x) {{ return System.Math.Sqrt(x); }}\n"
+ + " public static double tan (double x) {{ return System.Math.Tan(x); }}\n"
+ + " public static double tanh (double x) {{ return System.Math.Tanh(x); }}\n"
+ // -------------------------------------------------------
+ + " public static double __eval(params double[] __X)\n"
+ + " {{\n"
+ + " double x = __X[0];\n"
+ + " double y = __X[1];\n"
+ + " return {0};\n"
+ + " }}\n"
+ + " public static {2} __get()\n"
+ + " {{\n"
+ + " return __eval;\n"
+ + " }}\n"
+ + "}}";
- public static delRendererFunction Compile(string functionBody)
- {
- functionBody = functionBody.Trim().ToLower();
- if (functionBody.Contains(";"))
- throw new Exception("Function string cannot contain semicolon");
+ public static delRendererFunction Compile(string functionBody)
+ {
+ functionBody = functionBody.Trim().ToLower();
+ if (functionBody.Contains(";"))
+ throw new Exception("Function string cannot contain semicolon");
- string s_Class = string.Format(EVAL_CLASS, functionBody, typeof(delCompiledFunction).Namespace, typeof(delCompiledFunction).Name);
+ string s_Class = string.Format(EVAL_CLASS, functionBody, typeof(delCompiledFunction).Namespace, typeof(delCompiledFunction).Name);
- CSharpCodeProvider i_Provider = new CSharpCodeProvider();
- CompilerParameters i_Params = new CompilerParameters();
- i_Params.CompilerOptions = "/t:library";
- i_Params.GenerateInMemory = true;
- i_Params.ReferencedAssemblies.Add("mscorlib.dll");
- i_Params.ReferencedAssemblies.Add("System.dll");
- i_Params.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
+ CSharpCodeProvider i_Provider = new CSharpCodeProvider();
+ CompilerParameters i_Params = new CompilerParameters();
+ i_Params.CompilerOptions = "/t:library";
+ i_Params.GenerateInMemory = true;
+ i_Params.ReferencedAssemblies.Add("mscorlib.dll");
+ i_Params.ReferencedAssemblies.Add("System.dll");
+ i_Params.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
- CompilerResults i_Result = i_Provider.CompileAssemblyFromSource(i_Params, s_Class);
- if (i_Result.Errors.HasErrors)
- {
- StringBuilder s_Error = new StringBuilder();
- if (i_Result.Errors.Count == 1)
- s_Error.Append("Compilation error:\n");
- else
- s_Error.AppendFormat("{0} Compilation errors:\n", i_Result.Errors.Count);
+ CompilerResults i_Result = i_Provider.CompileAssemblyFromSource(i_Params, s_Class);
+ if (i_Result.Errors.HasErrors)
+ {
+ StringBuilder s_Error = new StringBuilder();
+ if (i_Result.Errors.Count == 1)
+ s_Error.Append("Compilation error:\n");
+ else
+ s_Error.AppendFormat("{0} Compilation errors:\n", i_Result.Errors.Count);
- foreach (CompilerError i_Error in i_Result.Errors)
- {
- s_Error.Append(i_Error.ErrorText);
- s_Error.Append("\n");
- }
+ foreach (CompilerError i_Error in i_Result.Errors)
+ {
+ s_Error.Append(i_Error.ErrorText);
+ s_Error.Append("\n");
+ }
- s_Error.Append("\nSupported math functions are:\n"
- + "e, pi, abs(), acos(), asin(), atan(), atan2(), ceil(), cos(), cosh(), "
- + "exp(), floor(), log(), log2(), log10(), max(), min(), pow(), "
- + "round(), sign(), sin(), sinh(), sqrt(), tan(), tanh()\n");
+ s_Error.Append("\nSupported math functions are:\n"
+ + "e, pi, abs(), acos(), asin(), atan(), atan2(), ceil(), cos(), cosh(), "
+ + "exp(), floor(), log(), log2(), log10(), max(), min(), pow(), "
+ + "round(), sign(), sin(), sinh(), sqrt(), tan(), tanh()\n");
- throw new Exception(s_Error.ToString());
- }
+ throw new Exception(s_Error.ToString());
+ }
- MethodInfo i_Method = i_Result.CompiledAssembly.GetType("Eval").GetMethod("__get");
- delCompiledFunction f_Compiled = (delCompiledFunction)i_Method.Invoke(null, null);
- delRendererFunction f_Render = delegate(double X, double Y)
- {
- return f_Compiled(X, Y);
- };
- return f_Render;
- }
- }
-}
+ MethodInfo i_Method = i_Result.CompiledAssembly.GetType("Eval").GetMethod("__get");
+ delCompiledFunction f_Compiled = (delCompiledFunction)i_Method.Invoke(null, null);
+ delRendererFunction f_Render = delegate (double X, double Y)
+ {
+ return f_Compiled(X, Y);
+ };
+ return f_Render;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/Lamp.cs b/HexcalMC/Base/Lamp.cs
index d06a06b..a44c58c 100644
--- a/HexcalMC/Base/Lamp.cs
+++ b/HexcalMC/Base/Lamp.cs
@@ -1,31 +1,24 @@
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
using System.Drawing;
-using System.Data;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
using System.Drawing.Drawing2D;
+using System.Windows.Forms;
namespace HexcalMC
{
- public partial class Lamp : UserControl
- {
- public Lamp()
- {
- InitializeComponent();
- base.SetStyle(ControlStyles.ResizeRedraw, true);
- this.DoubleBuffered = true;
- }
+ public partial class Lamp : UserControl
+ {
+ public Lamp()
+ {
+ InitializeComponent();
+ base.SetStyle(ControlStyles.ResizeRedraw, true);
+ this.DoubleBuffered = true;
+ }
- private void Lamp_Load(object sender, EventArgs e)
- {
+ private void Lamp_Load(object sender, EventArgs e)
+ {
+ }
- }
-
- private static Color[] _colors = new Color[]
+ private static Color[] _colors = new Color[]
{
Color.Green,
Color.Orange,
@@ -37,147 +30,151 @@ namespace HexcalMC
Color.Blue
};
- private const int Ok = 0;
- private const int Warn = 1;
- private const int Error = 2;
- private const int Debug = 4;
- private const int Undef = -1;
- private int _state = -1;
+ private const int Ok = 0;
+ private const int Warn = 1;
+ private const int Error = 2;
+ private const int Debug = 4;
+ private const int Undef = -1;
+ private int _state = -1;
+ private string _dtext = "";
+ private bool _shadow = false;
- private string _dtext = "";
- private bool _shadow = false;
+ ///
+ /// 设置颜色
+ ///
+ public int State
+ {
+ get
+ {
+ return this._state;
+ }
+ set
+ {
+ this._state = value;
+ this.Refresh();
+ }
+ }
- /// 设置颜色
- public int State
- {
- get
- {
- return this._state;
- }
- set
- {
- this._state = value;
- this.Refresh();
- }
- }
+ ///
+ /// 设置文字
+ ///
+ public string LText
+ {
+ get
+ {
+ return this._dtext;
+ }
+ set
+ {
+ this._dtext = value;
+ }
+ }
- /// 设置文字
- public string LText
- {
- get
- {
- return this._dtext;
- }
- set
- {
- this._dtext = value;
- }
- }
+ ///
+ /// 设置阴影
+ ///
+ public bool Shadow
+ {
+ get
+ {
+ return this._shadow;
+ }
+ set
+ {
+ this._shadow = value;
+ }
+ }
- /// 设置阴影
- public bool Shadow
- {
- get
- {
- return this._shadow;
- }
- set
- {
- this._shadow = value;
- }
- }
+ protected override void OnPaint(PaintEventArgs gr)
+ {
+ base.OnPaint(gr);
+ int num = Math.Min(base.Width, base.Height);
+ bool flag = num >= 8;
+ if (flag)
+ {
+ this.DrawLamp(gr.Graphics, num);
+ }
+ }
- protected override void OnPaint(PaintEventArgs gr)
- {
- base.OnPaint(gr);
- int num = Math.Min(base.Width, base.Height);
- bool flag = num >= 8;
- if (flag)
- {
- this.DrawLamp(gr.Graphics, num);
- }
- }
+ private void DrawLamp(Graphics g, int rad)
+ {
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ RectangleF rect = new RectangleF(1f, 1f, (float)(rad - 5), (float)(rad - 5));
+ float num = rect.Width / 12f;
+ RectangleF rect2 = new RectangleF(rect.Location, rect.Size);
+ rect2.Inflate(1f, 1f);
+ if (_shadow)
+ {
+ rect2.X += num;
+ rect2.Y += num;
+ g.FillEllipse(Brushes.Gray, rect2);
+ rect2.X -= num;
+ rect2.Y -= num;
+ }
+ g.FillEllipse(Brushes.Black, rect2);
- private void DrawLamp(Graphics g, int rad)
- {
- g.SmoothingMode = SmoothingMode.AntiAlias;
- RectangleF rect = new RectangleF(1f, 1f, (float)(rad - 5), (float)(rad - 5));
- float num = rect.Width / 12f;
- RectangleF rect2 = new RectangleF(rect.Location, rect.Size);
- rect2.Inflate(1f, 1f);
- if (_shadow)
- {
- rect2.X += num;
- rect2.Y += num;
- g.FillEllipse(Brushes.Gray, rect2);
- rect2.X -= num;
- rect2.Y -= num;
- }
- g.FillEllipse(Brushes.Black, rect2);
-
- if (_state != -1)
- {
- using (GraphicsPath graphicsPath = new GraphicsPath())
- {
- graphicsPath.AddEllipse(rect);
- int x = (int)(rect.X + rect.Width / 3f);
- int y = (int)(rect.Y + rect.Height / 3f);
- Color color = Lamp._colors[_state];
- using (PathGradientBrush pathGradientBrush = new PathGradientBrush(graphicsPath))
- {
- pathGradientBrush.CenterColor = Color.Snow;
- pathGradientBrush.CenterPoint = new Point(x, y);
- pathGradientBrush.SurroundColors = new Color[]
+ if (_state != -1)
+ {
+ using (GraphicsPath graphicsPath = new GraphicsPath())
+ {
+ graphicsPath.AddEllipse(rect);
+ int x = (int)(rect.X + rect.Width / 3f);
+ int y = (int)(rect.Y + rect.Height / 3f);
+ Color color = Lamp._colors[_state];
+ using (PathGradientBrush pathGradientBrush = new PathGradientBrush(graphicsPath))
+ {
+ pathGradientBrush.CenterColor = Color.Snow;
+ pathGradientBrush.CenterPoint = new Point(x, y);
+ pathGradientBrush.SurroundColors = new Color[]
{
color
};
- g.FillPath(pathGradientBrush, graphicsPath);
- }
- }
- }
- else
- {
- using (Brush brush = new SolidBrush(this.BackColor))
- {
- g.FillEllipse(brush, rect);
- }
- }
+ g.FillPath(pathGradientBrush, graphicsPath);
+ }
+ }
+ }
+ else
+ {
+ using (Brush brush = new SolidBrush(this.BackColor))
+ {
+ g.FillEllipse(brush, rect);
+ }
+ }
- if (_dtext.Length > 0)
- {
- float emSize = rect.Height / 6f;
- Font font = new Font("Microsoft Sans Serif", emSize, FontStyle.Bold, GraphicsUnit.Point, 0);
- int num2 = (int)font.GetHeight();
- int y2 = (int)rect.Height / 2 - num2 / 2;
- int num3 = (int)g.MeasureString(_dtext, font).Width;
- int num4 = (int)(rect.Width - (float)num3) / 2;
- bool flag3 = num4 < 0;
- if (flag3)
- {
- num4 = 0;
- }
- g.DrawString(_dtext, font, Brushes.Black, new Point(num4, y2));
- }
- }
+ if (_dtext.Length > 0)
+ {
+ float emSize = rect.Height / 6f;
+ Font font = new Font("Microsoft Sans Serif", emSize, FontStyle.Bold, GraphicsUnit.Point, 0);
+ int num2 = (int)font.GetHeight();
+ int y2 = (int)rect.Height / 2 - num2 / 2;
+ int num3 = (int)g.MeasureString(_dtext, font).Width;
+ int num4 = (int)(rect.Width - (float)num3) / 2;
+ bool flag3 = num4 < 0;
+ if (flag3)
+ {
+ num4 = 0;
+ }
+ g.DrawString(_dtext, font, Brushes.Black, new Point(num4, y2));
+ }
+ }
+ }
- }
+ public class LampColor
+ {
+ public const int Ok = 0;
+ public const int Warn = 1;
+ public const int Error = 2;
+ public const int Debug = 4;
+ public const int Undef = -1;
- public class LampColor
- {
- public const int Ok = 0;
- public const int Warn = 1;
- public const int Error = 2;
- public const int Debug = 4;
- public const int Undef = -1;
-
- public const int Green = 0;
- public const int Orange = 1;
- public const int Red = 2;
- public const int White = 3;
- public const int Silver = 4;
- public const int Transparent = 5;
- public const int Yellow = 6;
- public const int Blue = 7;
- }
-}
+ public const int Green = 0;
+ public const int Orange = 1;
+ public const int Red = 2;
+ public const int White = 3;
+ public const int Silver = 4;
+ public const int Transparent = 5;
+ public const int Yellow = 6;
+ public const int Blue = 7;
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/Scatter/Algebra.cs b/HexcalMC/Base/Scatter/Algebra.cs
index f89e459..294d507 100644
--- a/HexcalMC/Base/Scatter/Algebra.cs
+++ b/HexcalMC/Base/Scatter/Algebra.cs
@@ -1,105 +1,100 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
namespace Scatter
{
- static class Algerbra
- {
- public class Matrix
- {
- int rows;
- int columns;
+ internal static class Algerbra
+ {
+ public class Matrix
+ {
+ private int rows;
+ private int columns;
- private T[,] matrix;
+ private T[,] matrix;
- public Matrix(int n, int m)
- {
- matrix = new T[n, m];
- rows = n;
- columns = m;
- }
+ public Matrix(int n, int m)
+ {
+ matrix = new T[n, m];
+ rows = n;
+ columns = m;
+ }
- public void SetValByIdx(int m, int n, T x)
- {
- matrix[n, m] = x;
- }
+ public void SetValByIdx(int m, int n, T x)
+ {
+ matrix[n, m] = x;
+ }
- public T GetValByIndex(int n, int m)
- {
- return matrix[n, m];
- }
+ public T GetValByIndex(int n, int m)
+ {
+ return matrix[n, m];
+ }
- public void SetMatrix(T[] arr)
- {
- for (int r = 0; r < rows; r++)
- for (int c = 0; c < columns; c++)
- matrix[r, c] = arr[r * columns + c];
- }
+ public void SetMatrix(T[] arr)
+ {
+ for (int r = 0; r < rows; r++)
+ for (int c = 0; c < columns; c++)
+ matrix[r, c] = arr[r * columns + c];
+ }
- public static Matrix operator |(Matrix m1, Matrix m2)
- {
- Matrix m = new Matrix(m1.rows, m1.columns + m2.columns);
- for (int r = 0; r < m1.rows; r++)
- {
- for (int c = 0; c < m1.columns; c++)
- m.matrix[r, c] = m1.matrix[r, c];
- for (int c = 0; c < m2.columns; c++)
- m.matrix[r, c + m1.columns] = m2.matrix[r, c];
- }
- return m;
- }
+ public static Matrix operator |(Matrix m1, Matrix m2)
+ {
+ Matrix m = new Matrix(m1.rows, m1.columns + m2.columns);
+ for (int r = 0; r < m1.rows; r++)
+ {
+ for (int c = 0; c < m1.columns; c++)
+ m.matrix[r, c] = m1.matrix[r, c];
+ for (int c = 0; c < m2.columns; c++)
+ m.matrix[r, c + m1.columns] = m2.matrix[r, c];
+ }
+ return m;
+ }
- public static Matrix operator *(Matrix m1, Matrix m2)
- {
- Matrix m = new Matrix(m1.rows, m2.columns);
- for (int r = 0; r < m.rows; r++)
- for (int c = 0; c < m.columns; c++)
- {
- T tmp = (dynamic)0;
- for (int i = 0; i < m2.rows; i++)
- tmp += (dynamic)m1.matrix[r, i] * (dynamic)m2.matrix[i, c];
- m.matrix[r, c] = tmp;
- }
- return m;
- }
+ public static Matrix operator *(Matrix m1, Matrix m2)
+ {
+ Matrix m = new Matrix(m1.rows, m2.columns);
+ for (int r = 0; r < m.rows; r++)
+ for (int c = 0; c < m.columns; c++)
+ {
+ T tmp = (dynamic)0;
+ for (int i = 0; i < m2.rows; i++)
+ tmp += (dynamic)m1.matrix[r, i] * (dynamic)m2.matrix[i, c];
+ m.matrix[r, c] = tmp;
+ }
+ return m;
+ }
- public static Matrix operator ~(Matrix m)
- {
- Matrix tmp = new Matrix(m.columns, m.rows);
- for (int r = 0; r < m.rows; r++)
- for (int c = 0; c < m.columns; c++)
- tmp.matrix[c, r] = m.matrix[r, c];
- return tmp;
- }
+ public static Matrix operator ~(Matrix m)
+ {
+ Matrix tmp = new Matrix(m.columns, m.rows);
+ for (int r = 0; r < m.rows; r++)
+ for (int c = 0; c < m.columns; c++)
+ tmp.matrix[c, r] = m.matrix[r, c];
+ return tmp;
+ }
- public static Matrix operator -(Matrix m)
- {
- Matrix tmp = new Matrix(m.columns, m.rows);
- for (int r = 0; r < m.rows; r++)
- for (int c = 0; c < m.columns; c++)
- tmp.matrix[r, c] = -(dynamic)m.matrix[r, c];
- return tmp;
- }
+ public static Matrix operator -(Matrix m)
+ {
+ Matrix tmp = new Matrix(m.columns, m.rows);
+ for (int r = 0; r < m.rows; r++)
+ for (int c = 0; c < m.columns; c++)
+ tmp.matrix[r, c] = -(dynamic)m.matrix[r, c];
+ return tmp;
+ }
- public override string ToString()
- {
- String output = "";
- for (int r = 0; r < rows; r++)
- {
- output += "[\t";
- for (int c = 0; c < columns; c++)
- {
- output += matrix[r, c].ToString();
- if (c < columns - 1) output += ",\t";
- }
- output += "]\n";
- }
- return output;
- }
- }
-
-
- }
-}
+ public override string ToString()
+ {
+ String output = "";
+ for (int r = 0; r < rows; r++)
+ {
+ output += "[\t";
+ for (int c = 0; c < columns; c++)
+ {
+ output += matrix[r, c].ToString();
+ if (c < columns - 1) output += ",\t";
+ }
+ output += "]\n";
+ }
+ return output;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/Scatter/MouseWheelHandler.cs b/HexcalMC/Base/Scatter/MouseWheelHandler.cs
index 33576bf..18a26cf 100644
--- a/HexcalMC/Base/Scatter/MouseWheelHandler.cs
+++ b/HexcalMC/Base/Scatter/MouseWheelHandler.cs
@@ -1,68 +1,65 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Windows.Forms;
using System.Drawing;
+using System.Windows.Forms;
namespace Scatter
{
- public static class MouseWheelHandler
- {
- public static void Add(Control ctrl, Action onMouseWheel)
- {
- if (ctrl == null || onMouseWheel == null)
- throw new ArgumentNullException();
+ public static class MouseWheelHandler
+ {
+ public static void Add(Control ctrl, Action onMouseWheel)
+ {
+ if (ctrl == null || onMouseWheel == null)
+ throw new ArgumentNullException();
- var filter = new MouseWheelMessageFilter(ctrl, onMouseWheel);
- Application.AddMessageFilter(filter);
- ctrl.Disposed += (s, e) => Application.RemoveMessageFilter(filter);
- }
+ var filter = new MouseWheelMessageFilter(ctrl, onMouseWheel);
+ Application.AddMessageFilter(filter);
+ ctrl.Disposed += (s, e) => Application.RemoveMessageFilter(filter);
+ }
- class MouseWheelMessageFilter
- : IMessageFilter
- {
- private readonly Control _ctrl;
- private readonly Action _onMouseWheel;
+ private class MouseWheelMessageFilter
+ : IMessageFilter
+ {
+ private readonly Control _ctrl;
+ private readonly Action _onMouseWheel;
- public MouseWheelMessageFilter(Control ctrl, Action onMouseWheel)
- {
- _ctrl = ctrl;
- _onMouseWheel = onMouseWheel;
- }
+ public MouseWheelMessageFilter(Control ctrl, Action onMouseWheel)
+ {
+ _ctrl = ctrl;
+ _onMouseWheel = onMouseWheel;
+ }
- public bool PreFilterMessage(ref Message m)
- {
- var parent = _ctrl.Parent;
- if (parent != null && m.Msg == 0x20a) // WM_MOUSEWHEEL, find the control at screen position m.LParam
- {
- var pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
+ public bool PreFilterMessage(ref Message m)
+ {
+ var parent = _ctrl.Parent;
+ if (parent != null && m.Msg == 0x20a) // WM_MOUSEWHEEL, find the control at screen position m.LParam
+ {
+ var pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
- var clientPos = _ctrl.PointToClient(pos);
+ var clientPos = _ctrl.PointToClient(pos);
- if (_ctrl.ClientRectangle.Contains(clientPos)
- && ReferenceEquals(_ctrl, parent.GetChildAtPoint(parent.PointToClient(pos))))
- {
- var wParam = m.WParam.ToInt32();
- Func getButton =
- (flag, button) => ((wParam & flag) == flag) ? button : MouseButtons.None;
+ if (_ctrl.ClientRectangle.Contains(clientPos)
+ && ReferenceEquals(_ctrl, parent.GetChildAtPoint(parent.PointToClient(pos))))
+ {
+ var wParam = m.WParam.ToInt32();
+ Func getButton =
+ (flag, button) => ((wParam & flag) == flag) ? button : MouseButtons.None;
- var buttons = getButton(wParam & 0x0001, MouseButtons.Left)
- | getButton(wParam & 0x0010, MouseButtons.Middle)
- | getButton(wParam & 0x0002, MouseButtons.Right)
- | getButton(wParam & 0x0020, MouseButtons.XButton1)
- | getButton(wParam & 0x0040, MouseButtons.XButton2)
- ; // Not matching for these /*MK_SHIFT=0x0004;MK_CONTROL=0x0008*/
+ var buttons = getButton(wParam & 0x0001, MouseButtons.Left)
+ | getButton(wParam & 0x0010, MouseButtons.Middle)
+ | getButton(wParam & 0x0002, MouseButtons.Right)
+ | getButton(wParam & 0x0020, MouseButtons.XButton1)
+ | getButton(wParam & 0x0040, MouseButtons.XButton2)
+ ; // Not matching for these /*MK_SHIFT=0x0004;MK_CONTROL=0x0008*/
- var delta = wParam >> 16;
- var e = new MouseEventArgs(buttons, 0, clientPos.X, clientPos.Y, delta);
- _onMouseWheel(e);
+ var delta = wParam >> 16;
+ var e = new MouseEventArgs(buttons, 0, clientPos.X, clientPos.Y, delta);
+ _onMouseWheel(e);
- return true;
- }
- }
- return false;
- }
- }
- }
-}
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/Scatter/Projection.cs b/HexcalMC/Base/Scatter/Projection.cs
index 3e0b166..ff49e9d 100644
--- a/HexcalMC/Base/Scatter/Projection.cs
+++ b/HexcalMC/Base/Scatter/Projection.cs
@@ -1,67 +1,64 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Drawing;
-using System.Diagnostics;
namespace Scatter
{
- static class Projection
- {
- static public PointF Project(double[] x, double s_x, double s_y, double f, double[] d_w, double azimuth, double elevation)
- {
- Algerbra.Matrix Mext = GetMext(azimuth, elevation, d_w);
- Algerbra.Matrix Mint = GetMint(s_x, s_y, f);
- Algerbra.Matrix X_h = new Algerbra.Matrix(4, 1);
- X_h.SetMatrix(new double[] { x[0], x[1], x[2], 1.0});
- //Debug.Print((Mint * Mext).ToString());
- Algerbra.Matrix P = Mint * Mext * X_h;
- return new PointF((float)(P.GetValByIndex(0, 0) / P.GetValByIndex(2, 0)), (float)(P.GetValByIndex(1, 0) / P.GetValByIndex(2, 0)));
- }
+ internal static class Projection
+ {
+ public static PointF Project(double[] x, double s_x, double s_y, double f, double[] d_w, double azimuth, double elevation)
+ {
+ Algerbra.Matrix Mext = GetMext(azimuth, elevation, d_w);
+ Algerbra.Matrix Mint = GetMint(s_x, s_y, f);
+ Algerbra.Matrix X_h = new Algerbra.Matrix(4, 1);
+ X_h.SetMatrix(new double[] { x[0], x[1], x[2], 1.0 });
+ //Debug.Print((Mint * Mext).ToString());
+ Algerbra.Matrix P = Mint * Mext * X_h;
+ return new PointF((float)(P.GetValByIndex(0, 0) / P.GetValByIndex(2, 0)), (float)(P.GetValByIndex(1, 0) / P.GetValByIndex(2, 0)));
+ }
- static public PointF[] ProjectVector(List x, double s_x, double s_y, double f, double[] d_w, double azimuth, double elevation)
- {
- Algerbra.Matrix Mext = GetMext(azimuth, elevation, d_w);
- Algerbra.Matrix Mint = GetMint(s_x, s_y, f);
- Algerbra.Matrix X_h = new Algerbra.Matrix(4, 1);
+ public static PointF[] ProjectVector(List x, double s_x, double s_y, double f, double[] d_w, double azimuth, double elevation)
+ {
+ Algerbra.Matrix Mext = GetMext(azimuth, elevation, d_w);
+ Algerbra.Matrix Mint = GetMint(s_x, s_y, f);
+ Algerbra.Matrix X_h = new Algerbra.Matrix(4, 1);
- PointF[] Pvec = new PointF[x.Count];
- for (int i = 0; i < x.Count; i++)
- {
- X_h.SetMatrix(new double[] { x[i][0], x[i][1], x[i][2], 1.0 });
- Algerbra.Matrix P = Mint * Mext * X_h;
- Pvec[i] = new PointF((float)(P.GetValByIndex(0, 0) / P.GetValByIndex(2, 0)), (float)(P.GetValByIndex(1, 0) / P.GetValByIndex(2, 0)));
- }
- return Pvec;
- }
+ PointF[] Pvec = new PointF[x.Count];
+ for (int i = 0; i < x.Count; i++)
+ {
+ X_h.SetMatrix(new double[] { x[i][0], x[i][1], x[i][2], 1.0 });
+ Algerbra.Matrix P = Mint * Mext * X_h;
+ Pvec[i] = new PointF((float)(P.GetValByIndex(0, 0) / P.GetValByIndex(2, 0)), (float)(P.GetValByIndex(1, 0) / P.GetValByIndex(2, 0)));
+ }
+ return Pvec;
+ }
- static Algerbra.Matrix GetMint(double s_x, double s_y, double f)
- {
- Algerbra.Matrix Mint = new Algerbra.Matrix(3, 3);
- double o_x = s_x / 2;
- double o_y = s_y / 2;
- double a = 1;
- Mint.SetMatrix(new double[] { f, 0, o_x, 0, f * a, o_y, 0, 0, 1 });
- return Mint;
- }
+ private static Algerbra.Matrix GetMint(double s_x, double s_y, double f)
+ {
+ Algerbra.Matrix Mint = new Algerbra.Matrix(3, 3);
+ double o_x = s_x / 2;
+ double o_y = s_y / 2;
+ double a = 1;
+ Mint.SetMatrix(new double[] { f, 0, o_x, 0, f * a, o_y, 0, 0, 1 });
+ return Mint;
+ }
- static Algerbra.Matrix GetMext(double azimuth, double elevation, double[] d_w)
- {
- Algerbra.Matrix R = RotationMatrix(azimuth, elevation);
- Algerbra.Matrix dw = new Algerbra.Matrix(3, 1);
- dw.SetMatrix(d_w);
- Algerbra.Matrix Mext = R | (-R * dw);
- return Mext;
- }
+ private static Algerbra.Matrix GetMext(double azimuth, double elevation, double[] d_w)
+ {
+ Algerbra.Matrix R = RotationMatrix(azimuth, elevation);
+ Algerbra.Matrix dw = new Algerbra.Matrix(3, 1);
+ dw.SetMatrix(d_w);
+ Algerbra.Matrix Mext = R | (-R * dw);
+ return Mext;
+ }
- static Algerbra.Matrix RotationMatrix(double azimuth, double elevation)
- {
- Algerbra.Matrix R = new Algerbra.Matrix(3, 3);
- R.SetMatrix(new double[] { Math.Cos(azimuth), 0, -Math.Sin(azimuth),
- Math.Sin(azimuth)*Math.Sin(elevation), Math.Cos(elevation), Math.Cos(azimuth)*Math.Sin(elevation),
- Math.Cos(elevation)*Math.Sin(azimuth), -Math.Sin(elevation), Math.Cos(azimuth)*Math.Cos(elevation) });
- return R;
- }
- }
-}
+ private static Algerbra.Matrix RotationMatrix(double azimuth, double elevation)
+ {
+ Algerbra.Matrix R = new Algerbra.Matrix(3, 3);
+ R.SetMatrix(new double[] { Math.Cos(azimuth), 0, -Math.Sin(azimuth),
+ Math.Sin(azimuth)*Math.Sin(elevation), Math.Cos(elevation), Math.Cos(azimuth)*Math.Sin(elevation),
+ Math.Cos(elevation)*Math.Sin(azimuth), -Math.Sin(elevation), Math.Cos(azimuth)*Math.Cos(elevation) });
+ return R;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/Scatter/ScatterPlot.cs b/HexcalMC/Base/Scatter/ScatterPlot.cs
index b018067..477aa98 100644
--- a/HexcalMC/Base/Scatter/ScatterPlot.cs
+++ b/HexcalMC/Base/Scatter/ScatterPlot.cs
@@ -1,179 +1,171 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Drawing;
-using System.Data;
-using System.Linq;
-using System.Text;
using System.Windows.Forms;
-using System.Diagnostics;
namespace Scatter
{
- public partial class ScatterPlot : UserControl
- {
- List> Points = new List>();
- List ProjPoints = new List();
- private double f = 1000;
- private double d = 5;
- private double[] d_w = new double[3];
- private double last_azimuth, azimuth = 0, last_elevation, elevation = 0;
- private bool leftMousePressed = false;
- private PointF ptMouseClick;
+ public partial class ScatterPlot : UserControl
+ {
+ private List> Points = new List>();
+ private List ProjPoints = new List();
+ private double f = 1000;
+ private double d = 5;
+ private double[] d_w = new double[3];
+ private double last_azimuth, azimuth = 0, last_elevation, elevation = 0;
+ private bool leftMousePressed = false;
+ private PointF ptMouseClick;
- public double Distance
- {
- get { return d; }
- set { d = (value >= 0.1) ? d = value : d; UpdateProjection(); }
- }
+ public double Distance
+ {
+ get { return d; }
+ set { d = (value >= 0.1) ? d = value : d; UpdateProjection(); }
+ }
- public double F
- {
- get { return f; }
- set { f = value; UpdateProjection(); }
- }
+ public double F
+ {
+ get { return f; }
+ set { f = value; UpdateProjection(); }
+ }
- public double[] CameraPos
- {
- get { return d_w;}
- set { d_w = value; UpdateProjection(); }
- }
+ public double[] CameraPos
+ {
+ get { return d_w; }
+ set { d_w = value; UpdateProjection(); }
+ }
- public double Azimuth
- {
- get { return azimuth; }
- set { azimuth = value; UpdateProjection(); }
- }
-
- public double Elevation
- {
- get { return elevation; }
- set { elevation = value; UpdateProjection(); }
- }
+ public double Azimuth
+ {
+ get { return azimuth; }
+ set { azimuth = value; UpdateProjection(); }
+ }
- public ScatterPlot()
- {
- InitializeComponent();
- MouseWheelHandler.Add(this, MyOnMouseWheel);
- }
+ public double Elevation
+ {
+ get { return elevation; }
+ set { elevation = value; UpdateProjection(); }
+ }
- protected override CreateParams CreateParams
- {
- get
- {
- var cp = base.CreateParams;
- cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
- return cp;
- }
- }
+ public ScatterPlot()
+ {
+ InitializeComponent();
+ MouseWheelHandler.Add(this, MyOnMouseWheel);
+ }
- Color[] colorIdx = new Color[] { Color.Blue, Color.Red, Color.Green, Color.Orange, Color.Fuchsia, Color.Black };
+ protected override CreateParams CreateParams
+ {
+ get
+ {
+ var cp = base.CreateParams;
+ cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
+ return cp;
+ }
+ }
- protected override void OnPaint(PaintEventArgs e)
- {
- base.OnPaint(e);
+ private Color[] colorIdx = new Color[] { Color.Blue, Color.Red, Color.Green, Color.Orange, Color.Fuchsia, Color.Black };
- Graphics g = this.CreateGraphics();
- g.FillRectangle(Brushes.White, new Rectangle(0, 0, this.Width, this.Height));
- if (ProjPoints != null)
- {
- for (int i = 0; i < ProjPoints.Count; i++)
- {
- foreach (PointF p in ProjPoints[i])
- {
- g.FillEllipse(new SolidBrush(colorIdx[i % colorIdx.Length]), new RectangleF(p.X, p.Y, 4, 4));
- }
- }
- }
- }
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ base.OnPaint(e);
- public void AddPoint(double x, double y, double z, int series)
- {
- if (Points.Count - 1 < series)
- {
- Points.Add(new List());
- }
+ Graphics g = this.CreateGraphics();
+ g.FillRectangle(Brushes.White, new Rectangle(0, 0, this.Width, this.Height));
+ if (ProjPoints != null)
+ {
+ for (int i = 0; i < ProjPoints.Count; i++)
+ {
+ foreach (PointF p in ProjPoints[i])
+ {
+ g.FillEllipse(new SolidBrush(colorIdx[i % colorIdx.Length]), new RectangleF(p.X, p.Y, 4, 4));
+ }
+ }
+ }
+ }
- Points[series].Add(new double[] { x, y, z });
+ public void AddPoint(double x, double y, double z, int series)
+ {
+ if (Points.Count - 1 < series)
+ {
+ Points.Add(new List());
+ }
- foreach (List ser in Points)
- {
- if (ProjPoints.Count - 1 < series)
- ProjPoints.Add(Projection.ProjectVector(ser, this.Width, this.Height, f, d_w, azimuth, elevation));
- else
- ProjPoints[series] = Projection.ProjectVector(ser, this.Width, this.Height, f, d_w, azimuth, elevation);
- }
- this.Invalidate();
- }
+ Points[series].Add(new double[] { x, y, z });
- public void AddPoints(List points)
- {
- List _tmp = new List(points);
- Points.Add(_tmp);
- ProjPoints.Add(Projection.ProjectVector(Points[Points.Count-1], this.Width, this.Height, f, d_w, azimuth, elevation));
- UpdateProjection();
- }
+ foreach (List ser in Points)
+ {
+ if (ProjPoints.Count - 1 < series)
+ ProjPoints.Add(Projection.ProjectVector(ser, this.Width, this.Height, f, d_w, azimuth, elevation));
+ else
+ ProjPoints[series] = Projection.ProjectVector(ser, this.Width, this.Height, f, d_w, azimuth, elevation);
+ }
+ this.Invalidate();
+ }
- public void Clear()
- {
- ProjPoints.Clear();
- Points.Clear();
- Azimuth = 0;
- Elevation = 0;
- }
+ public void AddPoints(List points)
+ {
+ List _tmp = new List(points);
+ Points.Add(_tmp);
+ ProjPoints.Add(Projection.ProjectVector(Points[Points.Count - 1], this.Width, this.Height, f, d_w, azimuth, elevation));
+ UpdateProjection();
+ }
- private void ScatterPlot_MouseMove(object sender, MouseEventArgs e)
- {
- if (leftMousePressed)
- {
- azimuth = last_azimuth - (ptMouseClick.X - e.X) / 100;
- elevation = last_elevation + (ptMouseClick.Y - e.Y) / 100;
- UpdateProjection();
- }
- }
+ public void Clear()
+ {
+ ProjPoints.Clear();
+ Points.Clear();
+ Azimuth = 0;
+ Elevation = 0;
+ }
- private void ScatterPlot_SizeChanged(object sender, EventArgs e)
- {
- if (ProjPoints != null)
- UpdateProjection();
- }
+ private void ScatterPlot_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (leftMousePressed)
+ {
+ azimuth = last_azimuth - (ptMouseClick.X - e.X) / 100;
+ elevation = last_elevation + (ptMouseClick.Y - e.Y) / 100;
+ UpdateProjection();
+ }
+ }
- private void ScatterPlot_MouseDown(object sender, MouseEventArgs e)
- {
- if (e.Button == System.Windows.Forms.MouseButtons.Left)
- {
- leftMousePressed = true;
- ptMouseClick = new PointF(e.X, e.Y);
- last_azimuth = azimuth;
- last_elevation = elevation;
- }
- }
+ private void ScatterPlot_SizeChanged(object sender, EventArgs e)
+ {
+ if (ProjPoints != null)
+ UpdateProjection();
+ }
- private void ScatterPlot_MouseUp(object sender, MouseEventArgs e)
- {
- if (e.Button == System.Windows.Forms.MouseButtons.Left)
- leftMousePressed = false;
- }
+ private void ScatterPlot_MouseDown(object sender, MouseEventArgs e)
+ {
+ if (e.Button == System.Windows.Forms.MouseButtons.Left)
+ {
+ leftMousePressed = true;
+ ptMouseClick = new PointF(e.X, e.Y);
+ last_azimuth = azimuth;
+ last_elevation = elevation;
+ }
+ }
- private void MyOnMouseWheel(MouseEventArgs e)
- {
- Distance += -e.Delta / 500D;
- }
+ private void ScatterPlot_MouseUp(object sender, MouseEventArgs e)
+ {
+ if (e.Button == System.Windows.Forms.MouseButtons.Left)
+ leftMousePressed = false;
+ }
- private void UpdateProjection()
- {
- if (ProjPoints == null)
- return;
- double x = d * Math.Cos(elevation) * Math.Cos(azimuth);
- double y = d * Math.Cos(elevation) * Math.Sin(azimuth);
- double z = d * Math.Sin(elevation);
- d_w = new double[3] { -y, z, -x };
- for (int i = 0; i < ProjPoints.Count; i++)
- ProjPoints[i] = Projection.ProjectVector(Points[i], this.Width, this.Height, f, d_w, azimuth, elevation);
- this.Invalidate();
- }
+ private void MyOnMouseWheel(MouseEventArgs e)
+ {
+ Distance += -e.Delta / 500D;
+ }
- }
-
-
-}
+ private void UpdateProjection()
+ {
+ if (ProjPoints == null)
+ return;
+ double x = d * Math.Cos(elevation) * Math.Cos(azimuth);
+ double y = d * Math.Cos(elevation) * Math.Sin(azimuth);
+ double z = d * Math.Sin(elevation);
+ d_w = new double[3] { -y, z, -x };
+ for (int i = 0; i < ProjPoints.Count; i++)
+ ProjPoints[i] = Projection.ProjectVector(Points[i], this.Width, this.Height, f, d_w, azimuth, elevation);
+ this.Invalidate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Base/SharpGLViewportControl.cs b/HexcalMC/Base/SharpGLViewportControl.cs
index 5c5031c..7fe83aa 100644
--- a/HexcalMC/Base/SharpGLViewportControl.cs
+++ b/HexcalMC/Base/SharpGLViewportControl.cs
@@ -1,9 +1,9 @@
-using System;
+using SharpGL;
+using SharpGL.SceneGraph;
+using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
-using SharpGL;
-using SharpGL.SceneGraph;
using Telerik.WinControls.UI;
namespace HexcalMC.Base
@@ -22,7 +22,6 @@ namespace HexcalMC.Base
Dock = DockStyle.Fill
};
-
Controls.Add(openGLControl);
openGLControl.OpenGLInitialized += OpenGLControl_OpenGLInitialized;
@@ -31,7 +30,6 @@ namespace HexcalMC.Base
pointCloud = new List();
}
-
public void SetPointCloud(List points)
{
pointCloud = points;
diff --git a/HexcalMC/Form/AboutBox.cs b/HexcalMC/Form/AboutBox.cs
index fce52ca..14cd9f0 100644
--- a/HexcalMC/Form/AboutBox.cs
+++ b/HexcalMC/Form/AboutBox.cs
@@ -11,10 +11,10 @@ namespace HexcalMC.Form
{
InitializeComponent();
- // Initialize the AboutBox to display the product information from the assembly information.
- // Change assembly information settings for your application through either:
- // - Project->Properties->Application->Assembly Information
- // - AssemblyInfo.cs
+ // Initialize the AboutBox to display the product information from the assembly
+ // information. Change assembly information settings for your application through either:
+ // - Project->Properties->Application->Assembly Information
+ // - AssemblyInfo.cs
Text = string.Format("关于 {0}", AssemblyTitle);
radLabelProductName.Text = AssemblyProduct;
radLabelVersion.Text = string.Format("Version {0}", AssemblyVersion);
@@ -31,7 +31,6 @@ namespace HexcalMC.Form
Close();
}
-
#region Assembly Attribute Accessors
public string AssemblyTitle
@@ -51,7 +50,8 @@ namespace HexcalMC.Form
return titleAttribute.Title;
}
- // If there was no Title attribute, or if the Title attribute was the empty string, return the .exe name
+ // If there was no Title attribute, or if the Title attribute was the empty string,
+ // return the .exe name
return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
}
}
@@ -118,6 +118,6 @@ namespace HexcalMC.Form
}
}
- #endregion
+ #endregion Assembly Attribute Accessors
}
}
\ No newline at end of file
diff --git a/HexcalMC/Hexcal/TcpIpServer.cs b/HexcalMC/Hexcal/TcpIpServer.cs
index 1119f56..3a4a3f0 100644
--- a/HexcalMC/Hexcal/TcpIpServer.cs
+++ b/HexcalMC/Hexcal/TcpIpServer.cs
@@ -1,4 +1,5 @@
-using System;
+using HexcalMC.Base;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -6,7 +7,6 @@ using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
-using HexcalMC.Base;
using Timer = System.Windows.Forms.Timer;
namespace HexcalMC.Hexcal
@@ -36,7 +36,7 @@ namespace HexcalMC.Hexcal
private Thread _mWatchThread; // 负责监听客户端连接请求的线程;
///
- /// 使用模式,默认=1,接收任意数据显示;2=前两个字节为数据长度
+ /// 使用模式,默认=1,接收任意数据显示;2=前两个字节为数据长度
///
public int UseMode = 1;
@@ -207,8 +207,8 @@ namespace HexcalMC.Hexcal
if (length > 0)
{
string strData = Encoding.Default.GetString(arrMsgRec); // 将接受到的字节数据转化成字符串;
- //strData = strData.Substring(0, length);
- //RaisedMessage(sokClient.RemoteEndPoint.ToString(), strData.Replace("\0", "<0>"));
+ //strData = strData.Substring(0, length);
+ //RaisedMessage(sokClient.RemoteEndPoint.ToString(), strData.Replace("\0", "<0>"));
RaisedMessage(sokClient.RemoteEndPoint.ToString(), strData.Replace("\0", "."));
}
@@ -226,6 +226,7 @@ namespace HexcalMC.Hexcal
}
break;
+
case 3:
if (length > 0)
{
@@ -315,7 +316,6 @@ namespace HexcalMC.Hexcal
}
}
-
public void SendMessageToAllClients(byte[] arrMsg)
{
foreach (Socket soc in _dictSocket.Values)
@@ -388,7 +388,7 @@ namespace HexcalMC.Hexcal
for (int i = _dictSocket.Values.ToArray().Length - 1; i >= 0; i--)
{
if (_dictSocket.Values.ToArray()[i]
- .Poll(100, SelectMode.SelectRead)) //10毫秒,检查套接字状态, SelectMode 参数指定要监视的套接字的类别。
+ .Poll(100, SelectMode.SelectRead)) //10毫秒,检查套接字状态, SelectMode 参数指定要监视的套接字的类别。
{
// DictSocket.Remove(DictSocket.Keys.ToArray()[i]);
diff --git a/HexcalMC/HexcalMC.csproj b/HexcalMC/HexcalMC.csproj
index dfff1eb..e64ec9a 100644
--- a/HexcalMC/HexcalMC.csproj
+++ b/HexcalMC/HexcalMC.csproj
@@ -86,16 +86,22 @@
Hexagon.ico
- 6EDD851028CAF15F53977FDAABCEB5A0859CFE89
+ 496B16FFAF2CF6830CF56CFF2C6223A82DC3B1DF
- HexcalMC_TemporaryKey.pfx
+ HexcalMC_1_TemporaryKey.pfx
true
- true
+ false
+
+
+
+
+
+ true
@@ -197,11 +203,11 @@
AboutBox.cs
-
+
Form
-
- MainFrom.cs
+
+ MainForm.cs
Form
@@ -236,8 +242,8 @@
AboutBox.cs
-
- MainFrom.cs
+
+ MainForm.cs
Designer
MainFrom1.Designer.cs
@@ -267,7 +273,6 @@
-
SettingsSingleFileGenerator
diff --git a/HexcalMC/MainFrom.Designer.cs b/HexcalMC/MainForm.Designer.cs
similarity index 97%
rename from HexcalMC/MainFrom.Designer.cs
rename to HexcalMC/MainForm.Designer.cs
index 19eb2b3..6ab117a 100644
--- a/HexcalMC/MainFrom.Designer.cs
+++ b/HexcalMC/MainForm.Designer.cs
@@ -1,6 +1,6 @@
namespace HexcalMC
{
- partial class MainFrom
+ partial class MainForm
{
///
/// Required designer variable.
@@ -29,14 +29,12 @@
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainFrom));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.radLabelElement2 = new Telerik.WinControls.UI.RadLabelElement();
this.radTextBoxElement2 = new Telerik.WinControls.UI.RadTextBoxElement();
this.radButtonElement5 = new Telerik.WinControls.UI.RadButtonElement();
this.radTextBoxElement1 = new Telerik.WinControls.UI.RadTextBoxElement();
this.ribbonTab1 = new Telerik.WinControls.UI.RibbonTab();
- this.radRibbonBarGroup1 = new Telerik.WinControls.UI.RadRibbonBarGroup();
- this.btn_motion = new Telerik.WinControls.UI.RadButtonElement();
this.radRibbonBarGroup4 = new Telerik.WinControls.UI.RadRibbonBarGroup();
this.radRibbonBarButtonGroup4 = new Telerik.WinControls.UI.RadRibbonBarButtonGroup();
this.radLabelElement5 = new Telerik.WinControls.UI.RadLabelElement();
@@ -62,6 +60,8 @@
this.rtb_stop = new Telerik.WinControls.UI.RadButtonElement();
this.radRibbonBarGroup9 = new Telerik.WinControls.UI.RadRibbonBarGroup();
this.rtb_etalon = new Telerik.WinControls.UI.RadButtonElement();
+ this.radRibbonBarGroup1 = new Telerik.WinControls.UI.RadRibbonBarGroup();
+ this.btn_motion = new Telerik.WinControls.UI.RadButtonElement();
this.radRibbonBarGroup10 = new Telerik.WinControls.UI.RadRibbonBarGroup();
this.rtb_monitor = new Telerik.WinControls.UI.RadButtonElement();
this.ribbonTab2 = new Telerik.WinControls.UI.RibbonTab();
@@ -217,13 +217,13 @@
this.ribbonTab1.DisabledTextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
this.ribbonTab1.IsSelected = true;
this.ribbonTab1.Items.AddRange(new Telerik.WinControls.RadItem[] {
- this.radRibbonBarGroup1,
this.radRibbonBarGroup4,
this.radRibbonBarGroup5,
this.radRibbonBarGroup6,
this.radRibbonBarGroup8,
this.radRibbonBarGroup7,
this.radRibbonBarGroup9,
+ this.radRibbonBarGroup1,
this.radRibbonBarGroup10});
this.ribbonTab1.Name = "ribbonTab1";
this.ribbonTab1.Text = "常用";
@@ -231,27 +231,6 @@
this.ribbonTab1.UseCompatibleTextRendering = false;
this.ribbonTab1.UseMnemonic = false;
//
- // radRibbonBarGroup1
- //
- this.radRibbonBarGroup1.Alignment = System.Drawing.ContentAlignment.TopLeft;
- this.radRibbonBarGroup1.AutoSize = false;
- this.radRibbonBarGroup1.Bounds = new System.Drawing.Rectangle(0, 0, 77, 100);
- this.radRibbonBarGroup1.Items.AddRange(new Telerik.WinControls.RadItem[] {
- this.btn_motion});
- this.radRibbonBarGroup1.Margin = new System.Windows.Forms.Padding(0);
- this.radRibbonBarGroup1.MaxSize = new System.Drawing.Size(110, 100);
- this.radRibbonBarGroup1.MinSize = new System.Drawing.Size(110, 100);
- this.radRibbonBarGroup1.Name = "radRibbonBarGroup1";
- this.radRibbonBarGroup1.Text = "运动控制";
- this.radRibbonBarGroup1.UseCompatibleTextRendering = false;
- //
- // btn_motion
- //
- this.btn_motion.Image = global::HexcalMC.Properties.Resources.mothion_64;
- this.btn_motion.Name = "btn_motion";
- this.btn_motion.Text = "";
- this.btn_motion.Click += new System.EventHandler(this.btn_motion_Click);
- //
// radRibbonBarGroup4
//
this.radRibbonBarGroup4.Items.AddRange(new Telerik.WinControls.RadItem[] {
@@ -445,6 +424,7 @@
this.rtb_etalon});
this.radRibbonBarGroup9.Name = "radRibbonBarGroup9";
this.radRibbonBarGroup9.Text = "etalon校准";
+ this.radRibbonBarGroup9.Visibility = Telerik.WinControls.ElementVisibility.Hidden;
//
// rtb_etalon
//
@@ -453,18 +433,42 @@
this.rtb_etalon.Text = "";
this.rtb_etalon.Click += new System.EventHandler(this.rtb_etalon_Click);
//
+ // radRibbonBarGroup1
+ //
+ this.radRibbonBarGroup1.Alignment = System.Drawing.ContentAlignment.TopLeft;
+ this.radRibbonBarGroup1.AutoSize = false;
+ this.radRibbonBarGroup1.Bounds = new System.Drawing.Rectangle(0, 0, 77, 100);
+ this.radRibbonBarGroup1.Items.AddRange(new Telerik.WinControls.RadItem[] {
+ this.btn_motion});
+ this.radRibbonBarGroup1.Margin = new System.Windows.Forms.Padding(0);
+ this.radRibbonBarGroup1.MaxSize = new System.Drawing.Size(110, 100);
+ this.radRibbonBarGroup1.MinSize = new System.Drawing.Size(110, 100);
+ this.radRibbonBarGroup1.Name = "radRibbonBarGroup1";
+ this.radRibbonBarGroup1.Text = "运动控制";
+ this.radRibbonBarGroup1.UseCompatibleTextRendering = false;
+ this.radRibbonBarGroup1.Visibility = Telerik.WinControls.ElementVisibility.Hidden;
+ //
+ // btn_motion
+ //
+ this.btn_motion.Image = global::HexcalMC.Properties.Resources.mothion_64;
+ this.btn_motion.Name = "btn_motion";
+ this.btn_motion.Text = "";
+ this.btn_motion.Click += new System.EventHandler(this.btn_motion_Click);
+ //
// radRibbonBarGroup10
//
this.radRibbonBarGroup10.Items.AddRange(new Telerik.WinControls.RadItem[] {
this.rtb_monitor});
this.radRibbonBarGroup10.Name = "radRibbonBarGroup10";
this.radRibbonBarGroup10.Text = "速度监控";
+ this.radRibbonBarGroup10.Visibility = Telerik.WinControls.ElementVisibility.Hidden;
//
// rtb_monitor
//
this.rtb_monitor.AutoSize = false;
this.rtb_monitor.Bounds = new System.Drawing.Rectangle(0, 0, 66, 75);
this.rtb_monitor.Image = global::HexcalMC.Properties.Resources.speed;
+ this.rtb_monitor.ImageAlignment = System.Drawing.ContentAlignment.MiddleCenter;
this.rtb_monitor.Name = "rtb_monitor";
this.rtb_monitor.Text = "";
this.rtb_monitor.Click += new System.EventHandler(this.rtb_monitor_Click);
@@ -537,6 +541,12 @@
this.radMenuItem2});
this.radRibbonBar1.TabIndex = 0;
this.radRibbonBar1.Text = "运动补偿中间件";
+ ((Telerik.WinControls.UI.RadRibbonBarElement)(this.radRibbonBar1.GetChildAt(0))).Text = "运动补偿中间件";
+ ((Telerik.WinControls.UI.RadQuickAccessToolBar)(this.radRibbonBar1.GetChildAt(0).GetChildAt(2))).Visibility = Telerik.WinControls.ElementVisibility.Hidden;
+ ((Telerik.WinControls.UI.RadRibbonBarCaption)(this.radRibbonBar1.GetChildAt(0).GetChildAt(3))).Visibility = Telerik.WinControls.ElementVisibility.Visible;
+ ((Telerik.WinControls.UI.RadPageViewContentAreaElement)(this.radRibbonBar1.GetChildAt(0).GetChildAt(4).GetChildAt(1))).Visibility = Telerik.WinControls.ElementVisibility.Visible;
+ ((Telerik.WinControls.UI.RadApplicationMenuButtonElement)(this.radRibbonBar1.GetChildAt(0).GetChildAt(5))).Image = global::HexcalMC.Properties.Resources.Hexagon;
+ ((Telerik.WinControls.UI.RadApplicationMenuButtonElement)(this.radRibbonBar1.GetChildAt(0).GetChildAt(5))).Visibility = Telerik.WinControls.ElementVisibility.Visible;
//
// radRibbonBarBackstageView1
//
@@ -791,7 +801,7 @@
this.groupBox7.Size = new System.Drawing.Size(731, 154);
this.groupBox7.TabIndex = 4;
this.groupBox7.TabStop = false;
- this.groupBox7.Text = "Hexcal接口";
+ this.groupBox7.Text = "1. Hexcal接口";
//
// groupBox1
//
@@ -801,7 +811,7 @@
this.groupBox1.Controls.Add(this.label3);
this.groupBox1.Location = new System.Drawing.Point(14, 19);
this.groupBox1.Name = "groupBox1";
- this.groupBox1.Size = new System.Drawing.Size(701, 114);
+ this.groupBox1.Size = new System.Drawing.Size(263, 114);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "通讯状态";
@@ -820,7 +830,7 @@
//
// Btn_StopServer
//
- this.Btn_StopServer.Location = new System.Drawing.Point(113, 79);
+ this.Btn_StopServer.Location = new System.Drawing.Point(111, 63);
this.Btn_StopServer.Name = "Btn_StopServer";
this.Btn_StopServer.Size = new System.Drawing.Size(81, 30);
this.Btn_StopServer.TabIndex = 26;
@@ -830,7 +840,7 @@
//
// Btn_StartServer
//
- this.Btn_StartServer.Location = new System.Drawing.Point(18, 79);
+ this.Btn_StartServer.Location = new System.Drawing.Point(16, 63);
this.Btn_StartServer.Name = "Btn_StartServer";
this.Btn_StartServer.Size = new System.Drawing.Size(81, 30);
this.Btn_StartServer.TabIndex = 25;
@@ -858,7 +868,7 @@
this.groupBox5.Size = new System.Drawing.Size(731, 154);
this.groupBox5.TabIndex = 3;
this.groupBox5.TabStop = false;
- this.groupBox5.Text = "运动平台";
+ this.groupBox5.Text = "2. 运动平台";
//
// grpSafety
//
@@ -1302,7 +1312,7 @@
this.txtIP.Name = "txtIP";
this.txtIP.Size = new System.Drawing.Size(91, 21);
this.txtIP.TabIndex = 22;
- this.txtIP.Text = "100.0.0.100";
+ this.txtIP.Text = "10.0.0.100";
//
// btn_ACSStop
//
@@ -1372,7 +1382,7 @@
this.timer_RefreshUI.Interval = 1000;
this.timer_RefreshUI.Tick += new System.EventHandler(this.Timer_RefreshUI_Tick);
//
- // MainFrom
+ // MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
@@ -1383,7 +1393,7 @@
this.Controls.Add(this.radRibbonBarBackstageView1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = null;
- this.Name = "MainFrom";
+ this.Name = "MainForm";
//
//
//
diff --git a/HexcalMC/MainForm.cs b/HexcalMC/MainForm.cs
new file mode 100644
index 0000000..7272acb
--- /dev/null
+++ b/HexcalMC/MainForm.cs
@@ -0,0 +1,1637 @@
+using ACS.SPiiPlusNET;
+using HexcalMC.Base;
+using HexcalMC.Form;
+using HexcalMC.Hexcal;
+using HexcalMC.Properties;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Windows.Forms;
+using Telerik.WinControls.UI;
+using static HexcalMC.EtalonForm;
+
+namespace HexcalMC
+{
+ //定一个 回家状态枚举,包括 从未回家,正在回家,已经回家
+ public enum HomeStates
+ {
+ None, //默认状态
+ NotHome, //未回家
+ Homing, //回家中
+ Homed //回家完成
+ ,
+ }
+
+ //定义 运动状态枚举,包括 正在运动,运动到位,Jog运动
+ public enum MotionStates
+ {
+ None, //默认状态
+ Moving, //运动中
+ InPos, //运动到位
+ Jogging //jog中
+ ,
+ }
+
+ public partial class MainForm : RadRibbonForm
+ {
+ private readonly List _pointCloud = new List(); //运动中点集合
+
+ private bool _mBHexcalConnected;
+
+ private TcpIpServer _mTcpIpServer; //创建tcpserver,用于接收hexcal传来的指令,并解析传递平台
+
+ private int m_nTotalAxis; //定义总轴数
+ private Axis[] m_arrAxisList = null;
+
+ //定一个运动到位次数的变量和三个方法,开始统计运动到位次数,停止统计运动到位次数,获取运动到位次数
+
+ #region 运动到位次数
+
+ // 记录运动到位次数的变量
+ private int m_nInPosCount = 0;
+
+ private bool isCounting = false;
+ private bool isInPose = false; //运动到位状态
+
+ // 开始统计运动到位次数
+ public void StartCounting()
+ {
+ isCounting = true;
+ m_nInPosCount = 0; // 重置计数器
+ }
+
+ // 停止统计运动到位次数
+ public void StopCounting()
+ {
+ isCounting = false;
+ m_nInPosCount = 0; // 重置计数器
+ }
+
+ // 获取运动到位次数
+ public int GetInPosCount()
+ {
+ return m_nInPosCount;
+ }
+
+ public bool GetIsMoving()
+ {
+ return isInPose;
+ }
+
+ #endregion 运动到位次数
+
+ public MainForm()
+ {
+ InitializeComponent();
+
+ // 处理未被捕获的线程异常
+ Application.ThreadException += Application_ThreadException;
+
+ // 处理未被捕获的非UI线程异常
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ }
+
+ private void MainFrom_Load(object sender, EventArgs e)
+ {
+ FormBorderStyle = FormBorderStyle.FixedSingle; // 设置窗体边框样式为固定大小
+ MaximizeBox = false; // 禁用窗体的最大化按钮
+ DebugDfn.textBox_Msg = TextBoxMsg;
+
+ //加载配置文件
+ LoadConfig();
+ _acs = new Api(); //初始化 ACS运动控制类
+
+ //启动界面刷新
+ timer_RefreshUI.Start();
+
+ //Point3D point3D = new Point3D(800, 980, -290);
+ //IsWithinLimit(point3D);
+ }
+
+ private void MainFrom_Shown(object sender, EventArgs e) //窗体显示准备好接受用户输入时发生
+ {
+ ////启动服务端,用于接收hexcal传来的指令
+ //StartServer();
+
+ //if (_enableAcs)
+ //{
+ // Btn_ACSStart_Click(null, null); //模拟连接运动平台
+ //}
+ }
+
+ private void MainFrom_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ MyBase.TraceWriteLine("关闭程序");
+ DebugDfn._strEndTime = DateTime.Now.ToString("yyyy.MM.dd HH-mm-ss");
+ timer_RefreshUI.Stop();
+
+ Btn_StopServer_Click(null, null);
+ Btn_ACSStop_Click(null, null); //关闭ACS
+
+ string copyFileName =
+ DebugDfn.StrDebugSavePath
+ + "\\Debug("
+ + DebugDfn._strStartTime
+ + " To "
+ + DebugDfn._strEndTime
+ + ")"
+ + ".txt";
+ if (!File.Exists(DebugDfn.StrDebugSavePath))
+ {
+ //创建文件夹 DebugDfn.StrDebugSavePath
+ Directory.CreateDirectory(DebugDfn.StrDebugSavePath);
+ }
+
+ File.Copy(DebugDfn.StrDebugFile, copyFileName);
+
+ if (Errors.ErrorWrite != null)
+ Errors.ErrorWrite.Close();
+ if (Errors.OtherWrite != null)
+ Errors.OtherWrite.Close();
+ if (Errors.StatusWrite != null)
+ Errors.StatusWrite.Close();
+ }
+
+ private void LoadConfig() //加载配置文件
+ {
+ //判断配置文件是否存在
+ if (!File.Exists(StrConfigFile))
+ {
+ MessageBox.Show(
+ "配置文件不存在,请检查配置文件",
+ "异常",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error
+ );
+ return;
+ }
+
+ MotionSpeedxy = FileIni.ReadDouble(StrConfigFile, "MOTOR", "MOTION_SPEEDXY"); //运动定位速度
+ MotionSpeedz = FileIni.ReadDouble(StrConfigFile, "MOTOR", "MOTION_SPEEDZ");
+
+ //正限位
+ XMaxstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "X_MAXSTROKESW");
+ YMaxstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Y_MAXSTROKESW");
+ ZMaxstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Z_MAXSTROKESW");
+
+ //负限位
+ XMinstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "X_MINSTROKESW");
+ YMinstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Y_MINSTROKESW");
+ ZMinstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Z_MINSTROKESW");
+
+ port = FileIni.ReadInt(StrConfigFile, "MOTOR", "Port");
+ DebugDfn.AddLogText($"当前监听端口配置为: {port}");
+ DebugDfn.AddLogText($"当前xy运动速度配置为: {MotionSpeedxy}");
+ DebugDfn.AddLogText($"当前z运动速度配置为: {MotionSpeedz}");
+ }
+
+ private void Plot2D(List pointCloud)
+ {
+ // 清空画布
+ formsPlot1.Plot.Clear();
+
+ //pointCloud 是否为空
+ if (pointCloud.Count <= 0)
+ {
+ return;
+ }
+
+ List dataX = new List();
+ List dataY = new List();
+
+ foreach (Point3D point3D in pointCloud)
+ {
+ dataX.Add(point3D.X);
+ dataY.Add(point3D.Y);
+ }
+
+ formsPlot1.Plot.AddScatter(dataX.ToArray(), dataY.ToArray());
+ formsPlot1.Refresh();
+ }
+
+ #region 运动平台变量区
+
+ public Api _acs;
+
+ private const int MaxUiLimitCnt = 24;
+ private int _mNTotalAxis;
+
+ //private int _mNTotalBuffer = 0;
+ //private Axis[] _mArrAxisList = null;
+ public bool _mAcsConnected; //ACS通讯状态
+
+ // For update values
+ private MotorStates _mNMotorState; //运动状态
+
+ private ProgramStates _mNProgramState; //程序状态
+ private object _mObjReadVar;
+ private Array _mArrReadVector;
+
+ private double _mLfRPos,
+ _mLfFPos,
+ _mLfPe,
+ _mLfFvel; //参考位置,反馈位置 位置误差 反馈速度 double类型
+
+ private int _mNValues,
+ _mNOutputState;
+
+ private Label[] _mLblLeftLimit; //左限位
+ private Label[] _mLblRightLimit; //右限位
+ private Label[] _mlblMoving; //运动中
+ private Label[] _mlblAcc; //加速中
+ private Label[] _mlblInPos; //轴就位
+ private Label[] _mlblEnable; //使能
+ private bool[] axisEnabled = new bool[MaxUiLimitCnt]; //轴使能状态
+ public bool totalAxisEnabled = false;
+
+ private HomeStates _homeStates; //回家状态
+ private MotionStates _currentMotionState; //当前运动状态
+ private MotionStates _currentMotorStateLast;
+ private readonly int _motionTimeout = 50000; //定义运动超时时间
+
+ //定义启用的轴,后面运动时会使用
+ public static Axis[] UseAxis =
+ {
+ Axis.ACSC_AXIS_1,
+ Axis.ACSC_AXIS_0,
+ Axis.ACSC_AXIS_4,
+ Axis.ACSC_NONE,
+ };
+
+ //定义 XYZ三个轴的左右行程范围
+ public string StrConfigFile = Application.StartupPath + "\\File\\config.ini";
+
+ public static double MotionSpeedxy = 60;
+ public static double MotionSpeedz = 30;
+ public static double XMaxstrokesw = 730; //正限位
+ public static double YMaxstrokesw = 1000;
+ public static double ZMaxstrokesw = 5;
+
+ public static double XMinstrokesw = -30; //负限位
+ public static double YMinstrokesw = -10;
+ public static double ZMinstrokesw = -280;
+ public static int port = 1234; //默认监听端口
+
+ //定义一个3D点,存储当前平台实时位置
+ public Point3D _mPoint3D;
+
+ #endregion 运动平台变量区
+
+ #region hexcal软件交互
+
+ private void StartServer()
+ {
+ // 对_mTcpIpServer增加判断是否已经启动且存在设备连接
+ if (_mTcpIpServer != null && _mTcpIpServer.ConnectStatus)
+ {
+ //弹窗提醒已经启动
+ MyBase.TraceWriteLine("TCP服务端已经启动,请勿重复启动");
+ MessageBox.Show(
+ "TCP服务端已经启动,请勿重复启动",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ return;
+ }
+
+ //启动服务器,并获取数据,解析
+ _mTcpIpServer = new TcpIpServer(IPAddress.Any.ToString(), Convert.ToString(port));
+ _mTcpIpServer.UseMode = 1; //设置通讯返回数据流格式
+ try
+ {
+ //启动监听
+ if (_mTcpIpServer.StartListen())
+ {
+ //绑定两个事件 OnRaisedStatus 和OnRaisedMessage
+ _mTcpIpServer.OnRaisedMessage += ReceiveMessage; //接收消息回调
+ _mTcpIpServer.OnRaisedStatus += ReceiveStatus; //连接状态
+ _mTcpIpServer.DataReceived += ReceiveByte;
+ }
+ else
+ {
+ MessageBox.Show(
+ "TCP服务端启动失败,请检查网络连接,重新打开软件",
+ "异常",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error
+ );
+ }
+ }
+ catch (Exception ex)
+ {
+ DebugDfn.AddLogText("启动TCP服务端异常" + ex);
+ }
+ }
+
+ private void ReceiveByte(object sender, byte[] e)
+ {
+ DebugDfn.AddLogText("接收到" + BitConverter.ToString(e));
+ }
+
+ private void ReceiveMessage(string clientIp, string msg) //接收的内容
+ {
+ //打印ClientIP 和 Msg
+ DebugDfn.AddLogText("接收到" + clientIp + ": " + msg);
+
+ //根据源地址的不同,执行不同处理
+ string sourceIp = clientIp.Split(':')[0];
+ switch (sourceIp)
+ {
+ case "100.0.0.1":
+ ParseHexcalMsg(msg);
+ break;
+
+ case "100.0.0.2":
+ ParseHexcalMsg(msg);
+ break;
+
+ default:
+ DebugDfn.AddLogText("未知来源,没有应答");
+ break;
+ }
+ }
+
+ public static string ConstructString(string variableName, double[] values)
+ {
+ string result = variableName + " ";
+ for (int i = 0; i < values.Length; i++)
+ {
+ result += values[i].ToString("F6");
+ if (i < values.Length - 1)
+ {
+ result += ", ";
+ }
+ }
+
+ return result;
+ }
+
+ public static string ConstructPosString(Point3D point)
+ {
+ double[] values = { point.X, point.Y, point.Z, 0.0, 0.0, 0.0, 0.0 };
+ return ConstructString("POS", values);
+ }
+
+ public static Point3D ParsePoint3DFromCommand(string input)
+ {
+ string[] parts = input.Split(' ')[1].Split(',');
+ if (parts.Length >= 3)
+ {
+ double x = double.Parse(parts[0]);
+ double y = double.Parse(parts[1]);
+ double z = double.Parse(parts[2]);
+ return new Point3D(x, y, z);
+ }
+
+ throw new ArgumentException("输入字符串格式不正确。");
+ }
+
+ private void CheckPlatformStatus()
+ {
+ //检查平台状态,如果运动中,返回BUSY,否则返回READY
+ if (
+ _currentMotionState == MotionStates.None
+ || _currentMotionState == MotionStates.InPos
+ ) //默认或到位
+ {
+ SendMsgToHexcal("READY");
+ }
+ else
+ {
+ SendMsgToHexcal("BUSY");
+ }
+ }
+
+ private void ParseHexcalMsg(string msg) //编写一个Hexcal协议解析函数
+ {
+ //DebugDfn.AddLogText("正在解析 " + msg);
+
+ //去除Msg中\r\n
+ msg = msg.Replace("\r\n", "");
+
+ //判断是否含有故障ERROR字样
+ if (msg.Contains("ERROR"))
+ {
+ //弹窗提醒
+ MessageBox.Show("CMM错误", msg, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ if (msg.Contains("\x02") || msg.Contains("\u0002"))
+ {
+ //DebugDfn.AddLogText("接收到STX,开始解析");
+ CheckPlatformStatus();
+ }
+ else if (msg.Contains("\x03") || msg.Contains("\u0003"))
+ {
+ CheckPlatformStatus();
+ }
+ //else if (msg.Contains("^B")) //查询状态, READY或BUSY
+ //{
+ // checkPlatformStatus();
+ //}
+ else if (msg.Contains("CMMTYP")) //测量机类型
+ {
+ SendMsgToHexcal("CMMTYP MA 19617, FDC V15.00, 10 8 3 , 0");
+ }
+ else if (msg.Contains("VERSION")) //版本号
+ {
+ SendMsgToHexcal("00-000-000-00000 FDC V51.04.0000 DATE: 12/21/22 TIME: 12:50:55");
+ }
+ else if (msg.Contains("SHOW MAXSTROKESW")) //最大行程,根据实际情况填写
+ {
+ //MAXSTROKESW 233.200000,346.500000,15.100000,0.000000,0.000000,0.000000,0.000000
+
+ double[] values = { XMaxstrokesw, YMaxstrokesw, ZMaxstrokesw, 0.0, 0.0, 0.0, 0.0 };
+ string resultString = ConstructString("MAXSTROKESW", values);
+ SendMsgToHexcal(resultString);
+ }
+ else if (msg.Contains("SHOW MINSTROKESW")) //最小行程,根据实际情况填写
+ {
+ //MINSTROKESW -68.800000,-55.500000,-286.900000,0.000000,0.000000,0.000000,0.000000
+
+ double[] values = { XMinstrokesw, YMinstrokesw, ZMinstrokesw, 0.0, 0.0, 0.0, 0.0 };
+ string resultString = ConstructString("MINSTROKESW", values);
+ SendMsgToHexcal(resultString);
+ }
+ else if (msg.Contains("SHOW MAXVEL")) //最大速度
+ {
+ SendMsgToHexcal(
+ "MAXVEL 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
+ );
+ }
+ else if (msg.Contains("SHOW MAXACC")) //最大加速度
+ {
+ SendMsgToHexcal(
+ "MAXACC 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
+ );
+ }
+ else if (msg.Contains("SHOW SENSWKP"))
+ {
+ SendMsgToHexcal("X_ SENSWKP 4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
+ }
+ else if (msg.Contains("SHOW X_SENSAXIS"))
+ {
+ SendMsgToHexcal("X_SENSAXIS 6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
+ }
+ else if (msg.Contains("SHOW Y_SENSAXIS")) //查询Y轴
+ {
+ SendMsgToHexcal("Y_SENSAXIS 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
+ }
+ else if (msg.Contains("SHOW Z_SENSAXIS")) //查询Z轴
+ {
+ SendMsgToHexcal("Z_SENSAXIS 7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
+ }
+ else if (msg.Contains("SHOW TEMPCOMPTYPE")) //温度补偿,温度补偿 >1 表示支持温度补偿,此处不支持
+ {
+ SendMsgToHexcal("TEMPCOMPTYPE 0");
+ }
+ else if (msg.Contains("READTP"))
+ {
+ SendMsgToHexcal("READTP 0.000000");
+ }
+ else if (msg.Contains("SHOW ESTOP")) //查询急停状态,根据真是情况调整
+ {
+ SendMsgToHexcal("ESTOP FALSE");
+ }
+ else if (msg.Contains("CMHWST"))
+ {
+ SendMsgToHexcal("CMHWST 8257,0,1792,0");
+ }
+ else if (msg.Contains("SHOW MOVPAR")) //查询速度
+ {
+ SendMsgToHexcal(
+ "MOVPAR 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.00000 0,0.000000"
+ );
+ }
+ else if (msg.Contains("SHOW MAXVEL")) //查询最大速度
+ {
+ SendMsgToHexcal(
+ "MAXVEL 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
+ );
+ }
+ else if (msg.Contains("SHOW ACCEL")) //查询加速度
+ {
+ SendMsgToHexcal(
+ "ACCEL 1000.000000,1000.000000,1000.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
+ );
+ }
+ else if (msg.Contains("MOVPAR")) //设置速度 xyz 轴的速度
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("ACCEL")) //设置加速度
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("PRBPIN")) //设置侧头偏置
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("ENABLE TEMP")) //设置温度补偿
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("WKPPAR"))
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("SCLTMP"))
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("DISABLE GEO"))
+ {
+ SendMsgToHexcal("%");
+ }
+ else if (msg.Contains("AUTZER")) //回家指令
+ {
+ SendMsgToHexcal("%"); //收到并执行,同时状态改为忙碌
+
+ //执行回家
+ IsHomed();
+ }
+ else if (msg.Contains("MOVABS")) //移动指令,解析移动位置
+ {
+ //收到指令 ,形如 MOVABS 0.015000,127.172997,-114.897003,0.000000\r\n
+ SendMsgToHexcal("%");
+
+ Point3D point = ParsePoint3DFromCommand(msg);
+ SetPositionXyz(point); //开始移动
+
+ _pointCloud.Add(point); //添加到点集合
+ }
+ else if (msg.Contains("GETPOS")) //获取位置
+ {
+ //POS 167.553898,-55.400421,-208.548678,0.000000,0.000000,0.000000,0.000000
+ Point3D point3D = GetPositionXyz(); //获取当前位置
+ string resultString = ConstructPosString(point3D);
+ SendMsgToHexcal(resultString);
+ }
+ else
+ {
+ DebugDfn.AddLogText("未知命令,没有应答");
+ }
+ }
+
+ private void ReceiveStatus(TcpIpServer.EnumTcpIpServer iType, string msg)
+ {
+ //记录到日志
+ DebugDfn.AddLogText(iType + " : " + msg);
+
+ //根据连接状态,更新界面
+ switch (iType)
+ {
+ case TcpIpServer.EnumTcpIpServer.ClientConnect:
+ _mBHexcalConnected = true;
+ break;
+
+ default:
+ _mBHexcalConnected = false;
+ break;
+ }
+ }
+
+ private void SendMsgToHexcal(string msg)
+ {
+ if (_mTcpIpServer == null)
+ return;
+
+ //发送数据
+ DebugDfn.AddLogText("回复 " + msg);
+ _mTcpIpServer.SendMessageToAllClients(msg += "\r\n"); //回复内容末尾加上\r\n,协议要求
+ }
+
+ private void Btn_StartServer_Click(object sender, EventArgs e)
+ {
+ Btn_StartServer.Enabled = false;
+ Btn_StopServer.Enabled = true;
+ StartServer();
+ DebugDfn.AddLogText("TCP服务端启动成功 ");
+ }
+
+ private void Btn_StopServer_Click(object sender, EventArgs e)
+ {
+ //关闭服务端
+ if (_mTcpIpServer != null)
+ {
+ _mTcpIpServer.StopListen();
+ }
+
+ Btn_StopServer.Enabled = false;
+ Btn_StartServer.Enabled = true;
+ _mBHexcalConnected = false;
+ DebugDfn.AddLogText("TCP服务端已关闭");
+ }
+
+ #endregion hexcal软件交互
+
+ #region ACS平台相关
+
+ #region 异常抓取
+
+ //实现函数
+
+ private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
+ {
+ // 防止程序终止
+ MessageBox.Show(
+ "发生了未处理的异常:" + e.Exception.Message,
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ }
+
+ private static void CurrentDomain_UnhandledException(
+ object sender,
+ UnhandledExceptionEventArgs e
+ )
+ {
+ if (e.ExceptionObject is Exception ex)
+ {
+ HandleException(ex);
+ }
+ }
+
+ private static void HandleException(Exception ex)
+ {
+ MessageBox.Show(
+ $"发生了未处理的异常:{ex.Message}",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ }
+
+ //订阅报错
+ private void EnableFaultEvent()
+ {
+ _acs.EnableEvent(Interrupts.ACSC_INTR_COMM_CHANNEL_CLOSED);
+ _acs.EnableEvent(Interrupts.ACSC_INTR_EMERGENCY);
+ _acs.EnableEvent(Interrupts.ACSC_INTR_SYSTEM_ERROR);
+ _acs.EnableEvent(Interrupts.ACSC_INTR_ETHERCAT_ERROR);
+ _acs.EnableEvent(Interrupts.ACSC_INTR_MOTOR_FAILURE);
+ _acs.EnableEvent(Interrupts.ACSC_INTR_MOTION_FAILURE);
+ _acs.EnableEvent(Interrupts.ACSC_INTR_PHYSICAL_MOTION_END);
+ _acs.COMMCHANNELCLOSED += _acs_COMMCHANNELCLOSED;
+ _acs.MOTORFAILURE += _acs_MOTORFAILURE;
+ _acs.MOTIONFAILURE += _acs_MOTIONFAILURE;
+ _acs.SYSTEMERROR += _acs_SYSTEMERROR;
+ _acs.ETHERCATERROR += _acs_ETHERCATERROR;
+ _acs.EMERGENCY += _acs_EMERGENCY;
+ _acs.PHYSICALMOTIONEND += _acs_PHYSICAL_MOTION_END;
+ }
+
+ private void DisableFaultEvent()
+ {
+ _acs.COMMCHANNELCLOSED -= _acs_COMMCHANNELCLOSED;
+ _acs.MOTORFAILURE -= _acs_MOTORFAILURE;
+ _acs.MOTIONFAILURE -= _acs_MOTIONFAILURE;
+ _acs.SYSTEMERROR -= _acs_SYSTEMERROR;
+ _acs.ETHERCATERROR -= _acs_ETHERCATERROR;
+ _acs.EMERGENCY -= _acs_EMERGENCY;
+ _acs.PHYSICALMOTIONEND -= _acs_PHYSICAL_MOTION_END;
+
+ _acs.DisableEvent(Interrupts.ACSC_INTR_COMM_CHANNEL_CLOSED);
+ _acs.DisableEvent(Interrupts.ACSC_INTR_EMERGENCY);
+ _acs.DisableEvent(Interrupts.ACSC_INTR_SYSTEM_ERROR);
+ _acs.DisableEvent(Interrupts.ACSC_INTR_ETHERCAT_ERROR);
+ _acs.DisableEvent(Interrupts.ACSC_INTR_MOTOR_FAILURE);
+ _acs.DisableEvent(Interrupts.ACSC_INTR_MOTION_FAILURE);
+ _acs.DisableEvent(Interrupts.ACSC_INTR_PHYSICAL_MOTION_END);
+ }
+
+ //关联函数
+ private void _acs_EMERGENCY(ulong param)
+ {
+ DebugDfn.AddLogText($"[EStopError] Error Message:{_acs.GetErrorString((int)param)}");
+ }
+
+ private void _acs_ETHERCATERROR(ulong param)
+ {
+ DebugDfn.AddLogText(
+ $"[EtherCatError] Error Message:{_acs.GetErrorString((int)param)}"
+ );
+ }
+
+ private void _acs_SYSTEMERROR(ulong param)
+ {
+ DebugDfn.AddLogText($"[SystemError] Error Message:{_acs.GetErrorString((int)param)}");
+ }
+
+ private void _acs_MOTIONFAILURE(AxisMasks axis)
+ {
+ for (int i = 0; i < _acs.GetAxesCount(); i++)
+ {
+ if (((int)axis & (int)Math.Pow(2, i)) == Math.Pow(2, i))
+ {
+ if (_acs.GetMotionError((Axis)i) != 0)
+ {
+ //Motor无法自动捕获,需要在motion报错中获取
+ int errorcode = _acs.GetMotionError((Axis)i);
+
+ DebugDfn.AddLogText(
+ $"[MotionError] Axis:{i} Error Code:{errorcode} Error Message: {_acs.GetErrorString(errorcode)}"
+ );
+
+ int errorcodes = _acs.GetMotorError((Axis)i);
+
+ DebugDfn.AddLogText(
+ $"[MotorError] Axis:{i} Error Code:{errorcodes} Error Message:{_acs.GetErrorString(errorcodes)}"
+ );
+ }
+ }
+ }
+ }
+
+ private void _acs_MOTORFAILURE(AxisMasks axis)
+ {
+ for (int i = 0; i < _acs.GetAxesCount(); i++)
+ {
+ if (((int)axis & (int)Math.Pow(2, i)) == Math.Pow(2, i))
+ {
+ int errorcode = _acs.GetMotorError((Axis)i);
+
+ DebugDfn.AddLogText(
+ $"[MotorError] Axis:{i} Error Code:{errorcode} Error Message:{_acs.GetErrorString(errorcode)}"
+ );
+ }
+ }
+ }
+
+ private void _acs_COMMCHANNELCLOSED(ulong param)
+ {
+ DebugDfn.AddLogText($"[CommError] Error Message:{_acs.GetErrorString((int)param)}");
+ }
+
+ private void _acs_PHYSICAL_MOTION_END(AxisMasks axis)
+ {
+ int bit = 0x01;
+ int axisNo = 0;
+
+ for (int i = 0; i < 64; i++)
+ {
+ if ((int)axis == bit)
+ {
+ axisNo = i;
+ break;
+ }
+
+ bit = bit << 1;
+ }
+
+ //DebugDfn.AddLogText(string.Format(" - Axis {0}, Stoppped", axisNo));
+ }
+
+ #endregion 异常抓取
+
+ private void BtnEnable_Click(object sender, EventArgs e) //使能所有轴
+ {
+ if (_mAcsConnected)
+ {
+ //!!!! Important !! Must insert '-1' at the last
+ _acs.EnableM(UseAxis);
+ }
+ else
+ {
+ //弹窗提醒尚未连接
+ MessageBox.Show("未连接到运动平台,请先点击连接");
+ }
+ }
+
+ private void BtnDisable_Click(object sender, EventArgs e) //轴取消
+ {
+ // Disable all of axes
+ _acs.DisableAll();
+ }
+
+ private bool IsMotionInPose()
+ {
+ bool x_inpose = false,
+ y_inpose = false,
+ z_inpose = false;
+
+ _mNMotorState = _acs.GetMotorState(Axis.ACSC_AXIS_1);
+ if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
+ {
+ x_inpose = true;
+ }
+
+ _mNMotorState = _acs.GetMotorState(Axis.ACSC_AXIS_0);
+ if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
+ {
+ y_inpose = true;
+ }
+
+ _mNMotorState = _acs.GetMotorState(Axis.ACSC_AXIS_4);
+ if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
+ {
+ z_inpose = true;
+ }
+
+ if (x_inpose && y_inpose && z_inpose)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void TmrMonitor_Tick(object sender, EventArgs e) //用于刷新状态
+ {
+ if (_mAcsConnected)
+ {
+ try
+ {
+ _mPoint3D = GetPositionXyz(); //取平台当前位置
+
+ #region 更新限位及运动状态
+
+ //左右限位刷新
+ _mObjReadVar = _acs.ReadVariableAsVector(
+ "FAULT",
+ ProgramBuffer.ACSC_NONE,
+ 0,
+ _mNTotalAxis - 1
+ );
+ if (_mObjReadVar != null)
+ {
+ _mArrReadVector = _mObjReadVar as Array;
+ if (_mArrReadVector != null)
+ {
+ UpdateLimitState(0, (int)_mArrReadVector.GetValue(1)); //获取X轴
+ UpdateLimitState(1, (int)_mArrReadVector.GetValue(0));
+ UpdateLimitState(2, (int)_mArrReadVector.GetValue(4));
+ }
+ }
+
+ UpdateSingleAxisStatus(); //刷新运动状态
+
+ #endregion 更新限位及运动状态
+
+ #region 到位判断
+
+ if (IsMotionInPose())
+ {
+ _currentMotionState = MotionStates.InPos;
+ //DebugDfn.AddLogText("运动到位");
+ }
+ else
+ {
+ _currentMotionState = MotionStates.Moving;
+ //DebugDfn.AddLogText("运动中");
+ isInPose = false;
+ }
+
+ //增加判断 运动中到 运动到位,主动发送READY
+ if (
+ _currentMotionState == MotionStates.InPos
+ && _currentMotionState != _currentMotorStateLast
+ )
+ {
+ DebugDfn.AddLogText("运动到位");
+ isInPose = true;
+
+ if (isCounting)
+ {
+ m_nInPosCount++;
+ DebugDfn.AddLogText("到位次数" + m_nInPosCount);
+ }
+ }
+
+ _currentMotorStateLast = _currentMotionState;
+
+ #endregion 到位判断
+ }
+ catch (Exception ex)
+ {
+ DebugDfn.AddLogText("ACS平台刷新异常" + ex);
+ MessageBox.Show(
+ ex.Message,
+ "ERROR",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error
+ );
+ Btn_ACSStop_Click(null, null);
+ }
+ }
+ }
+
+ private void Btn_ACSStart_Click(object sender, EventArgs e) //连接
+ {
+ btn_ACSStart.Enabled = false;
+ btn_ACSStop.Enabled = true;
+
+ try
+ {
+ if (rdoTCP.Checked)
+ {
+ // TCP/IP (Ethernet) TCP/IP (Ethernet)
+ _acs.OpenCommEthernetTCP(
+ txtIP.Text, // IP Address (Default : 10.0.0.100)
+ Convert.ToInt32(txtPort.Text.Trim())
+ ); // TCP/IP Port nubmer (default : 701)
+ }
+ else if (rdoSimu.Checked)
+ {
+ // Simmulation mode
+ _acs.OpenCommSimulator();
+ }
+
+ _mAcsConnected = true;
+
+ //运动相关初始化操作
+ InitMotion();
+
+ // 启动定时器
+ tmrMonitor.Interval = 50;
+ tmrMonitor.Start();
+ }
+ catch (COMException comex)
+ {
+ MessageBox.Show(
+ "Connection fail",
+ "Error",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error
+ );
+ Debug.WriteLine("Connection fail" + comex.Message);
+
+ _mAcsConnected = false;
+ }
+ catch (Exception ex)
+ {
+ DebugDfn.AddLogText("ACS平台连接异常" + ex);
+ MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void Btn_ACSStop_Click(object sender, EventArgs e) //断开连接
+ {
+ if (_mAcsConnected)
+ {
+ //DisableFaultEvent(); //取消注册事件
+ _acs.CloseComm();
+ }
+
+ tmrMonitor.Stop();
+ _mAcsConnected = false;
+ btn_ACSStart.Enabled = true;
+ btn_ACSStop.Enabled = false;
+ }
+
+ private void UpdateLimitState(int axisNo, int fault) //刷新限位
+ {
+ if (axisNo < MaxUiLimitCnt)
+ {
+ if ((fault & (int)SafetyControlMasks.ACSC_SAFETY_LL) != 0)
+ _mLblLeftLimit[axisNo].Image = Resources.Error;
+ else
+ _mLblLeftLimit[axisNo].Image = Resources.Off;
+ if ((fault & (int)SafetyControlMasks.ACSC_SAFETY_RL) != 0)
+ _mLblRightLimit[axisNo].Image = Resources.Error;
+ else
+ _mLblRightLimit[axisNo].Image = Resources.Off;
+ }
+ }
+
+ public int TranslateAxisNumber(Axis originalAxisNumber)
+ {
+ int newAxisNumber = -1;
+
+ switch (originalAxisNumber)
+ {
+ case Axis.ACSC_AXIS_1: //X轴
+ newAxisNumber = 0;
+ break;
+
+ case Axis.ACSC_AXIS_0:
+ newAxisNumber = 1; //Y轴
+ break;
+
+ case Axis.ACSC_AXIS_4:
+ newAxisNumber = 2;
+ break;
+ }
+
+ return newAxisNumber;
+ }
+
+ private void UpdateSingleAxisStatus()
+ {
+ Axis axis = 0;
+ int _axisNo = 0;
+ for (int i = 0; i < UseAxis.Length; i++)
+ {
+ axis = UseAxis[i];
+
+ _axisNo = TranslateAxisNumber(UseAxis[i]);
+
+ // Get Motor State ACSPL+ Variable : MST (integer)
+ _mNMotorState = _acs.GetMotorState(axis);
+
+ if (_axisNo == -1)
+ {
+ return;
+ }
+
+ // 运动中
+ if ((_mNMotorState & MotorStates.ACSC_MST_MOVE) != 0)
+ {
+ _mlblMoving[_axisNo].Image = Resources.On;
+ }
+ else
+ {
+ _mlblMoving[_axisNo].Image = Resources.Off;
+ }
+
+ // 就位
+ if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
+ {
+ _mlblInPos[_axisNo].Image = Resources.On;
+ }
+ else
+ {
+ _mlblInPos[_axisNo].Image = Resources.Off;
+ }
+
+ // 加速
+ if ((_mNMotorState & MotorStates.ACSC_MST_ACC) != 0)
+ {
+ _mlblAcc[_axisNo].Image = Resources.On;
+ }
+ else
+ {
+ _mlblAcc[_axisNo].Image = Resources.Off;
+ }
+
+ // 使能
+ if ((_mNMotorState & MotorStates.ACSC_MST_ENABLE) != 0)
+ {
+ _mlblEnable[_axisNo].Image = Resources.On;
+ axisEnabled[_axisNo] = true; //轴使能
+ }
+ else
+ {
+ _mlblEnable[_axisNo].Image = Resources.Off;
+ axisEnabled[_axisNo] = false;
+ }
+ }
+
+ totalAxisEnabled = CalculateTotalEnabled(axisEnabled, 0, 1, 8);
+ DebugDfn.AddLogText($"总的使能状态为:{(totalAxisEnabled ? "使能" : "未使能")}");
+ }
+
+ private void IsHomed() //读取回家状态,当未回家时执行回家指令
+ {
+ // 1、连接状态检查,如果未连接,提示
+ if (!_mAcsConnected)
+ {
+ DebugDfn.AddLogText("[IsHomed] ACS平台未连接,请先点击连接");
+ MessageBox.Show(
+ "ACS平台未连接,请先点击连接",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ return;
+ }
+
+ // 2、回家状态检查是否已经回家
+ if (_mAcsConnected && _homeStates == HomeStates.Homed)
+ {
+ //弹窗提示
+ MessageBox.Show(
+ "轴已经回家",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ return;
+ }
+
+ // 3、读取回家状态
+ if (_acs != null && _mAcsConnected)
+ {
+ var yawHome = _acs.ReadVariable("YAW_HOME_DONE");
+
+ if (Convert.ToBoolean(yawHome))
+ {
+ //弹窗提示
+ MessageBox.Show(
+ "轴已经回家",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ _homeStates = HomeStates.Homed;
+ return;
+ }
+ // 4、执行回家指令,弹窗等待用户确认
+
+ DialogResult result = MessageBox.Show(
+ "轴未回家,即将执行回家指令",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+
+ if (result == DialogResult.OK)
+ {
+ // 在这里执行接下来的操作,例如回家指令
+
+ _acs.RunBuffer(ProgramBuffer.ACSC_BUFFER_6, null); //执行回家指令,这里的buffer6是回家指令的buffer
+ _homeStates = HomeStates.Homing;
+ _currentMotionState = MotionStates.Moving;
+ DebugDfn.AddLogText("回家运动中");
+
+ //等待回家完成
+ for (int i = 0; i < UseAxis.Length; i++)
+ {
+ _acs.WaitMotionEnd(UseAxis[i], _motionTimeout); //等待回家完成
+ }
+
+ _homeStates = HomeStates.Homed;
+ _currentMotionState = MotionStates.InPos;
+ DebugDfn.AddLogText("回家完成");
+ }
+ else
+ {
+ //弹窗提醒
+ MessageBox.Show(
+ "点击了取消,未进行任何动作",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ }
+ }
+ }
+
+ private void InitMotion() //定义 运动状态初始化函数,包括内部变量,轴启用,回家判断等
+ {
+ string strTemp;
+ //运动相关变量初始化
+ _homeStates = HomeStates.None;
+ _currentMotionState = MotionStates.None;
+ _currentMotionState = MotionStates.None;
+
+ if (!_mAcsConnected)
+ {
+ MessageBox.Show("未连接运动平台,请先连接运动平台");
+ return;
+ }
+
+ //轴启用,加电
+ _acs.EnableM(UseAxis);
+ for (int i = 0; i < UseAxis.Length; i++)
+ {
+ _acs.WaitMotorEnabled(UseAxis[i], 1, _motionTimeout); //等待电机使能
+ }
+
+ DebugDfn.AddLogText("电机已启用");
+
+ //回家
+ IsHomed();
+
+ //设置定位速度
+ SetSpeedXyz(MotionSpeedxy, MotionSpeedz);
+
+ //获取轴数量
+ strTemp = _acs.Transaction("?SYSINFO(13)");
+ _mNTotalAxis = Convert.ToInt32(strTemp.Trim());
+ _mLblLeftLimit = new Label[MaxUiLimitCnt]; //左限位
+ _mLblLeftLimit[0] = lblLL0;
+ _mLblLeftLimit[1] = lblLL1;
+ _mLblLeftLimit[2] = lblLL2;
+
+ _mLblRightLimit = new Label[MaxUiLimitCnt]; //右限位
+ _mLblRightLimit[0] = lblRL0;
+ _mLblRightLimit[1] = lblRL1;
+ _mLblRightLimit[2] = lblRL2;
+
+ _mlblMoving = new Label[MaxUiLimitCnt]; //运动中
+ _mlblMoving[0] = lblMoving0;
+ _mlblMoving[1] = lblMoving1;
+ _mlblMoving[2] = lblMoving2;
+
+ _mlblAcc = new Label[MaxUiLimitCnt]; // 加速中
+ _mlblAcc[0] = lblAcc0;
+ _mlblAcc[1] = lblAcc1;
+ _mlblAcc[2] = lblAcc2;
+
+ _mlblInPos = new Label[MaxUiLimitCnt]; //就位
+ _mlblInPos[0] = lblInPos0;
+ _mlblInPos[1] = lblInPos1;
+ _mlblInPos[2] = lblInPos2;
+
+ _mlblEnable = new Label[MaxUiLimitCnt]; //轴使能
+ _mlblEnable[0] = lblEnable0;
+ _mlblEnable[1] = lblEnable1;
+ _mlblEnable[2] = lblEnable2;
+
+ //EnableFaultEvent(); //订阅错误事件
+ }
+
+ public static bool IsWithinLimit(Point3D point) //判断点是否在行程范围内
+ {
+ if (
+ point.X >= XMinstrokesw
+ && point.X <= XMaxstrokesw
+ && point.Y >= YMinstrokesw
+ && point.Y <= YMaxstrokesw
+ && point.Z >= ZMinstrokesw
+ && point.Z <= ZMaxstrokesw
+ )
+ {
+ return true;
+ }
+
+ DebugDfn.AddLogText(point.X.ToString() + " " + point.Y.ToString() + " " + point.Z.ToString());
+ //打印 XMinstrokesw 等值
+ DebugDfn.AddLogText("XMinstrokesw超限: " + XMinstrokesw);
+ DebugDfn.AddLogText("XMaxstrokesw超限: " + XMaxstrokesw);
+ DebugDfn.AddLogText("YMinstrokesw超限: " + YMinstrokesw);
+ DebugDfn.AddLogText("YMaxstrokesw超限: " + YMaxstrokesw);
+ DebugDfn.AddLogText("ZMinstrokesw超限: " + ZMinstrokesw);
+ DebugDfn.AddLogText("ZMaxstrokesw超限: " + ZMaxstrokesw);
+ return false;
+ }
+
+ public static bool IsWithinLimit(Point point) //判断点是否在行程范围内
+ {
+ if (
+ point.X >= XMinstrokesw
+ && point.X <= XMaxstrokesw
+ && point.Y >= YMinstrokesw
+ && point.Y <= YMaxstrokesw
+ && point.Z >= ZMinstrokesw
+ && point.Z <= ZMaxstrokesw
+ )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public void SetPositionXyz(Point3D point3D) //运动到指定位置
+ {
+ if (!_mAcsConnected)
+ {
+ DebugDfn.AddLogText("ACS平台未连接,请先点击连接");
+ MessageBox.Show(
+ "ACS平台未连接,请先点击连接",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ return;
+ }
+
+ if (_currentMotionState != MotionStates.Moving)
+ {
+ _currentMotionState = MotionStates.Moving; //设置当前运动状态
+ //判断 point3D是否合法
+ if (point3D != null)
+ {
+ if (IsWithinLimit(point3D)) //判断点是否在行程范围内
+ {
+ double[] pointsArray = { point3D.X, point3D.Y, point3D.Z };
+ //判断各轴使能状态,如果未使能,则使能
+
+ if (!totalAxisEnabled)
+ {
+ _acs.EnableM(UseAxis);
+ for (int i = 0; i < UseAxis.Length; i++)
+ {
+ _acs.WaitMotorEnabled(UseAxis[i], 1, _motionTimeout); //等待电机使能
+ }
+
+ //DebugDfn.AddLogText("电机已启用");
+ }
+
+ //执行运动指令
+ _acs.ToPointM(MotionFlags.ACSC_NONE, UseAxis, pointsArray); //多轴运动到指定位置
+ }
+ else
+ {
+ DebugDfn.AddLogText("目标位置超出行程范围,请重新设置");
+ //MessageBox.Show(
+ // "目标位置超出行程范围,请重新设置",
+ // "异常",
+ // MessageBoxButtons.OK,
+ // MessageBoxIcon.Error
+ //);
+ }
+ }
+ else
+ {
+ DebugDfn.AddLogText("目标位置为空,请重新设置");
+ MessageBox.Show(
+ "目标位置为空,请重新设置",
+ "异常",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error
+ );
+ }
+ }
+ }
+
+ private Point3D GetPositionXyz(int positionMode = 1) //获取当前位置
+ {
+ double xPosition = 0,
+ yPosition = 0,
+ zPosition = 0;
+ Point3D point3D = new Point3D(xPosition, yPosition, zPosition);
+ if (!_mAcsConnected)
+ {
+ DebugDfn.AddLogText("ACS平台未连接,请先点击连接");
+ MessageBox.Show(
+ "ACS平台未连接,请先点击连接",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ return point3D;
+ }
+
+ //获取当前位置, 两种 GetRPosition,GetFPosition
+ if (positionMode == 1)
+ {
+ //获取反馈位置 Feedback position (Encoder value) ACSPL+ Variable : FPO (real)
+ xPosition = _acs.GetFPosition(UseAxis[0]);
+ yPosition = _acs.GetFPosition(UseAxis[1]);
+ zPosition = _acs.GetFPosition(UseAxis[2]);
+ //DebugDfn.AddLogText("反馈位置: " + xPosition + " " + yPosition + " " + zPosition);
+ }
+ else
+ {
+ //获取参考位置 ACSPL+ Variable : RPOS (real)
+ xPosition = _acs.GetRPosition(UseAxis[0]);
+ yPosition = _acs.GetRPosition(UseAxis[1]);
+ zPosition = _acs.GetRPosition(UseAxis[2]);
+ DebugDfn.AddLogText("参考位置: " + xPosition + " " + yPosition + " " + zPosition);
+ }
+
+ //构造point3D格式
+ point3D = new Point3D(xPosition, yPosition, zPosition);
+
+ return point3D;
+ }
+
+ private void SetSpeedXyz(double speedxy, double speedz) //获取运动参数
+ {
+ //获取实际速度
+ double feedbackVelocity = (double)
+ _acs.ReadVariable("FVEL", ProgramBuffer.ACSC_NONE, 0, 0);
+ DebugDfn.AddLogText("实际速度: " + feedbackVelocity);
+
+ //设置Y轴 速度参数
+ _acs.SetVelocity(Axis.ACSC_AXIS_0, speedxy);
+ _acs.SetAcceleration(Axis.ACSC_AXIS_0, speedxy * 10);
+ _acs.SetDeceleration(Axis.ACSC_AXIS_0, speedxy * 10);
+ _acs.SetKillDeceleration(Axis.ACSC_AXIS_0, speedxy * 100);
+ _acs.SetJerk(Axis.ACSC_AXIS_0, speedxy * 100);
+
+ //设置X轴速度参数
+ _acs.SetVelocity(Axis.ACSC_AXIS_1, speedxy);
+ _acs.SetAcceleration(Axis.ACSC_AXIS_1, speedxy * 10);
+ _acs.SetDeceleration(Axis.ACSC_AXIS_1, speedxy * 10);
+ _acs.SetKillDeceleration(Axis.ACSC_AXIS_1, speedxy * 100);
+ _acs.SetJerk(Axis.ACSC_AXIS_1, speedxy * 100);
+
+ //设置Z轴速度参数
+
+ _acs.SetVelocity(Axis.ACSC_AXIS_4, speedz);
+ _acs.SetAcceleration(Axis.ACSC_AXIS_4, speedz * 10);
+ _acs.SetDeceleration(Axis.ACSC_AXIS_4, speedz * 10);
+ _acs.SetKillDeceleration(Axis.ACSC_AXIS_4, speedz * 100);
+ _acs.SetJerk(Axis.ACSC_AXIS_4, speedz * 100);
+
+ DebugDfn.AddLogText($"速度设置完成 ");
+ }
+
+ private void rtb_quick_loc_Click(object sender, EventArgs e) //快速定位
+ {
+ // 获取文本框的值
+ double x = double.Parse(rtb_SetX.Text);
+ double y = double.Parse(rtb_Sety.Text);
+ double z = double.Parse(rtb_SetZ.Text);
+
+ // 构造 Point3D 对象
+ Point3D point = new Point3D(x, y, z);
+
+ SetPositionXyz(point);
+ }
+
+ private static bool CalculateTotalEnabled(bool[] axisEnabled, params int[] axisIndices) //判断轴使能状态
+ {
+ bool totalEnabled = true;
+ foreach (int index in axisIndices)
+ {
+ bool isEnabled = axisEnabled[index];
+ if (!isEnabled)
+ {
+ totalEnabled = false;
+ break;
+ }
+ }
+ return totalEnabled;
+ }
+
+ private void rtb_stop_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ Axis[] m_arrAxisList = new Axis[]
+ {
+ Axis.ACSC_AXIS_0,
+ Axis.ACSC_AXIS_1,
+ Axis.ACSC_AXIS_4,
+ Axis.ACSC_NONE,
+ };
+
+ if (m_arrAxisList != null)
+ _acs.HaltM(m_arrAxisList);
+
+ DebugDfn.AddLogText("立即停止 已发送命令");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ Debug.WriteLine(ex.Message);
+ }
+ }
+
+ private void rtb_home_Click(object sender, EventArgs e)
+ {
+ //弹窗提醒用户,是否执行回家指令
+ DialogResult result = MessageBox.Show(
+ "是否执行回家指令",
+ "提示",
+ MessageBoxButtons.OKCancel,
+ MessageBoxIcon.Information
+ );
+
+ if (result == DialogResult.Cancel)
+ {
+ return;
+ }
+
+ DebugDfn.AddLogText("回家指令已发送");
+ if (!_mAcsConnected)
+ {
+ DebugDfn.AddLogText("ACS平台未连接,请先点击连接");
+ MessageBox.Show(
+ "ACS平台未连接,请先点击连接",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+ return;
+ }
+
+ if (_currentMotionState != MotionStates.Moving)
+ {
+ _currentMotionState = MotionStates.Moving; //设置当前运动状态
+
+ _acs.RunBuffer(ProgramBuffer.ACSC_BUFFER_6, null); //执行回家指令,这里的buffer6是回家指令的buffer
+ _homeStates = HomeStates.Homing;
+ _currentMotionState = MotionStates.Moving;
+ DebugDfn.AddLogText("回家运动中");
+
+ //等待回家完成
+ for (int i = 0; i < UseAxis.Length; i++)
+ {
+ _acs.WaitMotionEnd(UseAxis[i], _motionTimeout); //等待回家完成
+ }
+
+ _homeStates = HomeStates.Homed;
+ _currentMotionState = MotionStates.InPos;
+ DebugDfn.AddLogText("回家完成");
+ }
+ }
+
+ private void rtb_etalon_Click(object sender, EventArgs e) //etalon校准
+ {
+ DebugDfn.AddLogText("Etalon校准");
+ //判断通讯对象是否存在
+ if (_acs == null || !_acs.IsConnected)
+ {
+ DebugDfn.AddLogText("未建立与运动平台通讯,请在主界面先建立通讯");
+
+ // 在合适的位置调用 MessageBox.Show() 方法
+ MessageBox.Show(
+ "未建立与运动平台通讯,请在主界面先建立通讯",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+
+ return;
+ }
+ else
+ {
+ EtalonForm etalonForm = new EtalonForm(this);
+ etalonForm.Show();
+ }
+ }
+
+ private void rtb_monitor_Click(object sender, EventArgs e)
+ {
+ DebugDfn.AddLogText("监控界面");
+ //判断通讯对象是否存在
+ if (_acs == null || !_acs.IsConnected)
+ {
+ DebugDfn.AddLogText("未建立与运动平台通讯,请在主界面先建立通讯");
+
+ // 在合适的位置调用 MessageBox.Show() 方法
+ MessageBox.Show(
+ "未建立与运动平台通讯,请在主界面先建立通讯",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+
+ return;
+ }
+ else
+ {
+ MonitorForm monitorForm = new MonitorForm(this);
+ monitorForm.Show();
+ }
+ }
+
+ #endregion ACS平台相关
+
+ #region 菜单栏
+
+ private void btn_motion_Click(object sender, EventArgs e)
+ {
+ //判断通讯对象是否存在
+ if (_acs == null || !_acs.IsConnected)
+ {
+ DebugDfn.AddLogText("未建立与运动平台通讯,请在主界面先建立通讯");
+
+ // 在合适的位置调用 MessageBox.Show() 方法
+ MessageBox.Show(
+ "未建立与运动平台通讯,请在主界面先建立通讯",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information
+ );
+
+ return;
+ }
+ else
+ {
+ Motion motion = new Motion(this);
+ motion.Show();
+ }
+ }
+
+ private void Rtb_about_Click(object sender, EventArgs e) //关于界面
+ {
+ AboutBox mAboutBox = new AboutBox();
+ mAboutBox.StartPosition = FormStartPosition.CenterScreen;
+ mAboutBox.Show();
+ }
+
+ private void rtb_demo_Click(object sender, EventArgs e)
+ {
+ DemoShow demoShow = new DemoShow(_acs);
+ demoShow.Show();
+ demoShow.BringToFront();
+ }
+
+ private void Timer_RefreshUI_Tick(object sender, EventArgs e) //UI刷新
+ {
+ //状态灯刷新
+ lamp_acs.State = _mAcsConnected ? LampColor.Green : LampColor.Silver;
+ lamp_hexcal.State = _mBHexcalConnected ? LampColor.Green : LampColor.Silver;
+
+ //时间栏
+ //获取当前时间,构造形如 精确到秒,例如 2023-10-08 16:01:23
+ rle_timer.Text = "当前时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+
+ Plot2D(_pointCloud); //绘图
+
+ //更新位置
+ if (_mPoint3D != null)
+ {
+ rtb_xPos.Text = _mPoint3D.X.ToString("F3");
+ rtb_yPos.Text = _mPoint3D.Y.ToString("F3");
+ rtb_zPos.Text = _mPoint3D.Z.ToString("F3");
+ }
+ }
+
+ #endregion 菜单栏
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/MainFrom.resx b/HexcalMC/MainForm.resx
similarity index 99%
rename from HexcalMC/MainFrom.resx
rename to HexcalMC/MainForm.resx
index 9c064aa..55aa34e 100644
--- a/HexcalMC/MainFrom.resx
+++ b/HexcalMC/MainForm.resx
@@ -632,13 +632,13 @@
- 328, 17
+ 164, 17
17, 17
- 25
+ 118
diff --git a/HexcalMC/MainFrom.cs b/HexcalMC/MainFrom.cs
deleted file mode 100644
index 28bc916..0000000
--- a/HexcalMC/MainFrom.cs
+++ /dev/null
@@ -1,1636 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Net;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Windows.Forms;
-using ACS.SPiiPlusNET;
-using HexcalMC.Base;
-using HexcalMC.Form;
-using HexcalMC.Hexcal;
-using HexcalMC.Properties;
-using Telerik.WinControls.UI;
-using static HexcalMC.EtalonForm;
-
-namespace HexcalMC
-{
- //定一个 回家状态枚举,包括 从未回家,正在回家,已经回家
- public enum HomeStates
- {
- None, //默认状态
- NotHome, //未回家
- Homing, //回家中
- Homed //回家完成
- ,
- }
-
- //定义 运动状态枚举,包括 正在运动,运动到位,Jog运动
- public enum MotionStates
- {
- None, //默认状态
- Moving, //运动中
- InPos, //运动到位
- Jogging //jog中
- ,
- }
-
- public partial class MainFrom : RadRibbonForm
- {
- private readonly List _pointCloud = new List(); //运动中点集合
-
- private bool _mBHexcalConnected;
-
- private TcpIpServer _mTcpIpServer; //创建tcpserver,用于接收hexcal传来的指令,并解析传递平台
-
- private int m_nTotalAxis; //定义总轴数
- private Axis[] m_arrAxisList = null;
-
- //定一个运动到位次数的变量和三个方法,开始统计运动到位次数,停止统计运动到位次数,获取运动到位次数
-
- #region 运动到位次数
-
-
- // 记录运动到位次数的变量
- private int m_nInPosCount = 0;
- private bool isCounting = false;
- private bool isInPose = false; //运动到位状态
-
- // 开始统计运动到位次数
- public void StartCounting()
- {
- isCounting = true;
- m_nInPosCount = 0; // 重置计数器
- }
-
- // 停止统计运动到位次数
- public void StopCounting()
- {
- isCounting = false;
- m_nInPosCount = 0; // 重置计数器
- }
-
- // 获取运动到位次数
- public int GetInPosCount()
- {
- return m_nInPosCount;
- }
-
- public bool GetIsMoving()
- {
- return isInPose;
- }
- #endregion
-
- public MainFrom()
- {
- InitializeComponent();
-
- // 处理未被捕获的线程异常
- Application.ThreadException += Application_ThreadException;
-
- // 处理未被捕获的非UI线程异常
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
- }
-
- private void MainFrom_Load(object sender, EventArgs e)
- {
- FormBorderStyle = FormBorderStyle.FixedSingle; // 设置窗体边框样式为固定大小
- MaximizeBox = false; // 禁用窗体的最大化按钮
- DebugDfn.textBox_Msg = TextBoxMsg;
-
- //加载配置文件
- LoadConfig();
- _acs = new Api(); //初始化 ACS运动控制类
-
- //启动界面刷新
- timer_RefreshUI.Start();
-
- Point3D point3D = new Point3D(800, 980, -290);
- IsWithinLimit(point3D);
-
- }
-
- private void MainFrom_Shown(object sender, EventArgs e) //窗体显示准备好接受用户输入时发生
- {
- ////启动服务端,用于接收hexcal传来的指令
- //StartServer();
-
- //if (_enableAcs)
- //{
- // Btn_ACSStart_Click(null, null); //模拟连接运动平台
- //}
- }
-
- private void MainFrom_FormClosed(object sender, FormClosedEventArgs e)
- {
- MyBase.TraceWriteLine("关闭程序");
- DebugDfn._strEndTime = DateTime.Now.ToString("yyyy.MM.dd HH-mm-ss");
- timer_RefreshUI.Stop();
-
- Btn_StopServer_Click(null, null);
- Btn_ACSStop_Click(null, null); //关闭ACS
-
- string copyFileName =
- DebugDfn.StrDebugSavePath
- + "\\Debug("
- + DebugDfn._strStartTime
- + " To "
- + DebugDfn._strEndTime
- + ")"
- + ".txt";
- if (!File.Exists(DebugDfn.StrDebugSavePath))
- {
- //创建文件夹 DebugDfn.StrDebugSavePath
- Directory.CreateDirectory(DebugDfn.StrDebugSavePath);
- }
-
- File.Copy(DebugDfn.StrDebugFile, copyFileName);
-
- if (Errors.ErrorWrite != null)
- Errors.ErrorWrite.Close();
- if (Errors.OtherWrite != null)
- Errors.OtherWrite.Close();
- if (Errors.StatusWrite != null)
- Errors.StatusWrite.Close();
- }
-
- private void LoadConfig() //加载配置文件
- {
- //判断配置文件是否存在
- if (!File.Exists(StrConfigFile))
- {
- MessageBox.Show(
- "配置文件不存在,请检查配置文件",
- "异常",
- MessageBoxButtons.OK,
- MessageBoxIcon.Error
- );
- return;
- }
-
- MotionSpeedxy = FileIni.ReadDouble(StrConfigFile, "MOTOR", "MOTION_SPEEDXY"); //运动定位速度
- MotionSpeedz = FileIni.ReadDouble(StrConfigFile, "MOTOR", "MOTION_SPEEDZ");
-
- //正限位
- XMaxstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "X_MAXSTROKESW");
- YMaxstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Y_MAXSTROKESW");
- ZMaxstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Z_MAXSTROKESW");
-
- //负限位
- XMinstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "X_MINSTROKESW");
- YMinstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Y_MINSTROKESW");
- ZMinstrokesw = FileIni.ReadDouble(StrConfigFile, "MOTOR", "Z_MINSTROKESW");
-
- port = FileIni.ReadInt(StrConfigFile, "MOTOR", "Port");
- DebugDfn.AddLogText($"当前监听端口配置为: {port}");
- DebugDfn.AddLogText($"当前xy运动速度配置为: {MotionSpeedxy}");
- DebugDfn.AddLogText($"当前z运动速度配置为: {MotionSpeedz}");
- }
-
- private void Plot2D(List pointCloud)
- {
- // 清空画布
- formsPlot1.Plot.Clear();
-
- //pointCloud 是否为空
- if (pointCloud.Count <= 0)
- {
- return;
- }
-
- List dataX = new List();
- List dataY = new List();
-
- foreach (Point3D point3D in pointCloud)
- {
- dataX.Add(point3D.X);
- dataY.Add(point3D.Y);
- }
-
- formsPlot1.Plot.AddScatter(dataX.ToArray(), dataY.ToArray());
- formsPlot1.Refresh();
- }
-
- #region 运动平台变量区
-
- public Api _acs;
-
- private const int MaxUiLimitCnt = 24;
- private int _mNTotalAxis;
-
- //private int _mNTotalBuffer = 0;
- //private Axis[] _mArrAxisList = null;
- public bool _mAcsConnected; //ACS通讯状态
-
- // For update values
- private MotorStates _mNMotorState; //运动状态
-
- private ProgramStates _mNProgramState; //程序状态
- private object _mObjReadVar;
- private Array _mArrReadVector;
- private double _mLfRPos,
- _mLfFPos,
- _mLfPe,
- _mLfFvel; //参考位置,反馈位置 位置误差 反馈速度 double类型
- private int _mNValues,
- _mNOutputState;
-
- private Label[] _mLblLeftLimit; //左限位
- private Label[] _mLblRightLimit; //右限位
- private Label[] _mlblMoving; //运动中
- private Label[] _mlblAcc; //加速中
- private Label[] _mlblInPos; //轴就位
- private Label[] _mlblEnable; //使能
- private bool[] axisEnabled = new bool[MaxUiLimitCnt]; //轴使能状态
- public bool totalAxisEnabled = false;
-
- private HomeStates _homeStates; //回家状态
- private MotionStates _currentMotionState; //当前运动状态
- private MotionStates _currentMotorStateLast;
- private readonly int _motionTimeout = 50000; //定义运动超时时间
-
- //定义启用的轴,后面运动时会使用
- public static Axis[] UseAxis =
- {
- Axis.ACSC_AXIS_1,
- Axis.ACSC_AXIS_0,
- Axis.ACSC_AXIS_8,
- Axis.ACSC_NONE,
- };
-
- //定义 XYZ三个轴的左右行程范围
- public string StrConfigFile = Application.StartupPath + "\\File\\config.ini";
-
- public static double MotionSpeedxy = 60;
- public static double MotionSpeedz = 30;
- public static double XMaxstrokesw = 730; //正限位
- public static double YMaxstrokesw = 1000;
- public static double ZMaxstrokesw = 5;
-
- public static double XMinstrokesw = -30; //负限位
- public static double YMinstrokesw = -10;
- public static double ZMinstrokesw = -280;
- public static int port = 1234; //默认监听端口
-
- //定义一个3D点,存储当前平台实时位置
- public Point3D _mPoint3D;
-
- #endregion 运动平台变量区
-
- #region hexcal软件交互
-
- private void StartServer()
- {
- // 对_mTcpIpServer增加判断是否已经启动且存在设备连接
- if (_mTcpIpServer != null && _mTcpIpServer.ConnectStatus)
- {
- //弹窗提醒已经启动
- MyBase.TraceWriteLine("TCP服务端已经启动,请勿重复启动");
- MessageBox.Show(
- "TCP服务端已经启动,请勿重复启动",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- return;
- }
-
- //启动服务器,并获取数据,解析
- _mTcpIpServer = new TcpIpServer(IPAddress.Any.ToString(), Convert.ToString(port));
- _mTcpIpServer.UseMode = 1; //设置通讯返回数据流格式
- try
- {
- //启动监听
- if (_mTcpIpServer.StartListen())
- {
- //绑定两个事件 OnRaisedStatus 和OnRaisedMessage
- _mTcpIpServer.OnRaisedMessage += ReceiveMessage; //接收消息回调
- _mTcpIpServer.OnRaisedStatus += ReceiveStatus; //连接状态
- _mTcpIpServer.DataReceived += ReceiveByte;
- }
- else
- {
- MessageBox.Show(
- "TCP服务端启动失败,请检查网络连接,重新打开软件",
- "异常",
- MessageBoxButtons.OK,
- MessageBoxIcon.Error
- );
- }
- }
- catch (Exception ex)
- {
- DebugDfn.AddLogText("启动TCP服务端异常" + ex);
- }
- }
-
- private void ReceiveByte(object sender, byte[] e)
- {
- DebugDfn.AddLogText("接收到" + BitConverter.ToString(e));
- }
-
- private void ReceiveMessage(string clientIp, string msg) //接收的内容
- {
- //打印ClientIP 和 Msg
- DebugDfn.AddLogText("接收到" + clientIp + ": " + msg);
-
- //根据源地址的不同,执行不同处理
- string sourceIp = clientIp.Split(':')[0];
- switch (sourceIp)
- {
- case "100.0.0.1":
- ParseHexcalMsg(msg);
- break;
-
- case "100.0.0.2":
- ParseHexcalMsg(msg);
- break;
-
- default:
- DebugDfn.AddLogText("未知来源,没有应答");
- break;
- }
- }
-
- public static string ConstructString(string variableName, double[] values)
- {
- string result = variableName + " ";
- for (int i = 0; i < values.Length; i++)
- {
- result += values[i].ToString("F6");
- if (i < values.Length - 1)
- {
- result += ", ";
- }
- }
-
- return result;
- }
-
- public static string ConstructPosString(Point3D point)
- {
- double[] values = { point.X, point.Y, point.Z, 0.0, 0.0, 0.0, 0.0 };
- return ConstructString("POS", values);
- }
-
- public static Point3D ParsePoint3DFromCommand(string input)
- {
- string[] parts = input.Split(' ')[1].Split(',');
- if (parts.Length >= 3)
- {
- double x = double.Parse(parts[0]);
- double y = double.Parse(parts[1]);
- double z = double.Parse(parts[2]);
- return new Point3D(x, y, z);
- }
-
- throw new ArgumentException("输入字符串格式不正确。");
- }
-
- private void CheckPlatformStatus()
- {
- //检查平台状态,如果运动中,返回BUSY,否则返回READY
- if (
- _currentMotionState == MotionStates.None
- || _currentMotionState == MotionStates.InPos
- ) //默认或到位
- {
- SendMsgToHexcal("READY");
- }
- else
- {
- SendMsgToHexcal("BUSY");
- }
- }
-
- private void ParseHexcalMsg(string msg) //编写一个Hexcal协议解析函数
- {
- //DebugDfn.AddLogText("正在解析 " + msg);
-
- //去除Msg中\r\n
- msg = msg.Replace("\r\n", "");
-
- //判断是否含有故障ERROR字样
- if (msg.Contains("ERROR"))
- {
- //弹窗提醒
- MessageBox.Show("CMM错误", msg, MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
-
- if (msg.Contains("\x02") || msg.Contains("\u0002"))
- {
- //DebugDfn.AddLogText("接收到STX,开始解析");
- CheckPlatformStatus();
- }
- else if (msg.Contains("\x03") || msg.Contains("\u0003"))
- {
- CheckPlatformStatus();
- }
- //else if (msg.Contains("^B")) //查询状态, READY或BUSY
- //{
- // checkPlatformStatus();
- //}
- else if (msg.Contains("CMMTYP")) //测量机类型
- {
- SendMsgToHexcal("CMMTYP MA 19617, FDC V15.00, 10 8 3 , 0");
- }
- else if (msg.Contains("VERSION")) //版本号
- {
- SendMsgToHexcal("00-000-000-00000 FDC V51.04.0000 DATE: 12/21/22 TIME: 12:50:55");
- }
- else if (msg.Contains("SHOW MAXSTROKESW")) //最大行程,根据实际情况填写
- {
- //MAXSTROKESW 233.200000,346.500000,15.100000,0.000000,0.000000,0.000000,0.000000
-
- double[] values = { XMaxstrokesw, YMaxstrokesw, ZMaxstrokesw, 0.0, 0.0, 0.0, 0.0 };
- string resultString = ConstructString("MAXSTROKESW", values);
- SendMsgToHexcal(resultString);
- }
- else if (msg.Contains("SHOW MINSTROKESW")) //最小行程,根据实际情况填写
- {
- //MINSTROKESW -68.800000,-55.500000,-286.900000,0.000000,0.000000,0.000000,0.000000
-
- double[] values = { XMinstrokesw, YMinstrokesw, ZMinstrokesw, 0.0, 0.0, 0.0, 0.0 };
- string resultString = ConstructString("MINSTROKESW", values);
- SendMsgToHexcal(resultString);
- }
- else if (msg.Contains("SHOW MAXVEL")) //最大速度
- {
- SendMsgToHexcal(
- "MAXVEL 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
- );
- }
- else if (msg.Contains("SHOW MAXACC")) //最大加速度
- {
- SendMsgToHexcal(
- "MAXACC 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
- );
- }
- else if (msg.Contains("SHOW SENSWKP"))
- {
- SendMsgToHexcal("X_ SENSWKP 4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
- }
- else if (msg.Contains("SHOW X_SENSAXIS"))
- {
- SendMsgToHexcal("X_SENSAXIS 6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
- }
- else if (msg.Contains("SHOW Y_SENSAXIS")) //查询Y轴
- {
- SendMsgToHexcal("Y_SENSAXIS 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
- }
- else if (msg.Contains("SHOW Z_SENSAXIS")) //查询Z轴
- {
- SendMsgToHexcal("Z_SENSAXIS 7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
- }
- else if (msg.Contains("SHOW TEMPCOMPTYPE")) //温度补偿,温度补偿 >1 表示支持温度补偿,此处不支持
- {
- SendMsgToHexcal("TEMPCOMPTYPE 0");
- }
- else if (msg.Contains("READTP"))
- {
- SendMsgToHexcal("READTP 0.000000");
- }
- else if (msg.Contains("SHOW ESTOP")) //查询急停状态,根据真是情况调整
- {
- SendMsgToHexcal("ESTOP FALSE");
- }
- else if (msg.Contains("CMHWST"))
- {
- SendMsgToHexcal("CMHWST 8257,0,1792,0");
- }
- else if (msg.Contains("SHOW MOVPAR")) //查询速度
- {
- SendMsgToHexcal(
- "MOVPAR 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.00000 0,0.000000"
- );
- }
- else if (msg.Contains("SHOW MAXVEL")) //查询最大速度
- {
- SendMsgToHexcal(
- "MAXVEL 300.000000,300.000000,300.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
- );
- }
- else if (msg.Contains("SHOW ACCEL")) //查询加速度
- {
- SendMsgToHexcal(
- "ACCEL 1000.000000,1000.000000,1000.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
- );
- }
- else if (msg.Contains("MOVPAR")) //设置速度 xyz 轴的速度
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("ACCEL")) //设置加速度
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("PRBPIN")) //设置侧头偏置
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("ENABLE TEMP")) //设置温度补偿
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("WKPPAR"))
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("SCLTMP"))
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("DISABLE GEO"))
- {
- SendMsgToHexcal("%");
- }
- else if (msg.Contains("AUTZER")) //回家指令
- {
- SendMsgToHexcal("%"); //收到并执行,同时状态改为忙碌
-
- //执行回家
- IsHomed();
- }
- else if (msg.Contains("MOVABS")) //移动指令,解析移动位置
- {
- //收到指令 ,形如 MOVABS 0.015000,127.172997,-114.897003,0.000000\r\n
- SendMsgToHexcal("%");
-
- Point3D point = ParsePoint3DFromCommand(msg);
- SetPositionXyz(point); //开始移动
-
- _pointCloud.Add(point); //添加到点集合
- }
- else if (msg.Contains("GETPOS")) //获取位置
- {
- //POS 167.553898,-55.400421,-208.548678,0.000000,0.000000,0.000000,0.000000
- Point3D point3D = GetPositionXyz(); //获取当前位置
- string resultString = ConstructPosString(point3D);
- SendMsgToHexcal(resultString);
- }
- else
- {
- DebugDfn.AddLogText("未知命令,没有应答");
- }
- }
-
- private void ReceiveStatus(TcpIpServer.EnumTcpIpServer iType, string msg)
- {
- //记录到日志
- DebugDfn.AddLogText(iType + " : " + msg);
-
- //根据连接状态,更新界面
- switch (iType)
- {
- case TcpIpServer.EnumTcpIpServer.ClientConnect:
- _mBHexcalConnected = true;
- break;
-
- default:
- _mBHexcalConnected = false;
- break;
- }
- }
-
- private void SendMsgToHexcal(string msg)
- {
- if (_mTcpIpServer == null)
- return;
-
- //发送数据
- DebugDfn.AddLogText("回复 " + msg);
- _mTcpIpServer.SendMessageToAllClients(msg += "\r\n"); //回复内容末尾加上\r\n,协议要求
- }
-
- private void Btn_StartServer_Click(object sender, EventArgs e)
- {
- Btn_StartServer.Enabled = false;
- Btn_StopServer.Enabled = true;
- StartServer();
- DebugDfn.AddLogText("TCP服务端启动成功 ");
- }
-
- private void Btn_StopServer_Click(object sender, EventArgs e)
- {
- //关闭服务端
- if (_mTcpIpServer != null)
- {
- _mTcpIpServer.StopListen();
- }
-
- Btn_StopServer.Enabled = false;
- Btn_StartServer.Enabled = true;
- _mBHexcalConnected = false;
- DebugDfn.AddLogText("TCP服务端已关闭");
- }
-
- #endregion hexcal软件交互
-
- #region ACS平台相关
-
- #region 异常抓取
-
- //实现函数
-
- private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
- {
- // 防止程序终止
- MessageBox.Show(
- "发生了未处理的异常:" + e.Exception.Message,
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- }
-
- private static void CurrentDomain_UnhandledException(
- object sender,
- UnhandledExceptionEventArgs e
- )
- {
- if (e.ExceptionObject is Exception ex)
- {
- HandleException(ex);
- }
- }
-
- private static void HandleException(Exception ex)
- {
- MessageBox.Show(
- $"发生了未处理的异常:{ex.Message}",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- }
-
- //订阅报错
- private void EnableFaultEvent()
- {
- _acs.EnableEvent(Interrupts.ACSC_INTR_COMM_CHANNEL_CLOSED);
- _acs.EnableEvent(Interrupts.ACSC_INTR_EMERGENCY);
- _acs.EnableEvent(Interrupts.ACSC_INTR_SYSTEM_ERROR);
- _acs.EnableEvent(Interrupts.ACSC_INTR_ETHERCAT_ERROR);
- _acs.EnableEvent(Interrupts.ACSC_INTR_MOTOR_FAILURE);
- _acs.EnableEvent(Interrupts.ACSC_INTR_MOTION_FAILURE);
- _acs.EnableEvent(Interrupts.ACSC_INTR_PHYSICAL_MOTION_END);
- _acs.COMMCHANNELCLOSED += _acs_COMMCHANNELCLOSED;
- _acs.MOTORFAILURE += _acs_MOTORFAILURE;
- _acs.MOTIONFAILURE += _acs_MOTIONFAILURE;
- _acs.SYSTEMERROR += _acs_SYSTEMERROR;
- _acs.ETHERCATERROR += _acs_ETHERCATERROR;
- _acs.EMERGENCY += _acs_EMERGENCY;
- _acs.PHYSICALMOTIONEND += _acs_PHYSICAL_MOTION_END;
- }
-
- private void DisableFaultEvent()
- {
- _acs.COMMCHANNELCLOSED -= _acs_COMMCHANNELCLOSED;
- _acs.MOTORFAILURE -= _acs_MOTORFAILURE;
- _acs.MOTIONFAILURE -= _acs_MOTIONFAILURE;
- _acs.SYSTEMERROR -= _acs_SYSTEMERROR;
- _acs.ETHERCATERROR -= _acs_ETHERCATERROR;
- _acs.EMERGENCY -= _acs_EMERGENCY;
- _acs.PHYSICALMOTIONEND -= _acs_PHYSICAL_MOTION_END;
-
- _acs.DisableEvent(Interrupts.ACSC_INTR_COMM_CHANNEL_CLOSED);
- _acs.DisableEvent(Interrupts.ACSC_INTR_EMERGENCY);
- _acs.DisableEvent(Interrupts.ACSC_INTR_SYSTEM_ERROR);
- _acs.DisableEvent(Interrupts.ACSC_INTR_ETHERCAT_ERROR);
- _acs.DisableEvent(Interrupts.ACSC_INTR_MOTOR_FAILURE);
- _acs.DisableEvent(Interrupts.ACSC_INTR_MOTION_FAILURE);
- _acs.DisableEvent(Interrupts.ACSC_INTR_PHYSICAL_MOTION_END);
- }
-
- //关联函数
- private void _acs_EMERGENCY(ulong param)
- {
- DebugDfn.AddLogText($"[EStopError] Error Message:{_acs.GetErrorString((int)param)}");
- }
-
- private void _acs_ETHERCATERROR(ulong param)
- {
- DebugDfn.AddLogText(
- $"[EtherCatError] Error Message:{_acs.GetErrorString((int)param)}"
- );
- }
-
- private void _acs_SYSTEMERROR(ulong param)
- {
- DebugDfn.AddLogText($"[SystemError] Error Message:{_acs.GetErrorString((int)param)}");
- }
-
- private void _acs_MOTIONFAILURE(AxisMasks axis)
- {
- for (int i = 0; i < _acs.GetAxesCount(); i++)
- {
- if (((int)axis & (int)Math.Pow(2, i)) == Math.Pow(2, i))
- {
- if (_acs.GetMotionError((Axis)i) != 0)
- {
- //Motor无法自动捕获,需要在motion报错中获取
- int errorcode = _acs.GetMotionError((Axis)i);
-
- DebugDfn.AddLogText(
- $"[MotionError] Axis:{i} Error Code:{errorcode} Error Message: {_acs.GetErrorString(errorcode)}"
- );
-
- int errorcodes = _acs.GetMotorError((Axis)i);
-
- DebugDfn.AddLogText(
- $"[MotorError] Axis:{i} Error Code:{errorcodes} Error Message:{_acs.GetErrorString(errorcodes)}"
- );
- }
- }
- }
- }
-
- private void _acs_MOTORFAILURE(AxisMasks axis)
- {
- for (int i = 0; i < _acs.GetAxesCount(); i++)
- {
- if (((int)axis & (int)Math.Pow(2, i)) == Math.Pow(2, i))
- {
- int errorcode = _acs.GetMotorError((Axis)i);
-
- DebugDfn.AddLogText(
- $"[MotorError] Axis:{i} Error Code:{errorcode} Error Message:{_acs.GetErrorString(errorcode)}"
- );
- }
- }
- }
-
- private void _acs_COMMCHANNELCLOSED(ulong param)
- {
- DebugDfn.AddLogText($"[CommError] Error Message:{_acs.GetErrorString((int)param)}");
- }
-
- private void _acs_PHYSICAL_MOTION_END(AxisMasks axis)
- {
- int bit = 0x01;
- int axisNo = 0;
-
- for (int i = 0; i < 64; i++)
- {
- if ((int)axis == bit)
- {
- axisNo = i;
- break;
- }
-
- bit = bit << 1;
- }
-
- //DebugDfn.AddLogText(string.Format(" - Axis {0}, Stoppped", axisNo));
- }
-
- #endregion 异常抓取
-
- private void BtnEnable_Click(object sender, EventArgs e) //使能所有轴
- {
- if (_mAcsConnected)
- {
- //!!!! Important !! Must insert '-1' at the last
- _acs.EnableM(UseAxis);
- }
- else
- {
- //弹窗提醒尚未连接
- MessageBox.Show("未连接到运动平台,请先点击连接");
- }
- }
-
- private void BtnDisable_Click(object sender, EventArgs e) //轴取消
- {
- // Disable all of axes
- _acs.DisableAll();
- }
-
- private bool IsMotionInPose()
- {
- bool x_inpose = false,
- y_inpose = false,
- z_inpose = false;
-
- _mNMotorState = _acs.GetMotorState(Axis.ACSC_AXIS_1);
- if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
- {
- x_inpose = true;
- }
-
- _mNMotorState = _acs.GetMotorState(Axis.ACSC_AXIS_0);
- if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
- {
- y_inpose = true;
- }
-
- _mNMotorState = _acs.GetMotorState(Axis.ACSC_AXIS_8);
- if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
- {
- z_inpose = true;
- }
-
- if (x_inpose && y_inpose && z_inpose)
- {
- return true;
- }
-
- return false;
- }
-
- private void TmrMonitor_Tick(object sender, EventArgs e) //用于刷新状态
- {
- if (_mAcsConnected)
- {
- try
- {
- _mPoint3D = GetPositionXyz(); //取平台当前位置
- #region 更新限位及运动状态
-
- //左右限位刷新
- _mObjReadVar = _acs.ReadVariableAsVector(
- "FAULT",
- ProgramBuffer.ACSC_NONE,
- 0,
- _mNTotalAxis - 1
- );
- if (_mObjReadVar != null)
- {
- _mArrReadVector = _mObjReadVar as Array;
- if (_mArrReadVector != null)
- {
- UpdateLimitState(0, (int)_mArrReadVector.GetValue(1)); //获取X轴
- UpdateLimitState(1, (int)_mArrReadVector.GetValue(0));
- UpdateLimitState(2, (int)_mArrReadVector.GetValue(8));
- }
- }
-
- UpdateSingleAxisStatus(); //刷新运动状态
- #endregion 更新限位及运动状态
-
- #region 到位判断
-
- if (IsMotionInPose())
- {
- _currentMotionState = MotionStates.InPos;
- //DebugDfn.AddLogText("运动到位");
- }
- else
- {
- _currentMotionState = MotionStates.Moving;
- //DebugDfn.AddLogText("运动中");
- isInPose = false;
- }
-
- //增加判断 运动中到 运动到位,主动发送READY
- if (
- _currentMotionState == MotionStates.InPos
- && _currentMotionState != _currentMotorStateLast
- )
- {
- DebugDfn.AddLogText("运动到位");
- isInPose = true;
-
- if (isCounting)
- {
- m_nInPosCount++;
- DebugDfn.AddLogText("到位次数" + m_nInPosCount);
- }
- }
-
- _currentMotorStateLast = _currentMotionState;
-
- #endregion 到位判断
- }
- catch (Exception ex)
- {
- DebugDfn.AddLogText("ACS平台刷新异常" + ex);
- MessageBox.Show(
- ex.Message,
- "ERROR",
- MessageBoxButtons.OK,
- MessageBoxIcon.Error
- );
- Btn_ACSStop_Click(null, null);
- }
- }
- }
-
- private void Btn_ACSStart_Click(object sender, EventArgs e) //连接
- {
- btn_ACSStart.Enabled = false;
- btn_ACSStop.Enabled = true;
-
- try
- {
- if (rdoTCP.Checked)
- {
- // TCP/IP (Ethernet)
- // TCP/IP (Ethernet)
- _acs.OpenCommEthernetTCP(
- txtIP.Text, // IP Address (Default : 10.0.0.100)
- Convert.ToInt32(txtPort.Text.Trim())
- ); // TCP/IP Port nubmer (default : 701)
- }
- else if (rdoSimu.Checked)
- {
- // Simmulation mode
- _acs.OpenCommSimulator();
- }
-
- _mAcsConnected = true;
-
- //运动相关初始化操作
- InitMotion();
-
- // 启动定时器
- tmrMonitor.Interval = 50;
- tmrMonitor.Start();
- }
- catch (COMException comex)
- {
- MessageBox.Show(
- "Connection fail",
- "Error",
- MessageBoxButtons.OK,
- MessageBoxIcon.Error
- );
- Debug.WriteLine("Connection fail" + comex.Message);
-
- _mAcsConnected = false;
- }
- catch (Exception ex)
- {
- DebugDfn.AddLogText("ACS平台连接异常" + ex);
- MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
-
- private void Btn_ACSStop_Click(object sender, EventArgs e) //断开连接
- {
- if (_mAcsConnected)
- {
- //DisableFaultEvent(); //取消注册事件
- _acs.CloseComm();
- }
-
- tmrMonitor.Stop();
- _mAcsConnected = false;
- btn_ACSStart.Enabled = true;
- btn_ACSStop.Enabled = false;
- }
-
- private void UpdateLimitState(int axisNo, int fault) //刷新限位
- {
- if (axisNo < MaxUiLimitCnt)
- {
- if ((fault & (int)SafetyControlMasks.ACSC_SAFETY_LL) != 0)
- _mLblLeftLimit[axisNo].Image = Resources.Error;
- else
- _mLblLeftLimit[axisNo].Image = Resources.Off;
- if ((fault & (int)SafetyControlMasks.ACSC_SAFETY_RL) != 0)
- _mLblRightLimit[axisNo].Image = Resources.Error;
- else
- _mLblRightLimit[axisNo].Image = Resources.Off;
- }
- }
-
- public int TranslateAxisNumber(Axis originalAxisNumber)
- {
- int newAxisNumber = -1;
-
- switch (originalAxisNumber)
- {
- case Axis.ACSC_AXIS_1: //X轴
- newAxisNumber = 0;
- break;
-
- case Axis.ACSC_AXIS_0:
- newAxisNumber = 1; //Y轴
- break;
-
- case Axis.ACSC_AXIS_8:
- newAxisNumber = 2;
- break;
- }
-
- return newAxisNumber;
- }
-
- private void UpdateSingleAxisStatus()
- {
- Axis axis = 0;
- int _axisNo = 0;
- for (int i = 0; i < UseAxis.Length; i++)
- {
- axis = UseAxis[i];
-
- _axisNo = TranslateAxisNumber(UseAxis[i]);
-
- // Get Motor State ACSPL+ Variable : MST (integer)
- _mNMotorState = _acs.GetMotorState(axis);
-
- if (_axisNo == -1)
- {
- return;
- }
-
- // 运动中
- if ((_mNMotorState & MotorStates.ACSC_MST_MOVE) != 0)
- {
- _mlblMoving[_axisNo].Image = Resources.On;
- }
- else
- {
- _mlblMoving[_axisNo].Image = Resources.Off;
- }
-
- // 就位
- if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0)
- {
- _mlblInPos[_axisNo].Image = Resources.On;
- }
- else
- {
- _mlblInPos[_axisNo].Image = Resources.Off;
- }
-
- // 加速
- if ((_mNMotorState & MotorStates.ACSC_MST_ACC) != 0)
- {
- _mlblAcc[_axisNo].Image = Resources.On;
- }
- else
- {
- _mlblAcc[_axisNo].Image = Resources.Off;
- }
-
- // 使能
- if ((_mNMotorState & MotorStates.ACSC_MST_ENABLE) != 0)
- {
- _mlblEnable[_axisNo].Image = Resources.On;
- axisEnabled[_axisNo] = true; //轴使能
- }
- else
- {
- _mlblEnable[_axisNo].Image = Resources.Off;
- axisEnabled[_axisNo] = false;
- }
- }
-
- totalAxisEnabled = CalculateTotalEnabled(axisEnabled, 0, 1, 8);
- DebugDfn.AddLogText($"总的使能状态为:{(totalAxisEnabled ? "使能" : "未使能")}");
- }
-
- private void IsHomed() //读取回家状态,当未回家时执行回家指令
- {
- // 1、连接状态检查,如果未连接,提示
- if (!_mAcsConnected)
- {
- DebugDfn.AddLogText("[IsHomed] ACS平台未连接,请先点击连接");
- MessageBox.Show(
- "ACS平台未连接,请先点击连接",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- return;
- }
-
- // 2、回家状态检查是否已经回家
- if (_mAcsConnected && _homeStates == HomeStates.Homed)
- {
- //弹窗提示
- MessageBox.Show(
- "轴已经回家",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- return;
- }
-
- // 3、读取回家状态
- if (_acs != null && _mAcsConnected)
- {
- var yawHome = _acs.ReadVariable("YAW_HOME_DONE");
-
- if (Convert.ToBoolean(yawHome))
- {
- //弹窗提示
- MessageBox.Show(
- "轴已经回家",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- _homeStates = HomeStates.Homed;
- return;
- }
- // 4、执行回家指令,弹窗等待用户确认
-
- DialogResult result = MessageBox.Show(
- "轴未回家,即将执行回家指令",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
-
- if (result == DialogResult.OK)
- {
- // 在这里执行接下来的操作,例如回家指令
-
- _acs.RunBuffer(ProgramBuffer.ACSC_BUFFER_6, null); //执行回家指令,这里的buffer6是回家指令的buffer
- _homeStates = HomeStates.Homing;
- _currentMotionState = MotionStates.Moving;
- DebugDfn.AddLogText("回家运动中");
-
- //等待回家完成
- for (int i = 0; i < UseAxis.Length; i++)
- {
- _acs.WaitMotionEnd(UseAxis[i], _motionTimeout); //等待回家完成
- }
-
- _homeStates = HomeStates.Homed;
- _currentMotionState = MotionStates.InPos;
- DebugDfn.AddLogText("回家完成");
- }
- else
- {
- //弹窗提醒
- MessageBox.Show(
- "点击了取消,未进行任何动作",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- }
- }
- }
-
- private void InitMotion() //定义 运动状态初始化函数,包括内部变量,轴启用,回家判断等
- {
- string strTemp;
- //运动相关变量初始化
- _homeStates = HomeStates.None;
- _currentMotionState = MotionStates.None;
- _currentMotionState = MotionStates.None;
-
- if (!_mAcsConnected)
- {
- MessageBox.Show("未连接运动平台,请先连接运动平台");
- return;
- }
-
- //轴启用,加电
- _acs.EnableM(UseAxis);
- for (int i = 0; i < UseAxis.Length; i++)
- {
- _acs.WaitMotorEnabled(UseAxis[i], 1, _motionTimeout); //等待电机使能
- }
-
- DebugDfn.AddLogText("电机已启用");
-
- //回家
- IsHomed();
-
- //设置定位速度
- SetSpeedXyz(MotionSpeedxy, MotionSpeedz);
-
- //获取轴数量
- strTemp = _acs.Transaction("?SYSINFO(13)");
- _mNTotalAxis = Convert.ToInt32(strTemp.Trim());
- _mLblLeftLimit = new Label[MaxUiLimitCnt]; //左限位
- _mLblLeftLimit[0] = lblLL0;
- _mLblLeftLimit[1] = lblLL1;
- _mLblLeftLimit[2] = lblLL2;
-
- _mLblRightLimit = new Label[MaxUiLimitCnt]; //右限位
- _mLblRightLimit[0] = lblRL0;
- _mLblRightLimit[1] = lblRL1;
- _mLblRightLimit[2] = lblRL2;
-
- _mlblMoving = new Label[MaxUiLimitCnt]; //运动中
- _mlblMoving[0] = lblMoving0;
- _mlblMoving[1] = lblMoving1;
- _mlblMoving[2] = lblMoving2;
-
- _mlblAcc = new Label[MaxUiLimitCnt]; // 加速中
- _mlblAcc[0] = lblAcc0;
- _mlblAcc[1] = lblAcc1;
- _mlblAcc[2] = lblAcc2;
-
- _mlblInPos = new Label[MaxUiLimitCnt]; //就位
- _mlblInPos[0] = lblInPos0;
- _mlblInPos[1] = lblInPos1;
- _mlblInPos[2] = lblInPos2;
-
- _mlblEnable = new Label[MaxUiLimitCnt]; //轴使能
- _mlblEnable[0] = lblEnable0;
- _mlblEnable[1] = lblEnable1;
- _mlblEnable[2] = lblEnable2;
-
- //EnableFaultEvent(); //订阅错误事件
- }
-
- public static bool IsWithinLimit(Point3D point) //判断点是否在行程范围内
- {
- if (
- point.X >= XMinstrokesw
- && point.X <= XMaxstrokesw
- && point.Y >= YMinstrokesw
- && point.Y <= YMaxstrokesw
- && point.Z >= ZMinstrokesw
- && point.Z <= ZMaxstrokesw
- )
- {
-
- return true;
- }
-
- DebugDfn.AddLogText(point.X.ToString() + " " + point.Y.ToString() + " " + point.Z.ToString());
- //打印 XMinstrokesw 等值
- DebugDfn.AddLogText("XMinstrokesw超限: " + XMinstrokesw);
- DebugDfn.AddLogText("XMaxstrokesw超限: " + XMaxstrokesw);
- DebugDfn.AddLogText("YMinstrokesw超限: " + YMinstrokesw);
- DebugDfn.AddLogText("YMaxstrokesw超限: " + YMaxstrokesw);
- DebugDfn.AddLogText("ZMinstrokesw超限: " + ZMinstrokesw);
- DebugDfn.AddLogText("ZMaxstrokesw超限: " + ZMaxstrokesw);
- return false;
- }
-
- public static bool IsWithinLimit(Point point) //判断点是否在行程范围内
- {
- if (
- point.X >= XMinstrokesw
- && point.X <= XMaxstrokesw
- && point.Y >= YMinstrokesw
- && point.Y <= YMaxstrokesw
- && point.Z >= ZMinstrokesw
- && point.Z <= ZMaxstrokesw
- )
- {
- return true;
- }
-
- return false;
- }
-
- public void SetPositionXyz(Point3D point3D) //运动到指定位置
- {
- if (!_mAcsConnected)
- {
- DebugDfn.AddLogText("ACS平台未连接,请先点击连接");
- MessageBox.Show(
- "ACS平台未连接,请先点击连接",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- return;
- }
-
- if (_currentMotionState != MotionStates.Moving)
- {
- _currentMotionState = MotionStates.Moving; //设置当前运动状态
- //判断 point3D是否合法
- if (point3D != null)
- {
- if (IsWithinLimit(point3D)) //判断点是否在行程范围内
- {
- double[] pointsArray = { point3D.X, point3D.Y, point3D.Z };
- //判断各轴使能状态,如果未使能,则使能
-
- if (!totalAxisEnabled)
- {
- _acs.EnableM(UseAxis);
- for (int i = 0; i < UseAxis.Length; i++)
- {
- _acs.WaitMotorEnabled(UseAxis[i], 1, _motionTimeout); //等待电机使能
- }
-
- //DebugDfn.AddLogText("电机已启用");
- }
-
- //执行运动指令
- _acs.ToPointM(MotionFlags.ACSC_NONE, UseAxis, pointsArray); //多轴运动到指定位置
- }
- else
- {
- DebugDfn.AddLogText("目标位置超出行程范围,请重新设置");
- //MessageBox.Show(
- // "目标位置超出行程范围,请重新设置",
- // "异常",
- // MessageBoxButtons.OK,
- // MessageBoxIcon.Error
- //);
- }
- }
- else
- {
- DebugDfn.AddLogText("目标位置为空,请重新设置");
- MessageBox.Show(
- "目标位置为空,请重新设置",
- "异常",
- MessageBoxButtons.OK,
- MessageBoxIcon.Error
- );
- }
- }
- }
-
- private Point3D GetPositionXyz(int positionMode = 1) //获取当前位置
- {
- double xPosition = 0,
- yPosition = 0,
- zPosition = 0;
- Point3D point3D = new Point3D(xPosition, yPosition, zPosition);
- if (!_mAcsConnected)
- {
- DebugDfn.AddLogText("ACS平台未连接,请先点击连接");
- MessageBox.Show(
- "ACS平台未连接,请先点击连接",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- return point3D;
- }
-
- //获取当前位置, 两种 GetRPosition,GetFPosition
- if (positionMode == 1)
- {
- //获取反馈位置 Feedback position (Encoder value) ACSPL+ Variable : FPO (real)
- xPosition = _acs.GetFPosition(UseAxis[0]);
- yPosition = _acs.GetFPosition(UseAxis[1]);
- zPosition = _acs.GetFPosition(UseAxis[2]);
- //DebugDfn.AddLogText("反馈位置: " + xPosition + " " + yPosition + " " + zPosition);
- }
- else
- {
- //获取参考位置 ACSPL+ Variable : RPOS (real)
- xPosition = _acs.GetRPosition(UseAxis[0]);
- yPosition = _acs.GetRPosition(UseAxis[1]);
- zPosition = _acs.GetRPosition(UseAxis[2]);
- DebugDfn.AddLogText("参考位置: " + xPosition + " " + yPosition + " " + zPosition);
- }
-
- //构造point3D格式
- point3D = new Point3D(xPosition, yPosition, zPosition);
-
- return point3D;
- }
-
- private void SetSpeedXyz(double speedxy, double speedz) //获取运动参数
- {
- //获取实际速度
- double feedbackVelocity = (double)
- _acs.ReadVariable("FVEL", ProgramBuffer.ACSC_NONE, 0, 0);
- DebugDfn.AddLogText("实际速度: " + feedbackVelocity);
-
- //设置Y轴 速度参数
- _acs.SetVelocity(Axis.ACSC_AXIS_0, speedxy);
- _acs.SetAcceleration(Axis.ACSC_AXIS_0, speedxy * 10);
- _acs.SetDeceleration(Axis.ACSC_AXIS_0, speedxy * 10);
- _acs.SetKillDeceleration(Axis.ACSC_AXIS_0, speedxy * 100);
- _acs.SetJerk(Axis.ACSC_AXIS_0, speedxy * 100);
-
- //设置X轴速度参数
- _acs.SetVelocity(Axis.ACSC_AXIS_1, speedxy);
- _acs.SetAcceleration(Axis.ACSC_AXIS_1, speedxy * 10);
- _acs.SetDeceleration(Axis.ACSC_AXIS_1, speedxy * 10);
- _acs.SetKillDeceleration(Axis.ACSC_AXIS_1, speedxy * 100);
- _acs.SetJerk(Axis.ACSC_AXIS_1, speedxy * 100);
-
- //设置Z轴速度参数
-
- _acs.SetVelocity(Axis.ACSC_AXIS_8, speedz);
- _acs.SetAcceleration(Axis.ACSC_AXIS_8, speedz * 10);
- _acs.SetDeceleration(Axis.ACSC_AXIS_8, speedz * 10);
- _acs.SetKillDeceleration(Axis.ACSC_AXIS_8, speedz * 100);
- _acs.SetJerk(Axis.ACSC_AXIS_8, speedz * 100);
-
- DebugDfn.AddLogText($"速度设置完成 ");
- }
-
- private void rtb_quick_loc_Click(object sender, EventArgs e) //快速定位
- {
- // 获取文本框的值
- double x = double.Parse(rtb_SetX.Text);
- double y = double.Parse(rtb_Sety.Text);
- double z = double.Parse(rtb_SetZ.Text);
-
- // 构造 Point3D 对象
- Point3D point = new Point3D(x, y, z);
-
- SetPositionXyz(point);
- }
-
- private static bool CalculateTotalEnabled(bool[] axisEnabled, params int[] axisIndices) //判断轴使能状态
- {
- bool totalEnabled = true;
- foreach (int index in axisIndices)
- {
- bool isEnabled = axisEnabled[index];
- if (!isEnabled)
- {
- totalEnabled = false;
- break;
- }
- }
- return totalEnabled;
- }
-
- private void rtb_stop_Click(object sender, EventArgs e)
- {
- try
- {
- Axis[] m_arrAxisList = new Axis[]
- {
- Axis.ACSC_AXIS_0,
- Axis.ACSC_AXIS_1,
- Axis.ACSC_AXIS_8,
- Axis.ACSC_NONE,
- };
-
- if (m_arrAxisList != null)
- _acs.HaltM(m_arrAxisList);
-
- DebugDfn.AddLogText("立即停止 已发送命令");
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
- Debug.WriteLine(ex.Message);
- }
- }
-
- private void rtb_home_Click(object sender, EventArgs e)
- {
- //弹窗提醒用户,是否执行回家指令
- DialogResult result = MessageBox.Show(
- "是否执行回家指令",
- "提示",
- MessageBoxButtons.OKCancel,
- MessageBoxIcon.Information
- );
-
- if (result == DialogResult.Cancel)
- {
- return;
- }
-
- DebugDfn.AddLogText("回家指令已发送");
- if (!_mAcsConnected)
- {
- DebugDfn.AddLogText("ACS平台未连接,请先点击连接");
- MessageBox.Show(
- "ACS平台未连接,请先点击连接",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
- return;
- }
-
- if (_currentMotionState != MotionStates.Moving)
- {
- _currentMotionState = MotionStates.Moving; //设置当前运动状态
-
- _acs.RunBuffer(ProgramBuffer.ACSC_BUFFER_6, null); //执行回家指令,这里的buffer6是回家指令的buffer
- _homeStates = HomeStates.Homing;
- _currentMotionState = MotionStates.Moving;
- DebugDfn.AddLogText("回家运动中");
-
- //等待回家完成
- for (int i = 0; i < UseAxis.Length; i++)
- {
- _acs.WaitMotionEnd(UseAxis[i], _motionTimeout); //等待回家完成
- }
-
- _homeStates = HomeStates.Homed;
- _currentMotionState = MotionStates.InPos;
- DebugDfn.AddLogText("回家完成");
- }
- }
-
- private void rtb_etalon_Click(object sender, EventArgs e) //etalon校准
- {
- DebugDfn.AddLogText("Etalon校准");
- //判断通讯对象是否存在
- if (_acs == null || !_acs.IsConnected)
- {
- DebugDfn.AddLogText("未建立与运动平台通讯,请在主界面先建立通讯");
-
- // 在合适的位置调用 MessageBox.Show() 方法
- MessageBox.Show(
- "未建立与运动平台通讯,请在主界面先建立通讯",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
-
- return;
- }
- else
- {
- EtalonForm etalonForm = new EtalonForm(this);
- etalonForm.Show();
- }
- }
-
- private void rtb_monitor_Click(object sender, EventArgs e)
- {
- DebugDfn.AddLogText("监控界面");
- //判断通讯对象是否存在
- if (_acs == null || !_acs.IsConnected)
- {
- DebugDfn.AddLogText("未建立与运动平台通讯,请在主界面先建立通讯");
-
- // 在合适的位置调用 MessageBox.Show() 方法
- MessageBox.Show(
- "未建立与运动平台通讯,请在主界面先建立通讯",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
-
- return;
- }
- else
- {
- MonitorForm monitorForm = new MonitorForm(this);
- monitorForm.Show();
- }
-
- }
-
- #endregion ACS平台相关
-
- #region 菜单栏
-
- private void btn_motion_Click(object sender, EventArgs e)
- {
- //判断通讯对象是否存在
- if (_acs == null || !_acs.IsConnected)
- {
- DebugDfn.AddLogText("未建立与运动平台通讯,请在主界面先建立通讯");
-
- // 在合适的位置调用 MessageBox.Show() 方法
- MessageBox.Show(
- "未建立与运动平台通讯,请在主界面先建立通讯",
- "提示",
- MessageBoxButtons.OK,
- MessageBoxIcon.Information
- );
-
- return;
- }
- else
- {
- Motion motion = new Motion(this);
- motion.Show();
- }
- }
-
- private void Rtb_about_Click(object sender, EventArgs e) //关于界面
- {
- AboutBox mAboutBox = new AboutBox();
- mAboutBox.StartPosition = FormStartPosition.CenterScreen;
- mAboutBox.Show();
- }
-
- private void rtb_demo_Click(object sender, EventArgs e)
- {
- DemoShow demoShow = new DemoShow(_acs);
- demoShow.Show();
- demoShow.BringToFront();
- }
-
- private void Timer_RefreshUI_Tick(object sender, EventArgs e) //UI刷新
- {
- //状态灯刷新
- lamp_acs.State = _mAcsConnected ? LampColor.Green : LampColor.Silver;
- lamp_hexcal.State = _mBHexcalConnected ? LampColor.Green : LampColor.Silver;
-
- //时间栏
- //获取当前时间,构造形如 精确到秒,例如 2023-10-08 16:01:23
- rle_timer.Text = "当前时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-
- Plot2D(_pointCloud); //绘图
-
- //更新位置
- if (_mPoint3D != null)
- {
- rtb_xPos.Text = _mPoint3D.X.ToString("F3");
- rtb_yPos.Text = _mPoint3D.Y.ToString("F3");
- rtb_zPos.Text = _mPoint3D.Z.ToString("F3");
- }
- }
-
- #endregion 菜单栏
- }
-}
diff --git a/HexcalMC/Motion/DemoShow.cs b/HexcalMC/Motion/DemoShow.cs
index 87d5dcd..a994570 100644
--- a/HexcalMC/Motion/DemoShow.cs
+++ b/HexcalMC/Motion/DemoShow.cs
@@ -1,7 +1,7 @@
-using System;
+using ACS.SPiiPlusNET;
+using System;
using System.Drawing;
using System.Windows.Forms;
-using ACS.SPiiPlusNET;
using Telerik.WinControls.UI;
namespace HexcalMC
@@ -55,11 +55,10 @@ namespace HexcalMC
//判断是否在演示中
if (Status == DemoStatus.InProgress)
{
- MessageBox.Show("演示正在进行中,请先停止演示","提示", MessageBoxButtons.OK,
+ MessageBox.Show("演示正在进行中,请先停止演示", "提示", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
-
//运行演示
try
{
@@ -121,14 +120,17 @@ namespace HexcalMC
lable_showstatus.ForeColor = Color.Black;
lable_showstatus.Text = "未演示";
break;
+
case DemoStatus.InProgress:
lable_showstatus.ForeColor = Color.Blue;
lable_showstatus.Text = "演示中";
break;
+
case DemoStatus.Completed:
lable_showstatus.ForeColor = Color.Green;
lable_showstatus.Text = "演示完成";
break;
+
case DemoStatus.Error:
lable_showstatus.ForeColor = Color.Red;
lable_showstatus.Text = "演示出错";
diff --git a/HexcalMC/Motion/EtalonForm.cs b/HexcalMC/Motion/EtalonForm.cs
index fd71a27..53e0482 100644
--- a/HexcalMC/Motion/EtalonForm.cs
+++ b/HexcalMC/Motion/EtalonForm.cs
@@ -1,806 +1,792 @@
-using System;
+using ACS.SPiiPlusNET;
+using HexcalMC.Base;
+using System;
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
-using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
-using System.Threading.Tasks;
-using System.Timers;
using System.Windows.Forms;
-using System.Windows.Media;
-using ACS.SPiiPlusNET;
-using HexcalMC.Base;
-using Plot3D;
-using ScottPlot;
-using ScottPlot.Plottable;
-using Telerik.WinControls;
using Telerik.WinControls.UI;
-using static Plot3D.Editor3D;
-using static Telerik.WinControls.UI.ValueMapper;
using cColorScheme = Plot3D.Editor3D.cColorScheme;
using cLine3D = Plot3D.Editor3D.cLine3D;
using cLineData = Plot3D.Editor3D.cLineData;
using cMessgData = Plot3D.Editor3D.cMessgData;
+
// classes
using cObject3D = Plot3D.Editor3D.cObject3D;
using cPoint3D = Plot3D.Editor3D.cPoint3D;
-using cPolygon3D = Plot3D.Editor3D.cPolygon3D;
-using cPolygonData = Plot3D.Editor3D.cPolygonData;
using cScatterData = Plot3D.Editor3D.cScatterData;
using cShape3D = Plot3D.Editor3D.cShape3D;
-using cSurfaceData = Plot3D.Editor3D.cSurfaceData;
-using cUserInput = Plot3D.Editor3D.cUserInput;
+
// callback function
-using delRendererFunction = Plot3D.Editor3D.delRendererFunction;
using eColorScheme = Plot3D.Editor3D.eColorScheme;
using eInvalidate = Plot3D.Editor3D.eInvalidate;
-using eLegendPos = Plot3D.Editor3D.eLegendPos;
using eMouseCtrl = Plot3D.Editor3D.eMouseCtrl;
using eNormalize = Plot3D.Editor3D.eNormalize;
-using eObjType = Plot3D.Editor3D.eObjType;
-using ePolygonMode = Plot3D.Editor3D.ePolygonMode;
+
// enums
using eRaster = Plot3D.Editor3D.eRaster;
using eScatterShape = Plot3D.Editor3D.eScatterShape;
using eSelEvent = Plot3D.Editor3D.eSelEvent;
using eSelType = Plot3D.Editor3D.eSelType;
-using eTooltip = Plot3D.Editor3D.eTooltip;
namespace HexcalMC
{
- internal enum eDemo
- {
- Math_Callback,
- Math_Formula,
- Surface_Fill,
- Surface_Grid,
- Surface_Fill_Missing,
- Surface_Grid_Missing,
- Nested_Graphs,
- Scatter_Plot,
- Connected_Lines,
- Scatter_Shapes,
- Pyramid,
- Sphere_Fill_Closed,
- Sphere_Fill_Open,
- Sphere_Grid,
- Valentine,
- Animation,
- }
-
- public partial class EtalonForm : System.Windows.Forms.Form
- {
- private eDemo me_Demo;
- private eColorScheme me_ColorScheme;
- private System.Windows.Forms.Timer mi_StatusTimer = new System.Windows.Forms.Timer();
- private cMessgData mi_MesgTop = new cMessgData("", -7, 7, System.Drawing.Color.Blue); // For special hint
- private cMessgData mi_MesgBottom = new cMessgData("", -7, -7, System.Drawing.Color.Gray); // For selection mode
-
- // Only for demo "Animation"
- private System.Windows.Forms.Timer mi_AnimationTimer = new System.Windows.Forms.Timer();
-
- private cScatterData mi_SinusData;
- private cPoint3D[] mi_Pyramid;
- private int ms32_AnimationAngle;
-
- // etalon解析变量
- private List etalon_points = new List();
-
- private List filteredPoints = new List(); //过滤后的点
- private List dwellTimes = new List();
-
- private static System.Windows.Forms.Timer refresh_time = new System.Windows.Forms.Timer();
- private static int currentIndex = 0;
-
- private readonly MainFrom mainFrom;
- private readonly Api _acs; //ACS控制器
- private double dwellTime = 2000; //停顿时间
-
- public Axis[] axes =
- {
- Axis.ACSC_AXIS_0,
- Axis.ACSC_AXIS_1,
- Axis.ACSC_AXIS_8,
- Axis.ACSC_NONE,
- }; //使能的轴
-
- public class Point
- {
- public double X { get; }
- public double Y { get; }
- public double Z { get; }
-
- public Point(double x, double y, double z)
- {
- X = x;
- Y = y;
- Z = z;
- }
-
- // 从 Point 转换
- public static Point3D FromPoint(Point point)
- {
- return new Point3D(point.X, point.Y, point.Z);
- }
- }
-
- public EtalonForm(MainFrom _mainFrom)
- {
- InitializeComponent();
- mainFrom = _mainFrom;
- this._acs = _mainFrom._acs;
- }
-
- private void EtalonForm_Load(object sender, EventArgs e)
- {
- InitScatterPlot(); //清空绘图
-
- DebugDfn.textBox_Msg = this.text_etalon_info;
- }
-
- #region 绘图区功能
- private void InitScatterPlot() //绘图区初始化
- {
- comboColors.Sorted = false;
- foreach (eColorScheme e_Scheme in Enum.GetValues(typeof(eColorScheme)))
- {
- comboColors.Items.Add(e_Scheme.ToString().Replace('_', ' '));
- }
- comboColors.SelectedIndex = (int)eColorScheme.Monochrome; //默认色卡
-
- comboRaster.Sorted = false;
- foreach (eRaster e_Raster in Enum.GetValues(typeof(eRaster)))
- {
- comboRaster.Items.Add(e_Raster);
- }
- comboRaster.SelectedIndex = (int)eRaster.Labels; //坐标系栅格样式
-
- //设置默认
- comboMouse.SelectedIndex = 0;
-
- // 设置定时器
- refresh_time.Interval = 100;
- refresh_time.Tick += new EventHandler(OnAnimationTimer);
- refresh_time.Start();
- }
-
- private void DemoScatterPlot(bool b_Lines)
- {
- comboColors.Enabled = true; // Some of the demos will disable this combobox
- me_ColorScheme = (eColorScheme)comboColors.SelectedIndex;
-
- checkMirrorX.Checked = editor3D.AxisX.Mirror;
- checkMirrorY.Checked = editor3D.AxisY.Mirror;
-
- // 3 pixels for line width and for circle radius
- const int SIZE = 3;
-
- cColorScheme i_Scheme = new cColorScheme(me_ColorScheme);
- cScatterData i_ShapeData = new cScatterData(i_Scheme);
- cLineData i_LineData = new cLineData(i_Scheme);
- List i_Points = new List();
-
- for (double P = -22.0; P < 22.0; P += 0.1)
- {
- double d_X = Math.Sin(P) * P;
- double d_Y = Math.Cos(P) * P;
- double d_Z = P;
- if (d_Z > 0.0)
- d_Z /= 3.0;
-
- cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
- if (b_Lines)
- {
- i_Points.Add(i_Point);
- }
- else // Shapes
- {
- // You can store the returned shape in a variable and later modify it's properties
- cShape3D i_Shape = i_ShapeData.AddShape(
- i_Point,
- eScatterShape.Circle,
- SIZE,
- null
- );
- }
- }
-
- // You can store the returned lines in a variable and later modify their properties
- cLine3D[] i_Lines = i_LineData.AddConnectedLines(i_Points, SIZE, null);
-
- // Depending on your use case you can also specify MaintainXY or MaintainXYZ here
- editor3D.Clear();
- editor3D.Normalize = eNormalize.Separate;
- editor3D.AddRenderData(i_ShapeData, i_LineData);
-
- editor3D.Selection.HighlightColor = System.Drawing.Color.FromArgb(90, 90, 90);
- editor3D.Selection.Callback = OnSelectEvent;
- editor3D.Selection.MultiSelect = true;
- editor3D.Selection.Enabled = true;
- editor3D.UndoBuffer.Enabled = true;
- editor3D.Invalidate();
- }
-
- private eInvalidate OnSelectEvent(
- eSelEvent e_Event,
- Keys e_Modifiers,
- int s32_DeltaX,
- int s32_DeltaY,
- cObject3D i_Object
- ) //
- {
- eInvalidate e_Invalidate = eInvalidate.NoChange;
-
- bool b_CTRL = (e_Modifiers & Keys.Control) > 0;
-
- // The left mouse button went down with ALT key down and CTRL key up
- if (e_Event == eSelEvent.MouseDown && !b_CTRL && i_Object != null)
- {
- i_Object.Selected = !i_Object.Selected;
-
- // After changing the selection status the object must be redrawn.
- e_Invalidate = eInvalidate.Invalidate;
- }
- else if (e_Event == eSelEvent.MouseDrag && b_CTRL)
- {
- // The user is dragging the mouse with ALT + CTRL keys down. Convert the mouse
- // movement in the 2D space into a movement in the 3D space.
- cPoint3D i_Project = editor3D.ReverseProject(s32_DeltaX, s32_DeltaY);
-
- // GetSelectedPoints() returns only unique points.
- cPoint3D[] i_Selected = editor3D.Selection.GetSelectedPoints(eSelType.All);
- foreach (cPoint3D i_Point in i_Selected)
- {
- switch (me_Demo)
- {
- case eDemo.Pyramid:
- case eDemo.Scatter_Shapes:
- case eDemo.Scatter_Plot:
- case eDemo.Connected_Lines:
- // The pyramid line end points / scatter shapes can be moved freely in
- // the 3D space
- i_Point.Move(i_Project.X, i_Project.Y, i_Project.Z);
- break;
-
- case eDemo.Surface_Fill:
- case eDemo.Surface_Grid:
- case eDemo.Surface_Fill_Missing:
- case eDemo.Surface_Grid_Missing:
- // The points in the Surface grid have a fixed X,Y position, only Z can
- // be modified.
- i_Point.Move(0, 0, i_Project.Z);
- break;
-
- default:
- Debug.Assert(false);
- break;
- }
- }
-
- // Set flag to recalculate the coordinate system, then Invalidate()
- e_Invalidate = eInvalidate.CoordSystem;
- }
-
- mi_StatusTimer.Stop();
-
- mi_StatusTimer.Start();
- return e_Invalidate;
- }
-
- private void comboRaster_SelectedIndexChanged(object sender, EventArgs e) //坐标栅格样式
- {
- editor3D.Raster = (eRaster)comboRaster.SelectedIndex;
- editor3D.Invalidate();
- }
-
- private void checkMirrorY_CheckedChanged(object sender, EventArgs e) //Y轴镜像
- {
- editor3D.AxisY.Mirror = checkMirrorY.Checked;
- editor3D.Invalidate();
- }
-
- private void checkMirrorX_CheckedChanged(object sender, EventArgs e) //x轴镜像
- {
- editor3D.AxisX.Mirror = checkMirrorX.Checked;
- editor3D.Invalidate();
- }
-
- private void btnReset_Click(object sender, EventArgs e) //重置视图
- {
- editor3D.SetCoefficients(1350, 70, 230);
- editor3D.Invalidate();
- }
-
- private void btnScreenshot_Click(object sender, EventArgs e) //截图保存
- {
- SaveFileDialog i_Dlg = new SaveFileDialog();
- i_Dlg.Title = "Save as PNG image";
- i_Dlg.Filter = "PNG Image|*.png";
- i_Dlg.DefaultExt = ".png";
-
- if (DialogResult.Cancel == i_Dlg.ShowDialog(this))
- return;
-
- Bitmap i_Bitmap = editor3D.GetScreenshot();
- try
- {
- i_Bitmap.Save(i_Dlg.FileName, ImageFormat.Png);
- }
- catch (Exception Ex)
- {
- MessageBox.Show(
- this,
- Ex.Message,
- "Error",
- MessageBoxButtons.OK,
- MessageBoxIcon.Error
- );
- }
- }
-
- private void comboMouse_SelectedIndexChanged(object sender, EventArgs e) //鼠标控制方式
- {
- switch (comboMouse.SelectedIndex)
- {
- case 0:
- editor3D.SetUserInputs(eMouseCtrl.L_Theta_R_Phi);
- labelMouseInfo.Text = "鼠标左键:升高,鼠标右键:旋转";
- break;
-
- case 1:
- editor3D.SetUserInputs(eMouseCtrl.L_Theta_L_Phi);
- labelMouseInfo.Text = "鼠标左键:升高和旋转";
- break;
-
- case 2:
- editor3D.SetUserInputs(eMouseCtrl.M_Theta_M_Phi);
- labelMouseInfo.Text = "鼠标中键:升高和旋转";
- break;
-
- default:
- Debug.Assert(false);
- break;
- }
-
- labelMouseInfo.Text +=
- ", 鼠标左键 + SHIFT:移动、鼠标左键 + CTRL 或滚轮:缩放、鼠标左键 + ALT:选择";
- }
-
- private void comboColors_SelectedIndexChanged(object sender, EventArgs e)
- {
- me_ColorScheme = (eColorScheme)comboColors.SelectedIndex;
-
- //判断 points 是否为空,表示当前是否已经有真实数据
- if (etalon_points.Count > 0)
- {
- PointScatterPlot(etalon_points); //更新真实数据
- }
- //else
- //{
- // DemoScatterPlot(false); //更新虚拟数据
- //}
- }
- #endregion
-
- #region etalon文件解析
- private void moveToPoint()
- {
- DebugDfn.AddLogText("添加到运动队列");
- int timeout = 5000;
-
- //判断电机状态
- if (!mainFrom.totalAxisEnabled)
- {
- DebugDfn.AddLogText("存在电机未使能");
-
- _acs.WaitMotorEnabled(Axis.ACSC_AXIS_0, 1, timeout); //Y轴
- // Wait axis 1 enabled during 5 sec
- _acs.WaitMotorEnabled(Axis.ACSC_AXIS_1, 1, timeout);
-
- _acs.WaitMotorEnabled(Axis.ACSC_AXIS_8, 1, timeout);
-
- //DebugDfn.AddLogText("电机已启用");
- }
-
- // 创建多点运动
- dwellTime = dwellTimes.Average() * 1000; //将秒转换为毫秒
- DebugDfn.AddLogText("平均停顿时间(毫秒):" + dwellTime);
- _acs.MultiPointM(MotionFlags.ACSC_NONE, axes, dwellTime);
-
- //判断是否有点
- if (filteredPoints.Count == 0)
- {
- DebugDfn.AddLogText("没有点");
- return;
- }
- //打印点的数量
- DebugDfn.AddLogText("待添加点的数量:" + filteredPoints.Count);
- // 添加符合条件的点到运动路径中
- foreach (var point in filteredPoints)
- {
- double[] points = new double[3];
- points[0] = point.Y;
- points[1] = point.X;
- points[2] = point.Z;
- _acs.AddPointM(axes, points);
- //打印添加的点
- DebugDfn.AddLogText("添加点:" + points[0] + " " + points[1] + " " + points[2]);
- }
- // 打印添加点的数量
- DebugDfn.AddLogText("已添加点的数量:" + filteredPoints.Count);
-
- // Finish the motion End of the multi-point motion
- _acs.EndSequenceM(axes);
-
- mainFrom.StopCounting();
- //启动统计
- mainFrom.StartCounting();
- }
-
- private void btn_startmove_Click(object sender, EventArgs e) //开始运动
- {
- int timeout = 5000;
- //判断电机状态
- if (!mainFrom.totalAxisEnabled)
- {
- DebugDfn.AddLogText("存在电机未使能");
-
- _acs.WaitMotorEnabled(Axis.ACSC_AXIS_0, 1, timeout); //Y轴
- // Wait axis 1 enabled during 5 sec
- _acs.WaitMotorEnabled(Axis.ACSC_AXIS_1, 1, timeout);
-
- _acs.WaitMotorEnabled(Axis.ACSC_AXIS_8, 1, timeout);
-
- //DebugDfn.AddLogText("电机已启用");
- }
-
- //判断是否有点
- if (filteredPoints.Count == 0)
- {
- MessageBox.Show("没有需要移动的点,请先导入", "ERROR");
- return;
- }
- //打印点的数量
- DebugDfn.AddLogText("待添加点的数量:" + filteredPoints.Count);
-
- //启动运动定时器
- timer_move.Start();
- refresh_time.Start();
- }
-
- private void btn_stop_Click(object sender, EventArgs e) //停止运动
- {
- DebugDfn.AddLogText("停止运动");
- try
- {
- Axis[] m_arrAxisList = new Axis[]
- {
- Axis.ACSC_AXIS_0,
- Axis.ACSC_AXIS_1,
- Axis.ACSC_AXIS_8,
- Axis.ACSC_NONE,
- };
-
- if (m_arrAxisList != null)
- _acs.HaltM(m_arrAxisList);
-
- DebugDfn.AddLogText("立即停止 已发送命令");
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
- Debug.WriteLine(ex.Message);
- }
-
- //停止统计
- mainFrom.StopCounting();
- refresh_time.Stop();
- timer_move.Stop();
- currentIndex = 0;
- }
-
- private void btn_etalon_import_Click(object sender, EventArgs e) //解析Etalon文件
- {
- //打开文件对号框,选择 mpf格式文件
- OpenFileDialog i_Dlg = new OpenFileDialog();
- i_Dlg.Title = "导入Etalon文件";
- i_Dlg.Filter = "Etalon文件|*.mpf";
- i_Dlg.DefaultExt = ".mpf";
- i_Dlg.Multiselect = false;
-
- if (DialogResult.Cancel == i_Dlg.ShowDialog(this))
- return;
-
- //读取文件
- string s_File = i_Dlg.FileName;
-
- //设置路径显示
-
- DebugDfn.AddLogText("导入Etalon文件:" + s_File);
-
- //解析文件
- parse_mpf_file(s_File);
-
- DebugDfn.AddLogText("Etalon文件解析完成");
- }
-
- private bool parse_mpf_file(string mpf_file_path) // 编写解析mpf文件的函数
- {
- //; Machine: ZIM
- //; Position: 1
- //; Created: 2/15/2023 12:29:13 PM
- //; Program: TRAC-CAL V48, Build: 10, 5/13/2022 8:29:25 AM
- //; File: 'cncData.xml'
- //G71
- //G90
- //G500
- //G01 X8000.000 Y200.000 Z-1400.000 F2000 //坐标
- //G04 F=2 // 停顿时间
- //G01 X7800.000 Y300.000 Z-1400.000 F2000
- //G04 F=2
- //G01 X7600.000 Y400.000 Z-1400.000 F2000
- //G04 F=2
- //G01 X7400.000 Y500.000 Z-1400.000 F2000
- //G04 F=2
- //G01 X7200.000 Y600.000 Z-1400.000 F2000
-
- //判断文件是否存在
- if (!File.Exists(mpf_file_path))
- {
- MessageBox.Show("文件不存在");
- return false;
- }
-
- //清空之前的数据
- etalon_points.Clear();
- filteredPoints.Clear();
- dwellTimes.Clear();
- currentIndex = 0; //重置当前点
-
- //读取文件
- string[] lines = File.ReadAllLines(mpf_file_path);
- Regex regex = new Regex(
- @"G01 X(?[-+]?\d*\.?\d+) Y(?[-+]?\d*\.?\d+) Z(?[-+]?\d*\.?\d+)"
- );
- Regex dwellRegex = new Regex(@"G04 F=(?\d+)");
-
- foreach (string line in lines)
- {
- Match match = regex.Match(line);
- if (match.Success)
- {
- double x = double.Parse(match.Groups["X"].Value);
- double y = double.Parse(match.Groups["Y"].Value);
- double z = double.Parse(match.Groups["Z"].Value);
- etalon_points.Add(new Point(x, y, z));
- }
-
- Match dwellMatch = dwellRegex.Match(line);
- if (dwellMatch.Success)
- {
- int f = int.Parse(dwellMatch.Groups["F"].Value);
- dwellTimes.Add(f);
- }
- }
-
- // 输出解析结果
- //Console.WriteLine("Points:");
- //foreach (var point in etalon_points)
- //{
- // Console.WriteLine($"X: {point.X}, Y: {point.Y}, Z: {point.Z}");
- //}
-
- //Console.WriteLine("Dwell Times:");
- //foreach (var dwellTime in dwellTimes)
- //{
- // Console.WriteLine($"F: {dwellTime}");
- //}
-
- //过滤点集
- foreach (var point in etalon_points)
- {
- if (MainFrom.IsWithinLimit(point)) //判断点是否在行程范围内
- {
- filteredPoints.Add(point);
- }
- }
-
- //打印过滤后的点集大小
- DebugDfn.AddLogText("过滤后的点集大小:" + filteredPoints.Count);
- PointScatterPlot(filteredPoints); //绘制散点图
- return true;
- }
-
- #endregion
-
- #region 3D绘图逻辑
- private void PointScatterPlot(List points) //绘制散点图
- {
- //清空绘图
- editor3D.Clear();
- editor3D.Normalize = eNormalize.Separate;
- editor3D.Invalidate();
-
- //判断点集是否为空
- comboColors.Enabled = true; // Some of the demos will disable this combobox
- me_ColorScheme = (eColorScheme)comboColors.SelectedIndex;
-
- checkMirrorX.Checked = editor3D.AxisX.Mirror;
- checkMirrorY.Checked = editor3D.AxisY.Mirror;
-
- // 3 pixels for line width and for circle radius
- const int SIZE = 3;
-
- cColorScheme i_Scheme = new cColorScheme(me_ColorScheme);
- cScatterData i_ShapeData = new cScatterData(i_Scheme);
- cLineData i_LineData = new cLineData(i_Scheme);
- List i_Points = new List();
-
- foreach (var point in points)
- {
- double d_X = point.X;
- double d_Y = point.Y;
- double d_Z = point.Z;
-
- cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
-
- // You can store the returned shape in a variable and later modify it's properties
- cShape3D i_Shape = i_ShapeData.AddShape(i_Point, eScatterShape.Circle, SIZE, null);
- }
-
- // You can store the returned lines in a variable and later modify their properties
- cLine3D[] i_Lines = i_LineData.AddConnectedLines(i_Points, SIZE, null);
-
- // Depending on your use case you can also specify MaintainXY or MaintainXYZ here
- editor3D.Clear();
- editor3D.Normalize = eNormalize.Separate;
- editor3D.AddRenderData(i_ShapeData, i_LineData);
-
- editor3D.Selection.HighlightColor = System.Drawing.Color.FromArgb(90, 90, 90);
- editor3D.Selection.Callback = OnSelectEvent;
- editor3D.Selection.MultiSelect = true;
- editor3D.Selection.Enabled = true;
- editor3D.UndoBuffer.Enabled = true;
- editor3D.Invalidate();
- }
-
- private void btn_clear_Click(object sender, EventArgs e) //清空绘图
- {
- editor3D.Clear();
- editor3D.Normalize = eNormalize.Separate;
- editor3D.Invalidate();
- }
-
- private void btn_draw_test_Click(object sender, EventArgs e)
- {
- DemoScatterPlot(false);
- }
-
- private void OnAnimationTimer(object sender, EventArgs e)
- {
- cScatterData i_ShapeData = new cScatterData();
- //currentIndex = mainFrom.GetInPosCount();//获取当前点的数量
-
- for (int i = 0; i < filteredPoints.Count; i++) //遍历所有点
- {
- if (i < currentIndex-1) //小于的部分,已跑完 为绿色
- {
- double d_X = filteredPoints[i].X;
- double d_Y = filteredPoints[i].Y;
- double d_Z = filteredPoints[i].Z;
-
- cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
- SolidBrush i_brush = new SolidBrush(System.Drawing.Color.Green);
- // You can store the returned shape in a variable and later modify it's properties
- cShape3D i_Shape = i_ShapeData.AddShape(
- i_Point,
- eScatterShape.Circle,
- 5,
- i_brush,
- null
- );
- }
- else if (i == currentIndex-1) //当前点为橙黄色
- {
- double d_X = filteredPoints[i].X;
- double d_Y = filteredPoints[i].Y;
- double d_Z = filteredPoints[i].Z;
-
- cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
- SolidBrush i_brush = new SolidBrush(System.Drawing.Color.OrangeRed);
- // You can store the returned shape in a variable and later modify it's properties
- cShape3D i_Shape = i_ShapeData.AddShape(
- i_Point,
- eScatterShape.Circle,
- 5,
- i_brush,
- null
- );
- }
- else
- {
- double d_X = filteredPoints[i].X;
- double d_Y = filteredPoints[i].Y;
- double d_Z = filteredPoints[i].Z;
-
- cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
- SolidBrush i_brush = new SolidBrush(System.Drawing.Color.Gray);
- // You can store the returned shape in a variable and later modify it's properties
- cShape3D i_Shape = i_ShapeData.AddShape(
- i_Point,
- eScatterShape.Circle,
- 3,
- i_brush,
- null
- );
- }
-
- editor3D.Clear();
- editor3D.Normalize = eNormalize.Separate;
- editor3D.AddRenderData(i_ShapeData);
- editor3D.Invalidate();
- }
- }
- #endregion
-
- #region 队列移动逻辑
- private void timer_move_Tick(object sender, EventArgs e)
- {
- // 检查是否到达目标点
- if (IsAtTarget())
- {
- // 移动到下一个点
- MoveToNextPoint();
- }
- }
-
- private void MoveToNextPoint()
- {
- if (filteredPoints.Count > 0 && currentIndex < filteredPoints.Count)
- {
- //增加2秒延时
- Thread.Sleep((int)dwellTime);
+ internal enum eDemo
+ {
+ Math_Callback,
+ Math_Formula,
+ Surface_Fill,
+ Surface_Grid,
+ Surface_Fill_Missing,
+ Surface_Grid_Missing,
+ Nested_Graphs,
+ Scatter_Plot,
+ Connected_Lines,
+ Scatter_Shapes,
+ Pyramid,
+ Sphere_Fill_Closed,
+ Sphere_Fill_Open,
+ Sphere_Grid,
+ Valentine,
+ Animation,
+ }
+
+ public partial class EtalonForm : System.Windows.Forms.Form
+ {
+ private eDemo me_Demo;
+ private eColorScheme me_ColorScheme;
+ private System.Windows.Forms.Timer mi_StatusTimer = new System.Windows.Forms.Timer();
+ private cMessgData mi_MesgTop = new cMessgData("", -7, 7, System.Drawing.Color.Blue); // For special hint
+ private cMessgData mi_MesgBottom = new cMessgData("", -7, -7, System.Drawing.Color.Gray); // For selection mode
+
+ // Only for demo "Animation"
+ private System.Windows.Forms.Timer mi_AnimationTimer = new System.Windows.Forms.Timer();
+
+ private cScatterData mi_SinusData;
+ private cPoint3D[] mi_Pyramid;
+ private int ms32_AnimationAngle;
+
+ // etalon解析变量
+ private List etalon_points = new List();
+
+ private List filteredPoints = new List(); //过滤后的点
+ private List dwellTimes = new List();
+
+ private static System.Windows.Forms.Timer refresh_time = new System.Windows.Forms.Timer();
+ private static int currentIndex = 0;
+
+ private readonly MainForm mainFrom;
+ private readonly Api _acs; //ACS控制器
+ private double dwellTime = 2000; //停顿时间
+
+ public Axis[] axes =
+ {
+ Axis.ACSC_AXIS_0,
+ Axis.ACSC_AXIS_1,
+ Axis.ACSC_AXIS_8,
+ Axis.ACSC_NONE,
+ }; //使能的轴
+
+ public class Point
+ {
+ public double X { get; }
+ public double Y { get; }
+ public double Z { get; }
+
+ public Point(double x, double y, double z)
+ {
+ X = x;
+ Y = y;
+ Z = z;
+ }
+
+ // 从 Point 转换
+ public static Point3D FromPoint(Point point)
+ {
+ return new Point3D(point.X, point.Y, point.Z);
+ }
+ }
+
+ public EtalonForm(MainForm _mainFrom)
+ {
+ InitializeComponent();
+ mainFrom = _mainFrom;
+ this._acs = _mainFrom._acs;
+ }
+
+ private void EtalonForm_Load(object sender, EventArgs e)
+ {
+ InitScatterPlot(); //清空绘图
+
+ DebugDfn.textBox_Msg = this.text_etalon_info;
+ }
+
+ #region 绘图区功能
+
+ private void InitScatterPlot() //绘图区初始化
+ {
+ comboColors.Sorted = false;
+ foreach (eColorScheme e_Scheme in Enum.GetValues(typeof(eColorScheme)))
+ {
+ comboColors.Items.Add(e_Scheme.ToString().Replace('_', ' '));
+ }
+ comboColors.SelectedIndex = (int)eColorScheme.Monochrome; //默认色卡
+
+ comboRaster.Sorted = false;
+ foreach (eRaster e_Raster in Enum.GetValues(typeof(eRaster)))
+ {
+ comboRaster.Items.Add(e_Raster);
+ }
+ comboRaster.SelectedIndex = (int)eRaster.Labels; //坐标系栅格样式
+
+ //设置默认
+ comboMouse.SelectedIndex = 0;
+
+ // 设置定时器
+ refresh_time.Interval = 100;
+ refresh_time.Tick += new EventHandler(OnAnimationTimer);
+ refresh_time.Start();
+ }
+
+ private void DemoScatterPlot(bool b_Lines)
+ {
+ comboColors.Enabled = true; // Some of the demos will disable this combobox
+ me_ColorScheme = (eColorScheme)comboColors.SelectedIndex;
+
+ checkMirrorX.Checked = editor3D.AxisX.Mirror;
+ checkMirrorY.Checked = editor3D.AxisY.Mirror;
+
+ // 3 pixels for line width and for circle radius
+ const int SIZE = 3;
+
+ cColorScheme i_Scheme = new cColorScheme(me_ColorScheme);
+ cScatterData i_ShapeData = new cScatterData(i_Scheme);
+ cLineData i_LineData = new cLineData(i_Scheme);
+ List i_Points = new List();
+
+ for (double P = -22.0; P < 22.0; P += 0.1)
+ {
+ double d_X = Math.Sin(P) * P;
+ double d_Y = Math.Cos(P) * P;
+ double d_Z = P;
+ if (d_Z > 0.0)
+ d_Z /= 3.0;
+
+ cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
+ if (b_Lines)
+ {
+ i_Points.Add(i_Point);
+ }
+ else // Shapes
+ {
+ // You can store the returned shape in a variable and later modify it's properties
+ cShape3D i_Shape = i_ShapeData.AddShape(
+ i_Point,
+ eScatterShape.Circle,
+ SIZE,
+ null
+ );
+ }
+ }
+
+ // You can store the returned lines in a variable and later modify their properties
+ cLine3D[] i_Lines = i_LineData.AddConnectedLines(i_Points, SIZE, null);
+
+ // Depending on your use case you can also specify MaintainXY or MaintainXYZ here
+ editor3D.Clear();
+ editor3D.Normalize = eNormalize.Separate;
+ editor3D.AddRenderData(i_ShapeData, i_LineData);
+
+ editor3D.Selection.HighlightColor = System.Drawing.Color.FromArgb(90, 90, 90);
+ editor3D.Selection.Callback = OnSelectEvent;
+ editor3D.Selection.MultiSelect = true;
+ editor3D.Selection.Enabled = true;
+ editor3D.UndoBuffer.Enabled = true;
+ editor3D.Invalidate();
+ }
+
+ private eInvalidate OnSelectEvent(
+ eSelEvent e_Event,
+ Keys e_Modifiers,
+ int s32_DeltaX,
+ int s32_DeltaY,
+ cObject3D i_Object
+ ) //
+ {
+ eInvalidate e_Invalidate = eInvalidate.NoChange;
+
+ bool b_CTRL = (e_Modifiers & Keys.Control) > 0;
+
+ // The left mouse button went down with ALT key down and CTRL key up
+ if (e_Event == eSelEvent.MouseDown && !b_CTRL && i_Object != null)
+ {
+ i_Object.Selected = !i_Object.Selected;
+
+ // After changing the selection status the object must be redrawn.
+ e_Invalidate = eInvalidate.Invalidate;
+ }
+ else if (e_Event == eSelEvent.MouseDrag && b_CTRL)
+ {
+ // The user is dragging the mouse with ALT + CTRL keys down. Convert the mouse
+ // movement in the 2D space into a movement in the 3D space.
+ cPoint3D i_Project = editor3D.ReverseProject(s32_DeltaX, s32_DeltaY);
+
+ // GetSelectedPoints() returns only unique points.
+ cPoint3D[] i_Selected = editor3D.Selection.GetSelectedPoints(eSelType.All);
+ foreach (cPoint3D i_Point in i_Selected)
+ {
+ switch (me_Demo)
+ {
+ case eDemo.Pyramid:
+ case eDemo.Scatter_Shapes:
+ case eDemo.Scatter_Plot:
+ case eDemo.Connected_Lines:
+ // The pyramid line end points / scatter shapes can be moved freely in
+ // the 3D space
+ i_Point.Move(i_Project.X, i_Project.Y, i_Project.Z);
+ break;
+
+ case eDemo.Surface_Fill:
+ case eDemo.Surface_Grid:
+ case eDemo.Surface_Fill_Missing:
+ case eDemo.Surface_Grid_Missing:
+ // The points in the Surface grid have a fixed X,Y position, only Z can
+ // be modified.
+ i_Point.Move(0, 0, i_Project.Z);
+ break;
+
+ default:
+ Debug.Assert(false);
+ break;
+ }
+ }
+
+ // Set flag to recalculate the coordinate system, then Invalidate()
+ e_Invalidate = eInvalidate.CoordSystem;
+ }
+
+ mi_StatusTimer.Stop();
+
+ mi_StatusTimer.Start();
+ return e_Invalidate;
+ }
+
+ private void comboRaster_SelectedIndexChanged(object sender, EventArgs e) //坐标栅格样式
+ {
+ editor3D.Raster = (eRaster)comboRaster.SelectedIndex;
+ editor3D.Invalidate();
+ }
+
+ private void checkMirrorY_CheckedChanged(object sender, EventArgs e) //Y轴镜像
+ {
+ editor3D.AxisY.Mirror = checkMirrorY.Checked;
+ editor3D.Invalidate();
+ }
+
+ private void checkMirrorX_CheckedChanged(object sender, EventArgs e) //x轴镜像
+ {
+ editor3D.AxisX.Mirror = checkMirrorX.Checked;
+ editor3D.Invalidate();
+ }
+
+ private void btnReset_Click(object sender, EventArgs e) //重置视图
+ {
+ editor3D.SetCoefficients(1350, 70, 230);
+ editor3D.Invalidate();
+ }
+
+ private void btnScreenshot_Click(object sender, EventArgs e) //截图保存
+ {
+ SaveFileDialog i_Dlg = new SaveFileDialog();
+ i_Dlg.Title = "Save as PNG image";
+ i_Dlg.Filter = "PNG Image|*.png";
+ i_Dlg.DefaultExt = ".png";
+
+ if (DialogResult.Cancel == i_Dlg.ShowDialog(this))
+ return;
+
+ Bitmap i_Bitmap = editor3D.GetScreenshot();
+ try
+ {
+ i_Bitmap.Save(i_Dlg.FileName, ImageFormat.Png);
+ }
+ catch (Exception Ex)
+ {
+ MessageBox.Show(
+ this,
+ Ex.Message,
+ "Error",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error
+ );
+ }
+ }
+
+ private void comboMouse_SelectedIndexChanged(object sender, EventArgs e) //鼠标控制方式
+ {
+ switch (comboMouse.SelectedIndex)
+ {
+ case 0:
+ editor3D.SetUserInputs(eMouseCtrl.L_Theta_R_Phi);
+ labelMouseInfo.Text = "鼠标左键:升高,鼠标右键:旋转";
+ break;
+
+ case 1:
+ editor3D.SetUserInputs(eMouseCtrl.L_Theta_L_Phi);
+ labelMouseInfo.Text = "鼠标左键:升高和旋转";
+ break;
+
+ case 2:
+ editor3D.SetUserInputs(eMouseCtrl.M_Theta_M_Phi);
+ labelMouseInfo.Text = "鼠标中键:升高和旋转";
+ break;
+
+ default:
+ Debug.Assert(false);
+ break;
+ }
+
+ labelMouseInfo.Text +=
+ ", 鼠标左键 + SHIFT:移动、鼠标左键 + CTRL 或滚轮:缩放、鼠标左键 + ALT:选择";
+ }
+
+ private void comboColors_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ me_ColorScheme = (eColorScheme)comboColors.SelectedIndex;
+
+ //判断 points 是否为空,表示当前是否已经有真实数据
+ if (etalon_points.Count > 0)
+ {
+ PointScatterPlot(etalon_points); //更新真实数据
+ }
+ //else
+ //{
+ // DemoScatterPlot(false); //更新虚拟数据
+ //}
+ }
+
+ #endregion 绘图区功能
+
+ #region etalon文件解析
+
+ private void moveToPoint()
+ {
+ DebugDfn.AddLogText("添加到运动队列");
+ int timeout = 5000;
+
+ //判断电机状态
+ if (!mainFrom.totalAxisEnabled)
+ {
+ DebugDfn.AddLogText("存在电机未使能");
+
+ _acs.WaitMotorEnabled(Axis.ACSC_AXIS_0, 1, timeout); //Y轴
+ // Wait axis 1 enabled during 5 sec
+ _acs.WaitMotorEnabled(Axis.ACSC_AXIS_1, 1, timeout);
+
+ _acs.WaitMotorEnabled(Axis.ACSC_AXIS_8, 1, timeout);
+
+ //DebugDfn.AddLogText("电机已启用");
+ }
+
+ // 创建多点运动
+ dwellTime = dwellTimes.Average() * 1000; //将秒转换为毫秒
+ DebugDfn.AddLogText("平均停顿时间(毫秒):" + dwellTime);
+ _acs.MultiPointM(MotionFlags.ACSC_NONE, axes, dwellTime);
+
+ //判断是否有点
+ if (filteredPoints.Count == 0)
+ {
+ DebugDfn.AddLogText("没有点");
+ return;
+ }
+ //打印点的数量
+ DebugDfn.AddLogText("待添加点的数量:" + filteredPoints.Count);
+ // 添加符合条件的点到运动路径中
+ foreach (var point in filteredPoints)
+ {
+ double[] points = new double[3];
+ points[0] = point.Y;
+ points[1] = point.X;
+ points[2] = point.Z;
+ _acs.AddPointM(axes, points);
+ //打印添加的点
+ DebugDfn.AddLogText("添加点:" + points[0] + " " + points[1] + " " + points[2]);
+ }
+ // 打印添加点的数量
+ DebugDfn.AddLogText("已添加点的数量:" + filteredPoints.Count);
+
+ // Finish the motion End of the multi-point motion
+ _acs.EndSequenceM(axes);
+
+ mainFrom.StopCounting();
+ //启动统计
+ mainFrom.StartCounting();
+ }
+
+ private void btn_startmove_Click(object sender, EventArgs e) //开始运动
+ {
+ int timeout = 5000;
+ //判断电机状态
+ if (!mainFrom.totalAxisEnabled)
+ {
+ DebugDfn.AddLogText("存在电机未使能");
+
+ _acs.WaitMotorEnabled(Axis.ACSC_AXIS_0, 1, timeout); //Y轴
+ // Wait axis 1 enabled during 5 sec
+ _acs.WaitMotorEnabled(Axis.ACSC_AXIS_1, 1, timeout);
+
+ _acs.WaitMotorEnabled(Axis.ACSC_AXIS_8, 1, timeout);
+
+ //DebugDfn.AddLogText("电机已启用");
+ }
+
+ //判断是否有点
+ if (filteredPoints.Count == 0)
+ {
+ MessageBox.Show("没有需要移动的点,请先导入", "ERROR");
+ return;
+ }
+ //打印点的数量
+ DebugDfn.AddLogText("待添加点的数量:" + filteredPoints.Count);
+
+ //启动运动定时器
+ timer_move.Start();
+ refresh_time.Start();
+ }
+
+ private void btn_stop_Click(object sender, EventArgs e) //停止运动
+ {
+ DebugDfn.AddLogText("停止运动");
+ try
+ {
+ Axis[] m_arrAxisList = new Axis[]
+ {
+ Axis.ACSC_AXIS_0,
+ Axis.ACSC_AXIS_1,
+ Axis.ACSC_AXIS_8,
+ Axis.ACSC_NONE,
+ };
+
+ if (m_arrAxisList != null)
+ _acs.HaltM(m_arrAxisList);
+
+ DebugDfn.AddLogText("立即停止 已发送命令");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ Debug.WriteLine(ex.Message);
+ }
+
+ //停止统计
+ mainFrom.StopCounting();
+ refresh_time.Stop();
+ timer_move.Stop();
+ currentIndex = 0;
+ }
+
+ private void btn_etalon_import_Click(object sender, EventArgs e) //解析Etalon文件
+ {
+ //打开文件对号框,选择 mpf格式文件
+ OpenFileDialog i_Dlg = new OpenFileDialog();
+ i_Dlg.Title = "导入Etalon文件";
+ i_Dlg.Filter = "Etalon文件|*.mpf";
+ i_Dlg.DefaultExt = ".mpf";
+ i_Dlg.Multiselect = false;
+
+ if (DialogResult.Cancel == i_Dlg.ShowDialog(this))
+ return;
+
+ //读取文件
+ string s_File = i_Dlg.FileName;
+
+ //设置路径显示
+
+ DebugDfn.AddLogText("导入Etalon文件:" + s_File);
+
+ //解析文件
+ parse_mpf_file(s_File);
+
+ DebugDfn.AddLogText("Etalon文件解析完成");
+ }
+
+ private bool parse_mpf_file(string mpf_file_path) // 编写解析mpf文件的函数
+ {
+ //; Machine: ZIM
+ //; Position: 1
+ //; Created: 2/15/2023 12:29:13 PM
+ //; Program: TRAC-CAL V48, Build: 10, 5/13/2022 8:29:25 AM
+ //; File: 'cncData.xml'
+ //G71
+ //G90
+ //G500
+ //G01 X8000.000 Y200.000 Z-1400.000 F2000 //坐标
+ //G04 F=2 // 停顿时间
+ //G01 X7800.000 Y300.000 Z-1400.000 F2000
+ //G04 F=2
+ //G01 X7600.000 Y400.000 Z-1400.000 F2000
+ //G04 F=2
+ //G01 X7400.000 Y500.000 Z-1400.000 F2000
+ //G04 F=2
+ //G01 X7200.000 Y600.000 Z-1400.000 F2000
+
+ //判断文件是否存在
+ if (!File.Exists(mpf_file_path))
+ {
+ MessageBox.Show("文件不存在");
+ return false;
+ }
+
+ //清空之前的数据
+ etalon_points.Clear();
+ filteredPoints.Clear();
+ dwellTimes.Clear();
+ currentIndex = 0; //重置当前点
+
+ //读取文件
+ string[] lines = File.ReadAllLines(mpf_file_path);
+ Regex regex = new Regex(
+ @"G01 X(?[-+]?\d*\.?\d+) Y(?[-+]?\d*\.?\d+) Z(?[-+]?\d*\.?\d+)"
+ );
+ Regex dwellRegex = new Regex(@"G04 F=(?\d+)");
+
+ foreach (string line in lines)
+ {
+ Match match = regex.Match(line);
+ if (match.Success)
+ {
+ double x = double.Parse(match.Groups["X"].Value);
+ double y = double.Parse(match.Groups["Y"].Value);
+ double z = double.Parse(match.Groups["Z"].Value);
+ etalon_points.Add(new Point(x, y, z));
+ }
+
+ Match dwellMatch = dwellRegex.Match(line);
+ if (dwellMatch.Success)
+ {
+ int f = int.Parse(dwellMatch.Groups["F"].Value);
+ dwellTimes.Add(f);
+ }
+ }
+
+ // 输出解析结果
+ //Console.WriteLine("Points:");
+ //foreach (var point in etalon_points)
+ //{
+ // Console.WriteLine($"X: {point.X}, Y: {point.Y}, Z: {point.Z}");
+ //}
+
+ //Console.WriteLine("Dwell Times:");
+ //foreach (var dwellTime in dwellTimes)
+ //{
+ // Console.WriteLine($"F: {dwellTime}");
+ //}
+
+ //过滤点集
+ foreach (var point in etalon_points)
+ {
+ if (MainForm.IsWithinLimit(point)) //判断点是否在行程范围内
+ {
+ filteredPoints.Add(point);
+ }
+ }
+
+ //打印过滤后的点集大小
+ DebugDfn.AddLogText("过滤后的点集大小:" + filteredPoints.Count);
+ PointScatterPlot(filteredPoints); //绘制散点图
+ return true;
+ }
+
+ #endregion etalon文件解析
+
+ #region 3D绘图逻辑
+
+ private void PointScatterPlot(List points) //绘制散点图
+ {
+ //清空绘图
+ editor3D.Clear();
+ editor3D.Normalize = eNormalize.Separate;
+ editor3D.Invalidate();
+
+ //判断点集是否为空
+ comboColors.Enabled = true; // Some of the demos will disable this combobox
+ me_ColorScheme = (eColorScheme)comboColors.SelectedIndex;
+
+ checkMirrorX.Checked = editor3D.AxisX.Mirror;
+ checkMirrorY.Checked = editor3D.AxisY.Mirror;
+
+ // 3 pixels for line width and for circle radius
+ const int SIZE = 3;
+
+ cColorScheme i_Scheme = new cColorScheme(me_ColorScheme);
+ cScatterData i_ShapeData = new cScatterData(i_Scheme);
+ cLineData i_LineData = new cLineData(i_Scheme);
+ List i_Points = new List();
+
+ foreach (var point in points)
+ {
+ double d_X = point.X;
+ double d_Y = point.Y;
+ double d_Z = point.Z;
+
+ cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
+
+ // You can store the returned shape in a variable and later modify it's properties
+ cShape3D i_Shape = i_ShapeData.AddShape(i_Point, eScatterShape.Circle, SIZE, null);
+ }
+
+ // You can store the returned lines in a variable and later modify their properties
+ cLine3D[] i_Lines = i_LineData.AddConnectedLines(i_Points, SIZE, null);
+
+ // Depending on your use case you can also specify MaintainXY or MaintainXYZ here
+ editor3D.Clear();
+ editor3D.Normalize = eNormalize.Separate;
+ editor3D.AddRenderData(i_ShapeData, i_LineData);
+
+ editor3D.Selection.HighlightColor = System.Drawing.Color.FromArgb(90, 90, 90);
+ editor3D.Selection.Callback = OnSelectEvent;
+ editor3D.Selection.MultiSelect = true;
+ editor3D.Selection.Enabled = true;
+ editor3D.UndoBuffer.Enabled = true;
+ editor3D.Invalidate();
+ }
+
+ private void btn_clear_Click(object sender, EventArgs e) //清空绘图
+ {
+ editor3D.Clear();
+ editor3D.Normalize = eNormalize.Separate;
+ editor3D.Invalidate();
+ }
+
+ private void btn_draw_test_Click(object sender, EventArgs e)
+ {
+ DemoScatterPlot(false);
+ }
+
+ private void OnAnimationTimer(object sender, EventArgs e)
+ {
+ cScatterData i_ShapeData = new cScatterData();
+ //currentIndex = mainFrom.GetInPosCount();//获取当前点的数量
+
+ for (int i = 0; i < filteredPoints.Count; i++) //遍历所有点
+ {
+ if (i < currentIndex - 1) //小于的部分,已跑完 为绿色
+ {
+ double d_X = filteredPoints[i].X;
+ double d_Y = filteredPoints[i].Y;
+ double d_Z = filteredPoints[i].Z;
+
+ cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
+ SolidBrush i_brush = new SolidBrush(System.Drawing.Color.Green);
+ // You can store the returned shape in a variable and later modify it's properties
+ cShape3D i_Shape = i_ShapeData.AddShape(
+ i_Point,
+ eScatterShape.Circle,
+ 5,
+ i_brush,
+ null
+ );
+ }
+ else if (i == currentIndex - 1) //当前点为橙黄色
+ {
+ double d_X = filteredPoints[i].X;
+ double d_Y = filteredPoints[i].Y;
+ double d_Z = filteredPoints[i].Z;
+
+ cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
+ SolidBrush i_brush = new SolidBrush(System.Drawing.Color.OrangeRed);
+ // You can store the returned shape in a variable and later modify it's properties
+ cShape3D i_Shape = i_ShapeData.AddShape(
+ i_Point,
+ eScatterShape.Circle,
+ 5,
+ i_brush,
+ null
+ );
+ }
+ else
+ {
+ double d_X = filteredPoints[i].X;
+ double d_Y = filteredPoints[i].Y;
+ double d_Z = filteredPoints[i].Z;
+
+ cPoint3D i_Point = new cPoint3D(d_X, d_Y, d_Z, "Scatter Point");
+ SolidBrush i_brush = new SolidBrush(System.Drawing.Color.Gray);
+ // You can store the returned shape in a variable and later modify it's properties
+ cShape3D i_Shape = i_ShapeData.AddShape(
+ i_Point,
+ eScatterShape.Circle,
+ 3,
+ i_brush,
+ null
+ );
+ }
+
+ editor3D.Clear();
+ editor3D.Normalize = eNormalize.Separate;
+ editor3D.AddRenderData(i_ShapeData);
+ editor3D.Invalidate();
+ }
+ }
+
+ #endregion 3D绘图逻辑
+
+ #region 队列移动逻辑
+
+ private void timer_move_Tick(object sender, EventArgs e)
+ {
+ // 检查是否到达目标点
+ if (IsAtTarget())
+ {
+ // 移动到下一个点
+ MoveToNextPoint();
+ }
+ }
+
+ private void MoveToNextPoint()
+ {
+ if (filteredPoints.Count > 0 && currentIndex < filteredPoints.Count)
+ {
+ //增加2秒延时
+ Thread.Sleep((int)dwellTime);
//int currentDwellTime = dwellTimes[currentIndex] * 1000; // 将秒转换为毫秒
//Thread.Sleep(currentDwellTime);
Point nextPoint = filteredPoints[currentIndex];
- //打印 nextPoint
- DebugDfn.AddLogText(
- "下发指令:"
- + currentIndex
- + " 点:"
- + nextPoint.X
- + " "
- + nextPoint.Y
- + " "
- + nextPoint.Z
- );
- currentIndex++;
- mainFrom.SetPositionXyz(Point.FromPoint(nextPoint)); //移动到下一个点
- }
- else
- {
- DebugDfn.AddLogText("All points have been visited.");
- timer_move.Stop();
- currentIndex = 0;
- }
- }
+ //打印 nextPoint
+ DebugDfn.AddLogText(
+ "下发指令:"
+ + currentIndex
+ + " 点:"
+ + nextPoint.X
+ + " "
+ + nextPoint.Y
+ + " "
+ + nextPoint.Z
+ );
+ currentIndex++;
+ mainFrom.SetPositionXyz(Point.FromPoint(nextPoint)); //移动到下一个点
+ }
+ else
+ {
+ DebugDfn.AddLogText("All points have been visited.");
+ timer_move.Stop();
+ currentIndex = 0;
+ }
+ }
- private bool IsAtTarget()
- {
- // 这里你需要实现实际的到位判断逻辑
- // 例如,通过读取传感器数据或运动控制器的状态
- // 这里假设总是返回 true 作为示例
+ private bool IsAtTarget()
+ {
+ // 这里你需要实现实际的到位判断逻辑 例如,通过读取传感器数据或运动控制器的状态 这里假设总是返回 true 作为示例
- return mainFrom.GetIsMoving();
- }
+ return mainFrom.GetIsMoving();
+ }
- #endregion
- }
-}
+ #endregion 队列移动逻辑
+ }
+}
\ No newline at end of file
diff --git a/HexcalMC/Motion/MonitorForm.cs b/HexcalMC/Motion/MonitorForm.cs
index 9cf9ad8..c4befca 100644
--- a/HexcalMC/Motion/MonitorForm.cs
+++ b/HexcalMC/Motion/MonitorForm.cs
@@ -1,26 +1,13 @@
using ACS.SPiiPlusNET;
using HexcalMC.Base;
-using ScottPlot.Renderable;
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Timers;
using System.Windows.Forms;
-using System.Windows.Markup;
-using Telerik.WinControls.UI;
-using static ScottPlot.Generate;
-using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar;
namespace HexcalMC
{
public partial class MonitorForm : System.Windows.Forms.Form
{
- private readonly MainFrom mainFrom;
+ private readonly MainForm mainFrom;
private readonly Api _acs; //ACS控制器
private double[] dataX; // X轴数据(时间戳)
@@ -28,7 +15,7 @@ namespace HexcalMC
private int dataLength = 200; // 显示的数据点数量:ml-citation{ref="4,5" data="citationList"}
private Random rand = new Random();
- public MonitorForm(MainFrom _mainFrom)
+ public MonitorForm(MainForm _mainFrom)
{
InitializeComponent();
@@ -65,7 +52,6 @@ namespace HexcalMC
}
// 定时器事件:更新数据并刷新图表
-
private void dataTimer_Tick(object sender, EventArgs e)
{
@@ -91,10 +77,8 @@ namespace HexcalMC
});
}
-
private double GetSpeed(int axis)
{
-
//判断对象是否为空
if (_acs == null)
{
diff --git a/HexcalMC/Motion/Motion.cs b/HexcalMC/Motion/Motion.cs
index 3fe1baf..cbb5e60 100644
--- a/HexcalMC/Motion/Motion.cs
+++ b/HexcalMC/Motion/Motion.cs
@@ -1,10 +1,10 @@
-using System;
+using ACS.SPiiPlusNET;
+using HexcalMC.Base;
+using HexcalMC.Properties;
+using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
-using ACS.SPiiPlusNET;
-using HexcalMC.Base;
-using HexcalMC.Properties;
using Telerik.WinControls.UI;
// ACS .NET Library
@@ -21,10 +21,9 @@ namespace HexcalMC
private readonly Api _acs;
private readonly int _motionTimeout = 50000; //延时时间
- private readonly MainFrom mainFrom;
+ private readonly MainForm mainFrom;
-
- private readonly Axis[] UseAxis = MainFrom.UseAxis; //获取激活的轴
+ private readonly Axis[] UseAxis = MainForm.UseAxis; //获取激活的轴
private MotionStates _currentMotionState = MotionStates.None; //运动状态
private Axis[] _mArrAxisList;
private Array _mArrReadVector;
@@ -32,11 +31,10 @@ namespace HexcalMC
private bool _mBConnected;
private Button[] _mBtnOutput;
-
//定义Jog运动 速度
private double _mJogVel = 10.0f;
- private Label[] _mLblInput;
+ private Label[] _mLblInput;
private Label[] _mLblLeftLimit; //左限位
private Label[] _mLblOutput;
@@ -45,6 +43,7 @@ namespace HexcalMC
// For update values
private MotorStates _mNMotorState; //运动状态
+
private ProgramStates _mNProgramState; //程序状态
private int _mNTotalAxis;
@@ -52,7 +51,7 @@ namespace HexcalMC
private int _mNValues, _mNOutputState;
private object _mObjReadVar;
- public Motion(MainFrom mainFrom)
+ public Motion(MainForm mainFrom)
{
InitializeComponent();
@@ -65,8 +64,7 @@ namespace HexcalMC
private void BtnSetZero_Click(object sender, EventArgs e)
{
- // Change current poisition as you want
- // SetFPosition(Axis number, new position)
+ // Change current poisition as you want SetFPosition(Axis number, new position)
_acs.SetFPosition((Axis)cboAxisNo.SelectedIndex, 0);
}
@@ -94,7 +92,7 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion 绝对运动
#region 通用IO
@@ -119,12 +117,10 @@ namespace HexcalMC
0 // 0 = OFF, 1 = ON
);
- // If your I/O device is EtherCAT type, you cannot use this function
- // You can use WriteVariable function and Command function
- //
- // Ex) If EtherCAT mapped variable is 'EC_DOUT'
- // Want to ON bit '3'
- // _ACS.Command("EC_DOUT.3=1");
+ // If your I/O device is EtherCAT type, you cannot use this function You can use
+ // WriteVariable function and Command function
+ //
+ // Ex) If EtherCAT mapped variable is 'EC_DOUT' Want to ON bit '3' _ACS.Command("EC_DOUT.3=1");
}
else
{
@@ -137,7 +133,7 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion 通用IO
#region 初始化
@@ -206,32 +202,25 @@ namespace HexcalMC
#region 副屏显示功能
- // Screen[] screens = Screen.AllScreens;
- // Screen secondaryScreen = screens.Length > 1 ? screens[1] : screens[0]; // 如果有多个屏幕,选择第二个屏幕作为副屏幕
+ // Screen[] screens = Screen.AllScreens; Screen secondaryScreen = screens.Length > 1 ?
+ // screens[1] : screens[0]; // 如果有多个屏幕,选择第二个屏幕作为副屏幕
//
- // // 创建一个新的窗体实例
- // Form secondaryForm = new Form();
+ // // 创建一个新的窗体实例 Form secondaryForm = new Form();
//
- // // 设置窗体的位置和大小以适应副屏幕
- // secondaryForm.StartPosition = FormStartPosition.Manual;
- // secondaryForm.Location = secondaryScreen.Bounds.Location;
- // secondaryForm.Size = secondaryScreen.Bounds.Size;
+ // // 设置窗体的位置和大小以适应副屏幕 secondaryForm.StartPosition = FormStartPosition.Manual;
+ // secondaryForm.Location = secondaryScreen.Bounds.Location; secondaryForm.Size = secondaryScreen.Bounds.Size;
//
- // // 可选:设置副屏窗体的标题、样式等其他属性
- // secondaryForm.Text = "副屏窗体";
- // // ... 其他属性设置
+ // // 可选:设置副屏窗体的标题、样式等其他属性 secondaryForm.Text = "副屏窗体"; // ... 其他属性设置
//
- // // 显示副屏窗体
- // secondaryForm.Show();
-
- #endregion
+ // // 显示副屏窗体 secondaryForm.Show();
+ #endregion 副屏显示功能
#region 初始化
InitMotion();
- #endregion
+ #endregion 初始化
}
private void Motion_FormClosed(object sender, FormClosedEventArgs e)
@@ -239,7 +228,7 @@ namespace HexcalMC
tmrMonitor.Stop();
}
- #endregion
+ #endregion 初始化
#region 通讯建立
@@ -248,7 +237,6 @@ namespace HexcalMC
string strTemp;
int i;
-
_mBConnected = _acs.IsConnected;
// Get Total number of axes
strTemp = _acs.Transaction("?SYSINFO(13)");
@@ -293,11 +281,9 @@ namespace HexcalMC
DebugDfn.AddLogText("运动平台已连接");
}
-
InitMotion();
_mBConnected = true;
}
-
catch (COMException comex)
{
MessageBox.Show("Connection fail", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
@@ -305,7 +291,6 @@ namespace HexcalMC
_mBConnected = false;
}
-
catch (Exception ex)
{
MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
@@ -323,7 +308,6 @@ namespace HexcalMC
btnClose.Enabled = false;
}
-
private void TernminateUMD_Connection()
{
try
@@ -343,12 +327,12 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion 通讯建立
#region UI 刷新
///
- /// Update Motion Profile from Controller
+ /// Update Motion Profile from Controller
///
private void UpdateProfile()
{
@@ -380,7 +364,7 @@ namespace HexcalMC
{
_mNMotorState = _acs.GetMotorState((Axis)iAxisNo);
- // Returned value is integer, you need to use bitmaks
+ // Returned value is integer, you need to use bitmaks
if ((_mNMotorState & MotorStates.ACSC_MST_MOVE) != 0) lblMoving.Image = Resources.On;
else lblMoving.Image = Resources.Off; // 运动中
if ((_mNMotorState & MotorStates.ACSC_MST_INPOS) != 0) lblInPos.Image = Resources.On;
@@ -390,16 +374,14 @@ namespace HexcalMC
if ((_mNMotorState & MotorStates.ACSC_MST_ENABLE) != 0) lblEnable.Image = Resources.On;
else lblEnable.Image = Resources.Off; // 使能
- // Reference position
- // ACSPL+ Variable : RPOS (real)
+ // Reference position ACSPL+ Variable : RPOS (real)
_mLfRPos = _acs.GetRPosition((Axis)iAxisNo); // 参考位置
- // Feedback position (Encoder value)
- // ACSPL+ Variable : FPO (real)
- _mLfFPos = _acs.GetFPosition((Axis)iAxisNo); //反馈位置
+ // Feedback position (Encoder value) ACSPL+ Variable : FPO (real)
+ _mLfFPos = _acs.GetFPosition((Axis)iAxisNo); //反馈位置
- // PE (Position Error)
- // There is no function in library. We need to use ReadVariable function
+ // PE (Position Error) There is no function in library. We need to use
+ // ReadVariable function
_mLfPe = (double)_acs.ReadVariable("PE", ProgramBuffer.ACSC_NONE, iAxisNo, iAxisNo); //位置误差
// Feedback Velocity
@@ -410,9 +392,9 @@ namespace HexcalMC
txtPE.Text = string.Format("{0:0.0000}", _mLfPe);
txtFVEL.Text = string.Format("{0:0.0000}", _mLfFvel);
- // Program State 运动状态
- // State : Compiled, Running, Suspended, Autoroutine is running (ON syntax)
- //
+ // Program State 运动状态 State : Compiled, Running, Suspended, Autoroutine is
+ // running (ON syntax)
+ //
// ACSPL+ Variable : PST (integer)
_mNProgramState = _acs.GetProgramState((ProgramBuffer)iBufferNo);
if ((_mNProgramState & ProgramStates.ACSC_PST_RUN) != 0)
@@ -426,8 +408,7 @@ namespace HexcalMC
lblPRG_Status.Text = "Stop";
}
- // Read left/right hardware limits state
- // ACSPL+ Variable : FAULT (integer)
+ // Read left/right hardware limits state ACSPL+ Variable : FAULT (integer)
_mObjReadVar =
_acs.ReadVariableAsVector("FAULT", ProgramBuffer.ACSC_NONE, 0, _mNTotalAxis - 1);
if (_mObjReadVar != null)
@@ -442,8 +423,8 @@ namespace HexcalMC
}
}
- // Read digital input/output (Port means all of bits)
- // If you want to read only 1 bit (not an integer), use "GetInput" function
+ // Read digital input/output (Port means all of bits) If you want to read only 1
+ // bit (not an integer), use "GetInput" function
_mNValues = _acs.GetInputPort(0); // _ACS.ReadVariableAsVector("IN", -1, 0, 0, -1, -1);
UpdateIoState(_mNValues, true);
@@ -461,7 +442,6 @@ namespace HexcalMC
}
}
-
private void UpdateLimitState(int axisNo, int fault) //刷新限位状态
{
if (axisNo < MaxUiLimitCnt)
@@ -475,7 +455,6 @@ namespace HexcalMC
}
}
-
private void UpdateIoState(int value, bool isInput) //刷新IO状态
{
int bitUpCnt = 0x01;
@@ -526,7 +505,7 @@ namespace HexcalMC
lbl_Z_target.Text = textBox_z.Text;
}
- #endregion
+ #endregion UI 刷新
#region 电机使能
@@ -535,12 +514,12 @@ namespace HexcalMC
// Enable selected axis
_acs.Enable((Axis)cboAxisNo.SelectedIndex);
- // If you want to enable several axes,
- //
+ // If you want to enable several axes,
+ //
// Ex) Eanble three axes (0, 1, 6)
//
- // int[] AxisList = new int[] { 0, 1, 6, -1 }; !!!! Important !! Must insert '-1' at the last
- // _ACS.EnableM(AxisList);
+ // int[] AxisList = new int[] { 0, 1, 6, -1 }; !!!! Important !! Must insert '-1' at the
+ // last _ACS.EnableM(AxisList);
}
private void BtnDisable_Click(object sender, EventArgs e)
@@ -556,7 +535,7 @@ namespace HexcalMC
_acs.DisableAll();
}
- #endregion
+ #endregion 电机使能
#region 相对移动
@@ -605,7 +584,7 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion 相对移动
#region 停止运动
@@ -637,7 +616,7 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion 停止运动
#region JOG 功能
@@ -671,7 +650,6 @@ namespace HexcalMC
}
}
-
private void BtnJogPos_MouseDown(object sender, MouseEventArgs e)
{
double lfVelocity = 0.0f;
@@ -702,7 +680,6 @@ namespace HexcalMC
{
_acs.Halt((Axis)cboAxisNo.SelectedIndex);
-
//自定义Jog
if (sender is Button button)
{
@@ -712,18 +689,23 @@ namespace HexcalMC
case "btn_X_left":
_acs.Halt(Axis.ACSC_AXIS_1);
break;
+
case "btn_X_right":
_acs.Halt(Axis.ACSC_AXIS_1);
break;
+
case "btn_Y_Forward":
_acs.Halt(Axis.ACSC_AXIS_0);
break;
+
case "btn_Y_Back":
_acs.Halt(Axis.ACSC_AXIS_0);
break;
+
case "btn_Z_Up":
_acs.Halt(Axis.ACSC_AXIS_8);
break;
+
case "btn_Z_Down":
_acs.Halt(Axis.ACSC_AXIS_8);
break;
@@ -731,7 +713,7 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion JOG 功能
#region 运行 Buffer Program
@@ -774,7 +756,7 @@ namespace HexcalMC
_acs.StopBuffer((ProgramBuffer)cboBufferNo.SelectedIndex);
}
- #endregion
+ #endregion 运行 Buffer Program
#region 修改运动参数
@@ -788,7 +770,7 @@ namespace HexcalMC
{
// Allow numbers (0 ~ 9), . (DOT), Backspace
if ((e.KeyChar >= 0x30 && e.KeyChar <= 0x39) || e.KeyChar == 0x2E || e.KeyChar == 0x08 ||
- e.KeyChar == (char)Keys.Return || e.KeyChar == (char)Keys.Enter)
+ e.KeyChar == (char)Keys.Return || e.KeyChar == (char)Keys.Enter)
{
if (e.KeyChar == 0x2E && textBox.Text.Contains(Convert.ToString(0x2E)))
e.KeyChar = (char)0x00;
@@ -799,21 +781,25 @@ namespace HexcalMC
lfTemp = Convert.ToDouble(textBox.Text.Trim());
switch (textBox.TabIndex)
{
- // Immediately change value (On the fly) : SetVelocityImm()
- // Affect next motion : SetVelocity()
+ // Immediately change value (On the fly) : SetVelocityImm() Affect
+ // next motion : SetVelocity()
case 0:
_acs.SetVelocityImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 1:
_acs.SetAccelerationImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 2:
_acs.SetDecelerationImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 3:
_acs.SetKillDecelerationImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 4:
_acs.SetJerkImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
@@ -859,15 +845,19 @@ namespace HexcalMC
case 0:
_acs.SetVelocityImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 1:
_acs.SetAccelerationImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 2:
_acs.SetDecelerationImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 3:
_acs.SetKillDecelerationImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
+
case 4:
_acs.SetJerkImm((Axis)cboAxisNo.SelectedIndex, lfTemp);
break;
@@ -881,7 +871,7 @@ namespace HexcalMC
}
}
- #endregion
+ #endregion 修改运动参数
#region 事件回调
@@ -896,8 +886,7 @@ namespace HexcalMC
{
int bit = 0x01;
int axisNo = 0;
- // Param value is bit number
- // Bit Number = Axis Number
+ // Param value is bit number Bit Number = Axis Number
for (int i = 0; i < 64; i++)
{
if ((int)axis == bit)
@@ -927,13 +916,11 @@ namespace HexcalMC
lstLog.Items.Add("PROGRAM_END event enabled");
}
-
private void ACS_PROGRAMEND(BufferMasks buffer)
{
int bit = 0x01;
int bufferNo = 0;
- // Param value is bit number
- // Bit Number = Axis Number
+ // Param value is bit number Bit Number = Axis Number
for (int i = 0; i < 32; i++)
{
if ((int)buffer == bit)
@@ -953,7 +940,7 @@ namespace HexcalMC
});
}
- #endregion
+ #endregion 事件回调
#region 命令行响应
@@ -998,8 +985,7 @@ namespace HexcalMC
rtxtTerminal.ScrollToCaret();
}
- #endregion
-
+ #endregion 命令行响应
#region 按钮
@@ -1007,7 +993,7 @@ namespace HexcalMC
{
//判断是否为textBox_x空
if (string.IsNullOrWhiteSpace(textBox_x.Text) || string.IsNullOrEmpty(textBox_y.Text) ||
- string.IsNullOrEmpty(textBox_z.Text))
+ string.IsNullOrEmpty(textBox_z.Text))
{
MessageBox.Show("输入文本框为空,请修改", "警告");
return;
@@ -1019,8 +1005,7 @@ namespace HexcalMC
double z = double.Parse(textBox_z.Text);
Point3D _point3D = new Point3D(x, y, z);
-
- if (MainFrom.IsWithinLimit(_point3D)) //判断点是否在行程范围内
+ if (MainForm.IsWithinLimit(_point3D)) //判断点是否在行程范围内
{
double[] pointsArray =
{
@@ -1053,7 +1038,6 @@ namespace HexcalMC
}
}
-
private void btn_home_Click(object sender, EventArgs e) //回零
{
double[] pointsArray =
@@ -1125,7 +1109,6 @@ namespace HexcalMC
{
_mJogVel = Convert.ToDouble(txtJogVel.Text.Trim());
-
_acs.Jog(
MotionFlags.ACSC_AMF_VELOCITY, // Velocity flag 速度标志
Axis.ACSC_AXIS_1, // Axis number
@@ -1172,7 +1155,6 @@ namespace HexcalMC
}
}
-
private void btn_Y_Back_MouseDown(object sender, MouseEventArgs e)
{
try
@@ -1181,7 +1163,6 @@ namespace HexcalMC
{
_mJogVel = Convert.ToDouble(txtJogVel.Text.Trim());
-
_acs.Jog(
MotionFlags.ACSC_AMF_VELOCITY, // Velocity flag 速度标志
Axis.ACSC_AXIS_0, // Axis number
@@ -1237,7 +1218,6 @@ namespace HexcalMC
{
_mJogVel = Convert.ToDouble(txtJogVel.Text.Trim());
-
_acs.Jog(
MotionFlags.ACSC_AMF_VELOCITY, // Velocity flag 速度标志
Axis.ACSC_AXIS_8, // Axis number
@@ -1257,9 +1237,6 @@ namespace HexcalMC
}
}
-
-
-
- #endregion
+ #endregion 按钮
}
}
\ No newline at end of file
diff --git a/HexcalMC/Program.cs b/HexcalMC/Program.cs
index f55bcbb..4b16055 100644
--- a/HexcalMC/Program.cs
+++ b/HexcalMC/Program.cs
@@ -1,9 +1,8 @@
-using System;
+using HexcalMC.Base;
+using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
-using HexcalMC.Base;
-
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@@ -49,7 +48,7 @@ namespace HexcalMC
Trace.Listeners.Add(logger);
MyBase.TraceWriteLine("--软件Main函数开始");
- Application.Run(new MainFrom());
+ Application.Run(new MainForm());
}
}
}
\ No newline at end of file
diff --git a/HexcalMC/Properties/AssemblyInfo.cs b/HexcalMC/Properties/AssemblyInfo.cs
index cdbfe6f..706f209 100644
--- a/HexcalMC/Properties/AssemblyInfo.cs
+++ b/HexcalMC/Properties/AssemblyInfo.cs
@@ -1,8 +1,6 @@
using System.Reflection;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-
[assembly: AssemblyTitle("HexcalMC")]
[assembly: AssemblyDescription("本软件为海克斯康制造智能技术(青岛)有限公司(简称海克斯康)版权所有。本软件的任何部分,未经海克斯康公司事前书面许可,均不能复制、存储在检索系统中,或者以任何形式传播,或以任何方法(机械、电子及其他方法)影印、复制。")]
[assembly: AssemblyConfiguration("")]
@@ -12,12 +10,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-
[assembly: ComVisible(false)]
-
[assembly: Guid("6215eb36-92d3-4f96-9331-1e8cbda161f4")]
-
[assembly: AssemblyVersion("0.0.3")]
-[assembly: AssemblyFileVersion("0.0.3")]
+[assembly: AssemblyFileVersion("0.0.3")]
\ No newline at end of file
diff --git a/HexcalMC/Resources/speed.png b/HexcalMC/Resources/speed.png
new file mode 100644
index 0000000..278ef9e
Binary files /dev/null and b/HexcalMC/Resources/speed.png differ
diff --git a/HexcalMC/app.config b/HexcalMC/app.config
index 8b88e42..220ed47 100644
--- a/HexcalMC/app.config
+++ b/HexcalMC/app.config
@@ -1,12 +1,12 @@
-
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/HexcalMC/bin/Debug/HexcalMC.exe b/HexcalMC/bin/Debug/HexcalMC.exe
deleted file mode 100644
index 95d7eee..0000000
Binary files a/HexcalMC/bin/Debug/HexcalMC.exe and /dev/null differ
diff --git a/HexcalMC/bin/x64/Debug/HexCal.exe.11172.dmp b/HexcalMC/bin/x64/Debug/HexCal.exe.11172.dmp
deleted file mode 100644
index 98d16b4..0000000
Binary files a/HexcalMC/bin/x64/Debug/HexCal.exe.11172.dmp and /dev/null differ
diff --git a/HexcalMC/bin/x64/Debug/HexcalMC.application b/HexcalMC/bin/x64/Debug/HexcalMC.application
index 9d97852..63e7384 100644
--- a/HexcalMC/bin/x64/Debug/HexcalMC.application
+++ b/HexcalMC/bin/x64/Debug/HexcalMC.application
@@ -21,7 +21,7 @@
- kw+hFfwnop75FYiMm+CkPl/Ui8EpkT8BuDrRnaok1W0=
+ lMEM3iQRIwDj5xL2Aaf6lHIuYYSvnK+5moagVnmWLIY=
diff --git a/HexcalMC/bin/x64/Debug/HexcalMC.exe b/HexcalMC/bin/x64/Debug/HexcalMC.exe
index 18f02ff..ea5470e 100644
Binary files a/HexcalMC/bin/x64/Debug/HexcalMC.exe and b/HexcalMC/bin/x64/Debug/HexcalMC.exe differ
diff --git a/HexcalMC/bin/x64/Debug/HexcalMC.exe.config b/HexcalMC/bin/x64/Debug/HexcalMC.exe.config
index 8b88e42..220ed47 100644
--- a/HexcalMC/bin/x64/Debug/HexcalMC.exe.config
+++ b/HexcalMC/bin/x64/Debug/HexcalMC.exe.config
@@ -1,12 +1,12 @@
-
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/HexcalMC/bin/x64/Debug/HexcalMC.exe.manifest b/HexcalMC/bin/x64/Debug/HexcalMC.exe.manifest
index b169e22..f0c23c3 100644
--- a/HexcalMC/bin/x64/Debug/HexcalMC.exe.manifest
+++ b/HexcalMC/bin/x64/Debug/HexcalMC.exe.manifest
@@ -55,14 +55,14 @@
-
+
- 5H0iiPDRd1YOihJPwyJD4tzIp7TNDn1eXdVNt91nqVg=
+ c//54i6mMSmqOysqRtrzN/1gINwehBCIXR60WbnWmfY=
@@ -303,13 +303,13 @@
32HYGtH3JppndtsILkwzp/GWhHh3Iq4VdbCQd7284ww=
-
+
- oxwFyeXS8BP9BAS4rAjMQJBMtU+40+onBndoHIYtMog=
+ Nehw75tWncmh7MKTU45ORJgPSsOI3SkwzVWBcGk6J7w=
diff --git a/HexcalMC/bin/x64/Debug/HexcalMC.pdb b/HexcalMC/bin/x64/Debug/HexcalMC.pdb
index 59e4f5e..8db9bcf 100644
Binary files a/HexcalMC/bin/x64/Debug/HexcalMC.pdb and b/HexcalMC/bin/x64/Debug/HexcalMC.pdb differ
diff --git a/HexcalMC/bin/x64/Debug/app.publish/HexcalMC.exe b/HexcalMC/bin/x64/Debug/app.publish/HexcalMC.exe
index 7ec9d39..ea5470e 100644
Binary files a/HexcalMC/bin/x64/Debug/app.publish/HexcalMC.exe and b/HexcalMC/bin/x64/Debug/app.publish/HexcalMC.exe differ