
csharp
好的,我来为CAD特征学习创建独立的WL模块,与现有的SolidWorks WL系统共享数据库但代码分离。
让我先看一下当前的目录结构,然后创建新的CAD专用模块:
好的,我来为CAD特征学习创建独立的WL模块,与SolidWorks的WL系统共享数据库但代码完全分离。
好的,我来创建独立的CAD WL学习模块,放在 `share/cad/wl_learning` 目录下,与SolidWorks的train模块完全分离,但共享同一个数据库。
## 1. 创建CAD WL图核实现
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace cad_tools
{
/// <summary>
/// CAD 2D图形节点 - 表示CAD图纸中的图形元素
/// </summary>
public class CADGraphEdgeNode
{
/// <summary>
/// 节点ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// 节点类型:Line, Arc, Circle, Polyline, Ellipse
/// </summary>
public string EdgeType { get; set; } = "Unknown";
/// <summary>
/// 当前WL标签
/// </summary>
public string CurrentLabel { get; set; } = "Unknown";
/// <summary>
/// 几何特征(长度、半径等)
/// </summary>
public double GeometryValue { get; set; }
/// <summary>
/// 角度特征
/// </summary>
public double Angle { get; set; }
/// <summary>
/// 是否水平
/// </summary>
public bool IsHorizontal { get; set; }
/// <summary>
/// 是否垂直
/// </summary>
public bool IsVertical { get; set; }
/// <summary>
/// 连接的节点ID列表
/// </summary>
public List<int> ConnectedNodes { get; set; } = new List<int>();
}
/// <summary>
/// CAD 2D图 - 表示一个视图或图形的拓扑结构
/// </summary>
public class CADGraphEdgeGraph
{
/// <summary>
/// 图形名称(视图名称或文件名)
/// </summary>
public string GraphName { get; set; } = "";
/// <summary>
/// 源文件名
/// </summary>
public string SourceFile { get; set; } = "";
/// <summary>
/// 节点列表
/// </summary>
public List<CADGraphEdgeNode> Nodes { get; set; } = new List<CADGraphEdgeNode>();
}
/// <summary>
/// CAD WL图核实现 - 计算2D图形拓扑相似度
/// </summary>
public static class CADWLGraphKernel
{
/// <summary>
/// 执行WL迭代,更新节点标签
/// </summary>
/// <param name="graph">CAD 2D图</param>
/// <param name="iterations">迭代次数(默认3层)</param>
/// <returns>每次迭代后的标签频率列表</returns>
public static List<Dictionary<string, int>> PerformWLIterations(CADGraphEdgeGraph graph, int iterations = 3)
{
var labelFrequenciesPerIter = new List<Dictionary<string, int>>();
if (graph == null || graph.Nodes.Count == 0)
{
Console.WriteLine("警告:空CAD图,无法执行WL迭代");
return labelFrequenciesPerIter;
}
// 初始化所有节点的标签(基于边类型和几何特征)
foreach (var node in graph.Nodes)
{
node.CurrentLabel = BuildInitialLabel(node);
}
// 统计初始迭代的标签频率(迭代0)
labelFrequenciesPerIter.Add(CountLabelFrequencies(graph));
// 执行WL迭代
for (int iter = 1; iter <= iterations; iter++)
{
var newLabels = new Dictionary<int, string>();
foreach (var node in graph.Nodes)
{
// 获取邻居标签并排序
var neighborLabels = node.ConnectedNodes
.Where(nid => graph.Nodes.Any(n => n.Id == nid))
.Select(nid => graph.Nodes.First(n => n.Id == nid).CurrentLabel)
.OrderBy(l => l)
.ToList();
// 构建新标签:当前标签 + 邻居标签列表
var labelBuilder = new StringBuilder();
labelBuilder.Append($"[{node.CurrentLabel}]{string.Join(",", neighborLabels)}");
// 使用MD5生成新标签(压缩长度)
newLabels[node.Id] = HashLabel(labelBuilder.ToString());
}
// 更新所有节点标签
foreach (var kvp in newLabels)
{
var node = graph.Nodes.First(n => n.Id == kvp.Key);
node.CurrentLabel = kvp.Value;
}
// 统计当前迭代的标签频率
labelFrequenciesPerIter.Add(CountLabelFrequencies(graph));
}
return labelFrequenciesPerIter;
}
/// <summary>
/// 构建初始标签(基于边类型和几何特征)
/// </summary>
private static string BuildInitialLabel(CADGraphEdgeNode node)
{
var parts = new List<string>
{
node.EdgeType,
node.IsHorizontal ? "H" : node.IsVertical ? "V" : "D",
Math.Round(node.GeometryValue, 2).ToString()
};
if (node.Angle > 0)
{
parts.Add($"A{Math.Round(node.Angle, 1)}");
}
return string.Join("_", parts);
}
/// <summary>
/// 哈希标签(使用MD5前8位)
/// </summary>
private static string HashLabel(string input)
{
using (var md5 = MD5.Create())
{
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
return BitConverter.ToString(bytes, 0, 4).Replace("-", "").ToLower();
}
}
/// <summary>
/// 统计CAD图中各标签的出现频率
/// </summary>
private static Dictionary<string, int> CountLabelFrequencies(CADGraphEdgeGraph graph)
{
var frequency = new Dictionary<string, int>();
foreach (var node in graph.Nodes)
{
if (!frequency.ContainsKey(node.CurrentLabel))
{
frequency[node.CurrentLabel] = 0;
}
frequency[node.CurrentLabel]++;
}
return frequency;
}
/// <summary>
/// 计算两个CAD图形的相似度(基于WL标签频率的余弦相似度)
/// </summary>
public static double CalculateSimilarity(
Dictionary<string, int> freq1,
Dictionary<string, int> freq2,
bool useCosine = true)
{
if (freq1.Count == 0 || freq2.Count == 0)
return 0.0;
if (useCosine)
{
return CalculateCosineSimilarity(freq1, freq2);
}
else
{
return CalculateJaccardSimilarity(freq1, freq2);
}
}
/// <summary>
/// 余弦相似度
/// </summary>
private static double CalculateCosineSimilarity(Dictionary<string, int> freq1, Dictionary<string, int> freq2)
{
// 获取所有标签的并集
var allLabels = new HashSet<string>(freq1.Keys);
allLabels.UnionWith(freq2.Keys);
double dotProduct = 0;
double norm1 = 0;
double norm2 = 0;
foreach (var label in allLabels)
{
int v1 = freq1.ContainsKey(label) ? freq1[label] : 0;
int v2 = freq2.ContainsKey(label) ? freq2[label] : 0;
dotProduct += v1 * v2;
norm1 += v1 * v1;
norm2 += v2 * v2;
}
if (norm1 == 0 || norm2 == 0)
return 0.0;
return dotProduct / (Math.Sqrt(norm1) * Math.Sqrt(norm2));
}
/// <summary>
/// Jaccard相似度
/// </summary>
private static double CalculateJaccardSimilarity(Dictionary<string, int> freq1, Dictionary<string, int> freq2)
{
var intersection = new HashSet<string>(freq1.Keys);
intersection.IntersectWith(freq2.Keys);
var union = new HashSet<string>(freq1.Keys);
union.UnionWith(freq2.Keys);
return union.Count == 0 ? 0.0 : (double)intersection.Count / union.Count;
}
/// <summary>
/// 批量计算多个CAD图形之间的相似度矩阵
/// </summary>
public static double[,] ComputeSimilarityMatrix(
List<CADGraphEdgeGraph> graphs,
int wlIterations = 3,
double decayFactor = 0.5)
{
int n = graphs.Count;
double[,] similarityMatrix = new double[n, n];
Console.WriteLine($"\n开始计算 {n} 个CAD图形的相似度矩阵...");
// 预先计算每个图形的WL迭代结果
var allFrequencies = new List<List<Dictionary<string, int>>>();
for (int i = 0; i < n; i++)
{
var frequencies = PerformWLIterations(graphs[i], iterations: wlIterations);
allFrequencies.Add(frequencies);
Console.WriteLine($" ✓ 图形 {i + 1}/{n} 完成WL迭代");
}
// 计算相似度矩阵
for (int i = 0; i < n; i++)
{
for (int j = i; j < n; j++)
{
double similarity = CalculateMultiIterationSimilarity(
allFrequencies[i],
allFrequencies[j],
decayFactor);
similarityMatrix[i, j] = similarity;
similarityMatrix[j, i] = similarity;
}
}
return similarityMatrix;
}
/// <summary>
/// 计算多轮迭代的综合相似度(带衰减因子)
/// </summary>
public static double CalculateMultiIterationSimilarity(
List<Dictionary<string, int>> freqList1,
List<Dictionary<string, int>> freqList2,
double decayFactor = 0.5)
{
if (freqList1.Count == 0 || freqList2.Count == 0)
return 0.0;
int maxIterations = Math.Min(freqList1.Count, freqList2.Count);
double totalSimilarity = 0.0;
double totalWeight = 0.0;
for (int i = 0; i < maxIterations; i++)
{
double weight = Math.Pow(decayFactor, i);
double sim = CalculateSimilarity(freqList1[i], freqList2[i], useCosine: true);
totalSimilarity += weight * sim;
totalWeight += weight;
}
return totalSimilarity / totalWeight;
}
}
}
2. 创建CAD图形构建器
csharp
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
namespace cad_tools
{
/// <summary>
/// CAD图形构建器 - 从CAD文档提取2D图形特征
/// </summary>
public static class CADGraphEdgeBuilder
{
/// <summary>
/// 从当前CAD文档构建图形
/// </summary>
/// <param name="acadDoc">CAD文档</param>
/// <param name="graphName">图形名称(可选,默认为文件名)</param>
/// <returns>CAD 2D图</returns>
public static CADGraphEdgeGraph BuildGraphFromDocument(AcadDocument acadDoc, string graphName = "")
{
var graph = new CADGraphEdgeGraph
{
SourceFile = acadDoc.FullName,
GraphName = string.IsNullOrEmpty(graphName)
? System.IO.Path.GetFileNameWithoutExtension(acadDoc.FullName)
: graphName
};
try
{
// 获取模型空间中的所有实体
var modelSpace = acadDoc.ModelSpace;
int nodeId = 0;
// 第一遍:创建所有节点
var nodeMap = new Dictionary<object, CADGraphEdgeNode>();
foreach (AcadEntity entity in modelSpace)
{
var node = CreateNodeFromEntity(entity, nodeId++);
if (node != null)
{
graph.Nodes.Add(node);
nodeMap[entity] = node;
}
}
// 第二遍:建立连接关系(基于端点重合)
BuildConnections(graph, nodeMap);
Console.WriteLine($"✓ 成功构建CAD图形:{graph.GraphName},包含 {graph.Nodes.Count} 个节点");
}
catch (Exception ex)
{
Console.WriteLine($"× 构建CAD图形失败:{ex.Message}");
}
return graph;
}
/// <summary>
/// 从CAD实体创建节点
/// </summary>
private static CADGraphEdgeNode CreateNodeFromEntity(AcadEntity entity, int nodeId)
{
var node = new CADGraphEdgeNode
{
Id = nodeId
};
try
{
switch (entity)
{
case AcadLine line:
node.EdgeType = "Line";
node.GeometryValue = CalculateLineLength(line);
node.IsHorizontal = Math.Abs(line.EndPoint[1] - line.StartPoint[1]) < 0.001;
node.IsVertical = Math.Abs(line.EndPoint[0] - line.StartPoint[0]) < 0.001;
node.Angle = CalculateLineAngle(line);
break;
case AcadArc arc:
node.EdgeType = "Arc";
node.GeometryValue = arc.Radius;
node.Angle = (arc.EndAngle - arc.StartAngle) * 180.0 / Math.PI;
break;
case AcadCircle circle:
node.EdgeType = "Circle";
node.GeometryValue = circle.Radius;
node.IsHorizontal = true;
node.IsVertical = true;
break;
case AcadLWPolyline polyline:
node.EdgeType = "Polyline";
node.GeometryValue = CalculatePolylineLength(polyline);
break;
case AcadEllipse ellipse:
node.EdgeType = "Ellipse";
node.GeometryValue = ellipse.MajorRadius;
break;
default:
return null; // 跳过不支持的实体类型
}
node.CurrentLabel = node.EdgeType;
}
catch (Exception ex)
{
Console.WriteLine($"警告:无法解析实体:{ex.Message}");
return null;
}
return node;
}
/// <summary>
/// 建立连接关系(基于端点重合)
/// </summary>
private static void BuildConnections(CADGraphEdgeGraph graph, Dictionary<object, CADGraphEdgeNode> nodeMap)
{
double tolerance = 0.001; // 容差
foreach (var nodeA in graph.Nodes)
{
foreach (var nodeB in graph.Nodes)
{
if (nodeA.Id == nodeB.Id) continue;
if (AreConnected(nodeA, nodeB, tolerance))
{
if (!nodeA.ConnectedNodes.Contains(nodeB.Id))
{
nodeA.ConnectedNodes.Add(nodeB.Id);
}
}
}
}
}
/// <summary>
/// 判断两个节点是否连接(需要具体实现端点检测)
/// </summary>
private static bool AreConnected(CADGraphEdgeNode nodeA, CADGraphEdgeNode nodeB, double tolerance)
{
// TODO: 实现端点重合检测逻辑
// 这里需要根据实体类型获取端点坐标
return false;
}
/// <summary>
/// 计算直线长度
/// </summary>
private static double CalculateLineLength(AcadLine line)
{
double dx = line.EndPoint[0] - line.StartPoint[0];
double dy = line.EndPoint[1] - line.StartPoint[1];
double dz = line.EndPoint[2] - line.StartPoint[2];
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
/// <summary>
/// 计算直线角度(相对于X轴)
/// </summary>
private static double CalculateLineAngle(AcadLine line)
{
double dx = line.EndPoint[0] - line.StartPoint[0];
double dy = line.EndPoint[1] - line.StartPoint[1];
double angle = Math.Atan2(dy, dx) * 180.0 / Math.PI;
return angle < 0 ? angle + 360 : angle;
}
/// <summary>
/// 计算多段线长度
/// </summary>
private static double CalculatePolylineLength(AcadLWPolyline polyline)
{
double length = 0;
var coords = polyline.Coordinates;
for (int i = 0; i < coords.Length - 2; i += 2)
{
double dx = coords[i + 2] - coords[i];
double dy = coords[i + 3] - coords[i + 1];
length += Math.Sqrt(dx * dx + dy * dy);
}
return length;
}
/// <summary>
/// 批量构建文件夹中所有CAD文档的图形
/// </summary>
public static List<CADGraphEdgeGraph> BuildGraphsFromFolder(string folderPath)
{
var graphs = new List<CADGraphEdgeGraph>();
if (!System.IO.Directory.Exists(folderPath))
{
Console.WriteLine($"错误:文件夹不存在:{folderPath}");
return graphs;
}
var dwgFiles = System.IO.Directory.GetFiles(folderPath, "*.dwg");
Console.WriteLine($"\n找到 {dwgFiles.Length} 个DWG文件");
var acadApp = CadConnect.GetOrCreateInstance();
if (acadApp == null)
{
Console.WriteLine("错误:无法连接到AutoCAD");
return graphs;
}
foreach (var dwgFile in dwgFiles)
{
try
{
Console.WriteLine($"\n处理:{System.IO.Path.GetFileName(dwgFile)}");
var doc = acadApp.Documents.Open(dwgFile, false, true);
var graph = BuildGraphFromDocument(doc, System.IO.Path.GetFileNameWithoutExtension(dwgFile));
graphs.Add(graph);
doc.Close(false);
}
catch (Exception ex)
{
Console.WriteLine($"× 处理失败:{ex.Message}");
}
}
return graphs;
}
}
}
3. 创建CAD标注数据库操作类(共享同一数据库)
csharp
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using Newtonsoft.Json;
namespace cad_tools
{
/// <summary>
/// CAD标注数据库 - 共享topology_labels.db,但使用独立表
/// </summary>
public class CADDimensionDatabase
{
private readonly string _connectionString;
private readonly string _dbPath;
public CADDimensionDatabase(string dbPath = "topology_labels.db")
{
_dbPath = dbPath;
_connectionString = $"Data Source={dbPath};Version=3;";
InitializeDatabase();
}
/// <summary>
/// 初始化CAD标注数据库表结构
/// </summary>
private void InitializeDatabase()
{
using (var connection = new SQLiteConnection(_connectionString))
{
connection.Open();
using (var cmd = new SQLiteCommand("PRAGMA foreign_keys = ON;", connection))
{
cmd.ExecuteNonQuery();
}
// CAD图形表
string createCADGraphsTable = @"
CREATE TABLE IF NOT EXISTS cad_graphs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
graph_name TEXT NOT NULL,
file_path TEXT NOT NULL,
view_name TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(graph_name, file_path, view_name)
);";
// CAD图节点表
string createCADNodesTable = @"
CREATE TABLE IF NOT EXISTS cad_nodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
graph_id INTEGER NOT NULL,
node_id INTEGER NOT NULL,
edge_type TEXT NOT NULL,
geometry_value REAL,
angle REAL,
is_horizontal INTEGER DEFAULT 0,
is_vertical INTEGER DEFAULT 0,
FOREIGN KEY (graph_id) REFERENCES cad_graphs(id) ON DELETE CASCADE,
UNIQUE(graph_id, node_id)
);";
// CAD图连接关系表
string createCADEdgesTable = @"
CREATE TABLE IF NOT EXISTS cad_edges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
graph_id INTEGER NOT NULL,
from_node INTEGER NOT NULL,
to_node INTEGER NOT NULL,
FOREIGN KEY (graph_id) REFERENCES cad_graphs(id) ON DELETE CASCADE
);";
// CAD WL结果表
string createCADWLResultsTable = @"
CREATE TABLE IF NOT EXISTS cad_wl_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
graph_id INTEGER NOT NULL,
iteration INTEGER NOT NULL,
label_frequencies TEXT NOT NULL,
FOREIGN KEY (graph_id) REFERENCES cad_graphs(id) ON DELETE CASCADE,
UNIQUE(graph_id, iteration)
);";
// CAD标注规则表(存储学习到的标注规则)
string createCADRulesTable = @"
CREATE TABLE IF NOT EXISTS cad_dimension_rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
graph_id INTEGER NOT NULL,
rule_name TEXT NOT NULL,
rule_type TEXT NOT NULL,
rule_pattern TEXT,
dimension_value TEXT,
dimension_type TEXT,
reference_nodes TEXT,
annotation_style TEXT,
confidence REAL DEFAULT 1.0,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (graph_id) REFERENCES cad_graphs(id) ON DELETE CASCADE
);";
// 创建索引
string createIndexes = @"
CREATE INDEX IF NOT EXISTS idx_cad_graph_name ON cad_graphs(graph_name);
CREATE INDEX IF NOT EXISTS idx_cad_wl_graph ON cad_wl_results(graph_id);
CREATE INDEX IF NOT EXISTS idx_cad_rule_type ON cad_dimension_rules(rule_type);
";
using (var command = new SQLiteCommand(connection))
{
command.CommandText = createCADGraphsTable;
command.ExecuteNonQuery();
command.CommandText = createCADNodesTable;
command.ExecuteNonQuery();
command.CommandText = createCADEdgesTable;
command.ExecuteNonQuery();
command.CommandText = createCADWLResultsTable;
command.ExecuteNonQuery();
command.CommandText = createCADRulesTable;
command.ExecuteNonQuery();
command.CommandText = createIndexes;
command.ExecuteNonQuery();
}
Console.WriteLine($"CAD标注数据库初始化完成:{_dbPath}");
}
}
/// <summary>
/// 保存CAD图形及其WL结果
/// </summary>
public int UpsertCADGraphWithWL(CADGraphEdgeGraph graph, List<Dictionary<string, int>> wlFrequencies)
{
using (var connection = new SQLiteConnection(_connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
// 插入或更新图形信息
int graphId = GetOrCreateCADGraph(connection, graph.GraphName, graph.SourceFile, "");
// 保存节点信息
SaveNodes(connection, graphId, graph.Nodes, transaction);
// 保存连接关系
SaveEdges(connection, graphId, graph.Nodes, transaction);
// 删除旧的WL结果
string deleteOld = "DELETE FROM cad_wl_results WHERE graph_id = @graph_id";
using (var cmd = new SQLiteCommand(deleteOld, connection, transaction))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.ExecuteNonQuery();
}
// 保存WL结果
for (int iter = 0; iter < wlFrequencies.Count; iter++)
{
string insertWL = @"
INSERT INTO cad_wl_results (graph_id, iteration, label_frequencies)
VALUES (@graph_id, @iteration, @frequencies)";
using (var cmd = new SQLiteCommand(insertWL, connection, transaction))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.Parameters.AddWithValue("@iteration", iter);
cmd.Parameters.AddWithValue("@frequencies", JsonConvert.SerializeObject(wlFrequencies[iter]));
cmd.ExecuteNonQuery();
}
}
transaction.Commit();
Console.WriteLine($"✓ CAD图形 '{graph.GraphName}' 已存储({graph.Nodes.Count} 个节点)");
return graphId;
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine($"× 存储CAD图形失败:{ex.Message}");
throw;
}
}
}
}
/// <summary>
/// 添加标注规则
/// </summary>
public void AddDimensionRule(int graphId, string ruleName, string ruleType,
string dimensionValue, string dimensionType, string annotationStyle,
double confidence = 1.0, string notes = "")
{
using (var connection = new SQLiteConnection(_connectionString))
{
connection.Open();
string insert = @"
INSERT INTO cad_dimension_rules (
graph_id, rule_name, rule_type, dimension_value,
dimension_type, annotation_style, confidence, notes)
VALUES (@graph_id, @rule_name, @rule_type, @dimension_value,
@dimension_type, @annotation_style, @confidence, @notes)";
using (var cmd = new SQLiteCommand(insert, connection))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.Parameters.AddWithValue("@rule_name", ruleName);
cmd.Parameters.AddWithValue("@rule_type", ruleType);
cmd.Parameters.AddWithValue("@dimension_value", dimensionValue);
cmd.Parameters.AddWithValue("@dimension_type", dimensionType);
cmd.Parameters.AddWithValue("@annotation_style", annotationStyle);
cmd.Parameters.AddWithValue("@confidence", confidence);
cmd.Parameters.AddWithValue("@notes", notes);
cmd.ExecuteNonQuery();
}
Console.WriteLine($"✓ 已添加标注规则:{ruleName} = {dimensionValue}");
}
}
/// <summary>
/// 根据WL相似度查找推荐的标注规则
/// </summary>
public List<(string RuleName, string DimensionValue, string DimensionType,
double Similarity, double Confidence, string AnnotationStyle)>
FindRecommendedRules(List<Dictionary<string, int>> wlFrequencies,
int topK = 10,
double minSimilarity = 0.5)
{
var results = new List<(string, string, string, double, double, string)>();
if (wlFrequencies.Count == 0)
{
Console.WriteLine("警告:WL频率数据为空");
return results;
}
int queryIteration = wlFrequencies.Count - 1;
using (var connection = new SQLiteConnection(_connectionString))
{
connection.Open();
// 获取所有已标注的CAD图形及其WL结果
string query = @"
SELECT g.id, g.graph_name, dr.rule_name, dr.dimension_value,
dr.dimension_type, dr.annotation_style, dr.confidence,
wr.label_frequencies
FROM cad_graphs g
INNER JOIN cad_wl_results wr ON g.id = wr.graph_id
INNER JOIN cad_dimension_rules dr ON g.id = dr.graph_id
WHERE wr.iteration = @iteration
ORDER BY g.graph_name";
var graphData = new Dictionary<string, (CADGraphEdgeWLData data, int graphId)>();
using (var cmd = new SQLiteCommand(query, connection))
{
cmd.Parameters.AddWithValue("@iteration", queryIteration);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
int graphId = reader.GetInt32(0);
string graphName = reader.GetString(1);
string ruleName = reader.GetString(2);
string dimensionValue = reader.GetString(3);
string dimensionType = reader.GetString(4);
string annotationStyle = reader.GetString(5);
double confidence = reader.GetDouble(6);
string freqJson = reader.GetString(7);
var frequencies = JsonConvert.DeserializeObject<Dictionary<string, int>>(freqJson);
if (frequencies == null) continue;
if (!graphData.ContainsKey(graphName))
{
graphData[graphName] = (new CADGraphEdgeWLData(), graphId);
}
if (graphData[graphName].data.WLFreqs.Count == 0)
{
graphData[graphName].data.WLFreqs.Add(frequencies);
}
graphData[graphName].data.Rules.Add(new CADRuleData(
ruleName, dimensionValue, dimensionType,
annotationStyle, confidence));
}
}
}
// 计算相似度
foreach (var kvp in graphData)
{
string graphName = kvp.Key;
var (data, graphId) = kvp.Value;
double similarity = CADWLGraphKernel.CalculateSimilarity(
wlFrequencies[queryIteration],
data.WLFreqs[0]);
if (similarity >= minSimilarity)
{
foreach (var rule in data.Rules)
{
results.Add((rule.RuleName, rule.DimensionValue,
rule.DimensionType, similarity,
rule.Confidence, rule.AnnotationStyle));
}
}
}
// 按相似度排序
results = results
.OrderByDescending(r => r.Item4)
.ThenByDescending(r => r.Item5)
.Take(topK)
.ToList();
}
return results;
}
/// <summary>
/// 获取或创建CAD图形记录
/// </summary>
private int GetOrCreateCADGraph(SQLiteConnection connection,
string graphName,
string filePath,
string viewName)
{
string select = "SELECT id FROM cad_graphs WHERE graph_name = @graph_name AND file_path = @file_path AND (view_name = @view_name OR (view_name IS NULL AND @view_name = ''))";
using (var cmd = new SQLiteCommand(select, connection))
{
cmd.Parameters.AddWithValue("@graph_name", graphName);
cmd.Parameters.AddWithValue("@file_path", filePath);
cmd.Parameters.AddWithValue("@view_name", viewName);
var result = cmd.ExecuteScalar();
if (result != null)
{
return Convert.ToInt32(result);
}
}
string insert = @"
INSERT INTO cad_graphs (graph_name, file_path, view_name)
VALUES (@graph_name, @file_path, @view_name)";
using (var cmd = new SQLiteCommand(insert, connection))
{
cmd.Parameters.AddWithValue("@graph_name", graphName);
cmd.Parameters.AddWithValue("@file_path", filePath);
cmd.Parameters.AddWithValue("@view_name", string.IsNullOrEmpty(viewName) ? (object)DBNull.Value : viewName);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT last_insert_rowid()";
return Convert.ToInt32(cmd.ExecuteScalar());
}
}
/// <summary>
/// 保存节点信息
/// </summary>
private void SaveNodes(SQLiteConnection connection, int graphId,
List<CADGraphEdgeNode> nodes, SQLiteTransaction transaction)
{
// 删除旧节点
string deleteOld = "DELETE FROM cad_nodes WHERE graph_id = @graph_id";
using (var cmd = new SQLiteCommand(deleteOld, connection, transaction))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.ExecuteNonQuery();
}
// 插入新节点
foreach (var node in nodes)
{
string insert = @"
INSERT INTO cad_nodes (graph_id, node_id, edge_type, geometry_value,
angle, is_horizontal, is_vertical)
VALUES (@graph_id, @node_id, @edge_type, @geometry_value,
@angle, @is_horizontal, @is_vertical)";
using (var cmd = new SQLiteCommand(insert, connection, transaction))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.Parameters.AddWithValue("@node_id", node.Id);
cmd.Parameters.AddWithValue("@edge_type", node.EdgeType);
cmd.Parameters.AddWithValue("@geometry_value", node.GeometryValue);
cmd.Parameters.AddWithValue("@angle", node.Angle);
cmd.Parameters.AddWithValue("@is_horizontal", node.IsHorizontal ? 1 : 0);
cmd.Parameters.AddWithValue("@is_vertical", node.IsVertical ? 1 : 0);
cmd.ExecuteNonQuery();
}
}
}
/// <summary>
/// 保存连接关系
/// </summary>
private void SaveEdges(SQLiteConnection connection, int graphId,
List<CADGraphEdgeNode> nodes, SQLiteTransaction transaction)
{
string deleteOld = "DELETE FROM cad_edges WHERE graph_id = @graph_id";
using (var cmd = new SQLiteCommand(deleteOld, connection, transaction))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.ExecuteNonQuery();
}
foreach (var node in nodes)
{
foreach (var connectedId in node.ConnectedNodes)
{
string insert = @"
INSERT INTO cad_edges (graph_id, from_node, to_node)
VALUES (@graph_id, @from_node, @to_node)";
using (var cmd = new SQLiteCommand(insert, connection, transaction))
{
cmd.Parameters.AddWithValue("@graph_id", graphId);
cmd.Parameters.AddWithValue("@from_node", node.Id);
cmd.Parameters.AddWithValue("@to_node", connectedId);
cmd.ExecuteNonQuery();
}
}
}
}
}
/// <summary>
/// CAD图形WL数据辅助类
/// </summary>
public class CADGraphEdgeWLData
{
public List<Dictionary<string, int>> WLFreqs { get; set; } = new List<Dictionary<string, int>>();
public List<CADRuleData> Rules { get; set; } = new List<CADRuleData>();
}
/// <summary>
/// CAD标注规则数据
/// </summary>
public class CADRuleData
{
public string RuleName { get; set; }
public string DimensionValue { get; set; }
public string DimensionType { get; set; }
public string AnnotationStyle { get; set; }
public double Confidence { get; set; }
public CADRuleData(string ruleName, string dimensionValue, string dimensionType,
string annotationStyle, double confidence)
{
RuleName = ruleName;
DimensionValue = dimensionValue;
DimensionType = dimensionType;
AnnotationStyle = annotationStyle;
Confidence = confidence;
}
}
}
4. 创建使用示例
csharp
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.Interop;
namespace cad_tools
{
/// <summary>
/// CAD WL学习示例
/// </summary>
public static class CADWLExamples
{
/// <summary>
/// 示例1:学习已有标注的图纸
/// </summary>
public static void LearnFromAnnotatedDrawing(string dwgPath)
{
Console.WriteLine("\n========== CAD特征学习示例 ==========\n");
// 1. 连接到CAD
var acadApp = CadConnect.GetOrCreateInstance();
if (acadApp == null)
{
Console.WriteLine("错误:无法连接到AutoCAD");
return;
}
// 2. 打开图纸
Console.WriteLine($"打开图纸:{dwgPath}");
var doc = acadApp.Documents.Open(dwgPath, false, false);
// 3. 构建图形
Console.WriteLine("\n构建图形特征...");
var graph = CADGraphEdgeBuilder.BuildGraphFromDocument(doc, "示例图形");
// 4. 执行WL迭代
Console.WriteLine("\n执行WL迭代...");
var wlFreq = CADWLGraphKernel.PerformWLIterations(graph, iterations: 3);
// 5. 保存到数据库
var database = new CADDimensionDatabase();
int graphId = database.UpsertCADGraphWithWL(graph, wlFreq);
// 6. 添加标注规则(这里需要手动输入或从图纸提取)
Console.WriteLine("\n添加标注规则...");
database.AddDimensionRule(graphId,
ruleName: "高度标注",
ruleType: "linear",
dimensionValue: "27.30",
dimensionType: "垂直尺寸",
annotationStyle: "默认",
confidence: 1.0,
notes: "学习自示例图纸");
database.AddDimensionRule(graphId,
ruleName: "宽度标注",
ruleType: "linear",
dimensionValue: "12.80",
dimensionType: "水平尺寸",
annotationStyle: "默认",
confidence: 1.0,
notes: "学习自示例图纸");
doc.Close(false);
Console.WriteLine("\n✓ 学习完成!");
}
/// <summary>
/// 示例2:根据已有图纸推荐标注
/// </summary>
public static void RecommendDimensionsForNewDrawing(string newDwgPath)
{
Console.WriteLine("\n========== CAD标注推荐示例 ==========\n");
var acadApp = CadConnect.GetOrCreateInstance();
if (acadApp == null) return;
Console.WriteLine($"打开新图纸:{newDwgPath}");
var doc = acadApp.Documents.Open(newDwgPath, false, true);
// 构建图形
var graph = CADGraphEdgeBuilder.BuildGraphFromDocument(doc, "新图形");
var wlFreq = CADWLGraphKernel.PerformWLIterations(graph, iterations: 3);
// 查询推荐规则
var database = new CADDimensionDatabase();
var recommendations = database.FindRecommendedRules(wlFreq, topK: 10, minSimilarity: 0.5);
Console.WriteLine($"\n找到 {recommendations.Count} 条推荐标注规则:\n");
for (int i = 0; i < Math.Min(5, recommendations.Count); i++)
{
var (ruleName, dimValue, dimType, similarity, confidence, style) = recommendations[i];
Console.WriteLine($"推荐 {i + 1}:");
Console.WriteLine($" 规则:{ruleName}");
Console.WriteLine($" 标注值:{dimValue}");
Console.WriteLine($" 类型:{dimType}");
Console.WriteLine($" 相似度:{similarity:F3} ({similarity * 100:F1}%)");
Console.WriteLine($" 置信度:{confidence:F2}");
Console.WriteLine();
}
doc.Close(false);
}
/// <summary>
/// 示例3:批量学习文件夹中的图纸
/// </summary>
public static void BatchLearnFromFolder(string folderPath)
{
Console.WriteLine($"\n========== 批量学习:{folderPath} ==========\n");
var graphs = CADGraphEdgeBuilder.BuildGraphsFromFolder(folderPath);
var database = new CADDimensionDatabase();
foreach (var graph in graphs)
{
Console.WriteLine($"\n处理:{graph.GraphName}");
var wlFreq = CADWLGraphKernel.PerformWLIterations(graph, iterations: 3);
database.UpsertCADGraphWithWL(graph, wlFreq);
}
Console.WriteLine($"\n✓ 批量学习完成!共处理 {graphs.Count} 个图形");
}
/// <summary>
/// 示例4:计算图形相似度矩阵
/// </summary>
public static void ComputeSimilarityMatrix(string folderPath)
{
Console.WriteLine("\n========== 计算相似度矩阵 ==========\n");
var graphs = CADGraphEdgeBuilder.BuildGraphsFromFolder(folderPath);
var matrix = CADWLGraphKernel.ComputeSimilarityMatrix(graphs, wlIterations: 3, decayFactor: 0.5);
Console.WriteLine("\n相似度矩阵:\n");
Console.Write("".PadRight(20));
for (int j = 0; j < graphs.Count; j++)
{
Console.Write($"{graphs[j].GraphName.Substring(0, Math.Min(10, graphs[j].GraphName.Length))}".PadRight(15));
}
Console.WriteLine();
for (int i = 0; i < graphs.Count; i++)
{
Console.Write($"{graphs[i].GraphName.Substring(0, Math.Min(10, graphs[i].GraphName.Length))}".PadRight(20));
for (int j = 0; j < graphs.Count; j++)
{
Console.Write($"{matrix[i, j]:F3}".PadRight(15));
}
Console.WriteLine();
}
}
}
}
创建目录结构
bash
share/cad/wl_learning/
├── cad_wl_graph_kernel.cs # CAD WL图核实现
├── cad_graph_builder.cs # CAD图形构建器
├── cad_dimension_database.cs # CAD标注数据库(共享topology_labels.db)
└── cad_wl_examples.cs # 使用示例
使用说明
这个CAD WL学习模块与SolidWorks的WL系统:
独立的部分:
- 完全独立的命名空间
cad_tools - 独立的类和方法,不依赖SolidWorks API
- 独立的图形数据结构(基于2D边而非3D面)
共享的部分:
- 使用同一个
topology_labels.db数据库 - 独立的表结构(
cad_graphs,cad_nodes,cad_edges,cad_wl_results,cad_dimension_rules) - 互不干扰,各自管理自己的数据
使用流程:
- 学习阶段:用已标注的图纸训练,提取图形特征和标注规则
- 推荐阶段:新图纸输入,通过WL相似度匹配,推荐标注规则
- 标注阶段:根据推荐自动添加尺寸标注
你可以直接用这些代码来学习你图中的标注样式!