237 lines
9.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace FurnitureExporter.Tests
{
public class AssemblySemanticAnalyzerTests
{
[Fact]
public void Analyze_BuildsAssemblySection_AndDoesNotEmitInstallOrderFields()
{
var exportData = new ExportData
{
Panels = new List<PanelInfo>
{
CreatePanel(
"panel_001",
"side_panel",
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 0.0, 0.0, 0.0 },
new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_002",
"shelf",
new[] { 200.0, 280.0, 359.0 },
new[] { 0.0, 0.0, 1.0 },
new[] { 18.0, 0.0, 350.0 },
new[] { 382.0, 560.0, 368.0 }),
},
};
exportData.Assembly = new AssemblySemanticAnalyzer().Analyze(
exportData.Panels,
exportData.Holes,
exportData.HoleGroups,
exportData.HardwareTypes);
var json = JsonConvert.SerializeObject(
exportData,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
});
var root = JObject.Parse(json);
Assert.Equal("1.0", root["assembly"]?["version"]?.Value<string>());
Assert.NotNull(root["assembly"]?["panelAssemblies"]);
Assert.NotNull(root["assembly"]?["connections"]);
Assert.DoesNotContain("installOrder", json, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Analyze_PrefersHardwareRelation_ForConnectedPanel()
{
var panels = new List<PanelInfo>
{
CreatePanel(
"panel_001",
"side_panel",
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 0.0, 0.0, 0.0 },
new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_002",
"shelf",
new[] { 200.0, 280.0, 359.0 },
new[] { 0.0, 0.0, 1.0 },
new[] { 18.0, 0.0, 350.0 },
new[] { 382.0, 560.0, 368.0 }),
};
var holes = new List<HoleInfo>
{
new HoleInfo
{
Id = "panel_001_hole_001",
PanelId = "panel_001",
Position = new[] { 9.0, 260.0, 360.0 },
Normal = new[] { 1.0, 0.0, 0.0 },
Diameter = 15.0,
Radius = 7.5,
Depth = 12.0,
Type = "blind",
},
new HoleInfo
{
Id = "panel_001_hole_002",
PanelId = "panel_001",
Position = new[] { 16.0, 260.0, 360.0 },
Normal = new[] { 0.0, 1.0, 0.0 },
Diameter = 8.0,
Radius = 4.0,
Depth = 10.0,
Type = "blind",
},
};
var holeGroups = new List<HoleGroupInfo>
{
new HoleGroupInfo
{
Id = "panel_001_group_000",
PanelId = "panel_001",
GroupType = "eccentric_screw",
HoleIds = new[] { "panel_001_hole_001", "panel_001_hole_002" },
Position = new[] { 9.0, 260.0, 360.0 },
Normal = new[] { 1.0, 0.0, 0.0 },
Confidence = 0.96,
Reason = "偏心件孔组",
},
};
panels[0].Holes = holes.ToList();
panels[0].HoleGroups = holeGroups.ToList();
var hardwareTypes = new List<HardwareType>
{
new HardwareType
{
TypeId = "type_000",
InferredHardwareType = "connector",
Instances = new List<HardwareInstance>
{
new HardwareInstance
{
Position = new Position3D { X = 12.0, Y = 260.0, Z = 360.0 },
PrimaryPanelId = "panel_001",
SecondaryPanelId = "panel_002",
BoundHoleGroupId = "panel_001_group_000",
BoundHoleIds = new[] { "panel_001_hole_001", "panel_001_hole_002" },
BindingMode = "hole_group",
BindingConfidence = 0.97,
BindingReason = "优先按孔组绑定",
NearbyPanelIds = new[] { "panel_001", "panel_002" },
},
},
},
};
var assembly = new AssemblySemanticAnalyzer().Analyze(panels, holes, holeGroups, hardwareTypes);
var connection = Assert.Single(
assembly.Connections.Where(item =>
item.SourcePanelId == "panel_001" &&
item.TargetPanelId == "panel_002"));
var panelAssembly = Assert.Single(assembly.PanelAssemblies.Where(item => item.PanelId == "panel_001"));
Assert.Equal("hole_group_hardware", connection.ConnectionType);
Assert.Equal("slide_in", panelAssembly.MountMode);
Assert.Equal("left", panelAssembly.AssemblyDirection);
Assert.Equal("panel_002", panelAssembly.PrimarySupportPanelId);
Assert.Contains("panel_002", panelAssembly.TargetPanelIds);
Assert.Contains("panel_002", panelAssembly.DependsOn);
Assert.Contains("panel_002", panelAssembly.Mates.Right ?? new List<string>());
Assert.True(panelAssembly.Confidence >= 0.75);
}
[Fact]
public void Analyze_FallsBackToShelfDirection_WhenNoHardwareBindingExists()
{
var panels = new List<PanelInfo>
{
CreatePanel(
"panel_left",
"side_panel",
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 0.0, 0.0, 0.0 },
new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_right",
"side_panel",
new[] { 391.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 382.0, 0.0, 0.0 },
new[] { 400.0, 560.0, 720.0 }),
CreatePanel(
"panel_shelf",
"shelf",
new[] { 200.0, 280.0, 359.0 },
new[] { 0.0, 0.0, 1.0 },
new[] { 18.0, 0.0, 350.0 },
new[] { 382.0, 560.0, 368.0 }),
};
var assembly = new AssemblySemanticAnalyzer().Analyze(
panels,
new List<HoleInfo>(),
new List<HoleGroupInfo>(),
new List<HardwareType>());
var shelf = Assert.Single(assembly.PanelAssemblies.Where(item => item.PanelId == "panel_shelf"));
Assert.Equal("drop_in", shelf.MountMode);
Assert.Equal("top", shelf.AssemblyDirection);
Assert.Contains("panel_left", shelf.TargetPanelIds);
Assert.Contains("panel_right", shelf.TargetPanelIds);
Assert.Contains("panel_left", shelf.Mates.Left ?? new List<string>());
Assert.Contains("panel_right", shelf.Mates.Right ?? new List<string>());
Assert.True(shelf.TravelDistance > 0);
}
private static PanelInfo CreatePanel(
string id,
string panelType,
double[] position,
double[] normal,
double[] boundingBoxMin,
double[] boundingBoxMax)
{
return new PanelInfo
{
Id = id,
PanelType = panelType,
Position = position,
Normal = normal,
BoundingBox = new BoundingBox3D
{
Min = boundingBoxMin,
Max = boundingBoxMax,
},
Size = new[]
{
Math.Round(boundingBoxMax[0] - boundingBoxMin[0], 2),
Math.Round(boundingBoxMax[1] - boundingBoxMin[1], 2),
Math.Round(boundingBoxMax[2] - boundingBoxMin[2], 2),
},
};
}
}
}