237 lines
9.0 KiB
C#
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),
|
|
},
|
|
};
|
|
}
|
|
}
|
|
}
|