2026-06-05 13:44:39 +09:00

1030 lines
42 KiB
C#

/*
* Copyright (c) 2022 LG Electronics Inc.
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
*/
using Aurora;
using Aurora.Api;
using Aurora.View;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace AuroraUIComponent.UserControls
{
public enum ePattern
{
Unknown = -1,
Dot,
Line,
Rect,
LWPolyLine
}
/// <summary>
/// Interaction logic for AuroraCanvas.xaml
/// </summary>
public partial class AuroraCanvas : Canvas
{
protected static Logger Log = new Logger("AuroraCanvas");
public const double EPS7 = 1e-7;
public double FrameWidth {
get => (double)GetValue(FrameWidthProperty);
set {
if (FrameWidth != value) {
SetValue(FrameWidthProperty, value);
}
}
}
public static readonly DependencyProperty FrameWidthProperty
= DependencyProperty.Register(
nameof(FrameWidth),
typeof(double),
typeof(AuroraCanvas),
new PropertyMetadata(100.0, OnCanvasParamChanged));
public double FrameHeight {
get => (double)GetValue(FrameHeightProperty);
set {
if (FrameHeight != value) {
SetValue(FrameHeightProperty, value);
}
}
}
public static readonly DependencyProperty FrameHeightProperty
= DependencyProperty.Register(
nameof(FrameHeight),
typeof(double),
typeof(AuroraCanvas),
new PropertyMetadata(100.0, OnCanvasParamChanged));
public bool ShowCenterLine {
get => (bool)GetValue(ShowCenterLineProperty);
set {
if (ShowCenterLine != value) {
SetValue(ShowCenterLineProperty, value);
}
}
}
public static readonly DependencyProperty ShowCenterLineProperty
= DependencyProperty.Register(
nameof(ShowCenterLine),
typeof(bool),
typeof(AuroraCanvas),
new PropertyMetadata(false, OnCanvasParamChanged));
public BitmapImage BgImage {
get => (BitmapImage)GetValue(BgImageProperty);
set {
if (BgImage != value) {
SetValue(BgImageProperty, value);
}
}
}
public static readonly DependencyProperty BgImageProperty
= DependencyProperty.Register(
nameof(BgImage),
typeof(BitmapImage),
typeof(AuroraCanvas),
new PropertyMetadata(null, OnCanvasParamChanged));
protected static void OnCanvasParamChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var canvasCell = d as AuroraCanvas;
canvasCell.UpdateCanvas();
}
protected void UpdateCanvas()
{
InvalidateVisual();
}
#region 점/선/사각형 표시 추가
public static readonly DependencyProperty DrawDotListProperty =
DependencyProperty.Register(
nameof(DrawDotList),
typeof(IList<DotPattern.Dot>),
typeof(AuroraCanvas),
new PropertyMetadata(null, OnCanvasParamChanged));
public IList<DotPattern.Dot> DrawDotList {
get {
//if (GetValue(DrawDotListProperty) == null) {
// SetValue(DrawDotListProperty, new IList<DotPattern.Dot>());
//}
return (IList<DotPattern.Dot>)GetValue(DrawDotListProperty);
}
set {
SetValue(DrawDotListProperty, value);
if (DrawDotList is ObservableCollection<DotPattern.Dot> obcList) {
obcList.CollectionChanged += Canvas_CollectionChanged;
}
}
}
public static readonly DependencyProperty DrawLineListProperty =
DependencyProperty.Register(
nameof(DrawLineList),
typeof(IList<LinePattern.Line>),
typeof(AuroraCanvas), new PropertyMetadata(null, OnCanvasParamChanged));
public IList<LinePattern.Line> DrawLineList {
get {
//if (GetValue(DrawLineListProperty) == null) {
// SetValue(DrawLineListProperty, new IList<LinePattern.Line>());
//}
return (IList<LinePattern.Line>)GetValue(DrawLineListProperty);
}
set {
SetValue(DrawLineListProperty, value);
if (DrawLineList is ObservableCollection<LinePattern.Line> obcList) {
obcList.CollectionChanged += Canvas_CollectionChanged;
}
}
}
public static readonly DependencyProperty DrawRectListProperty =
DependencyProperty.Register(
nameof(DrawRectList),
typeof(IList<RectPattern.Rect>),
typeof(AuroraCanvas),
new PropertyMetadata(null, OnCanvasParamChanged));
public IList<RectPattern.Rect> DrawRectList {
get {
//if (GetValue(DrawRectListProperty) == null) {
// SetValue(DrawRectListProperty, new IList<RectPattern.Rect>());
//}
return (IList<RectPattern.Rect>)GetValue(DrawRectListProperty);
}
set {
SetValue(DrawRectListProperty, value);
if (DrawRectList is ObservableCollection<RectPattern.Rect> obcList) {
obcList.CollectionChanged += Canvas_CollectionChanged;
}
}
}
public static readonly DependencyProperty DrawLWPolyLineProperty =
DependencyProperty.Register(
nameof(DrawLWPolyLine),
typeof(IList<LWPolyLinePattern.PathElement>),
typeof(AuroraCanvas),
new PropertyMetadata(null, OnCanvasParamChanged));
public IList<LWPolyLinePattern.PathElement> DrawLWPolyLine {
get {
//if (GetValue(DrawLWPolyLineProperty) == null) {
// SetValue(DrawLWPolyLineProperty, new IList<LWPolyLinePattern.PathElement>());
//}
return (IList<LWPolyLinePattern.PathElement>)GetValue(DrawLWPolyLineProperty);
}
set {
SetValue(DrawLWPolyLineProperty, value);
if (DrawLWPolyLine is ObservableCollection<LWPolyLinePattern.PathElement> obcList) {
obcList.CollectionChanged += Canvas_CollectionChanged;
}
}
}
public static readonly DependencyProperty DrawCrossListProperty =
DependencyProperty.Register("DrawCrossList", typeof(IList<CrossPattern.Cross>), typeof(AuroraCanvas), new PropertyMetadata(null));
public IList<CrossPattern.Cross> DrawCrossList {
get {
//if (GetValue(DrawCrossListProperty) == null) {
// SetValue(DrawCrossListProperty, new IList<CrossPattern.Cross>());
//}
return (IList<CrossPattern.Cross>)GetValue(DrawCrossListProperty);
}
set {
SetValue(DrawCrossListProperty, value);
if (DrawCrossList is ObservableCollection<CrossPattern.Cross> obcList) {
obcList.CollectionChanged += Canvas_CollectionChanged;
}
}
}
protected void Canvas_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
InvalidateVisual();
}
#endregion
#region Flip
public static readonly DependencyProperty IsFlipYProperty
= DependencyProperty.Register(
nameof(IsFlipY),
typeof(bool),
typeof(AuroraCanvas),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(d, e) => (d as AuroraCanvas).OnFlipChanged(e)
));
public bool IsFlipY {
get { return (bool)GetValue(IsFlipYProperty); }
set { SetValue(IsFlipYProperty, value); }
}
protected void OnFlipChanged(object e)
{
InvalidateVisual();
}
public static readonly DependencyProperty IsDataChangedProperty
= DependencyProperty.Register(
nameof(IsDataChanged),
typeof(bool),
typeof(AuroraCanvas),
new PropertyMetadata(false, OnIsDataChanged));
public bool IsDataChanged {
get { return (bool)GetValue(IsDataChangedProperty); }
set { SetValue(IsDataChangedProperty, value); }
}
protected static void OnIsDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var canvasGlass = d as AuroraCanvas;
canvasGlass.InvalidateVisual();
}
public static readonly DependencyProperty BlockCanvasProperty
= DependencyProperty.Register(
nameof(BlockCanvas),
typeof(bool),
typeof(AuroraCanvas),
new FrameworkPropertyMetadata(false,
(d, e) => (d as AuroraCanvas).OnBlockCanvasChanged(e)
));
public bool BlockCanvas {
get { return (bool)GetValue(BlockCanvasProperty); }
set { SetValue(BlockCanvasProperty, value); }
}
protected void OnBlockCanvasChanged(object e)
{
InvalidateVisual();
}
#endregion
#region 클릭 위치 관련
public static readonly DependencyProperty LastSavedXProperty
= DependencyProperty.Register(
nameof(LastSavedX),
typeof(double),
typeof(AuroraCanvas),
new FrameworkPropertyMetadata(0.0));
public double LastSavedX {
get { return (double)GetValue(LastSavedXProperty); }
set { SetValue(LastSavedXProperty, value); }
}
public static readonly DependencyProperty LastSavedYProperty
= DependencyProperty.Register(
nameof(LastSavedY),
typeof(double),
typeof(AuroraCanvas),
new FrameworkPropertyMetadata(0.0));
public double LastSavedY {
get { return (double)GetValue(LastSavedYProperty); }
set { SetValue(LastSavedYProperty, value); }
}
#endregion
public static readonly Brush DUMMY_GLASS_PEN_BRUSH = Brushes.Transparent;
public static readonly Brush DUMMY_GLASS_BG_BRUSH = Brushes.Black;
public static readonly Brush DEFAULT_SHAPE_BRUSH = Brushes.Red;
public const int FONT_HEIGHT = 10;
protected readonly double PixelDip;
protected double OriX, OriY;
protected double CFrameW, CFrameH;
protected double iViewMarginW = 20;
protected double iViewMarginH = 20;
protected const double MARGIN_RATIO = 10;
protected double FrameW;
protected double FrameH;
protected double RatioX, RatioY, RatioNorm;
protected double _ViewRatio = 1.0;
public AuroraCanvas()
{
SnapsToDevicePixels = true;
PixelDip = VisualTreeHelper.GetDpi(this).PixelsPerDip;
MouseLeftButtonUp += Handle_MouseLeftButtonUp;
// Canvas 좌표 표시 Text
tbCanvasCoordPos = new TextBlock() { Foreground = Brushes.White, FontSize = 11 };
tbCanvasCoordPos.Margin = new Thickness(10, 10, 0, 0);
Children.Add(tbCanvasCoordPos);
MouseMove += Canvas_MouseMove;
if (this.ContextMenu == null) {
this.ContextMenu = new ContextMenu();
}
#region MagnificationWindow : 일부 영역 확대 Window 구현 - Context Menu로 등록
// LangToken Resource Find (번역 적용을 위해)
var source = TryFindResource("langTokens");
Binding bindingX5 = new Binding("Header");
bindingX5.Source = source;
bindingX5.Path = new PropertyPath("[OpenMagnifyingView]");
var menuItemX5 = new MenuItem();
menuItemX5.SetBinding(MenuItem.HeaderProperty, bindingX5);
menuItemX5.Click += Click_ShowMagnificationWindowX5;
this.ContextMenu.Items.Add(menuItemX5);
IsVisibleChanged += OnVisibleChanged;
#endregion
}
protected void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool isVisible && isVisible == false) {
// 다른 메뉴로 이동하는 등, Canvas가 Visible하지 않게 되면 같이 Close
if (MagWindow != null) {
MagWindow.Close();
}
}
}
protected void Handle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Point mousePosition = Mouse.GetPosition(this);
LastSavedX = FromCanvasX(mousePosition.X);
LastSavedY = FromCanvasY(mousePosition.Y);
if (MagWindow != null) { // 이미 열려있을 경우에만 갱신 (아닐 경우에는 No Action)
ShowMagnificationWindow(LastSavedX, LastSavedY);
}
}
#region Canvas 좌표 표시 Text
protected TextBlock tbCanvasCoordPos;
protected bool MoveLock = false;
protected bool IsContextOpen = false;
protected Point LastMousePosition; // Canvas좌표
protected string _CanvasXText;
protected string _CanvasYText;
protected double LastCanvasPositionX = 0;
protected double LastCanvasPositionY = 0; // Canvas 기본좌표 그대로
protected void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (tbCanvasCoordPos?.Visibility != Visibility.Visible) {
return;
}
if (MoveLock || IsContextOpen) {
return;
}
var point = e.GetPosition(this);
LastCanvasPositionX = point.X;
LastCanvasPositionY = point.Y;
LastMousePosition.X = FromCanvasX(point.X);
LastMousePosition.Y = FromCanvasY(point.Y);
_CanvasXText = LastMousePosition.X.ToString("F1");
_CanvasYText = LastMousePosition.Y.ToString("F1");
tbCanvasCoordPos.Text = $"X:{_CanvasXText} Y:{_CanvasYText}";
}
#endregion
protected override void OnRender(DrawingContext dc)
{
//Log.FuncI();
base.OnRender(dc);
if (FrameWidth <= 0 || FrameHeight <= 0) {
return;
}
dc.PushClip(new RectangleGeometry(new Rect(new Point(0, 0), RenderSize)));
FrameW = FrameWidth;
FrameH = FrameHeight;
iViewMarginW = ActualWidth * MARGIN_RATIO / 100 / 2;
iViewMarginH = ActualHeight * MARGIN_RATIO / 100 / 2;
// Frame 윤곽선의 actual width & height
CFrameW = ActualWidth - iViewMarginW * 2;
CFrameH = ActualHeight - iViewMarginH * 2;
// 가로/세로 비율 맞추기
_ViewRatio = FrameH / FrameW;
// 크기/마진 재계산
if (_ViewRatio > (CFrameH / CFrameW)) {
CFrameW = CFrameH / _ViewRatio;
iViewMarginW = (ActualWidth - CFrameW) / 2;
} else {
CFrameH = CFrameW * _ViewRatio;
iViewMarginH = (ActualHeight - CFrameH) / 2;
}
OriX = iViewMarginW;
if (!IsFlipY) {
OriY = iViewMarginH + CFrameH; // Origin : 좌하단
} else {
OriY = iViewMarginH; // Origin : 좌상단
}
// FrameW, FrameH : 실물 Frame size
// Ratio 는 Canvas 크기 / 실물 Frame 크기
RatioX = (ActualWidth - (iViewMarginW * 2)) / FrameW;
RatioY = (ActualHeight - (iViewMarginH * 2)) / FrameH;
RatioNorm = Math.Sqrt(RatioX * RatioX + RatioY * RatioY);
Render(dc);
}
protected virtual void Render(DrawingContext dc)
{
//Log.FuncI();
DrawDummyFrame(dc); // Frame 외곽선
DrawExtraShapes(dc);
}
protected void DrawExtraShapes(DrawingContext dc)
{
if (DrawLWPolyLine != null) {
//foreach (var item in DrawLWPolyLine) {
// DrawDot(dc, item.X, item.Y, 0, 0, 2, (item.CustomBrush != null) ? item.CustomBrush : Brushes.Red);
//}
//// 연결선 : 일단 직선만. (곡선까지 적용하려면 추가 코드 작업 필요)
//for (int i = 1; i < DrawLWPolyLine.Count; i++) {
// DrawLine(dc, DrawLWPolyLine[i - 1].X, DrawLWPolyLine[i - 1].Y, DrawLWPolyLine[i].X, DrawLWPolyLine[i].Y, 0, 0, (DrawLWPolyLine[i].CustomBrush != null) ? DrawLWPolyLine[i].CustomBrush : Brushes.Red);
//}
var pathList = DrawLWPolyLine;
if (DrawLWPolyLine != null && DrawLWPolyLine.Count > 0) {
DrawLinesAndArcFromLWPolyLine(dc, DrawLWPolyLine, (DrawLWPolyLine[0].CustomBrush != null) ? DrawLWPolyLine[0].CustomBrush : Brushes.Red, 1, false, false);
}
}
if (DrawDotList != null) {
foreach (var item in DrawDotList) {
DrawDot(dc, item.ORG_X, item.ORG_Y, 0, 0, 2, (item.CustomBrush != null) ? item.CustomBrush : Brushes.Red);
}
}
if (DrawLineList != null) {
foreach (var item in DrawLineList) {
DrawDot(dc, item.START_X, item.START_Y, 0, 0, 1, (item.CustomBrush != null) ? item.CustomBrush : Brushes.Red);
DrawLine(dc, item.START_X, item.START_Y, item.END_X, item.END_Y, 0, 0, (item.CustomBrush != null) ? item.CustomBrush : Brushes.Red);
DrawDot(dc, item.END_X, item.END_Y, 0, 0, 1, (item.CustomBrush != null) ? item.CustomBrush : Brushes.Red);
}
}
if (DrawRectList != null) {
foreach (var item in DrawRectList) {
DrawRectangle(dc, item.START_X, item.START_Y, item.END_X, item.END_Y, 0, 0, (item.LineBrush != null) ? item.LineBrush : Brushes.Red, 1, item.FillBrush);
if (!string.IsNullOrEmpty(item.Label)) {
bool isHorizontal = Math.Abs(item.END_X - item.START_X) > Math.Abs(item.END_Y - item.START_Y);
DrawText(dc, item.Label,
Math.Min(item.START_X, item.END_X),
Math.Min(item.START_Y, item.END_Y) - 8, 0, 0, item.LineBrush);
}
}
}
if (DrawCrossList != null) {
foreach (var item in DrawCrossList) {
DrawCross(dc, item.CENTER_X, item.CENTER_Y, item.LENGTH_X, item.LENGTH_Y, 0, 0, (item.CustomBrush != null) ? item.CustomBrush : Brushes.Red, item.DASH, 3);
}
}
}
protected void DrawDummyFrame(DrawingContext dc)
{
// Frame 외곽선
Rect frameRect = new Rect();
frameRect.X = iViewMarginW;
frameRect.Y = iViewMarginH;
frameRect.Width = CFrameW + 1; // +1 : Brush 영역 확보 (한쪽 면이 선에 가려서 잘림)
frameRect.Height = CFrameH + 1; // +1 : Brush 영역 확보 (한쪽 면이 선에 가려서 잘림)
//dc.DrawRectangle(Brushes.White, DummyFramePen, frameRect);
if (BgImage != null) {
ImageBrush imageBrush = new ImageBrush {
//ImageSource = new BitmapImage(new Uri("pack://application:,,,/YourImage.png")),
ImageSource = BgImage,
Stretch = Stretch.UniformToFill
};
dc.DrawRectangle(imageBrush, new Pen(DUMMY_GLASS_PEN_BRUSH, 1), frameRect);
} else {
dc.DrawRectangle(DUMMY_GLASS_BG_BRUSH, new Pen(DUMMY_GLASS_PEN_BRUSH, 1), frameRect);
}
// Flip을 확인하기 위한 Guide UI 그리기
//DrawFlipGuideImage(dc);
// Origin
//Point orig = new Point();
//orig.X = OriX;
//orig.Y = OriY;
//dc.DrawEllipse(Brushes.Yellow, new Pen(Brushes.Yellow, 1), orig, 4, 4);
//if (ShowKeyRegend) {
// DrawText(dc, "FrameKey(Frame)", -5, 10, 0, 0, Brushes.White, align: TextAlignment.Right);
// DrawText(dc, "FrameKey(Motor)", -5, 18, 0, 0, Brushes.Yellow, align: TextAlignment.Right);
// DrawText(dc, "TPKey(Frame)", -5, 30, 0, 0, Brushes.GreenYellow, align: TextAlignment.Right);
// DrawText(dc, "TPKey(Motor)", -5, 38, 0, 0, Brushes.Green, align: TextAlignment.Right);
//}
//-- 십자선 그리기
if (ShowCenterLine) {
//var dashSize = Math.Min(FrameWidth / 100, FrameHeight / 100);
// NOTE: 전체 frame 중심선 Dash 크기를 바꾸고 싶으면 아래 숫자 변경
var dashSize = 0.005;
DrawCross(dc, FrameWidth / 2, FrameHeight / 2, FrameWidth, FrameHeight, 0, 0, Brushes.Red, dashSize);
}
}
#region Canvas 좌표변환
protected double ToCanvasX(double value)
{
return OriX + value * RatioX;
}
protected double ToCanvasY(double value)
{
if (!IsFlipY) {
return OriY - value * RatioY;
} else {
return OriY + value * RatioY;
}
}
protected double ToCanvasNorm(double value)
{
var lengthX = value * RatioX;
var lengthY = value * RatioY;
return Math.Sqrt(lengthX * lengthX + lengthY * lengthY) / Math.Sqrt(2);
}
protected double FromCanvasX(double value)
{
return (value - OriX) / RatioX;
}
protected double FromCanvasY(double value)
{
//return (OriY - value) / RatioY;
if (!IsFlipY) {
return (OriY - value) / RatioY;
} else {
return (value - OriY) / RatioY;
}
}
#endregion
#region Drawing Shapes
public (double x, double sizeX, double y, double sizeY) GetCanvasPosAndSize(double startX, double startY, double endX, double endY, double areaStartX = 0, double areaStartY = 0)
{
var x1 = ToCanvasX(areaStartX + startX);
var x2 = ToCanvasX(areaStartX + endX);
var y1 = ToCanvasY(areaStartY + startY);
var y2 = ToCanvasY(areaStartY + endY);
return (x1 < x2 ? x1 : x2, Math.Abs(x1 - x2) + 1, y1 < y2 ? y1 : y2, Math.Abs(y1 - y2) + 1);
}
public (double x, double sizeX, double y, double sizeY) GetCanvasPosAndSize2(double orgX, double orgY, double sizeX, double sizeY, double areaStartX = 0, double areaStartY = 0)
{
var x1 = ToCanvasX(areaStartX + orgX - sizeX / 2);
var x2 = ToCanvasX(areaStartX + orgX + sizeX / 2);
var y1 = ToCanvasY(areaStartY + orgY - sizeY / 2);
var y2 = ToCanvasY(areaStartY + orgY + sizeY / 2);
return (x1 < x2 ? x1 : x2, Math.Abs(x1 - x2) + 1, y1 < y2 ? y1 : y2, Math.Abs(y1 - y2) + 1);
}
protected void DrawRectangle(DrawingContext dc, double startX, double startY, double endX, double endY, double areaStartX, double areaStartY, Brush brush = null, double brushSize = 1.0, Brush fillBrush = null)
{
if (dc != null) {
var r2 = new Rect();
var dim = GetCanvasPosAndSize(startX, startY, endX, endY, areaStartX, areaStartY);
r2.X = dim.x;
r2.Width = dim.sizeX;
r2.Y = dim.y;
r2.Height = dim.sizeY;
if (brush == null) {
brush = Brushes.White;
}
dc.DrawRectangle(fillBrush, new Pen(brush, brushSize), r2);
}
}
protected void DrawCross(DrawingContext dc, double origX, double origY, double sizeX, double sizeY, double areaStartX, double areaStartY, Brush brush = null, double dash = -1, double brushSize = 1.0) // areaStart : 미지정 시 frame 원점
{
if (dc != null) {
var x1 = ToCanvasX(areaStartX + origX - sizeX / 2);
var x2 = ToCanvasX(areaStartX + origX + sizeX / 2);
var y1 = ToCanvasY(areaStartY + origY - sizeY / 2);
var y2 = ToCanvasY(areaStartY + origY + sizeY / 2);
var startPoint = new Point();
var endPoint = new Point();
startPoint.X = x1 < x2 ? x1 : x2;
startPoint.Y = (y1 + y2) / 2;
endPoint.X = x1 < x2 ? x2 : x1;
endPoint.Y = (y1 + y2) / 2;
if (brush == null) {
brush = Brushes.White;
}
Pen pen = new Pen(brush, brushSize);
if (dash > 0) {
pen.DashStyle = new DashStyle(new double[] { dash * FrameW, dash * FrameH }, 0);
}
dc.DrawLine(pen, startPoint, endPoint);
startPoint = new Point();
endPoint = new Point();
startPoint.X = (x1 + x2) / 2;
startPoint.Y = y1 < y2 ? y1 : y2;
endPoint.X = (x1 + x2) / 2;
endPoint.Y = y1 < y2 ? y2 : y1;
dc.DrawLine(pen, startPoint, endPoint);
}
}
protected void DrawLine(DrawingContext dc, double startX, double startY, double endX, double endY, double areaStartX, double areaStartY, Brush brush = null, double brushSize = 1.0)
{
if (dc != null) {
var startPoint = new Point();
var endPoint = new Point();
startPoint.X = ToCanvasX(areaStartX + startX);
startPoint.Y = ToCanvasY(areaStartY + startY);
endPoint.X = ToCanvasX(areaStartX + endX);
endPoint.Y = ToCanvasY(areaStartY + endY);
if (brush == null) {
brush = Brushes.White;
}
dc.DrawLine(new Pen(brush, brushSize), startPoint, endPoint);
}
}
protected void DrawDot(DrawingContext dc, double originX, double originY, double areaStartX, double areaStartY, double dotSize, Brush brush = null, double brushSize = 1.0)
{
if (dc != null) {
if (Math.Abs(dotSize) < 0.001) {
dotSize = 2;
}
var r2 = new Point();
r2.X = ToCanvasX(areaStartX + originX);
r2.Y = ToCanvasY(areaStartY + originY);
var dotSizeCanvas = ToCanvasNorm(dotSize);
if (brush == null) {
brush = Brushes.White;
}
dc.DrawEllipse(brush, new Pen(brush, brushSize), r2, dotSizeCanvas, dotSizeCanvas);
}
}
protected void DrawArcFromLWPolyLine(DrawingContext dc, double startX, double startY, double endX, double endY, double bulge, double areaStartX, double areaStartY, Brush brush = null, double brushSize = 1.0)
{
if (dc != null) {
if (brush == null) {
brush = Brushes.White;
}
Pen pen = new Pen(brush, brushSize);
startX = ToCanvasX(areaStartX + startX);
startY = ToCanvasY(areaStartY + startY);
endX = ToCanvasX(areaStartX + endX);
endY = ToCanvasY(areaStartY + endY);
/* // C++ 원본코드 dA와 dB 가 복잡한데, 어차피 필요없으니 굳이 분석하지 말자. Best 로직도 아닌것 같음
double dT = 4 * Math.Atan(bulge);
double dA = Math.Sin(dT) * Math.Sin(dT) / (1 - Math.Cos(dT)) - Math.Cos(dT) + 1;
double dB = endY - startX * Math.Sin(dT) + Math.Sin(dT) * (endX - startX * Math.Cos(dT) + startY * Math.Sin(dT)) / (1 - Math.Cos(dT)) - startY * Math.Cos(dT);
// arc 의 센터 위치인 것 같음
double dYc = dB / dA;
double dXc = (endX - startX * Math.Cos(dT) + (startY - dYc) * Math.Sin(dT)) / (1 - Math.Cos(dT));
double dDX = endX - startX; // Bx - Ax
double dDY = endY - startY; // By - Ay
double dD = Math.Sqrt(dDX * dDX + dDY * dDY) / 2.0; // chord 길이의 절반
double dR = Math.Abs(dD / Math.Sin(dT / 2.0)); // arc 의 반지름
double dAngleS = Math.Atan2(startY - dYc, startX - dXc) * 180.0 / (Math.Atan(1.0) * 4.0);
double dAngleE = dAngleS + dT * 180.0 / (Math.Atan(1.0) * 4.0);
if (dT < 0) {
dAngleS = dAngleE;
dAngleE = dAngleS - dT * 180.0 / (Math.Atan(1.0) * 4.0);
}
*/
// WPF 의 ArcSegment 를 사용하면 간단하게 LWPolyline 의 bulge 처리가 가능함
// 필요한 정보는 반지름과 중심각임..
// (주의) : 원호는 4가지 case가 존재할 수 있는데, 다양한 Bulge 에 대한 검증은 필요함
double absBulge = Math.Abs(bulge);
double chordLength = Math.Sqrt(Math.Pow(endX - startX, 2) + Math.Pow(endY - startY, 2));
//double subtendedAngle = 4 * Math.Atan(absBulge);
double radius = chordLength * ((1.0 + Math.Pow(absBulge, 2)) / (4.0 * absBulge));
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = new Point(startX, startY); // 시작점
ArcSegment arcSegment = new ArcSegment();
arcSegment.Point = new Point(endX, endY); // 끝점
arcSegment.Size = new Size(radius, radius); // arc 의 반지름
arcSegment.SweepDirection = bulge > 0 ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
//arcSegment.IsLargeArc = Math.Abs(dAngleE - dAngleS) > 180;
pathFigure.Segments.Add(arcSegment);
pathGeometry.Figures.Add(pathFigure);
dc.DrawGeometry(null, pen, pathGeometry);
}
}
protected void DrawLinesAndArcFromLWPolyLine(DrawingContext dc, IList<LWPolyLinePattern.PathElement> pathList, Brush brush = null, double brushSize = 1.0, bool showLabel = false, bool showPos = false)
{
if (dc != null) {
if (brush == null) {
brush = DEFAULT_SHAPE_BRUSH;
}
Pen pen = new Pen(brush, brushSize);
PathGeometry pathGeometry = new PathGeometry();
for (int i = 0; i < pathList.Count - 1; i++) {
DrawDot(dc, pathList[i].X, pathList[i].Y, 0, 0, 2, brush, brushSize);
if (showLabel) {
DrawText(dc, $"#{i}", pathList[i].X + 5, pathList[i].Y + 5, 0, 0, brush);
}
if (showPos) {
DrawText(dc, $"#{i}[{pathList[i].X.ToString("N2")},{pathList[i].Y.ToString("N2")}],Bulge[{pathList[i].Bulge.ToString("N2")}]", 50, i * 10 + 5, 0, 0, brush);
}
var startX = ToCanvasX(pathList[i].X);
var startY = ToCanvasY(pathList[i].Y);
var endX = ToCanvasX(pathList[i + 1].X);
var endY = ToCanvasY(pathList[i + 1].Y);
var bulge = pathList[i + 1].Bulge;
PathFigure pathFigure = new PathFigure();
if (Math.Abs(bulge) < EPS7) {
pathFigure.StartPoint = new Point(startX, startY);
LineSegment lineSegment = new LineSegment() { Point = new Point(endX, endY) };
pathFigure.Segments.Add(lineSegment);
} else {
/*
double dT = 4 * Math.Atan(pathList[i + 1].Bulge);
double dA = Math.Sin(dT) * Math.Sin(dT) / (1 - Math.Cos(dT)) - Math.Cos(dT) + 1;
double dB = endY - startX * Math.Sin(dT) + Math.Sin(dT) * (endX - startX * Math.Cos(dT) + startY * Math.Sin(dT)) / (1 - Math.Cos(dT)) - startY * Math.Cos(dT);
double dYc = dB / dA;
double dXc = (endX - startX * Math.Cos(dT) + (startY - dYc) * Math.Sin(dT)) / (1 - Math.Cos(dT));
double dDX = endX - startX;
double dDY = endY - startY;
double dD = Math.Sqrt(dDX * dDX + dDY * dDY) / 2.0;
double dR = Math.Abs(dD / Math.Sin(dT / 2.0));
double dAngleS = Math.Atan2(startY - dYc, startX - dXc) * 180.0 / (Math.Atan(1.0) * 4.0);
double dAngleE = dAngleS + dT * 180.0 / (Math.Atan(1.0) * 4.0);
if (dT < 0) {
dAngleS = dAngleE;
dAngleE = dAngleS - dT * 180.0 / (Math.Atan(1.0) * 4.0);
}
*/
// WPF 의 ArcSegment 를 사용하면 간단하게 LWPolyline 의 bulge 처리가 가능함
// 필요한 정보는 반지름과 중심각인데, 중심각도 필요없어 보임.
// (주의) : 원호는 4가지 case가 존재할 수 있는데, 다양한 Bulge 에 대한 검증은 필요함
double absBulge = Math.Abs(bulge);
double chordLength = Math.Sqrt(Math.Pow(endX - startX, 2) + Math.Pow(endY - startY, 2));
//double subtendedAngle = 4 * Math.Atan(absBulge);
double radius = chordLength * ((1.0 + Math.Pow(absBulge, 2)) / (4.0 * absBulge));
pathFigure.StartPoint = new Point(startX, startY); // 시작점
ArcSegment arcSegment = new ArcSegment();
arcSegment.Point = new Point(endX, endY); // 끝점
arcSegment.Size = new Size(radius, radius); // circular arc 의 반지름
arcSegment.SweepDirection = bulge > 0 ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
//arcSegment.IsLargeArc = Math.Abs(dAngleE - dAngleS) > 180;
pathFigure.Segments.Add(arcSegment);
/* // 레이저 스캐너용 연산함수 테스트...나중에 지우자...
PolyLinePattern.CalArcCenterAndAngle(pathList[i].X, pathList[i].Y, pathList[i + 1].X, pathList[i + 1].Y,
bulge, out double centerX, out double centerY, out double centralAngle);
Debug.WriteLine($"Bulge: i: {i}, bulge: {bulge}, centerX: {centerX}, centerY: {centerY}, centralAng: {centralAngle*180.0/Math.PI}");
*/
}
pathGeometry.Figures.Add(pathFigure);
}
dc.DrawGeometry(null, pen, pathGeometry);
}
}
protected void DrawText(DrawingContext dc, string text, double origX, double origY, double areaStartX, double areaStartY, Brush brush = null, int fontHeight = FONT_HEIGHT, TextAlignment align = TextAlignment.Center)
{
if (dc != null) {
if (brush == null) {
brush = DEFAULT_SHAPE_BRUSH;
}
var formattedText = new FormattedText(
text, CultureInfo.GetCultureInfo("ko-KR"),
FlowDirection.RightToLeft, new Typeface("Arial"), fontHeight, brush, PixelDip);
formattedText.TextAlignment = align;
var x = ToCanvasX(areaStartX + origX) - formattedText.Width / 8;
var y = ToCanvasY(areaStartY + origY) - formattedText.Height / 2;
var textPos = new Point(x, y);
dc.DrawText(formattedText, textPos);
}
}
protected void DrawText(DrawingContext dc, List<string> texts, double origX, double origY, double areaStartX, double areaStartY, Brush brush = null)
{
if (dc != null) {
for (int index = 0; index < texts.Count; index++) {
if (brush == null) {
brush = DEFAULT_SHAPE_BRUSH;
}
var formattedText = new FormattedText(
texts[index], CultureInfo.GetCultureInfo("ko-KR"),
FlowDirection.LeftToRight, new Typeface("Arial"), FONT_HEIGHT, brush, PixelDip);
formattedText.TextAlignment = TextAlignment.Center;
var x = ToCanvasX(areaStartX + origX);
var y = ToCanvasY(areaStartY + origY) + formattedText.Height * (index - texts.Count / 2);
var textPos = new Point(x, y);
dc.DrawText(formattedText, textPos);
}
}
}
#endregion
#region MagnificationWindow : 일부 영역 확대 Window 구현
protected void Click_ShowMagnificationWindowX5(object sender, RoutedEventArgs e)
{
ShowMagnificationWindow(LastMousePosition.X, LastMousePosition.Y, 5); // MouseMove 위치 기준으로 띄움
//ShowMagnificationWindow(LastSavedX, LastSavedY, 5); // 마지막 마우스 좌클릭 위치 기준으로 띄움
// => Context Menu 누르기 전에 눌러놓고 띄워야 한다는 걸 알고 쓰면 정확한 좌표를 쓸수있는 장점은 있지만
// 마우스 우클릭 Context Menu를 직관적으로 쓰는 것이 나을 수도 있어서 MouseMove 위치 기준으로 띄우게 해 둡니다.
}
protected const string TITLE_MAGNIFYING_VIEW = "Magnification";
protected double MagnifyRatio = 5;
protected Window MagWindow;
protected UCMagnification MagContent;
protected void ShowMagnificationWindow(double centerX, double centerY, double magnifyRatio = 0) // 확대하고싶은 배율 (이에 맞는 영역이 capture됨)
{
if (magnifyRatio > 0) MagnifyRatio = magnifyRatio;
if (MagnifyRatio <= 0) MagnifyRatio = 1;
Log.I($"Center[{centerX}, {centerY}], Ratio[{MagnifyRatio}]");
// [2024.03.29. ym.park] Close하고 다시 여는 대신 Window Content만 업데이트하는 것으로 변경
//if (MagWindow != null) {
// MagWindow.Close();
//}
double captureSizeRef = 500;
double windowSize = captureSizeRef * 1;
double captureSize = windowSize / MagnifyRatio;
if (MagContent == null) {
MagContent = new UCMagnification();
MagContent.Ratio = MagnifyRatio;
MagContent.CenterX = centerX;
MagContent.CenterY = centerY;
MagContent.CmdRatioChanged = new DelegateCommand(ProcessRatioChanged);
MagWindow = new Window() { Content = MagContent };
MagWindow.Title = TITLE_MAGNIFYING_VIEW;
//MagWindow.Width = windowSizeRef * MagnifyRatio;
//MagWindow.Height = windowSizeRef * MagnifyRatio;
MagWindow.Width = windowSize;
MagWindow.Height = windowSize;
MagWindow.Left = 0;
MagWindow.Top = 0;
MagWindow.Topmost = true;
MagWindow.Closed += OnMagWindowClosed;
} else {
MagContent.Ratio = MagnifyRatio;
MagContent.CenterX = centerX;
MagContent.CenterY = centerY;
}
MagContent.RectangleBrush = GetCaptureVisualBrush(
ToCanvasX(centerX) - captureSize / 2,
ToCanvasY(centerY) - captureSize / 2,
captureSize, captureSize);
MagContent.Caption = $"DateTime : {DateTime.Now.ToString("yyyyMMdd_HH:mm:ss")}\nCenter (X:{centerX.ToString("N3")} Y:{centerY.ToString("N3")})";
// Show Window
MagWindow.Show();
MagWindow.Focus();
}
protected void ProcessRatioChanged(object obj)
{
if (obj == null) return;
if (obj is Tuple<double, double, double> item) {
ShowMagnificationWindow(item.Item1, item.Item2, item.Item3); // centerX, centerY, ratio
}
}
protected void OnMagWindowClosed(object sender, EventArgs e)
{
MagWindow = null;
MagContent = null;
}
protected VisualBrush GetCaptureVisualBrush(double startPosX, double startPosY, double width, double height)
{
// AuroraCanvas를 VisualBrush로 저장
VisualBrush brush = new VisualBrush();
brush.Visual = this;
// To show a specific part of the Canvas, set the Viewbox property of the VisualBrush
brush.Viewbox = new Rect(startPosX, startPosY, width, height);
brush.ViewboxUnits = BrushMappingMode.Absolute;
return brush;
}
#endregion
}
}