Add tests for image layer, image plane, inventory, saved layout, commands, documentation, manifest, and packaging
- Implement tests for image layer functionality including alpha curve and depth layer behavior. - Create tests for image plane evaluation and modifier input handling. - Add tests to verify the saved asset inventory and its persistence. - Introduce tests for the documentation command to ensure correct project version deployment. - Validate the extension manifest structure and required permissions. - Develop tests for the packaging process, ensuring correct platform handling and dependency management. - Enhance image depth plane group functionality with new split threshold input and surface processing.
This commit is contained in:
207
tests/support/image_objects.py
Normal file
207
tests/support/image_objects.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import bpy
|
||||
import numpy as np
|
||||
|
||||
|
||||
from anyimage.common import image as image_data
|
||||
from anyimage.common import object as object_data
|
||||
from anyimage.common.selection import SelectionMask
|
||||
from anyimage.operators.cutout_tool import geometry as cutout_geometry
|
||||
from anyimage.operators.cutout_tool import object as cutout_object
|
||||
|
||||
|
||||
def _inputs(group):
|
||||
return [
|
||||
item.name
|
||||
for item in group.interface.items_tree
|
||||
if item.item_type == "SOCKET" and item.in_out == "INPUT"
|
||||
]
|
||||
|
||||
|
||||
def _group_node(group, node_tree):
|
||||
return next(
|
||||
node
|
||||
for node in node_tree.nodes
|
||||
if node.bl_idname == "ShaderNodeGroup" and node.node_tree == group
|
||||
)
|
||||
|
||||
|
||||
def _create_cutout_shape(
|
||||
source,
|
||||
shape,
|
||||
detail,
|
||||
*,
|
||||
bounds=None,
|
||||
depth_image=None,
|
||||
depth_metadata=None,
|
||||
selection_values=None,
|
||||
inward_axis=cutout_object.CutoutInwardAxis.NEGATIVE_Z,
|
||||
):
|
||||
if bounds is None:
|
||||
bounds = (0, 0, *tuple(int(value) for value in source.data.size))
|
||||
left, top, right, bottom = bounds
|
||||
if selection_values is None:
|
||||
selection_values = np.ones((bottom - top, right - left), dtype=np.float32)
|
||||
selection_mask = SelectionMask(selection_values, bounds)
|
||||
source_rgba = image_data.image_rgba(source.data)
|
||||
color_image = cutout_object.create_cropped_color_image(
|
||||
source,
|
||||
selection_mask.bounds,
|
||||
source_rgba,
|
||||
)
|
||||
base_shape = cutout_geometry.build_base_shape(
|
||||
bpy.context,
|
||||
source,
|
||||
detail,
|
||||
selection_mask.values,
|
||||
selection_mask.bounds,
|
||||
)
|
||||
return cutout_object.create_shape_object(
|
||||
bpy.context,
|
||||
source,
|
||||
shape,
|
||||
detail,
|
||||
selection_mask.values,
|
||||
selection_mask.bounds,
|
||||
base_shape,
|
||||
depth_image=depth_image,
|
||||
depth_metadata=depth_metadata,
|
||||
inward_axis=inward_axis,
|
||||
color_image=color_image,
|
||||
)
|
||||
|
||||
|
||||
def _depth_image(points, valid, name="Depth"):
|
||||
points = np.asarray(points, dtype=np.float32)
|
||||
valid = np.asarray(valid, dtype=bool)
|
||||
height, width = valid.shape
|
||||
pixels = np.zeros((height, width, 4), dtype=np.float32)
|
||||
pixels[..., :3] = points
|
||||
pixels[..., 3] = valid
|
||||
image = bpy.data.images.new(
|
||||
name,
|
||||
width=width,
|
||||
height=height,
|
||||
alpha=True,
|
||||
float_buffer=True,
|
||||
)
|
||||
image.colorspace_settings.name = "Non-Color"
|
||||
image.alpha_mode = "CHANNEL_PACKED"
|
||||
image.pixels.foreach_set(np.flipud(pixels).ravel())
|
||||
image.pack()
|
||||
return image
|
||||
|
||||
|
||||
def _depth_metadata(intrinsics, image_size, reference_depth):
|
||||
return {
|
||||
"intrinsics": tuple(tuple(float(value) for value in row) for row in intrinsics),
|
||||
"image_size": tuple(int(value) for value in image_size),
|
||||
"reference_depth": float(reference_depth),
|
||||
}
|
||||
|
||||
|
||||
def _evaluated_positions(obj):
|
||||
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
||||
result = evaluated.to_mesh()
|
||||
try:
|
||||
return np.asarray([tuple(vertex.co) for vertex in result.vertices])
|
||||
finally:
|
||||
evaluated.to_mesh_clear()
|
||||
|
||||
|
||||
def _add_cutout_modifier(obj, shape):
|
||||
group = cutout_object.load_cutout_node_group(shape)
|
||||
modifier = obj.modifiers.new(group.name, "NODES")
|
||||
modifier.node_group = group
|
||||
if shape in {"FLAT", "DEPTH_SOLID"}:
|
||||
object_data.set_modifier_input(modifier, object_data.modifier_input_identifier(group, "Mode"), 1)
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(group, "Inward Axis"),
|
||||
0,
|
||||
)
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(group, "Cleanup Threshold"),
|
||||
0.0,
|
||||
)
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(group, "Smooth Weight"),
|
||||
0.0,
|
||||
)
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(group, "Smooth"),
|
||||
1,
|
||||
)
|
||||
return modifier
|
||||
|
||||
|
||||
def _depth_surface_step_object():
|
||||
vertices = [(float(x), float(y), 0.0) for y in range(3) for x in range(3)]
|
||||
faces = []
|
||||
for y in range(2):
|
||||
for x in range(2):
|
||||
a = y * 3 + x
|
||||
faces.extend(((a, a + 1, a + 4), (a, a + 4, a + 3)))
|
||||
data = bpy.data.meshes.new("Depth Surface Step")
|
||||
data.from_pydata(vertices, (), faces)
|
||||
uv_layer = data.uv_layers.new(name="UVMap")
|
||||
for loop in data.loops:
|
||||
coordinate = data.vertices[loop.vertex_index].co
|
||||
uv_layer.data[loop.index].uv = (coordinate.x * 0.5, coordinate.y * 0.5)
|
||||
obj = bpy.data.objects.new("Depth Surface Step", data)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
modifier = _add_cutout_modifier(obj, "DEPTH_SOLID")
|
||||
|
||||
points = np.empty((3, 3, 3), dtype=np.float32)
|
||||
for y in range(3):
|
||||
for x in range(3):
|
||||
points[y, x] = (float(x), -float(y), 1.0)
|
||||
points[0, 0, 2] = 6.0
|
||||
depth_image = _depth_image(points, np.ones((3, 3), dtype=bool))
|
||||
for name, value in (
|
||||
("Depth Image", depth_image),
|
||||
("Depth Scale", 1.0),
|
||||
("Uniform Scale", 1.0),
|
||||
("Reference Depth", 1.0),
|
||||
("Thickness", 0.0),
|
||||
("Smooth", 0),
|
||||
):
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(modifier.node_group, name),
|
||||
value,
|
||||
)
|
||||
return obj, modifier
|
||||
|
||||
|
||||
def _evaluated_topology(obj):
|
||||
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
||||
result = evaluated.to_mesh()
|
||||
try:
|
||||
face_edges = {
|
||||
tuple(sorted((start, end)))
|
||||
for polygon in result.polygons
|
||||
for start, end in zip(
|
||||
polygon.vertices,
|
||||
(*polygon.vertices[1:], polygon.vertices[0]),
|
||||
)
|
||||
}
|
||||
mesh_edges = {
|
||||
tuple(sorted(edge.vertices))
|
||||
for edge in result.edges
|
||||
}
|
||||
face_vertices = {
|
||||
vertex
|
||||
for polygon in result.polygons
|
||||
for vertex in polygon.vertices
|
||||
}
|
||||
return (
|
||||
len(result.vertices),
|
||||
len(result.polygons),
|
||||
mesh_edges - face_edges,
|
||||
set(range(len(result.vertices))) - face_vertices,
|
||||
)
|
||||
finally:
|
||||
evaluated.to_mesh_clear()
|
||||
Reference in New Issue
Block a user