Add comprehensive documentation for SnapForm extension

- Created development.md for development setup and licensing information.
- Added features.md detailing functionalities and usage instructions for developers and users.
- Introduced index.md to serve as a navigation hub for the documentation.
- Developed outputs.md to explain output formats and their specifications.
- Established service.md to describe the AI Service architecture and environment setup.
- Created surface.md to outline the process of converting Image Empty to perspective meshes.
This commit is contained in:
2026-08-13 23:57:09 +08:00
parent caeb6d706c
commit 13fab9ce88
28 changed files with 998 additions and 830 deletions

View File

@@ -333,33 +333,6 @@ class AddonTest(unittest.TestCase):
float(np.median(edge_lengths) * np.sqrt(10.0) * 2.0),
)
def test_surface_background_filter_compacts_geometry_and_attributes(self):
vertices = np.arange(18, dtype=np.float32).reshape(6, 3)
faces = np.asarray(((0, 1, 2), (2, 3, 4)), dtype=np.int32)
uv = np.arange(12, dtype=np.float32).reshape(6, 2)
attributes = {
key: np.arange(6 if name in self.addon.image_mesh.POINT_ATTRIBUTES else 2)
for name, (_data_type, key) in {
**self.addon.image_mesh.POINT_ATTRIBUTES,
**self.addon.image_mesh.FACE_ATTRIBUTES,
}.items()
}
attributes["face_background"] = np.asarray((True, False))
result = self.addon.image_mesh.remove_surface_background(
vertices,
faces,
uv,
attributes,
)
filtered_vertices, filtered_faces, filtered_uv, filtered_attributes = result
np.testing.assert_array_equal(filtered_vertices, vertices[[2, 3, 4]])
np.testing.assert_array_equal(filtered_faces, ((0, 1, 2),))
np.testing.assert_array_equal(filtered_uv, uv[[2, 3, 4]])
self.assertEqual(len(filtered_attributes["point_depth"]), 3)
self.assertEqual(len(filtered_attributes["face_background"]), 1)
def test_registers_image_background_operators(self):
self.assertIn(self.addon.RemoveImageBackground, self.addon.CLASSES)
self.assertIn(self.addon.ApplyRemovedBackground, self.addon.CLASSES)
@@ -706,7 +679,10 @@ class AddonTest(unittest.TestCase):
"snapform_keep_original",
},
)
self.assertEqual(operator.__annotations__["snapform_model"]["name"], "Model")
self.assertEqual(
operator.__annotations__["snapform_model"]["name"],
"Depth From",
)
self.assertEqual(
operator.__annotations__["snapform_unshaded"]["name"],
"Unshaded",
@@ -1980,7 +1956,7 @@ class AddonTest(unittest.TestCase):
self.assertIn("DEFAULT_CLOSED", panel.bl_options)
self.assertEqual(
[panel.bl_label for panel in panels],
["DA3 Output Test", "MoGe-2 API Test", "BEN2 Removal Test"],
["DA3", "MoGe-2", "BEN2"],
)
def test_service_panel_exposes_shared_service_state_and_controls(self):

View File

@@ -601,6 +601,21 @@ class ImageDepthTest(unittest.TestCase):
"img.001.depth",
)
def test_moge_map_names_use_source_image_name(self):
source_object = SimpleNamespace(
name="obj_img",
data=SimpleNamespace(name="img.png"),
)
self.assertEqual(
self.image_depth.normal_data_name(source_object),
"img.normal",
)
self.assertEqual(
self.image_depth.mask_data_name(source_object),
"img.mask",
)
def test_copy_depth_image_renames_still_source(self):
class FakeImage:
def __init__(self, name):

View File

@@ -45,6 +45,8 @@ class ServiceTest(unittest.TestCase):
cache.ben2_key = None
cache.da3_model = None
cache.da3_key = None
cache.moge_model = None
cache.moge_key = None
return cache
def test_ben2_model_is_reused_for_matching_runtime_key(self):
@@ -111,6 +113,39 @@ class ServiceTest(unittest.TestCase):
self.assertEqual(loads, ["model"])
self.assertEqual(cache.status()["da3"], "model")
def test_depth_model_cache_keeps_only_da3_or_moge(self):
cache = self.create_cache()
cache.ben2_model = object()
cache.ben2_key = ("ben2", "cuda")
cache.moge_model = object()
cache.moge_key = ("moge", "cuda")
device = SimpleNamespace(type="cuda")
da3 = object()
def load_da3(_model_dir, _device, key):
cache.da3_model = da3
cache.da3_key = key
return da3
with patch.object(cache, "_load_da3", side_effect=load_da3):
self.assertIs(cache.get_da3("da3", device), da3)
self.assertIsNone(cache.moge_model)
self.assertIsNotNone(cache.ben2_model)
moge = object()
def load_moge(_model_dir, _device, key):
cache.moge_model = moge
cache.moge_key = key
return moge
with patch.object(cache, "_load_moge", side_effect=load_moge):
self.assertIs(cache.get_moge("moge", device), moge)
self.assertIsNone(cache.da3_model)
self.assertIsNotNone(cache.ben2_model)
def test_service_tracks_clients_and_rejects_shared_shutdown(self):
fake_models = SimpleNamespace(
status=lambda: {"ben2": "", "da3": ""},

View File

@@ -1365,14 +1365,23 @@ class WorkerTest(unittest.TestCase):
self.assertNotIn("postprocess_method", result.files)
self.assertEqual(result["point_depth"].shape, (9,))
self.assertEqual(
result["point_confidence_percentile"].shape,
(9,),
)
self.assertEqual(
result["face_edge_stretch"].shape,
result["face_confidence_percentile"].shape,
(8,),
)
self.assertEqual(result["face_background"].shape, (8,))
self.assertEqual(
set(result.files),
{
"vertices",
"faces",
"uv",
"intrinsics",
"image_size",
"reference_depth",
"point_depth",
"face_confidence_percentile",
"face_depth_discontinuity",
},
)
np.testing.assert_allclose(result["uv"][0], [0.0, 1.0])
np.testing.assert_allclose(result["uv"][-1], [1.0, 0.0])
self.assertTrue(texture_path.is_file())