1822 lines
68 KiB
C#
1822 lines
68 KiB
C#
using SolidWorks.Interop.sldworks;
|
|
using SolidWorks.Interop.swconst;
|
|
using System.Globalization;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
namespace SolidWorksHoverProbe;
|
|
|
|
internal static class Program
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = false };
|
|
private static string _lastSignature = "";
|
|
private static int? _lastFeatureManagerWidthBeforeNormalize;
|
|
private static int? _lastFeatureManagerWidthAfterNormalize;
|
|
private static SldWorks _swApp;
|
|
private static ModelDoc2 _activeDoc;
|
|
private static DrawingDoc _drawingDoc;
|
|
private static DDrawingDocEvents_Event _drawingEvents;
|
|
private static DDrawingDocEvents_DynamicHighlightNotifyEventHandler _dynamicHighlightHandler;
|
|
private static DMouseEvents_Event _mouseEvents;
|
|
private static DMouseEvents_MouseMoveNotifyEventHandler _mouseMoveHandler;
|
|
private static bool _suppressEventProbeOutput;
|
|
|
|
[DllImport("ole32.dll", CharSet = CharSet.Unicode)]
|
|
private static extern int CLSIDFromProgID(string progId, out Guid clsid);
|
|
|
|
[DllImport("oleaut32.dll", PreserveSig = false)]
|
|
[return: MarshalAs(UnmanagedType.IUnknown)]
|
|
private static extern object GetActiveObject(ref Guid clsid, IntPtr reserved);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool GetCursorPos(out Point point);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool SetCursorPos(int x, int y);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool ClientToScreen(IntPtr hWnd, ref Point point);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool GetClientRect(IntPtr hWnd, out Rect rect);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool BringWindowToTop(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool IsIconic(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern short GetAsyncKeyState(int vKey);
|
|
|
|
private const int VkEscape = 0x1B;
|
|
private const int VkControl = 0x11;
|
|
private const uint WmMouseMove = 0x0200;
|
|
private const int SwRestore = 9;
|
|
private const uint SwpNoSize = 0x0001;
|
|
private const uint SwpNoMove = 0x0002;
|
|
private const uint SwpShowWindow = 0x0040;
|
|
private static readonly IntPtr HwndTopMost = new(-1);
|
|
private static readonly IntPtr HwndNoTopMost = new(-2);
|
|
|
|
[STAThread]
|
|
private static int Main(string[] args)
|
|
{
|
|
try
|
|
{
|
|
var sw = ConnectSolidWorks();
|
|
_swApp = sw;
|
|
_activeDoc = sw.ActiveDoc as ModelDoc2
|
|
?? throw new InvalidOperationException("SolidWorks does not have an active document.");
|
|
|
|
if (Safe(() => _activeDoc.GetType(), 0) != (int)swDocumentTypes_e.swDocDRAWING)
|
|
_activeDoc = FindFirstOpenDrawing(sw)
|
|
?? throw new InvalidOperationException("No open SolidWorks drawing found. Please open or activate a SLDDRW window first.");
|
|
|
|
var activateErrors = 0;
|
|
Safe(() => sw.ActivateDoc3(_activeDoc.GetTitle(), false, (int)swRebuildOnActivation_e.swDontRebuildActiveDoc, ref activateErrors), null);
|
|
|
|
_drawingDoc = _activeDoc as DrawingDoc
|
|
?? throw new InvalidOperationException("Active document cannot be cast to DrawingDoc.");
|
|
|
|
if (args.Any(a => string.Equals(a, "--hover-grid", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
RunGridProbe(ParseGridOptions(args));
|
|
return 0;
|
|
}
|
|
|
|
if (args.Any(a => string.Equals(a, "--draw-ownership-contours", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
DrawOwnershipContours(ParseContourOptions(args));
|
|
return 0;
|
|
}
|
|
|
|
if (args.Any(a => string.Equals(a, "--print-sheet-origin", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
NormalizeDrawingView(ParseZoomFitDelayMs(args));
|
|
PrintSheetOriginCalibration();
|
|
return 0;
|
|
}
|
|
|
|
if (args.Any(a => string.Equals(a, "--move-sheet-point", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
RunMoveSheetPoint(args);
|
|
return 0;
|
|
}
|
|
|
|
if (args.Any(a => string.Equals(a, "--probe-sheet-point", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
RunProbeSheetPoint(args);
|
|
return 0;
|
|
}
|
|
|
|
if (args.Any(a => string.Equals(a, "--manual-hover-timing", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
RunManualHoverTiming(args);
|
|
return 0;
|
|
}
|
|
|
|
if (args.Any(a => string.Equals(a, "--legacy-hover-probe", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
RunLegacyHoverProbe(args);
|
|
return 0;
|
|
}
|
|
|
|
var pollMs = args.Length > 0 && int.TryParse(args[0], out var parsed) ? Math.Max(parsed, 50) : 200;
|
|
RunLegacyHoverProbe(args, pollMs);
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.Error.WriteLine(ex);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
private static void RunLegacyHoverProbe(string[] args, int? positionalPollMs = null)
|
|
{
|
|
var pollMs = positionalPollMs ?? ParseIntArg(args, "--poll-ms", 200, min: 50);
|
|
var durationMs = ParseIntArg(args, "--duration-ms", 0, min: 0);
|
|
var started = DateTime.UtcNow;
|
|
|
|
HookEvents();
|
|
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
Console.WriteLine("SolidWorks legacy hover probe is running.");
|
|
Console.WriteLine("Move the mouse manually over a drawing entity without clicking.");
|
|
Console.WriteLine(durationMs > 0 ? $"Will stop after {durationMs} ms." : "Press Ctrl+C to stop.");
|
|
Console.WriteLine();
|
|
|
|
while (durationMs <= 0 || (DateTime.UtcNow - started).TotalMilliseconds < durationMs)
|
|
{
|
|
PrintProbe("poll");
|
|
Thread.Sleep(pollMs);
|
|
}
|
|
|
|
PrintProbe("final");
|
|
Console.WriteLine("SolidWorks legacy hover probe finished.");
|
|
}
|
|
|
|
private static void RunGridProbe(GridOptions options)
|
|
{
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
|
|
BringSolidWorksToFront();
|
|
NormalizeDrawingView(options.ZoomFitDelayMs);
|
|
_suppressEventProbeOutput = true;
|
|
HookEvents();
|
|
|
|
var sheet = _drawingDoc.GetCurrentSheet() as Sheet
|
|
?? throw new InvalidOperationException("Cannot get current drawing sheet.");
|
|
|
|
double width = 0;
|
|
double height = 0;
|
|
_ = sheet.GetSize(ref width, ref height);
|
|
if (width <= 0 || height <= 0)
|
|
throw new InvalidOperationException($"Invalid sheet size: width={width}, height={height}");
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(options.OutputPath))!);
|
|
|
|
var step = options.StepMm / 1000.0;
|
|
var columns = (int)Math.Floor(width / step) + 1;
|
|
var rows = (int)Math.Floor(height / step) + 1;
|
|
var scanBounds = ReadDrawingViewBounds().ToList();
|
|
if (!string.IsNullOrWhiteSpace(options.ViewName))
|
|
scanBounds = scanBounds
|
|
.Where(b => string.Equals(b.Name, options.ViewName, StringComparison.OrdinalIgnoreCase))
|
|
.ToList();
|
|
var total = CountGridPointsToVisit(rows, columns, step, width, height, scanBounds, options);
|
|
var written = 0L;
|
|
var hits = 0L;
|
|
var visited = 0L;
|
|
var stoppedByEsc = false;
|
|
var started = DateTime.Now;
|
|
|
|
Console.WriteLine("SolidWorks hover-grid ownership probe is running.");
|
|
Console.WriteLine($"zoom_fit_applied=true zoom_fit_delay_ms={options.ZoomFitDelayMs}");
|
|
Console.WriteLine($"feature_manager_width_before={_lastFeatureManagerWidthBeforeNormalize?.ToString() ?? "unknown"} after={_lastFeatureManagerWidthAfterNormalize?.ToString() ?? "unknown"}");
|
|
Console.WriteLine($"sheet={Safe(() => sheet.GetName(), "")}");
|
|
Console.WriteLine($"size_mm=({width * 1000.0:F3},{height * 1000.0:F3}) step_mm={options.StepMm:G6}");
|
|
if (!string.IsNullOrWhiteSpace(options.ViewName))
|
|
Console.WriteLine($"view_name={options.ViewName}");
|
|
if (!double.IsNaN(options.RowYMm))
|
|
Console.WriteLine($"row_y_mm={options.RowYMm:G6}");
|
|
Console.WriteLine($"reverse_x={options.ReverseX}");
|
|
Console.WriteLine($"sheet_correction_mm=({options.SheetCorrectionXM * 1000.0:G6},{options.SheetCorrectionYM * 1000.0:G6})");
|
|
foreach (var item in options.ViewSheetCorrections.OrderBy(item => item.Key, StringComparer.OrdinalIgnoreCase))
|
|
Console.WriteLine($"view_sheet_correction_mm[{item.Key}]=({item.Value.XM * 1000.0:G6},{item.Value.YM * 1000.0:G6})");
|
|
Console.WriteLine($"clear_before_point={options.ClearBeforePoint} clear_after_point={options.ClearAfterPoint} clear_delay_ms={options.ClearDelayMs}");
|
|
Console.WriteLine($"points={total} views_only={options.ViewsOnly} output={Path.GetFullPath(options.OutputPath)}");
|
|
foreach (var bound in scanBounds)
|
|
Console.WriteLine($"view={bound.Name} outline_mm=({bound.XMin * 1000.0:F3},{bound.YMin * 1000.0:F3},{bound.XMax * 1000.0:F3},{bound.YMax * 1000.0:F3})");
|
|
Console.WriteLine("Press Esc to stop after the current point, even when SolidWorks has focus.");
|
|
Console.WriteLine();
|
|
|
|
using var writer = new StreamWriter(options.OutputPath, false, new UTF8Encoding(false));
|
|
WriteJsonLine(writer, new GridHeaderRecord(
|
|
"header",
|
|
Safe(() => _activeDoc.GetTitle(), ""),
|
|
Safe(() => sheet.GetName(), ""),
|
|
width,
|
|
height,
|
|
options.StepMm,
|
|
columns,
|
|
rows));
|
|
|
|
for (var row = 0; row < rows; row++)
|
|
{
|
|
var y = Math.Min(row * step, height);
|
|
if (!ShouldVisitGridRow(y, options))
|
|
continue;
|
|
|
|
foreach (var column in EnumerateColumns(columns, options.ReverseX))
|
|
{
|
|
if (ShouldStopByEsc())
|
|
{
|
|
stoppedByEsc = true;
|
|
break;
|
|
}
|
|
|
|
var x = Math.Min(column * step, width);
|
|
var scanView = FindContainingView(scanBounds, x, y);
|
|
if (options.ViewsOnly && scanView == null)
|
|
continue;
|
|
|
|
if (options.ClearBeforePoint)
|
|
ClearHoverState(options.ClearDelayMs);
|
|
|
|
var correction = ResolveSheetCorrection(options, scanView?.Name);
|
|
var record = ProbeHoverGridPoint(
|
|
row,
|
|
column,
|
|
x,
|
|
y,
|
|
options.HoverDelayMs,
|
|
options.PollMs,
|
|
correction.XM,
|
|
correction.YM,
|
|
scanView?.Name ?? "");
|
|
visited++;
|
|
|
|
if (record.Hit)
|
|
hits++;
|
|
|
|
if (options.IncludeEmpty || record.Hit)
|
|
{
|
|
WriteJsonLine(writer, record);
|
|
written++;
|
|
}
|
|
|
|
if (options.ClearAfterPoint)
|
|
ClearHoverState(options.ClearDelayMs);
|
|
|
|
if (options.MaxPoints > 0 && visited >= options.MaxPoints)
|
|
break;
|
|
}
|
|
|
|
if (row % Math.Max(1, options.ProgressEveryRows) == 0 || row == rows - 1)
|
|
{
|
|
var elapsed = DateTime.Now - started;
|
|
Console.WriteLine($"row={row + 1}/{rows} visited={visited}/{total} hits={hits} written={written} elapsed={elapsed:hh\\:mm\\:ss}");
|
|
}
|
|
|
|
if (stoppedByEsc || options.MaxPoints > 0 && visited >= options.MaxPoints)
|
|
break;
|
|
}
|
|
|
|
_activeDoc.ClearSelection2(true);
|
|
Console.WriteLine();
|
|
Console.WriteLine($"done visited={visited} hits={hits} written={written} stopped_by_esc={stoppedByEsc}");
|
|
}
|
|
|
|
private static bool ShouldStopByEsc()
|
|
{
|
|
if ((GetAsyncKeyState(VkEscape) & 0x8000) != 0)
|
|
return true;
|
|
|
|
try
|
|
{
|
|
if (!Console.KeyAvailable)
|
|
return false;
|
|
|
|
var stop = false;
|
|
while (Console.KeyAvailable)
|
|
{
|
|
var key = Console.ReadKey(intercept: true);
|
|
if (key.Key == ConsoleKey.Escape)
|
|
stop = true;
|
|
}
|
|
|
|
return stop;
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static void ClearHoverState(int clearDelayMs)
|
|
{
|
|
Safe(() =>
|
|
{
|
|
_activeDoc.ClearSelection2(true);
|
|
return true;
|
|
}, false);
|
|
|
|
var clearPoint = GetHoverClearPoint();
|
|
if (clearPoint != null)
|
|
MoveMouseAndNotify(clearPoint);
|
|
|
|
WaitForHoverClear(clearDelayMs);
|
|
}
|
|
|
|
private static void WaitForHoverClear(int clearDelayMs)
|
|
{
|
|
var timeoutMs = Math.Max(1, clearDelayMs);
|
|
var selectionMgr = _activeDoc.SelectionManager as SelectionMgr;
|
|
var started = DateTime.UtcNow;
|
|
|
|
while (true)
|
|
{
|
|
var preselected = Safe(() => selectionMgr.GetPreSelectedObject(), null);
|
|
if (!ReadEntityOwnership(preselected, null).IsEntity)
|
|
return;
|
|
|
|
var waitedMs = (int)Math.Round((DateTime.UtcNow - started).TotalMilliseconds);
|
|
if (waitedMs >= timeoutMs)
|
|
return;
|
|
|
|
Thread.Sleep(Math.Min(5, timeoutMs - waitedMs));
|
|
}
|
|
}
|
|
|
|
private static void RunManualHoverTiming(string[] args)
|
|
{
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
|
|
var pollMs = ParseIntArg(args, "--poll-ms", 10, min: 1);
|
|
var timeoutMs = ParseIntArg(args, "--timeout-ms", 10000, min: 1);
|
|
|
|
Console.WriteLine("Manual hover timing probe.");
|
|
Console.WriteLine("Put the mouse on a SolidWorks drawing entity.");
|
|
Console.WriteLine("Press Ctrl to start timing, or Esc to cancel.");
|
|
|
|
while (IsKeyDown(VkControl))
|
|
Thread.Sleep(10);
|
|
|
|
while (!IsKeyDown(VkControl))
|
|
{
|
|
if (IsKeyDown(VkEscape))
|
|
{
|
|
Console.WriteLine("cancelled");
|
|
return;
|
|
}
|
|
|
|
Thread.Sleep(5);
|
|
}
|
|
|
|
var started = DateTime.UtcNow;
|
|
var attempts = 0;
|
|
object firstObject = null;
|
|
EntityOwnership firstOwnership = null;
|
|
|
|
while ((DateTime.UtcNow - started).TotalMilliseconds <= timeoutMs)
|
|
{
|
|
attempts++;
|
|
var selectionMgr = _activeDoc.SelectionManager as SelectionMgr;
|
|
var preselected = Safe(() => selectionMgr.GetPreSelectedObject(), null);
|
|
var ownership = ReadEntityOwnership(preselected, null);
|
|
|
|
if (ownership.IsEntity)
|
|
{
|
|
firstObject = preselected;
|
|
firstOwnership = ownership;
|
|
break;
|
|
}
|
|
|
|
Thread.Sleep(pollMs);
|
|
}
|
|
|
|
var elapsedMs = (DateTime.UtcNow - started).TotalMilliseconds;
|
|
GetCursorPos(out var cursor);
|
|
|
|
Console.WriteLine($"elapsed_ms={elapsedMs:F1}");
|
|
Console.WriteLine($"attempts={attempts}");
|
|
Console.WriteLine($"cursor_screen=({cursor.X},{cursor.Y})");
|
|
|
|
if (firstOwnership == null)
|
|
{
|
|
Console.WriteLine("hit=false");
|
|
var probe = ReadProbe("manual-hover-timeout");
|
|
foreach (var line in probe.Lines)
|
|
Console.WriteLine(line);
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine("hit=true");
|
|
Console.WriteLine("object=" + DescribeObject(firstObject));
|
|
Console.WriteLine("entity_component=" + firstOwnership.EntityComponent);
|
|
foreach (var drawingComponent in firstOwnership.DrawingComponents)
|
|
Console.WriteLine($"drawing_component[{drawingComponent.View}]=" + drawingComponent.DrawingComponent);
|
|
}
|
|
|
|
private static void DrawOwnershipContours(ContourOptions options)
|
|
{
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
if (!File.Exists(options.InputPath))
|
|
throw new FileNotFoundException($"ownership grid file not found: {options.InputPath}");
|
|
|
|
var groups = ReadOwnershipGroups(options).ToList();
|
|
if (groups.Count == 0)
|
|
throw new InvalidOperationException($"No ownership groups with at least {options.MinGroupPoints} points: {options.InputPath}");
|
|
|
|
var sheet = _drawingDoc.GetCurrentSheet() as Sheet
|
|
?? throw new InvalidOperationException("Cannot get current drawing sheet.");
|
|
var sheetName = Safe(() => sheet.GetName(), "");
|
|
if (!string.IsNullOrWhiteSpace(sheetName))
|
|
Safe(() => _drawingDoc.ActivateSheet(sheetName), false);
|
|
|
|
_activeDoc.ClearSelection2(true);
|
|
var sketch = _activeDoc.SketchManager
|
|
?? throw new InvalidOperationException("Cannot get SketchManager.");
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(options.OutputPath))!);
|
|
var drawn = new List<ContourRecord>();
|
|
foreach (var group in groups)
|
|
{
|
|
var minX = group.Points.Min(point => point.XM) - options.PadMm / 1000.0;
|
|
var minY = group.Points.Min(point => point.YM) - options.PadMm / 1000.0;
|
|
var maxX = group.Points.Max(point => point.XM) + options.PadMm / 1000.0;
|
|
var maxY = group.Points.Max(point => point.YM) + options.PadMm / 1000.0;
|
|
|
|
Safe(() => sketch.CreateLine(minX, minY, 0, maxX, minY, 0), null);
|
|
Safe(() => sketch.CreateLine(maxX, minY, 0, maxX, maxY, 0), null);
|
|
Safe(() => sketch.CreateLine(maxX, maxY, 0, minX, maxY, 0), null);
|
|
Safe(() => sketch.CreateLine(minX, maxY, 0, minX, minY, 0), null);
|
|
|
|
drawn.Add(new ContourRecord(
|
|
group.Key,
|
|
group.DisplayName,
|
|
group.Points.Count,
|
|
minX,
|
|
minY,
|
|
maxX,
|
|
maxY,
|
|
minX * 1000.0,
|
|
minY * 1000.0,
|
|
maxX * 1000.0,
|
|
maxY * 1000.0));
|
|
}
|
|
|
|
Safe(() => _activeDoc.ForceRebuild3(false), false);
|
|
File.WriteAllText(options.OutputPath, JsonSerializer.Serialize(new
|
|
{
|
|
input = Path.GetFullPath(options.InputPath),
|
|
output = Path.GetFullPath(options.OutputPath),
|
|
sheet = sheetName,
|
|
group_count = drawn.Count,
|
|
min_group_points = options.MinGroupPoints,
|
|
pad_mm = options.PadMm,
|
|
contours = drawn
|
|
}, new JsonSerializerOptions { WriteIndented = true }));
|
|
|
|
Console.WriteLine($"drawn_contours={drawn.Count}");
|
|
Console.WriteLine($"output={Path.GetFullPath(options.OutputPath)}");
|
|
foreach (var item in drawn.Take(20))
|
|
Console.WriteLine($"{item.PointCount} pts bbox_mm=({item.MinXMm:F3},{item.MinYMm:F3},{item.MaxXMm:F3},{item.MaxYMm:F3}) {item.DisplayName}");
|
|
}
|
|
|
|
private static IEnumerable<OwnershipGroup> ReadOwnershipGroups(ContourOptions options)
|
|
{
|
|
var groups = new Dictionary<string, OwnershipGroup>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var line in File.ReadLines(options.InputPath, Encoding.UTF8))
|
|
{
|
|
if (string.IsNullOrWhiteSpace(line))
|
|
continue;
|
|
|
|
using var doc = JsonDocument.Parse(line);
|
|
var root = doc.RootElement;
|
|
if (!string.Equals(ReadJsonString(root, "Kind"), "point", StringComparison.OrdinalIgnoreCase) &&
|
|
!string.Equals(ReadJsonString(root, "kind"), "point", StringComparison.OrdinalIgnoreCase))
|
|
continue;
|
|
if (!ReadJsonBool(root, "Hit") && !ReadJsonBool(root, "hit"))
|
|
continue;
|
|
|
|
var ownership = root.TryGetProperty("Ownership", out var ownershipPascal)
|
|
? ownershipPascal
|
|
: root.TryGetProperty("ownership", out var ownershipCamel) ? ownershipCamel : default;
|
|
if (ownership.ValueKind != JsonValueKind.Object)
|
|
continue;
|
|
|
|
var entityComponent = FirstNonEmpty(
|
|
ReadJsonString(ownership, "EntityComponent"),
|
|
ReadJsonString(ownership, "entity_component"));
|
|
if (string.IsNullOrWhiteSpace(entityComponent) || string.Equals(entityComponent, "null", StringComparison.OrdinalIgnoreCase))
|
|
continue;
|
|
|
|
var drawingComponent = FirstNonEmptyDrawingComponent(ownership);
|
|
var key = options.GroupByDrawingComponent
|
|
? $"{entityComponent}|{drawingComponent}"
|
|
: entityComponent;
|
|
var displayName = options.GroupByDrawingComponent && !string.IsNullOrWhiteSpace(drawingComponent)
|
|
? $"{entityComponent} / {drawingComponent}"
|
|
: entityComponent;
|
|
|
|
var x = ReadJsonDouble(root, "SheetXM", "sheet_xm");
|
|
var y = ReadJsonDouble(root, "SheetYM", "sheet_ym");
|
|
if (x == null || y == null)
|
|
continue;
|
|
|
|
if (!groups.TryGetValue(key, out var group))
|
|
{
|
|
group = new OwnershipGroup(key, displayName);
|
|
groups.Add(key, group);
|
|
}
|
|
|
|
group.Points.Add(new OwnershipPoint(x.Value, y.Value));
|
|
}
|
|
|
|
return groups.Values
|
|
.Where(group => group.Points.Count >= options.MinGroupPoints)
|
|
.OrderByDescending(group => group.Points.Count);
|
|
}
|
|
|
|
private static string FirstNonEmptyDrawingComponent(JsonElement ownership)
|
|
{
|
|
var components = ownership.TryGetProperty("DrawingComponents", out var pascal)
|
|
? pascal
|
|
: ownership.TryGetProperty("drawing_components", out var snake) ? snake : default;
|
|
if (components.ValueKind != JsonValueKind.Array)
|
|
return "";
|
|
|
|
foreach (var item in components.EnumerateArray())
|
|
{
|
|
var value = FirstNonEmpty(
|
|
ReadJsonString(item, "DrawingComponent"),
|
|
ReadJsonString(item, "drawing_component"));
|
|
if (!string.IsNullOrWhiteSpace(value) && !string.Equals(value, "null", StringComparison.OrdinalIgnoreCase))
|
|
return value;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
private static ContourOptions ParseContourOptions(string[] args)
|
|
{
|
|
var inputPath = "";
|
|
var outputPath = Path.Combine("runtime", "solidworks-hover-grid", "ownership-contours.json");
|
|
var minGroupPoints = 2;
|
|
var padMm = 2.0;
|
|
var groupByDrawingComponent = false;
|
|
|
|
for (var i = 0; i < args.Length; i++)
|
|
{
|
|
var arg = args[i];
|
|
if (string.Equals(arg, "--input", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
inputPath = args[++i];
|
|
else if (string.Equals(arg, "--output", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
outputPath = args[++i];
|
|
else if (string.Equals(arg, "--min-group-points", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
minGroupPoints = Math.Max(1, int.Parse(args[++i], CultureInfo.InvariantCulture));
|
|
else if (string.Equals(arg, "--pad-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
padMm = Math.Max(0.0, double.Parse(args[++i], CultureInfo.InvariantCulture));
|
|
else if (string.Equals(arg, "--group-by-drawing-component", StringComparison.OrdinalIgnoreCase))
|
|
groupByDrawingComponent = true;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(inputPath))
|
|
throw new ArgumentException("--draw-ownership-contours requires --input.");
|
|
return new ContourOptions(inputPath, outputPath, minGroupPoints, padMm, groupByDrawingComponent);
|
|
}
|
|
|
|
private static string FirstNonEmpty(params string[] values)
|
|
{
|
|
return values.FirstOrDefault(value => !string.IsNullOrWhiteSpace(value)) ?? "";
|
|
}
|
|
|
|
private static string ReadJsonString(JsonElement element, string propertyName)
|
|
{
|
|
return element.ValueKind == JsonValueKind.Object &&
|
|
element.TryGetProperty(propertyName, out var property) &&
|
|
property.ValueKind == JsonValueKind.String
|
|
? property.GetString() ?? ""
|
|
: "";
|
|
}
|
|
|
|
private static bool ReadJsonBool(JsonElement element, string propertyName)
|
|
{
|
|
return element.ValueKind == JsonValueKind.Object &&
|
|
element.TryGetProperty(propertyName, out var property) &&
|
|
property.ValueKind == JsonValueKind.True;
|
|
}
|
|
|
|
private static double? ReadJsonDouble(JsonElement element, params string[] propertyNames)
|
|
{
|
|
foreach (var propertyName in propertyNames)
|
|
{
|
|
if (element.ValueKind == JsonValueKind.Object &&
|
|
element.TryGetProperty(propertyName, out var property) &&
|
|
property.ValueKind == JsonValueKind.Number &&
|
|
property.TryGetDouble(out var value))
|
|
return value;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static void RunMoveSheetPoint(string[] args)
|
|
{
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
|
|
var sheetXMm = ParseDoubleArg(args, "--sheet-x-mm", double.NaN);
|
|
var sheetYMm = ParseDoubleArg(args, "--sheet-y-mm", double.NaN);
|
|
var holdMs = ParseIntArg(args, "--hold-ms", 10000, min: 0);
|
|
var zoomFitDelayMs = ParseZoomFitDelayMs(args);
|
|
var sheetCorrectionXM = ParseDoubleArg(args, "--sheet-correction-x-mm", 0.0) / 1000.0;
|
|
var sheetCorrectionYM = ParseDoubleArg(args, "--sheet-correction-y-mm", 0.0) / 1000.0;
|
|
if (double.IsNaN(sheetXMm) || double.IsNaN(sheetYMm))
|
|
throw new ArgumentException("--move-sheet-point requires --sheet-x-mm and --sheet-y-mm.");
|
|
|
|
BringSolidWorksToFront();
|
|
NormalizeDrawingView(zoomFitDelayMs);
|
|
|
|
var target = ProjectSheetPointToScreen(sheetXMm / 1000.0, sheetYMm / 1000.0, sheetCorrectionXM, sheetCorrectionYM);
|
|
if (target == null)
|
|
throw new InvalidOperationException("Cannot project sheet point to screen.");
|
|
|
|
MoveMouseForHover(target);
|
|
|
|
Console.WriteLine("Moved mouse to SolidWorks sheet point.");
|
|
Console.WriteLine($"sheet_mm=({sheetXMm:F3},{sheetYMm:F3})");
|
|
Console.WriteLine($"sheet_correction_mm=({sheetCorrectionXM * 1000.0:F3},{sheetCorrectionYM * 1000.0:F3})");
|
|
Console.WriteLine($"projected_sheet_mm=({sheetXMm + sheetCorrectionXM * 1000.0:F3},{sheetYMm + sheetCorrectionYM * 1000.0:F3})");
|
|
Console.WriteLine($"screen=({target.ScreenX},{target.ScreenY}) client=({target.ClientX},{target.ClientY})");
|
|
Console.WriteLine($"hold_ms={holdMs}");
|
|
Thread.Sleep(holdMs);
|
|
}
|
|
|
|
private static void RunProbeSheetPoint(string[] args)
|
|
{
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
|
|
var sheetXMm = ParseDoubleArg(args, "--sheet-x-mm", double.NaN);
|
|
var sheetYMm = ParseDoubleArg(args, "--sheet-y-mm", double.NaN);
|
|
var maxHoverWaitMs = ParseIntArg(args, "--max-hover-wait-ms", 10000, min: 0);
|
|
var pollMs = ParseIntArg(args, "--poll-ms", 50, min: 1);
|
|
var zoomFitDelayMs = ParseZoomFitDelayMs(args);
|
|
var sheetCorrectionXM = ParseDoubleArg(args, "--sheet-correction-x-mm", 0.0) / 1000.0;
|
|
var sheetCorrectionYM = ParseDoubleArg(args, "--sheet-correction-y-mm", 0.0) / 1000.0;
|
|
if (double.IsNaN(sheetXMm) || double.IsNaN(sheetYMm))
|
|
throw new ArgumentException("--probe-sheet-point requires --sheet-x-mm and --sheet-y-mm.");
|
|
|
|
BringSolidWorksToFront();
|
|
NormalizeDrawingView(zoomFitDelayMs);
|
|
|
|
var sheetX = sheetXMm / 1000.0;
|
|
var sheetY = sheetYMm / 1000.0;
|
|
var target = ProjectSheetPointToScreen(sheetX, sheetY, sheetCorrectionXM, sheetCorrectionYM);
|
|
if (target == null)
|
|
throw new InvalidOperationException("Cannot project sheet point to screen.");
|
|
|
|
HookEvents();
|
|
PrintProbe("before-move");
|
|
MoveMouseForHover(target);
|
|
|
|
var selectionMgr = _activeDoc.SelectionManager as SelectionMgr;
|
|
var started = DateTime.UtcNow;
|
|
var waitedMs = 0;
|
|
var polls = 0;
|
|
object preselected = null;
|
|
EntityOwnership ownership = null;
|
|
object firstPreselected = null;
|
|
EntityOwnership firstOwnership = null;
|
|
var firstHitMs = 0;
|
|
|
|
while (true)
|
|
{
|
|
polls++;
|
|
preselected = Safe(() => selectionMgr.GetPreSelectedObject(), null);
|
|
ownership = ReadEntityOwnership(preselected, null);
|
|
waitedMs = (int)Math.Round((DateTime.UtcNow - started).TotalMilliseconds);
|
|
if (ownership.IsEntity && firstOwnership == null)
|
|
{
|
|
firstPreselected = preselected;
|
|
firstOwnership = ownership;
|
|
firstHitMs = waitedMs;
|
|
}
|
|
|
|
PrintProbe("poll");
|
|
|
|
if (waitedMs >= maxHoverWaitMs)
|
|
break;
|
|
|
|
Thread.Sleep(pollMs);
|
|
}
|
|
|
|
PrintProbe("final");
|
|
Console.WriteLine("SolidWorks sheet point hover ownership probe finished.");
|
|
Console.WriteLine($"sheet_mm=({sheetXMm:F3},{sheetYMm:F3})");
|
|
Console.WriteLine($"sheet_correction_mm=({sheetCorrectionXM * 1000.0:F3},{sheetCorrectionYM * 1000.0:F3})");
|
|
Console.WriteLine($"projected_sheet_mm=({sheetXMm + sheetCorrectionXM * 1000.0:F3},{sheetYMm + sheetCorrectionYM * 1000.0:F3})");
|
|
Console.WriteLine($"screen=({target.ScreenX},{target.ScreenY}) client=({target.ClientX},{target.ClientY})");
|
|
Console.WriteLine($"waited_ms={waitedMs}");
|
|
Console.WriteLine($"polls={polls}");
|
|
Console.WriteLine($"hit={firstOwnership?.IsEntity == true}");
|
|
Console.WriteLine($"first_hit_ms={firstHitMs}");
|
|
Console.WriteLine("preselected=" + DescribeObject(firstPreselected ?? preselected));
|
|
|
|
if (firstOwnership?.IsEntity != true)
|
|
return;
|
|
|
|
Console.WriteLine("entity_component=" + firstOwnership.EntityComponent);
|
|
foreach (var drawingComponent in firstOwnership.DrawingComponents)
|
|
Console.WriteLine($"drawing_component[{drawingComponent.View}]=" + drawingComponent.DrawingComponent);
|
|
}
|
|
|
|
private static int ParseIntArg(string[] args, string name, int defaultValue, int min)
|
|
{
|
|
for (var i = 0; i < args.Length; i++)
|
|
{
|
|
if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
return Math.Max(min, int.Parse(args[++i]));
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
private static double ParseDoubleArg(string[] args, string name, double defaultValue)
|
|
{
|
|
for (var i = 0; i < args.Length; i++)
|
|
{
|
|
if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
return double.Parse(args[++i], CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
private static bool IsKeyDown(int virtualKey)
|
|
{
|
|
return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
|
|
}
|
|
|
|
private static void PrintSheetOriginCalibration()
|
|
{
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
|
|
var sheet = _drawingDoc.GetCurrentSheet() as Sheet
|
|
?? throw new InvalidOperationException("Cannot get current drawing sheet.");
|
|
|
|
double sheetWidth = 0;
|
|
double sheetHeight = 0;
|
|
_ = sheet.GetSize(ref sheetWidth, ref sheetHeight);
|
|
if (sheetWidth <= 0 || sheetHeight <= 0)
|
|
throw new InvalidOperationException($"Invalid sheet size: width={sheetWidth}, height={sheetHeight}");
|
|
|
|
var modelView = _activeDoc.ActiveView as ModelView
|
|
?? throw new InvalidOperationException("Cannot get active ModelView.");
|
|
|
|
var hwndValue = Safe(() => modelView.GetViewHWndx64(), 0L);
|
|
if (hwndValue == 0)
|
|
hwndValue = Safe(() => modelView.GetViewHWnd(), 0);
|
|
if (hwndValue == 0)
|
|
throw new InvalidOperationException("Cannot get SolidWorks graphics view HWND.");
|
|
|
|
var hwnd = new IntPtr(hwndValue);
|
|
if (!GetClientRect(hwnd, out var clientRect))
|
|
throw new InvalidOperationException("GetClientRect failed.");
|
|
|
|
var clientTopLeft = new Point(0, 0);
|
|
if (!ClientToScreen(hwnd, ref clientTopLeft))
|
|
throw new InvalidOperationException("ClientToScreen failed.");
|
|
|
|
var clientWidth = clientRect.Right - clientRect.Left;
|
|
var clientHeight = clientRect.Bottom - clientRect.Top;
|
|
var scale = Math.Min(clientWidth / sheetWidth, clientHeight / sheetHeight);
|
|
var sheetScreenWidth = sheetWidth * scale;
|
|
var sheetScreenHeight = sheetHeight * scale;
|
|
var sheetLeft = clientTopLeft.X + (clientWidth - sheetScreenWidth) / 2.0;
|
|
var sheetTop = clientTopLeft.Y + (clientHeight - sheetScreenHeight) / 2.0;
|
|
var originX = sheetLeft;
|
|
var originY = sheetTop + sheetScreenHeight;
|
|
|
|
Console.WriteLine("SolidWorks sheet origin calibration");
|
|
Console.WriteLine($"doc_title={Safe(() => _activeDoc.GetTitle(), "")}");
|
|
Console.WriteLine($"sheet={Safe(() => sheet.GetName(), "")}");
|
|
Console.WriteLine($"sheet_size_mm=({sheetWidth * 1000.0:F3},{sheetHeight * 1000.0:F3})");
|
|
Console.WriteLine($"view_hwnd=0x{hwndValue:X}");
|
|
Console.WriteLine($"client_screen_rect=({clientTopLeft.X},{clientTopLeft.Y},{clientTopLeft.X + clientWidth},{clientTopLeft.Y + clientHeight})");
|
|
Console.WriteLine($"client_size_px=({clientWidth},{clientHeight})");
|
|
Console.WriteLine($"feature_manager_width_before={_lastFeatureManagerWidthBeforeNormalize?.ToString() ?? "unknown"} after={_lastFeatureManagerWidthAfterNormalize?.ToString() ?? "unknown"}");
|
|
Console.WriteLine($"fit_scale_px_per_mm={scale / 1000.0:F6}");
|
|
Console.WriteLine($"sheet_screen_rect=({sheetLeft:F1},{sheetTop:F1},{sheetLeft + sheetScreenWidth:F1},{sheetTop + sheetScreenHeight:F1})");
|
|
Console.WriteLine($"sheet_origin_screen_px=({originX:F1},{originY:F1})");
|
|
|
|
foreach (var bound in ReadDrawingViewBounds())
|
|
{
|
|
var left = sheetLeft + bound.XMin * scale;
|
|
var right = sheetLeft + bound.XMax * scale;
|
|
var top = sheetTop + (sheetHeight - bound.YMax) * scale;
|
|
var bottom = sheetTop + (sheetHeight - bound.YMin) * scale;
|
|
Console.WriteLine($"view={bound.Name} sheet_outline_mm=({bound.XMin * 1000.0:F3},{bound.YMin * 1000.0:F3},{bound.XMax * 1000.0:F3},{bound.YMax * 1000.0:F3}) screen_rect=({left:F1},{top:F1},{right:F1},{bottom:F1})");
|
|
}
|
|
}
|
|
|
|
private static void NormalizeDrawingView(int delayMs)
|
|
{
|
|
HideFeatureManagerPane();
|
|
|
|
Safe(() =>
|
|
{
|
|
_activeDoc.ViewZoomtofit2();
|
|
return 0;
|
|
}, 0);
|
|
|
|
Thread.Sleep(Math.Max(0, delayMs));
|
|
}
|
|
|
|
private static void HideFeatureManagerPane()
|
|
{
|
|
_lastFeatureManagerWidthBeforeNormalize = Safe(() => _activeDoc.GetFeatureManagerWidth(), -1);
|
|
|
|
Safe(() =>
|
|
{
|
|
_activeDoc.SetFeatureManagerWidth(0);
|
|
return 0;
|
|
}, 0);
|
|
|
|
_lastFeatureManagerWidthAfterNormalize = Safe(() => _activeDoc.GetFeatureManagerWidth(), -1);
|
|
}
|
|
|
|
private static int ParseZoomFitDelayMs(string[] args)
|
|
{
|
|
var delayMs = 500;
|
|
for (var i = 0; i < args.Length; i++)
|
|
{
|
|
if (string.Equals(args[i], "--zoom-fit-delay-ms", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
delayMs = Math.Max(0, int.Parse(args[++i]));
|
|
}
|
|
|
|
return delayMs;
|
|
}
|
|
|
|
private static IEnumerable<ViewBounds> ReadDrawingViewBounds()
|
|
{
|
|
var viewObj = Safe(() => _drawingDoc.GetFirstView(), null);
|
|
var guard = 0;
|
|
while (viewObj is View view && guard++ < 200)
|
|
{
|
|
var name = Safe(() => view.GetName2(), Safe(() => view.Name, ""));
|
|
var type = Safe(() => view.Type, 0);
|
|
var outline = DoubleArray(Safe(() => view.GetOutline(), null));
|
|
|
|
if (guard > 1 && outline.Count >= 4)
|
|
{
|
|
yield return new ViewBounds(
|
|
name,
|
|
Math.Min(outline[0], outline[2]),
|
|
Math.Min(outline[1], outline[3]),
|
|
Math.Max(outline[0], outline[2]),
|
|
Math.Max(outline[1], outline[3]));
|
|
}
|
|
|
|
viewObj = Safe(() => view.GetNextView(), null);
|
|
}
|
|
}
|
|
|
|
private static bool ContainsPoint(IReadOnlyList<ViewBounds> bounds, double x, double y)
|
|
{
|
|
return bounds.Any(b => x >= b.XMin && x <= b.XMax && y >= b.YMin && y <= b.YMax);
|
|
}
|
|
|
|
private static ViewBounds FindContainingView(IReadOnlyList<ViewBounds> bounds, double x, double y)
|
|
{
|
|
return bounds
|
|
.Where(b => x >= b.XMin && x <= b.XMax && y >= b.YMin && y <= b.YMax)
|
|
.OrderBy(b => (b.XMax - b.XMin) * (b.YMax - b.YMin))
|
|
.FirstOrDefault();
|
|
}
|
|
|
|
private static SheetCorrection ResolveSheetCorrection(GridOptions options, string viewName)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(viewName) &&
|
|
options.ViewSheetCorrections.TryGetValue(viewName, out var correction))
|
|
{
|
|
return correction;
|
|
}
|
|
|
|
return new SheetCorrection(options.SheetCorrectionXM, options.SheetCorrectionYM);
|
|
}
|
|
|
|
private static long CountGridPointsToVisit(
|
|
int rows,
|
|
int columns,
|
|
double step,
|
|
double width,
|
|
double height,
|
|
IReadOnlyList<ViewBounds> bounds,
|
|
GridOptions options)
|
|
{
|
|
var count = 0L;
|
|
for (var row = 0; row < rows; row++)
|
|
{
|
|
var y = Math.Min(row * step, height);
|
|
if (!ShouldVisitGridRow(y, options))
|
|
continue;
|
|
|
|
for (var column = 0; column < columns; column++)
|
|
{
|
|
var x = Math.Min(column * step, width);
|
|
if (!options.ViewsOnly || ContainsPoint(bounds, x, y))
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private static bool ShouldVisitGridRow(double y, GridOptions options)
|
|
{
|
|
var yMm = y * 1000.0;
|
|
|
|
if (!double.IsNaN(options.RowYMm))
|
|
return Math.Abs(yMm - options.RowYMm) <= options.StepMm / 2.0;
|
|
|
|
if (!double.IsNaN(options.RowYMinMm) && yMm < options.RowYMinMm - options.StepMm / 2.0)
|
|
return false;
|
|
|
|
if (!double.IsNaN(options.RowYMaxMm) && yMm > options.RowYMaxMm + options.StepMm / 2.0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static IEnumerable<int> EnumerateColumns(int columns, bool reverse)
|
|
{
|
|
if (!reverse)
|
|
{
|
|
for (var column = 0; column < columns; column++)
|
|
yield return column;
|
|
}
|
|
else
|
|
{
|
|
for (var column = columns - 1; column >= 0; column--)
|
|
yield return column;
|
|
}
|
|
}
|
|
|
|
private static GridPointRecord ProbeHoverGridPoint(
|
|
int row,
|
|
int column,
|
|
double sheetX,
|
|
double sheetY,
|
|
int maxHoverWaitMs,
|
|
int pollMs,
|
|
double sheetCorrectionXM,
|
|
double sheetCorrectionYM,
|
|
string scanViewName)
|
|
{
|
|
var target = ProjectSheetPointToScreen(sheetX, sheetY, sheetCorrectionXM, sheetCorrectionYM);
|
|
if (target == null)
|
|
{
|
|
return EmptyGridPointRecord(row, column, sheetX, sheetY, scanViewName, sheetCorrectionXM, sheetCorrectionYM, "project_failed");
|
|
}
|
|
|
|
MoveMouseForHover(target);
|
|
|
|
var selectionMgr = _activeDoc.SelectionManager as SelectionMgr;
|
|
var started = DateTime.UtcNow;
|
|
object preselected = null;
|
|
EntityOwnership ownership = null;
|
|
|
|
while (true)
|
|
{
|
|
preselected = Safe(() => selectionMgr.GetPreSelectedObject(), null);
|
|
ownership = ReadEntityOwnership(preselected, null);
|
|
if (ownership.IsEntity)
|
|
break;
|
|
|
|
var waitedMs = (int)Math.Round((DateTime.UtcNow - started).TotalMilliseconds);
|
|
if (waitedMs >= maxHoverWaitMs)
|
|
break;
|
|
|
|
Thread.Sleep(Math.Max(1, pollMs));
|
|
}
|
|
|
|
if (!ownership.IsEntity)
|
|
{
|
|
return EmptyGridPointRecord(row, column, sheetX, sheetY, scanViewName, sheetCorrectionXM, sheetCorrectionYM, "preselected_not_entity");
|
|
}
|
|
|
|
return new GridPointRecord(
|
|
"point",
|
|
row,
|
|
column,
|
|
sheetX,
|
|
sheetY,
|
|
sheetX * 1000.0,
|
|
sheetY * 1000.0,
|
|
scanViewName,
|
|
sheetCorrectionXM * 1000.0,
|
|
sheetCorrectionYM * 1000.0,
|
|
true,
|
|
"HOVER",
|
|
"preselected",
|
|
0,
|
|
DescribeObject(preselected),
|
|
"null",
|
|
new List<double>(),
|
|
null,
|
|
ownership);
|
|
}
|
|
|
|
private static GridPointRecord EmptyGridPointRecord(
|
|
int row,
|
|
int column,
|
|
double sheetX,
|
|
double sheetY,
|
|
string scanViewName,
|
|
double sheetCorrectionXM,
|
|
double sheetCorrectionYM,
|
|
string reason)
|
|
{
|
|
return new GridPointRecord(
|
|
"point",
|
|
row,
|
|
column,
|
|
sheetX,
|
|
sheetY,
|
|
sheetX * 1000.0,
|
|
sheetY * 1000.0,
|
|
scanViewName,
|
|
sheetCorrectionXM * 1000.0,
|
|
sheetCorrectionYM * 1000.0,
|
|
false,
|
|
reason,
|
|
"",
|
|
0,
|
|
"null",
|
|
"null",
|
|
new List<double>(),
|
|
null,
|
|
new EntityOwnership(false, "null", new List<DrawingComponentOwnership>()));
|
|
}
|
|
|
|
private static void MoveMouseForHover(ScreenPoint target)
|
|
{
|
|
var warmup = target with
|
|
{
|
|
ScreenX = target.ScreenX - 1,
|
|
ClientX = target.ClientX - 1
|
|
};
|
|
|
|
MoveMouseAndNotify(warmup);
|
|
Thread.Sleep(10);
|
|
MoveMouseAndNotify(target);
|
|
}
|
|
|
|
private static void MoveMouseAndNotify(ScreenPoint point)
|
|
{
|
|
SetCursorPos(point.ScreenX, point.ScreenY);
|
|
var lParam = MakeLParam(point.ClientX, point.ClientY);
|
|
PostMessage(point.HWnd, WmMouseMove, IntPtr.Zero, lParam);
|
|
}
|
|
|
|
private static ScreenPoint GetHoverClearPoint()
|
|
{
|
|
var modelView = _activeDoc.ActiveView as ModelView;
|
|
if (modelView == null)
|
|
return null;
|
|
|
|
var hwndValue = Safe(() => modelView.GetViewHWndx64(), 0L);
|
|
if (hwndValue == 0)
|
|
hwndValue = Safe(() => modelView.GetViewHWnd(), 0);
|
|
if (hwndValue == 0)
|
|
return null;
|
|
|
|
var hwnd = new IntPtr(hwndValue);
|
|
if (!GetClientRect(hwnd, out var clientRect))
|
|
return null;
|
|
|
|
var clientTopLeft = new Point(0, 0);
|
|
if (!ClientToScreen(hwnd, ref clientTopLeft))
|
|
return null;
|
|
|
|
var clientX = Math.Max(2, clientRect.Right - clientRect.Left - 4);
|
|
var clientY = Math.Max(2, clientRect.Bottom - clientRect.Top - 4);
|
|
return new ScreenPoint(
|
|
hwnd,
|
|
clientTopLeft.X + clientX,
|
|
clientTopLeft.Y + clientY,
|
|
clientX,
|
|
clientY);
|
|
}
|
|
|
|
private static IntPtr MakeLParam(int lowWord, int highWord)
|
|
{
|
|
return new IntPtr((highWord << 16) | (lowWord & 0xFFFF));
|
|
}
|
|
|
|
private static ScreenPoint ProjectSheetPointToScreen(double sheetX, double sheetY, double sheetCorrectionX = 0.0, double sheetCorrectionY = 0.0)
|
|
{
|
|
var modelView = _activeDoc.ActiveView as ModelView;
|
|
if (modelView == null)
|
|
return null;
|
|
|
|
BringSolidWorksToFront();
|
|
|
|
var hwndValue = Safe(() => modelView.GetViewHWndx64(), 0L);
|
|
if (hwndValue == 0)
|
|
hwndValue = Safe(() => modelView.GetViewHWnd(), 0);
|
|
if (hwndValue == 0)
|
|
return null;
|
|
|
|
var hwnd = new IntPtr(hwndValue);
|
|
SetForegroundWindow(hwnd);
|
|
|
|
if (!GetClientRect(hwnd, out var clientRect))
|
|
return null;
|
|
|
|
var clientTopLeft = new Point(0, 0);
|
|
if (!ClientToScreen(hwnd, ref clientTopLeft))
|
|
return null;
|
|
|
|
var sheet = _drawingDoc.GetCurrentSheet() as Sheet;
|
|
if (sheet == null)
|
|
return null;
|
|
|
|
double sheetWidth = 0;
|
|
double sheetHeight = 0;
|
|
_ = sheet.GetSize(ref sheetWidth, ref sheetHeight);
|
|
if (sheetWidth <= 0 || sheetHeight <= 0)
|
|
return null;
|
|
|
|
var clientWidth = clientRect.Right - clientRect.Left;
|
|
var clientHeight = clientRect.Bottom - clientRect.Top;
|
|
var scale = Math.Min(clientWidth / sheetWidth, clientHeight / sheetHeight);
|
|
var sheetScreenWidth = sheetWidth * scale;
|
|
var sheetScreenHeight = sheetHeight * scale;
|
|
var sheetLeft = clientTopLeft.X + (clientWidth - sheetScreenWidth) / 2.0;
|
|
var sheetTop = clientTopLeft.Y + (clientHeight - sheetScreenHeight) / 2.0;
|
|
|
|
var projectedSheetX = sheetX + sheetCorrectionX;
|
|
var projectedSheetY = sheetY + sheetCorrectionY;
|
|
var screenX = sheetLeft + projectedSheetX * scale;
|
|
var screenY = sheetTop + (sheetHeight - projectedSheetY) * scale;
|
|
var screenXi = (int)Math.Round(screenX);
|
|
var screenYi = (int)Math.Round(screenY);
|
|
return new ScreenPoint(
|
|
hwnd,
|
|
screenXi,
|
|
screenYi,
|
|
screenXi - clientTopLeft.X,
|
|
screenYi - clientTopLeft.Y);
|
|
}
|
|
|
|
private static void BringSolidWorksToFront()
|
|
{
|
|
if (_swApp == null)
|
|
return;
|
|
|
|
Safe(() =>
|
|
{
|
|
_swApp.Visible = true;
|
|
return true;
|
|
}, false);
|
|
|
|
Safe(() =>
|
|
{
|
|
var errors = 0;
|
|
_swApp.ActivateDoc3(
|
|
_activeDoc.GetTitle(),
|
|
false,
|
|
(int)swRebuildOnActivation_e.swDontRebuildActiveDoc,
|
|
ref errors);
|
|
return true;
|
|
}, false);
|
|
|
|
var frame = Safe(() => _swApp.Frame() as Frame, null);
|
|
var frameHwndValue = frame == null ? 0L : Safe(() => frame.GetHWndx64(), 0L);
|
|
if (frameHwndValue == 0 && frame != null)
|
|
frameHwndValue = Safe(() => frame.GetHWnd(), 0);
|
|
if (frameHwndValue == 0)
|
|
return;
|
|
|
|
var frameHwnd = new IntPtr(frameHwndValue);
|
|
if (IsIconic(frameHwnd))
|
|
ShowWindow(frameHwnd, SwRestore);
|
|
SetWindowPos(frameHwnd, HwndTopMost, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpShowWindow);
|
|
BringWindowToTop(frameHwnd);
|
|
SetForegroundWindow(frameHwnd);
|
|
Thread.Sleep(80);
|
|
SetWindowPos(frameHwnd, HwndNoTopMost, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpShowWindow);
|
|
}
|
|
|
|
private static EntityOwnership ReadEntityOwnership(object obj, View selectedView)
|
|
{
|
|
if (obj is not IEntity entity)
|
|
return new EntityOwnership(false, "null", new List<DrawingComponentOwnership>());
|
|
|
|
var component = Safe(() => entity.GetComponent() as Component2, null);
|
|
var drawingComponents = new List<DrawingComponentOwnership>();
|
|
|
|
foreach (var view in EnumerateCandidateViews(selectedView))
|
|
{
|
|
var drawingComponent = Safe(() => entity.GetDrawingComponent(view), null);
|
|
drawingComponents.Add(new DrawingComponentOwnership(
|
|
DescribeView(view),
|
|
drawingComponent == null ? "null" : DescribeDrawingComponent(drawingComponent)));
|
|
}
|
|
|
|
return new EntityOwnership(true, DescribeComponent(component), drawingComponents);
|
|
}
|
|
|
|
private static GridOptions ParseGridOptions(string[] args)
|
|
{
|
|
var outputPath = Path.Combine(
|
|
"runtime",
|
|
"solidworks-hover-grid",
|
|
"grid-ownership-" + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".jsonl");
|
|
var stepMm = 1.0;
|
|
var includeEmpty = false;
|
|
var hoverDelayMs = 60;
|
|
var pollMs = 10;
|
|
var zoomFitDelayMs = 500;
|
|
var viewsOnly = false;
|
|
var viewName = "";
|
|
var rowYMm = double.NaN;
|
|
var rowYMinMm = double.NaN;
|
|
var rowYMaxMm = double.NaN;
|
|
var reverseX = false;
|
|
var maxPoints = 0;
|
|
var progressEveryRows = 25;
|
|
var clearBeforePoint = true;
|
|
var clearAfterPoint = false;
|
|
var clearDelayMs = 20;
|
|
var sheetCorrectionXM = 0.0;
|
|
var sheetCorrectionYM = 0.0;
|
|
var viewSheetCorrections = new Dictionary<string, SheetCorrection>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
for (var i = 0; i < args.Length; i++)
|
|
{
|
|
var arg = args[i];
|
|
if (string.Equals(arg, "--output", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
outputPath = args[++i];
|
|
else if (string.Equals(arg, "--step-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
stepMm = Math.Max(0.001, double.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--include-empty", StringComparison.OrdinalIgnoreCase))
|
|
includeEmpty = true;
|
|
else if (string.Equals(arg, "--hover-delay-ms", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
hoverDelayMs = Math.Max(0, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--max-hover-wait-ms", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
hoverDelayMs = Math.Max(0, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--poll-ms", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
pollMs = Math.Max(1, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--zoom-fit-delay-ms", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
zoomFitDelayMs = Math.Max(0, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--views-only", StringComparison.OrdinalIgnoreCase))
|
|
viewsOnly = true;
|
|
else if (string.Equals(arg, "--view-name", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
viewName = args[++i];
|
|
else if (string.Equals(arg, "--row-y-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
rowYMm = double.Parse(args[++i]);
|
|
else if (string.Equals(arg, "--row-y-min-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
rowYMinMm = double.Parse(args[++i], CultureInfo.InvariantCulture);
|
|
else if (string.Equals(arg, "--row-y-max-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
rowYMaxMm = double.Parse(args[++i], CultureInfo.InvariantCulture);
|
|
else if (string.Equals(arg, "--reverse-x", StringComparison.OrdinalIgnoreCase))
|
|
reverseX = true;
|
|
else if (string.Equals(arg, "--max-points", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
maxPoints = Math.Max(0, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--progress-every-rows", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
progressEveryRows = Math.Max(1, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--clear-before-point", StringComparison.OrdinalIgnoreCase))
|
|
clearBeforePoint = true;
|
|
else if (string.Equals(arg, "--no-clear-before-point", StringComparison.OrdinalIgnoreCase))
|
|
clearBeforePoint = false;
|
|
else if (string.Equals(arg, "--clear-after-point", StringComparison.OrdinalIgnoreCase))
|
|
clearAfterPoint = true;
|
|
else if (string.Equals(arg, "--no-clear-after-point", StringComparison.OrdinalIgnoreCase))
|
|
clearAfterPoint = false;
|
|
else if (string.Equals(arg, "--clear-delay-ms", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
clearDelayMs = Math.Max(0, int.Parse(args[++i]));
|
|
else if (string.Equals(arg, "--sheet-correction-x-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
sheetCorrectionXM = double.Parse(args[++i], CultureInfo.InvariantCulture) / 1000.0;
|
|
else if (string.Equals(arg, "--sheet-correction-y-mm", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
|
sheetCorrectionYM = double.Parse(args[++i], CultureInfo.InvariantCulture) / 1000.0;
|
|
else if ((string.Equals(arg, "--view-sheet-correction-mm", StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(arg, "--view-correction-mm", StringComparison.OrdinalIgnoreCase)) &&
|
|
i + 1 < args.Length)
|
|
{
|
|
var value = args[++i];
|
|
var separator = value.LastIndexOf('=');
|
|
if (separator <= 0 || separator >= value.Length - 1)
|
|
throw new ArgumentException("--view-sheet-correction-mm requires viewName=dxMm,dyMm.");
|
|
|
|
var name = value[..separator].Trim();
|
|
var parts = value[(separator + 1)..].Split(',', StringSplitOptions.TrimEntries);
|
|
if (parts.Length != 2)
|
|
throw new ArgumentException("--view-sheet-correction-mm requires viewName=dxMm,dyMm.");
|
|
|
|
viewSheetCorrections[name] = new SheetCorrection(
|
|
double.Parse(parts[0], CultureInfo.InvariantCulture) / 1000.0,
|
|
double.Parse(parts[1], CultureInfo.InvariantCulture) / 1000.0);
|
|
}
|
|
}
|
|
|
|
return new GridOptions(
|
|
outputPath,
|
|
stepMm,
|
|
includeEmpty,
|
|
hoverDelayMs,
|
|
pollMs,
|
|
zoomFitDelayMs,
|
|
viewsOnly,
|
|
viewName,
|
|
rowYMm,
|
|
rowYMinMm,
|
|
rowYMaxMm,
|
|
reverseX,
|
|
maxPoints,
|
|
progressEveryRows,
|
|
clearBeforePoint,
|
|
clearAfterPoint,
|
|
clearDelayMs,
|
|
sheetCorrectionXM,
|
|
sheetCorrectionYM,
|
|
viewSheetCorrections);
|
|
}
|
|
|
|
private static void WriteJsonLine<T>(TextWriter writer, T value)
|
|
{
|
|
writer.WriteLine(JsonSerializer.Serialize(value, JsonOptions));
|
|
}
|
|
|
|
private static SldWorks ConnectSolidWorks()
|
|
{
|
|
int hr = CLSIDFromProgID("SldWorks.Application", out var clsid);
|
|
if (hr < 0)
|
|
Marshal.ThrowExceptionForHR(hr);
|
|
return (SldWorks)GetActiveObject(ref clsid, IntPtr.Zero);
|
|
}
|
|
|
|
private static ModelDoc2 FindFirstOpenDrawing(SldWorks sw)
|
|
{
|
|
var docObj = Safe(() => sw.IGetFirstDocument2(), null);
|
|
var guard = 0;
|
|
while (docObj is ModelDoc2 doc && guard++ < 200)
|
|
{
|
|
if (Safe(() => doc.GetType(), 0) == (int)swDocumentTypes_e.swDocDRAWING)
|
|
return doc;
|
|
|
|
docObj = Safe(() => doc.IGetNext(), null);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static void HookEvents()
|
|
{
|
|
try
|
|
{
|
|
_drawingEvents = _drawingDoc as DDrawingDocEvents_Event;
|
|
if (_drawingEvents != null)
|
|
{
|
|
_dynamicHighlightHandler = OnDynamicHighlight;
|
|
_drawingEvents.DynamicHighlightNotify += _dynamicHighlightHandler;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("DynamicHighlightNotify hook failed: " + ex.Message);
|
|
}
|
|
|
|
try
|
|
{
|
|
var modelView = _activeDoc.ActiveView as ModelView;
|
|
var mouse = modelView?.GetMouse();
|
|
_mouseEvents = mouse as DMouseEvents_Event;
|
|
if (_mouseEvents != null)
|
|
{
|
|
_mouseMoveHandler = OnMouseMove;
|
|
_mouseEvents.MouseMoveNotify += _mouseMoveHandler;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("MouseMoveNotify hook failed: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private static int OnDynamicHighlight(bool highlightState)
|
|
{
|
|
if (_suppressEventProbeOutput)
|
|
return 0;
|
|
|
|
PrintProbe("DynamicHighlightNotify highlight=" + highlightState);
|
|
return 0;
|
|
}
|
|
|
|
private static int OnMouseMove(int x, int y, int wParam)
|
|
{
|
|
if (_suppressEventProbeOutput)
|
|
return 0;
|
|
|
|
PrintProbe($"MouseMoveNotify x={x} y={y}");
|
|
return 0;
|
|
}
|
|
|
|
private static void PrintProbe(string trigger)
|
|
{
|
|
try
|
|
{
|
|
var probe = ReadProbe(trigger);
|
|
if (probe.Signature == _lastSignature)
|
|
return;
|
|
|
|
_lastSignature = probe.Signature;
|
|
Console.WriteLine("---- " + DateTime.Now.ToString("HH:mm:ss.fff") + " " + trigger + " ----");
|
|
foreach (var line in probe.Lines)
|
|
Console.WriteLine(line);
|
|
Console.WriteLine();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("probe failed: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private static ProbeResult ReadProbe(string trigger)
|
|
{
|
|
var lines = new List<string>();
|
|
GetCursorPos(out var cursor);
|
|
lines.Add($"cursor_screen=({cursor.X},{cursor.Y})");
|
|
lines.Add("doc_title=" + Safe(() => _activeDoc.GetTitle(), ""));
|
|
|
|
var selectionMgr = _activeDoc.SelectionManager as SelectionMgr;
|
|
if (selectionMgr == null)
|
|
{
|
|
lines.Add("selection_manager=null");
|
|
return new ProbeResult(trigger + "|selection_manager=null", lines);
|
|
}
|
|
|
|
var preselected = Safe(() => selectionMgr.GetPreSelectedObject(), null);
|
|
lines.Add("preselected=" + DescribeObject(preselected));
|
|
AppendEntityOwnership(lines, "preselected", preselected, null);
|
|
|
|
var selectedCount = Safe(() => selectionMgr.GetSelectedObjectCount2(-1), 0);
|
|
lines.Add("selected_count=" + selectedCount);
|
|
for (var i = 1; i <= Math.Min(selectedCount, 5); i++)
|
|
{
|
|
var selected = Safe(() => selectionMgr.GetSelectedObject6(i, -1), null);
|
|
var selectedType = Safe(() => selectionMgr.GetSelectedObjectType3(i, -1), 0);
|
|
var drawingView = Safe(() => selectionMgr.GetSelectedObjectsDrawingView2(i, -1), null);
|
|
var point = DoubleArray(Safe(() => selectionMgr.GetSelectionPoint2(i, -1), null));
|
|
lines.Add($"selected[{i}].type={SelectionTypeName(selectedType)}({selectedType})");
|
|
lines.Add($"selected[{i}].object={DescribeObject(selected)}");
|
|
lines.Add($"selected[{i}].drawing_view={DescribeView(drawingView)}");
|
|
if (point.Count > 0)
|
|
lines.Add($"selected[{i}].point_m=({string.Join(",", point.Select(v => v.ToString("G6")))})");
|
|
AppendEntityOwnership(lines, $"selected[{i}]", selected, drawingView);
|
|
}
|
|
|
|
var signature = string.Join("|", lines);
|
|
return new ProbeResult(signature, lines);
|
|
}
|
|
|
|
private static void AppendEntityOwnership(List<string> lines, string prefix, object obj, View selectedView)
|
|
{
|
|
if (obj is not IEntity entity)
|
|
{
|
|
lines.Add(prefix + ".entity=no");
|
|
return;
|
|
}
|
|
|
|
lines.Add(prefix + ".entity=yes");
|
|
|
|
var component = Safe(() => entity.GetComponent() as Component2, null);
|
|
lines.Add(prefix + ".entity_component=" + DescribeComponent(component));
|
|
|
|
var candidateViews = EnumerateCandidateViews(selectedView).ToList();
|
|
foreach (var view in candidateViews)
|
|
{
|
|
var drawingComponent = Safe(() => entity.GetDrawingComponent(view), null);
|
|
if (drawingComponent == null)
|
|
{
|
|
lines.Add(prefix + $".drawing_component[{DescribeView(view)}]=null");
|
|
continue;
|
|
}
|
|
|
|
lines.Add(prefix + $".drawing_component[{DescribeView(view)}]=" + DescribeDrawingComponent(drawingComponent));
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<View> EnumerateCandidateViews(View selectedView)
|
|
{
|
|
if (selectedView != null)
|
|
yield return selectedView;
|
|
|
|
var activeView = Safe(() => _drawingDoc.ActiveDrawingView as View, null);
|
|
if (activeView != null && !SameComObject(activeView, selectedView))
|
|
yield return activeView;
|
|
|
|
var viewObj = Safe(() => _drawingDoc.GetFirstView(), null);
|
|
var guard = 0;
|
|
while (viewObj is View view && guard++ < 200)
|
|
{
|
|
if (!SameComObject(view, selectedView) && !SameComObject(view, activeView))
|
|
yield return view;
|
|
viewObj = Safe(() => view.GetNextView(), null);
|
|
}
|
|
}
|
|
|
|
private static string DescribeObject(object obj)
|
|
{
|
|
if (obj == null)
|
|
return "null";
|
|
|
|
var parts = new List<string>
|
|
{
|
|
"runtime=" + (obj.GetType().FullName ?? obj.GetType().Name),
|
|
"com=" + ComIdentity(obj)
|
|
};
|
|
|
|
AddIf(parts, "name", Safe(() => Convert.ToString(GetMember(obj, "Name")), ""));
|
|
AddIf(parts, "name2", Safe(() => Convert.ToString(GetMember(obj, "Name2")), ""));
|
|
AddIf(parts, "typeName", Safe(() => Convert.ToString(Invoke(obj, "GetTypeName2")), ""));
|
|
AddIf(parts, "selectName", Safe(() => Convert.ToString(Invoke(obj, "GetSelectByIDString")), ""));
|
|
|
|
return string.Join("; ", parts);
|
|
}
|
|
|
|
private static string DescribeDrawingComponent(DrawingComponent drawingComponent)
|
|
{
|
|
if (drawingComponent == null)
|
|
return "null";
|
|
|
|
var component = Safe(() => drawingComponent.Component as Component2, null);
|
|
return string.Join("; ", new[]
|
|
{
|
|
"drawing_name=" + Safe(() => drawingComponent.Name, ""),
|
|
"visible=" + Safe(() => drawingComponent.Visible, false),
|
|
"component=" + DescribeComponent(component)
|
|
});
|
|
}
|
|
|
|
private static string DescribeComponent(Component2 component)
|
|
{
|
|
if (component == null)
|
|
return "null";
|
|
|
|
return string.Join("; ", new[]
|
|
{
|
|
"name=" + Safe(() => component.Name2, ""),
|
|
"path=" + Safe(() => component.GetPathName(), ""),
|
|
"config=" + Safe(() => component.ReferencedConfiguration, "")
|
|
});
|
|
}
|
|
|
|
private static string DescribeView(View view)
|
|
{
|
|
if (view == null)
|
|
return "null";
|
|
|
|
var name = Safe(() => view.GetName2(), Safe(() => view.Name, ""));
|
|
var type = Safe(() => view.Type, 0);
|
|
return $"{name}/type={DrawingViewTypeName(type)}({type})";
|
|
}
|
|
|
|
private static string DrawingViewTypeName(int typeCode)
|
|
{
|
|
return typeCode switch
|
|
{
|
|
1 => "Model",
|
|
2 => "Projected",
|
|
3 => "Auxiliary",
|
|
4 => "Section",
|
|
5 => "Detail",
|
|
6 => "Relative",
|
|
7 => "Detached",
|
|
8 => "Standard",
|
|
9 => "Named",
|
|
10 => "Empty",
|
|
_ => "Unknown"
|
|
};
|
|
}
|
|
|
|
private static string SelectionTypeName(int type)
|
|
{
|
|
return Enum.GetName(typeof(swSelectType_e), type) ?? "unknown";
|
|
}
|
|
|
|
private static object Invoke(object target, string name, params object[] args)
|
|
{
|
|
return target?.GetType().InvokeMember(
|
|
name,
|
|
System.Reflection.BindingFlags.InvokeMethod,
|
|
null,
|
|
target,
|
|
args);
|
|
}
|
|
|
|
private static object GetMember(object target, string name)
|
|
{
|
|
return target?.GetType().InvokeMember(
|
|
name,
|
|
System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.GetField,
|
|
null,
|
|
target,
|
|
Array.Empty<object>());
|
|
}
|
|
|
|
private static void AddIf(List<string> parts, string key, string value)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
parts.Add(key + "=" + value);
|
|
}
|
|
|
|
private static string ComIdentity(object obj)
|
|
{
|
|
if (obj == null)
|
|
return "";
|
|
|
|
try
|
|
{
|
|
var ptr = Marshal.GetIUnknownForObject(obj);
|
|
try { return "0x" + ptr.ToInt64().ToString("X"); }
|
|
finally { Marshal.Release(ptr); }
|
|
}
|
|
catch
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private static bool SameComObject(object left, object right)
|
|
{
|
|
if (left == null || right == null)
|
|
return false;
|
|
return ComIdentity(left) == ComIdentity(right);
|
|
}
|
|
|
|
private static List<double> DoubleArray(object value)
|
|
{
|
|
if (value == null)
|
|
return new List<double>();
|
|
if (value is double[] doubles)
|
|
return doubles.ToList();
|
|
if (value is object[] objects)
|
|
return objects.Where(x => x != null).Select(Convert.ToDouble).ToList();
|
|
if (value is Array array)
|
|
{
|
|
var values = new List<double>();
|
|
foreach (var item in array)
|
|
{
|
|
if (item != null)
|
|
values.Add(Convert.ToDouble(item));
|
|
}
|
|
return values;
|
|
}
|
|
return new List<double>();
|
|
}
|
|
|
|
private static T Safe<T>(Func<T> action, T fallback)
|
|
{
|
|
try { return action(); }
|
|
catch { return fallback; }
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct Point
|
|
{
|
|
public int X;
|
|
public int Y;
|
|
|
|
public Point(int x, int y)
|
|
{
|
|
X = x;
|
|
Y = y;
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct Rect
|
|
{
|
|
public int Left;
|
|
public int Top;
|
|
public int Right;
|
|
public int Bottom;
|
|
}
|
|
|
|
private sealed record ProbeResult(string Signature, List<string> Lines);
|
|
|
|
private sealed record GridOptions(
|
|
string OutputPath,
|
|
double StepMm,
|
|
bool IncludeEmpty,
|
|
int HoverDelayMs,
|
|
int PollMs,
|
|
int ZoomFitDelayMs,
|
|
bool ViewsOnly,
|
|
string ViewName,
|
|
double RowYMm,
|
|
double RowYMinMm,
|
|
double RowYMaxMm,
|
|
bool ReverseX,
|
|
int MaxPoints,
|
|
int ProgressEveryRows,
|
|
bool ClearBeforePoint,
|
|
bool ClearAfterPoint,
|
|
int ClearDelayMs,
|
|
double SheetCorrectionXM,
|
|
double SheetCorrectionYM,
|
|
IReadOnlyDictionary<string, SheetCorrection> ViewSheetCorrections);
|
|
|
|
private sealed record SheetCorrection(double XM, double YM);
|
|
|
|
private sealed record ContourOptions(
|
|
string InputPath,
|
|
string OutputPath,
|
|
int MinGroupPoints,
|
|
double PadMm,
|
|
bool GroupByDrawingComponent);
|
|
|
|
private sealed record ViewBounds(string Name, double XMin, double YMin, double XMax, double YMax);
|
|
|
|
private sealed record ScreenPoint(IntPtr HWnd, int ScreenX, int ScreenY, int ClientX, int ClientY);
|
|
|
|
private sealed record OwnershipPoint(double XM, double YM);
|
|
|
|
private sealed class OwnershipGroup(string key, string displayName)
|
|
{
|
|
public string Key { get; } = key;
|
|
public string DisplayName { get; } = displayName;
|
|
public List<OwnershipPoint> Points { get; } = new();
|
|
}
|
|
|
|
private sealed record ContourRecord(
|
|
string Key,
|
|
string DisplayName,
|
|
int PointCount,
|
|
double MinXM,
|
|
double MinYM,
|
|
double MaxXM,
|
|
double MaxYM,
|
|
double MinXMm,
|
|
double MinYMm,
|
|
double MaxXMm,
|
|
double MaxYMm);
|
|
|
|
private sealed record GridHeaderRecord(
|
|
string Kind,
|
|
string DocumentTitle,
|
|
string SheetName,
|
|
double SheetWidthM,
|
|
double SheetHeightM,
|
|
double StepMm,
|
|
int Columns,
|
|
int Rows);
|
|
|
|
private sealed record GridPointRecord(
|
|
string Kind,
|
|
int Row,
|
|
int Column,
|
|
double SheetXM,
|
|
double SheetYM,
|
|
double SheetXMm,
|
|
double SheetYMm,
|
|
string ScanViewName,
|
|
double AppliedSheetCorrectionXMm,
|
|
double AppliedSheetCorrectionYMm,
|
|
bool Hit,
|
|
string RequestedSelectType,
|
|
string SelectedTypeName,
|
|
int SelectedTypeCode,
|
|
string SelectedObject,
|
|
string DrawingView,
|
|
IReadOnlyList<double> SelectionPointM,
|
|
double? SelectionDistanceM,
|
|
EntityOwnership Ownership);
|
|
|
|
private sealed record EntityOwnership(
|
|
bool IsEntity,
|
|
string EntityComponent,
|
|
IReadOnlyList<DrawingComponentOwnership> DrawingComponents);
|
|
|
|
private sealed record DrawingComponentOwnership(string View, string DrawingComponent);
|
|
}
|
|
|