reverse3d 0.11.2__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {reverse3d-0.11.2/reverse3d.egg-info → reverse3d-0.12.0}/PKG-INFO +2 -1
- {reverse3d-0.11.2 → reverse3d-0.12.0}/README.md +1 -0
- reverse3d-0.12.0/reverse3d/_version.py +1 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reconstruction/silhouette.py +63 -19
- {reverse3d-0.11.2 → reverse3d-0.12.0/reverse3d.egg-info}/PKG-INFO +2 -1
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_silhouette.py +19 -0
- reverse3d-0.11.2/reverse3d/_version.py +0 -1
- {reverse3d-0.11.2 → reverse3d-0.12.0}/LICENSE +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/NOTICE +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/pyproject.toml +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/__init__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/__main__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/_about.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/agent/__init__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/agent/compact.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/agent/mcp_server.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/agent/prompts.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/agent/skill/SKILL.md +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/agent/tools.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/autopilot.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/cli.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/config.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/doctor.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/errors.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/hardware.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/imaging.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/logging_utils.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/mesh_io.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/offline.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/profiles.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/py.typed +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/recipes.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reconstruction/__init__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reconstruction/depth_estimation.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reconstruction/photogrammetry.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reconstruction/pointcloud.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reconstruction/texture.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/rendering/__init__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/rendering/_blender_job.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/rendering/blender_render.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/rendering/preview.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reverse_eng/__init__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reverse_eng/geometry_fit.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reverse_eng/measure.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reverse_eng/mesh_cleaner.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/reverse_eng/parametric.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/scenes.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/security.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/setup_agents.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/userconfig.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/vision/__init__.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/vision/classifier.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/vision/feature_matcher.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/vision/mask_extractor.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/vision/segmenter.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d/watch.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d.egg-info/SOURCES.txt +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d.egg-info/dependency_links.txt +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d.egg-info/entry_points.txt +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d.egg-info/requires.txt +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/reverse3d.egg-info/top_level.txt +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/setup.cfg +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_agent_tools.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_authorship.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_automation.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_cli.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_cli_automation.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_color.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_config.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_geometry_fit.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_heic.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_mesh.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_offline_autopilot.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_optional_extras.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_reconstruction.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_render.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_scenes.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_security.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_texture.py +0 -0
- {reverse3d-0.11.2 → reverse3d-0.12.0}/tests/test_vision.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reverse3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: AI-agent-ready toolkit for 3D reverse engineering: image analysis, photogrammetry, mesh repair, primitive fitting and headless rendering.
|
|
5
5
|
Author-email: Voxel Enterprise <info@voxel.lat>
|
|
6
6
|
Maintainer-email: Voxel Enterprise <info@voxel.lat>
|
|
@@ -421,6 +421,7 @@ Si las fotos están borrosas o falta una herramienta, **no reintenta en vano**:
|
|
|
421
421
|
|---|---|---|
|
|
422
422
|
| Recortes perfectos | 0,943 | 0,941 |
|
|
423
423
|
| 15 % de recortes defectuosos | 0,000 (pierde el objeto) | **0,921** |
|
|
424
|
+
| Concavidad invisible a las siluetas (error en la ranura) | 0,154 | **0,091** (refinamiento por fotos, Core 0.3) |
|
|
424
425
|
|
|
425
426
|
*(Coincidencia volumétrica, IoU: 1 = idéntico.)*
|
|
426
427
|
|
|
@@ -352,6 +352,7 @@ Si las fotos están borrosas o falta una herramienta, **no reintenta en vano**:
|
|
|
352
352
|
|---|---|---|
|
|
353
353
|
| Recortes perfectos | 0,943 | 0,941 |
|
|
354
354
|
| 15 % de recortes defectuosos | 0,000 (pierde el objeto) | **0,921** |
|
|
355
|
+
| Concavidad invisible a las siluetas (error en la ranura) | 0,154 | **0,091** (refinamiento por fotos, Core 0.3) |
|
|
355
356
|
|
|
356
357
|
*(Coincidencia volumétrica, IoU: 1 = idéntico.)*
|
|
357
358
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.12.0"
|
|
@@ -92,6 +92,8 @@ class HullResult:
|
|
|
92
92
|
engine: str = "reverse3d"
|
|
93
93
|
#: photos reverse3d-core judged inconsistent with the rest (moved object, bad cut-out)
|
|
94
94
|
unreliable: list[str] = field(default_factory=list)
|
|
95
|
+
#: vertices reverse3d-core moved by photo consistency (0 without it)
|
|
96
|
+
refined_vertices: int = 0
|
|
95
97
|
|
|
96
98
|
def to_dict(self) -> dict[str, object]:
|
|
97
99
|
return asdict(self)
|
|
@@ -298,6 +300,9 @@ def _support_plane(
|
|
|
298
300
|
return None
|
|
299
301
|
|
|
300
302
|
|
|
303
|
+
# A re-cut may add what the first boxes cropped (a base: well under +60 %), not more.
|
|
304
|
+
_MAX_GROWTH = 1.6
|
|
305
|
+
|
|
301
306
|
# Base completion: the typical cross-section is measured in this band of the
|
|
302
307
|
# object's height, and a complete one is searched for no higher than this.
|
|
303
308
|
_BASE_TYPICAL = (0.2, 0.6)
|
|
@@ -679,6 +684,20 @@ def visual_hull(
|
|
|
679
684
|
|
|
680
685
|
n = resolution
|
|
681
686
|
|
|
687
|
+
def project_all(grid: np.ndarray) -> list[tuple[np.ndarray, np.ndarray, np.ndarray]]:
|
|
688
|
+
"""Pixel of every voxel in every photo (int16, see below) and whether it is inside."""
|
|
689
|
+
projections = []
|
|
690
|
+
for (small, scale), view in zip(images, views, strict=True):
|
|
691
|
+
uv, z = _project(grid, view)
|
|
692
|
+
# int16 pixel coordinates (work images are at most 640 px): 4x less memory
|
|
693
|
+
# than int64 for millions of voxels times hundreds of photos.
|
|
694
|
+
u = np.clip(np.round(uv[:, 0] * scale), -1, _PIXEL_MAX).astype(np.int16)
|
|
695
|
+
v = np.clip(np.round(uv[:, 1] * scale), -1, _PIXEL_MAX).astype(np.int16)
|
|
696
|
+
inside = (z > 0) & (u >= 0) & (v >= 0) & (u < small.shape[1]) & (v < small.shape[0])
|
|
697
|
+
projections.append((u, v, inside))
|
|
698
|
+
|
|
699
|
+
return projections
|
|
700
|
+
|
|
682
701
|
def frame(
|
|
683
702
|
center: np.ndarray,
|
|
684
703
|
radius: float,
|
|
@@ -715,16 +734,7 @@ def visual_hull(
|
|
|
715
734
|
axis = np.linspace(-radius, radius, n)
|
|
716
735
|
gx, gy, gz = np.meshgrid(axis, axis, axis, indexing="ij")
|
|
717
736
|
grid = np.stack([gx.ravel(), gy.ravel(), gz.ravel()], axis=1) + center
|
|
718
|
-
projections =
|
|
719
|
-
for (small, scale), view in zip(images, views, strict=True):
|
|
720
|
-
uv, z = _project(grid, view)
|
|
721
|
-
# int16 pixel coordinates (work images are at most 640 px): 4x less memory
|
|
722
|
-
# than int64 for millions of voxels times hundreds of photos.
|
|
723
|
-
u = np.clip(np.round(uv[:, 0] * scale), -1, _PIXEL_MAX).astype(np.int16)
|
|
724
|
-
v = np.clip(np.round(uv[:, 1] * scale), -1, _PIXEL_MAX).astype(np.int16)
|
|
725
|
-
inside = (z > 0) & (u >= 0) & (v >= 0) & (u < small.shape[1]) & (v < small.shape[0])
|
|
726
|
-
projections.append((u, v, inside))
|
|
727
|
-
|
|
737
|
+
projections = project_all(grid)
|
|
728
738
|
return masks, grid, projections
|
|
729
739
|
|
|
730
740
|
masks, grid, projections = frame(center, radius)
|
|
@@ -781,32 +791,46 @@ def visual_hull(
|
|
|
781
791
|
return (seen >= 3) & (votes >= seen - allowed)
|
|
782
792
|
|
|
783
793
|
keep = carve(masks, 2)
|
|
794
|
+
|
|
784
795
|
# The cameras' convergence point is biased when many photos are close-ups of one
|
|
785
796
|
# part (a cap): if the carved object reaches the edge of the cube, re-centre the
|
|
786
797
|
# cube on it, enlarge it and start again (masks included, their box depends on it).
|
|
787
|
-
|
|
788
|
-
cube =
|
|
789
|
-
|
|
798
|
+
def touches_edge(mask: np.ndarray) -> bool:
|
|
799
|
+
cube = mask.reshape(n, n, n)
|
|
800
|
+
return bool(
|
|
790
801
|
cube[0].any()
|
|
791
802
|
or cube[-1].any()
|
|
792
803
|
or cube[:, 0].any()
|
|
793
804
|
or cube[:, -1].any()
|
|
794
805
|
or cube[:, :, 0].any()
|
|
795
806
|
or cube[:, :, -1].any()
|
|
796
|
-
)
|
|
797
|
-
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
if touches_edge(keep):
|
|
810
|
+
# Re-centred once; kept only if the object then fits (a cut-out that leaked
|
|
811
|
+
# into the table or the room would keep growing instead).
|
|
812
|
+
saved = (center, radius, masks, grid, keep)
|
|
798
813
|
occupied = grid[keep]
|
|
799
814
|
center = (occupied.min(axis=0) + occupied.max(axis=0)) / 2
|
|
800
815
|
radius *= 1.5
|
|
801
|
-
logger.info("Object reaches the carving volume's edge: re-centred and enlarged")
|
|
802
816
|
projections.clear() # free the previous projections first (gigabytes)
|
|
803
817
|
masks, grid, projections = frame(center, radius)
|
|
804
818
|
keep = carve(masks, 2)
|
|
819
|
+
if touches_edge(keep) or int(keep.sum()) > _MAX_GROWTH * 3.375 * int(saved[4].sum()):
|
|
820
|
+
logger.info("Re-centring did not help; keeping the first carving volume")
|
|
821
|
+
center, radius, masks, grid, keep = saved
|
|
822
|
+
projections.clear()
|
|
823
|
+
projections = project_all(grid)
|
|
824
|
+
else:
|
|
825
|
+
logger.info("Object reached the carving volume's edge: re-centred and enlarged")
|
|
805
826
|
# The first boxes come from the cameras' convergence point and can crop the object
|
|
806
827
|
# (its base, when many photos are close-ups of the top). Re-cut every photo inside
|
|
807
|
-
# a box around where the carved object projects, with margin, while it grows
|
|
828
|
+
# a box around where the carved object projects, with margin, while it grows -
|
|
829
|
+
# but only by what a cropped base can explain: a cut-out that leaks into the
|
|
830
|
+
# table or the room grows far more, and is undone.
|
|
808
831
|
for _ in range(2):
|
|
809
832
|
before = int(keep.sum())
|
|
833
|
+
saved_masks, saved_keep = masks, keep
|
|
810
834
|
boxes = [
|
|
811
835
|
_box_around(keep & inside, u, v, small.shape)
|
|
812
836
|
for (u, v, inside), (small, _) in zip(projections, images, strict=True)
|
|
@@ -814,9 +838,16 @@ def visual_hull(
|
|
|
814
838
|
projections.clear() # free the previous projections first (gigabytes)
|
|
815
839
|
masks, grid, projections = frame(center, radius, boxes)
|
|
816
840
|
keep = carve(masks, 2)
|
|
817
|
-
|
|
841
|
+
after = int(keep.sum())
|
|
842
|
+
if after > _MAX_GROWTH * before or (touches_edge(keep) and not touches_edge(saved_keep)):
|
|
843
|
+
logger.info(
|
|
844
|
+
"Photo boxes leaked into the background (%d -> %d voxels): undone", before, after
|
|
845
|
+
)
|
|
846
|
+
masks, keep = saved_masks, saved_keep
|
|
818
847
|
break
|
|
819
|
-
|
|
848
|
+
if after < 1.05 * before:
|
|
849
|
+
break
|
|
850
|
+
logger.info("Object grew to %d voxels with photo boxes around it", after)
|
|
820
851
|
# The AI segmenter is only loaded (or downloaded) once the photos proved usable.
|
|
821
852
|
ai = segmenter() if segmenter is not None else None
|
|
822
853
|
if ai is not None:
|
|
@@ -934,6 +965,18 @@ def visual_hull(
|
|
|
934
965
|
mesh = trimesh.Trimesh(verts, faces, process=True)
|
|
935
966
|
trimesh.smoothing.filter_taubin(mesh, lamb=0.5, nu=-0.53, iterations=15)
|
|
936
967
|
mesh.fix_normals()
|
|
968
|
+
refined_vertices = 0
|
|
969
|
+
refine_mesh = getattr(core, "refine_mesh", None) if core_cameras is not None else None
|
|
970
|
+
if refine_mesh is not None:
|
|
971
|
+
# reverse3d-core >= 0.3: photo consistency carves what silhouettes cannot see.
|
|
972
|
+
try:
|
|
973
|
+
mesh, stats = refine_mesh(
|
|
974
|
+
mesh, core_cameras, [small for small, _ in images], 0.5 * (2 * radius / (n - 1))
|
|
975
|
+
)
|
|
976
|
+
refined_vertices = int(stats.get("moved", 0))
|
|
977
|
+
logger.info("Photo consistency refined %d vertices", refined_vertices)
|
|
978
|
+
except Exception as exc: # never fail a job because of the optional engine
|
|
979
|
+
logger.warning("reverse3d-core refinement failed (%s); keeping the hull", exc)
|
|
937
980
|
# Real colours from the photos (kept by PLY export; the model gets them as GLB).
|
|
938
981
|
mesh.visual = trimesh.visual.ColorVisuals(
|
|
939
982
|
mesh, vertex_colors=_vertex_colors(mesh, views, images, masks)
|
|
@@ -972,4 +1015,5 @@ def visual_hull(
|
|
|
972
1015
|
]
|
|
973
1016
|
if core_weights is not None and engine != "reverse3d"
|
|
974
1017
|
else [],
|
|
1018
|
+
refined_vertices,
|
|
975
1019
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reverse3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: AI-agent-ready toolkit for 3D reverse engineering: image analysis, photogrammetry, mesh repair, primitive fitting and headless rendering.
|
|
5
5
|
Author-email: Voxel Enterprise <info@voxel.lat>
|
|
6
6
|
Maintainer-email: Voxel Enterprise <info@voxel.lat>
|
|
@@ -421,6 +421,7 @@ Si las fotos están borrosas o falta una herramienta, **no reintenta en vano**:
|
|
|
421
421
|
|---|---|---|
|
|
422
422
|
| Recortes perfectos | 0,943 | 0,941 |
|
|
423
423
|
| 15 % de recortes defectuosos | 0,000 (pierde el objeto) | **0,921** |
|
|
424
|
+
| Concavidad invisible a las siluetas (error en la ranura) | 0,154 | **0,091** (refinamiento por fotos, Core 0.3) |
|
|
424
425
|
|
|
425
426
|
*(Coincidencia volumétrica, IoU: 1 = idéntico.)*
|
|
426
427
|
|
|
@@ -421,3 +421,22 @@ def test_legs_stay_legs_when_the_base_is_completed() -> None:
|
|
|
421
421
|
out = _complete_base(volume, grid, np.array([0.0, 0.0, 1.0]), 0.0, voxel).reshape(-1) > 0.5
|
|
422
422
|
between = (np.abs(x) < 0.4) & (np.abs(y) < 0.4) & (z < 0.5)
|
|
423
423
|
assert not out[between].any()
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class _RefiningCore(_FakeCore):
|
|
427
|
+
"""A fake reverse3d-core >= 0.3 that also refines the mesh by photo consistency."""
|
|
428
|
+
|
|
429
|
+
def refine_mesh(self, mesh, cameras, images, step): # type: ignore[no-untyped-def]
|
|
430
|
+
assert len(cameras) == len(images) and step > 0
|
|
431
|
+
return mesh, {"moved": 42, "vertices": len(mesh.vertices), "mean_offset": 0.0}
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def test_the_core_refines_the_hull_by_photo_consistency(
|
|
435
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
436
|
+
) -> None:
|
|
437
|
+
from reverse3d.reconstruction import silhouette
|
|
438
|
+
|
|
439
|
+
monkeypatch.setattr(silhouette, "core_engine", lambda: _RefiningCore())
|
|
440
|
+
images, text = _scene(tmp_path)
|
|
441
|
+
result = visual_hull(images, text, tmp_path / "hull.ply", resolution=48, refinements=1)
|
|
442
|
+
assert result.refined_vertices == 42
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.11.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|