391 lines
14 KiB
C#
391 lines
14 KiB
C#
using System;
|
||
using System.Windows.Forms;
|
||
using System.IO;
|
||
using System.Diagnostics;
|
||
using System.Threading.Tasks;
|
||
using netDxf;
|
||
using netDxf.Entities;
|
||
using System.Drawing;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Configuration;
|
||
|
||
namespace GeneralDWGViewer
|
||
{
|
||
public partial class Form1 : Form
|
||
{
|
||
private string? odaConverterPath;
|
||
private List<EntityObject> entities = new List<EntityObject>();
|
||
private float scale = 1.0f;
|
||
private PointF pan = new PointF(0, 0);
|
||
private bool isPanning = false;
|
||
private System.Drawing.Point lastMousePosition;
|
||
|
||
public Form1()
|
||
{
|
||
InitializeComponent();
|
||
this.DoubleBuffered = true;
|
||
this.MouseWheel += Form1_MouseWheel;
|
||
this.MouseDown += Form1_MouseDown;
|
||
this.MouseMove += Form1_MouseMove;
|
||
this.MouseUp += Form1_MouseUp;
|
||
InitializeOdaConverterPath();
|
||
}
|
||
|
||
private void InitializeOdaConverterPath()
|
||
{
|
||
odaConverterPath = Environment.GetEnvironmentVariable("ODA_CONVERTER_PATH");
|
||
|
||
if (string.IsNullOrEmpty(odaConverterPath))
|
||
{
|
||
odaConverterPath = ConfigurationManager.AppSettings["OdaConverterPath"];
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(odaConverterPath))
|
||
{
|
||
string[] commonPaths = new string[]
|
||
{
|
||
@"C:\Program Files\ODA\ODAFileConverter 25.8.0\ODAFileConverter.exe",
|
||
@"C:\Program Files\ODA\ODAFileConverter\ODAFileConverter.exe",
|
||
@"C:\Program Files (x86)\ODA\ODAFileConverter\ODAFileConverter.exe"
|
||
};
|
||
|
||
foreach (string path in commonPaths)
|
||
{
|
||
if (File.Exists(path))
|
||
{
|
||
odaConverterPath = path;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(odaConverterPath))
|
||
{
|
||
PromptInstallOdaConverter();
|
||
}
|
||
}
|
||
|
||
private void PromptInstallOdaConverter()
|
||
{
|
||
DialogResult result = MessageBox.Show(
|
||
"ODA File Converter is not found on your system. This software is required to open DWG files.\n\n" +
|
||
"Would you like to visit the ODA website to download and install ODA File Converter?",
|
||
"ODA File Converter Not Found",
|
||
MessageBoxButtons.YesNo,
|
||
MessageBoxIcon.Question
|
||
);
|
||
|
||
if (result == DialogResult.Yes)
|
||
{
|
||
// <20><><EFBFBD><EFBFBD> ODA <20><>վ
|
||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||
{
|
||
FileName = "https://www.opendesign.com/guestfiles/oda_file_converter",
|
||
UseShellExecute = true
|
||
});
|
||
|
||
MessageBox.Show(
|
||
"After installing ODA File Converter, please restart this application.\n\n" +
|
||
"If the application still cannot find ODA File Converter, you can manually set the path in the application settings.",
|
||
"Installation Instructions",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information
|
||
);
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show(
|
||
"Without ODA File Converter, you will not be able to open DWG files.\n" +
|
||
"You can still use this application to view DXF files.",
|
||
"ODA File Converter Not Installed",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Warning
|
||
);
|
||
}
|
||
}
|
||
|
||
private void Form1_MouseWheel(object? sender, MouseEventArgs e)
|
||
{
|
||
float oldScale = scale;
|
||
if (e.Delta > 0)
|
||
scale *= 1.1f;
|
||
else
|
||
scale /= 1.1f;
|
||
|
||
if (Math.Abs(scale) < 0.0001f)
|
||
{
|
||
scale = 0.0001f * Math.Sign(scale);
|
||
}
|
||
|
||
PointF mousePos = e.Location;
|
||
pan.X = mousePos.X - (mousePos.X - pan.X) * scale / oldScale;
|
||
pan.Y = mousePos.Y - (mousePos.Y - pan.Y) * scale / oldScale;
|
||
|
||
this.Invalidate();
|
||
}
|
||
|
||
#nullable disable
|
||
private void Form1_MouseDown(object sender, MouseEventArgs e)
|
||
{
|
||
if (e.Button == MouseButtons.Left)
|
||
{
|
||
isPanning = true;
|
||
lastMousePosition = e.Location;
|
||
Cursor = Cursors.Hand;
|
||
}
|
||
}
|
||
|
||
private void Form1_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
if (isPanning)
|
||
{
|
||
int dx = e.X - lastMousePosition.X;
|
||
int dy = e.Y - lastMousePosition.Y;
|
||
pan.X += dx;
|
||
pan.Y += dy;
|
||
lastMousePosition = e.Location;
|
||
this.Invalidate();
|
||
}
|
||
}
|
||
|
||
private void Form1_MouseUp(object sender, MouseEventArgs e)
|
||
{
|
||
if (e.Button == MouseButtons.Left)
|
||
{
|
||
isPanning = false;
|
||
Cursor = Cursors.Default;
|
||
}
|
||
}
|
||
#nullable enable
|
||
|
||
private void loadButton_Click(object sender, EventArgs e)
|
||
{
|
||
using (OpenFileDialog openFileDialog = new OpenFileDialog())
|
||
{
|
||
openFileDialog.Filter = "CAD files (*.dwg;*.dxf)|*.dwg;*.dxf|All files (*.*)|*.*";
|
||
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
string filePath = openFileDialog.FileName;
|
||
if (Path.GetExtension(filePath).ToLower() == ".dwg" && string.IsNullOrEmpty(odaConverterPath))
|
||
{
|
||
MessageBox.Show(
|
||
"ODA File Converter is not installed. You cannot open DWG files.\n" +
|
||
"Please install ODA File Converter and restart the application.",
|
||
"Cannot Open DWG File",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Error
|
||
);
|
||
return;
|
||
}
|
||
_ = ProcessCadFileAsync(filePath);
|
||
}
|
||
}
|
||
}
|
||
|
||
private async Task ProcessCadFileAsync(string filePath)
|
||
{
|
||
try
|
||
{
|
||
loadButton.Enabled = false;
|
||
progressBar.Visible = true;
|
||
|
||
string fileExtension = Path.GetExtension(filePath).ToLower();
|
||
string dxfFilePath = filePath;
|
||
|
||
if (fileExtension == ".dwg")
|
||
{
|
||
dxfFilePath = await ConvertDwgToDxfAsync(filePath);
|
||
}
|
||
|
||
await ProcessDxfFileAsync(dxfFilePath);
|
||
|
||
if (fileExtension == ".dwg")
|
||
{
|
||
File.Delete(dxfFilePath);
|
||
}
|
||
|
||
CalculateScaleAndPan();
|
||
this.Invalidate();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"Error processing file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
finally
|
||
{
|
||
loadButton.Enabled = true;
|
||
progressBar.Visible = false;
|
||
}
|
||
}
|
||
|
||
private async Task<string> ConvertDwgToDxfAsync(string dwgFilePath)
|
||
{
|
||
if (string.IsNullOrEmpty(odaConverterPath) || !File.Exists(odaConverterPath))
|
||
{
|
||
throw new Exception("ODA File Converter not found. Please set the correct path.");
|
||
}
|
||
|
||
string dxfFilePath = Path.ChangeExtension(dwgFilePath, ".dxf");
|
||
string outputDir = Path.GetDirectoryName(dxfFilePath) ?? string.Empty;
|
||
|
||
ProcessStartInfo psi = new ProcessStartInfo
|
||
{
|
||
FileName = odaConverterPath,
|
||
Arguments = $"\"{Path.GetDirectoryName(dwgFilePath)}\" \"{outputDir}\" \"ACAD2018\" \"DXF\" \"0\" \"1\" \"{Path.GetFileName(dwgFilePath)}\"",
|
||
CreateNoWindow = true,
|
||
UseShellExecute = false
|
||
};
|
||
|
||
using (Process process = new Process { StartInfo = psi })
|
||
{
|
||
process.Start();
|
||
await process.WaitForExitAsync();
|
||
}
|
||
|
||
if (!File.Exists(dxfFilePath))
|
||
{
|
||
throw new Exception("Failed to convert DWG to DXF");
|
||
}
|
||
|
||
return dxfFilePath;
|
||
}
|
||
|
||
private async Task ProcessDxfFileAsync(string filePath)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
DxfDocument dxf = DxfDocument.Load(filePath);
|
||
entities.Clear();
|
||
entities.AddRange(dxf.Entities.Lines);
|
||
entities.AddRange(dxf.Entities.Circles);
|
||
entities.AddRange(dxf.Entities.Arcs);
|
||
entities.AddRange(dxf.Entities.Polylines2D);
|
||
entities.AddRange(dxf.Entities.Polylines3D);
|
||
});
|
||
}
|
||
|
||
private void CalculateScaleAndPan()
|
||
{
|
||
if (entities.Count == 0) return;
|
||
|
||
double minX = double.MaxValue, minY = double.MaxValue;
|
||
double maxX = double.MinValue, maxY = double.MinValue;
|
||
|
||
foreach (var entity in entities)
|
||
{
|
||
UpdateBoundingBox(entity, ref minX, ref minY, ref maxX, ref maxY);
|
||
}
|
||
|
||
double width = maxX - minX;
|
||
double height = maxY - minY;
|
||
float scaleX = (this.ClientSize.Width - 20) / (float)width;
|
||
float scaleY = (this.ClientSize.Height - 20) / (float)height;
|
||
scale = Math.Min(scaleX, scaleY);
|
||
|
||
if (Math.Abs(scale) < 0.0001f)
|
||
{
|
||
scale = 0.0001f * Math.Sign(scale);
|
||
}
|
||
|
||
pan = new PointF(
|
||
(float)(-minX * scale + (this.ClientSize.Width - (float)width * scale) / 2),
|
||
(float)(maxY * scale + (this.ClientSize.Height - (float)height * scale) / 2)
|
||
);
|
||
}
|
||
|
||
private void UpdateBoundingBox(EntityObject entity, ref double minX, ref double minY, ref double maxX, ref double maxY)
|
||
{
|
||
switch (entity)
|
||
{
|
||
case Line line:
|
||
UpdateMinMax(line.StartPoint.X, line.StartPoint.Y, ref minX, ref minY, ref maxX, ref maxY);
|
||
UpdateMinMax(line.EndPoint.X, line.EndPoint.Y, ref minX, ref minY, ref maxX, ref maxY);
|
||
break;
|
||
case Circle circle:
|
||
UpdateMinMax(circle.Center.X - circle.Radius, circle.Center.Y - circle.Radius, ref minX, ref minY, ref maxX, ref maxY);
|
||
UpdateMinMax(circle.Center.X + circle.Radius, circle.Center.Y + circle.Radius, ref minX, ref minY, ref maxX, ref maxY);
|
||
break;
|
||
case Arc arc:
|
||
UpdateMinMax(arc.Center.X - arc.Radius, arc.Center.Y - arc.Radius, ref minX, ref minY, ref maxX, ref maxY);
|
||
UpdateMinMax(arc.Center.X + arc.Radius, arc.Center.Y + arc.Radius, ref minX, ref minY, ref maxX, ref maxY);
|
||
break;
|
||
case Polyline2D polyline:
|
||
foreach (var vertex in polyline.Vertexes)
|
||
{
|
||
UpdateMinMax(vertex.Position.X, vertex.Position.Y, ref minX, ref minY, ref maxX, ref maxY);
|
||
}
|
||
break;
|
||
case Polyline3D polyline:
|
||
foreach (var vertex in polyline.Vertexes)
|
||
{
|
||
UpdateMinMax(vertex.X, vertex.Y, ref minX, ref minY, ref maxX, ref maxY);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void UpdateMinMax(double x, double y, ref double minX, ref double minY, ref double maxX, ref double maxY)
|
||
{
|
||
minX = Math.Min(minX, x);
|
||
minY = Math.Min(minY, y);
|
||
maxX = Math.Max(maxX, x);
|
||
maxY = Math.Max(maxY, y);
|
||
}
|
||
|
||
protected override void OnPaint(PaintEventArgs e)
|
||
{
|
||
base.OnPaint(e);
|
||
|
||
e.Graphics.Clear(Color.White);
|
||
e.Graphics.ResetTransform();
|
||
e.Graphics.TranslateTransform(pan.X, pan.Y);
|
||
|
||
if (Math.Abs(scale) < 0.0001f)
|
||
{
|
||
scale = 0.0001f * Math.Sign(scale);
|
||
}
|
||
e.Graphics.ScaleTransform(scale, -scale);
|
||
|
||
foreach (var entity in entities)
|
||
{
|
||
DrawEntity(entity, e.Graphics);
|
||
}
|
||
}
|
||
|
||
private void DrawEntity(EntityObject entity, Graphics g)
|
||
{
|
||
using (Pen pen = new Pen(Color.Black, 1 / scale))
|
||
{
|
||
switch (entity)
|
||
{
|
||
case Line line:
|
||
g.DrawLine(pen, (float)line.StartPoint.X, (float)line.StartPoint.Y, (float)line.EndPoint.X, (float)line.EndPoint.Y);
|
||
break;
|
||
case Circle circle:
|
||
float diameter = (float)(circle.Radius * 2);
|
||
g.DrawEllipse(pen, (float)(circle.Center.X - circle.Radius), (float)(circle.Center.Y - circle.Radius), diameter, diameter);
|
||
break;
|
||
case Arc arc:
|
||
RectangleF rect = new RectangleF((float)(arc.Center.X - arc.Radius), (float)(arc.Center.Y - arc.Radius), (float)(arc.Radius * 2), (float)(arc.Radius * 2));
|
||
g.DrawArc(pen, rect, (float)arc.StartAngle, (float)(arc.EndAngle - arc.StartAngle));
|
||
break;
|
||
case Polyline2D polyline:
|
||
DrawPolyline(g, pen, polyline.Vertexes.Select(v => new PointF((float)v.Position.X, (float)v.Position.Y)).ToArray());
|
||
break;
|
||
case Polyline3D polyline:
|
||
DrawPolyline(g, pen, polyline.Vertexes.Select(v => new PointF((float)v.X, (float)v.Y)).ToArray());
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void DrawPolyline(Graphics g, Pen pen, PointF[] points)
|
||
{
|
||
if (points.Length > 1)
|
||
{
|
||
g.DrawLines(pen, points);
|
||
}
|
||
}
|
||
}
|
||
} |