feat: Update version to 0.13.0 in project files and adjust dependencies
This commit is contained in:
@@ -45,6 +45,7 @@ def _pie_button(
|
||||
refine_selection,
|
||||
generate_normal,
|
||||
mesh_resolution,
|
||||
inward_axis,
|
||||
):
|
||||
operator = pie.operator(
|
||||
"anyimage.cutout_selection_to_shape",
|
||||
@@ -57,6 +58,7 @@ def _pie_button(
|
||||
operator.refine_selection = refine_selection
|
||||
operator.generate_normal = generate_normal
|
||||
operator.mesh_resolution = mesh_resolution
|
||||
operator.inward_axis = inward_axis
|
||||
|
||||
|
||||
def open_shape_pie(
|
||||
@@ -76,6 +78,7 @@ def open_shape_pie(
|
||||
getattr(settings, "cutout_generate_normal", False)
|
||||
)
|
||||
mesh_resolution = cutout_mesh_resolution(source_object.data.size, point_spacing)
|
||||
inward_axis = str(getattr(settings, "cutout_inward_axis", "NEGATIVE_Z"))
|
||||
|
||||
def draw(menu, _context):
|
||||
pie = menu.layout.menu_pie()
|
||||
@@ -91,6 +94,7 @@ def open_shape_pie(
|
||||
refine_selection,
|
||||
generate_normal,
|
||||
mesh_resolution,
|
||||
inward_axis,
|
||||
)
|
||||
|
||||
context.window_manager.popup_menu_pie(
|
||||
|
||||
@@ -45,8 +45,8 @@ from ...common.image import (
|
||||
)
|
||||
from ...common.depth import load_depth_metadata, load_depth_result_image
|
||||
from ...common.viewport import (
|
||||
ImageSelectionGesture,
|
||||
apply_selection_settings,
|
||||
CropSelectionGesture,
|
||||
apply_crop_settings,
|
||||
gesture_property,
|
||||
report_image_edit_exception,
|
||||
resolve_image_edit_click,
|
||||
@@ -64,9 +64,11 @@ from .geometry import (
|
||||
)
|
||||
from .interaction import open_shape_pie
|
||||
from .object import (
|
||||
CutoutInwardAxis,
|
||||
NormalMode,
|
||||
create_cropped_color_image,
|
||||
create_shape_object,
|
||||
orient_object_normal_image,
|
||||
)
|
||||
from .shape import CUTOUT_SHAPES, DEPTH_CUTOUT_SHAPES
|
||||
|
||||
@@ -101,6 +103,7 @@ def create_cutout_shape(
|
||||
depth_metadata=None,
|
||||
normal_image=None,
|
||||
normal_mode=NormalMode.NONE,
|
||||
inward_axis=CutoutInwardAxis.NEGATIVE_Z,
|
||||
color_image,
|
||||
):
|
||||
try:
|
||||
@@ -123,6 +126,7 @@ def create_cutout_shape(
|
||||
depth_metadata=depth_metadata,
|
||||
normal_image=normal_image,
|
||||
normal_mode=normal_mode,
|
||||
inward_axis=inward_axis,
|
||||
color_image=color_image,
|
||||
)
|
||||
except Exception:
|
||||
@@ -235,6 +239,10 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
)
|
||||
generate_normal: bpy.props.BoolProperty(default=False, options=_HIDDEN)
|
||||
refine_selection: bpy.props.BoolProperty(default=False, options=_HIDDEN)
|
||||
inward_axis: bpy.props.StringProperty(
|
||||
default=CutoutInwardAxis.NEGATIVE_Z.value,
|
||||
options=_HIDDEN,
|
||||
)
|
||||
|
||||
def _needs_generation(self):
|
||||
return (
|
||||
@@ -245,6 +253,11 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.inward_axis = getattr(
|
||||
self,
|
||||
"inward_axis",
|
||||
CutoutInwardAxis.NEGATIVE_Z.value,
|
||||
)
|
||||
source_object = require_image_empty(self.source_object_name)
|
||||
if video_total_frames(source_object.data) != 1:
|
||||
raise ValueError("Cutout currently supports still images")
|
||||
@@ -285,6 +298,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
self.mesh_resolution,
|
||||
content_values,
|
||||
selection_mask.bounds,
|
||||
inward_axis=self.inward_axis,
|
||||
color_image=color_image,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
@@ -355,6 +369,11 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
return parameters
|
||||
|
||||
def response(self, context, result):
|
||||
inward_axis = getattr(
|
||||
self,
|
||||
"inward_axis",
|
||||
CutoutInwardAxis.NEGATIVE_Z.value,
|
||||
)
|
||||
source_object = require_image_empty(self.source_object_name)
|
||||
source_bounds = tuple(json.loads(self.selection_bounds_json))
|
||||
color_image, source_alpha, color_bounds = _generated_color_result(
|
||||
@@ -389,6 +408,8 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
result.file(normal_key),
|
||||
source_object,
|
||||
)
|
||||
if normal_mode == NormalMode.OBJECT:
|
||||
orient_object_normal_image(normal_image, inward_axis)
|
||||
depth_metadata = None
|
||||
depth_mode = depth_mode_for_shape(self.shape)
|
||||
if depth_mode != "NONE":
|
||||
@@ -410,6 +431,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
depth_metadata=depth_metadata,
|
||||
normal_image=normal_image,
|
||||
normal_mode=normal_mode,
|
||||
inward_axis=inward_axis,
|
||||
color_image=color_image,
|
||||
)
|
||||
except Exception:
|
||||
@@ -426,7 +448,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
cleanup_image_input(self.input_path, True)
|
||||
|
||||
|
||||
class SelectCutoutSelection(ImageSelectionGesture, bpy.types.Operator):
|
||||
class SelectCutoutSelection(CropSelectionGesture, bpy.types.Operator):
|
||||
bl_idname = "anyimage.select_cutout_selection"
|
||||
bl_label = "Start Cutout Selection"
|
||||
bl_description = "Lasso an Image Empty selection, then choose its mesh shape"
|
||||
@@ -444,10 +466,8 @@ class SelectCutoutSelection(ImageSelectionGesture, bpy.types.Operator):
|
||||
_pie_event = None
|
||||
|
||||
def _set_status(self, context):
|
||||
invert = "On" if self.invert else "Off"
|
||||
context.workspace.status_text_set(
|
||||
f"Cutout Lasso: drag and release for shapes; "
|
||||
f"F toggles Invert ({invert}); RMB or Esc cancels"
|
||||
"Cutout Lasso: drag and release for shapes; RMB or Esc cancels"
|
||||
)
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -462,9 +482,9 @@ class SelectCutoutSelection(ImageSelectionGesture, bpy.types.Operator):
|
||||
if source_object is None:
|
||||
return {"CANCELLED"}
|
||||
if video_total_frames(source_object.data) > 1:
|
||||
self.report({"WARNING"}, "Image tools only support a still image")
|
||||
self.report({"WARNING"}, "Cutout only supports a still image")
|
||||
return {"CANCELLED"}
|
||||
apply_selection_settings(self, context)
|
||||
apply_crop_settings(self, context)
|
||||
self.invert = False
|
||||
self.source_object_name = source_object.name
|
||||
self.source_matrix_data = serialize_matrix(source_object.matrix_world)
|
||||
@@ -476,7 +496,7 @@ class SelectCutoutSelection(ImageSelectionGesture, bpy.types.Operator):
|
||||
|
||||
def modal(self, context, event):
|
||||
self._pie_event = event
|
||||
return super().modal(context, event)
|
||||
return CropSelectionGesture.modal(self, context, event)
|
||||
|
||||
def _submit_selection_path(self, context, selection_path):
|
||||
try:
|
||||
@@ -500,7 +520,7 @@ class CutoutTool(bpy.types.WorkSpaceTool):
|
||||
bl_idname = "anyimage.cutout_lasso"
|
||||
bl_label = "Cutout"
|
||||
bl_description = "Lasso an Image Empty and choose a mesh shape"
|
||||
bl_icon = "ops.sculpt.lasso_mask"
|
||||
bl_icon = "ops.mesh.primitive_sphere_add_gizmo"
|
||||
bl_operator = SelectCutoutSelection.bl_idname
|
||||
bl_keymap = (
|
||||
(
|
||||
@@ -521,3 +541,4 @@ class CutoutTool(bpy.types.WorkSpaceTool):
|
||||
draw_ai_property(layout, settings, "cutout_refine_selection", status)
|
||||
draw_ai_property(layout, settings, "cutout_generate_normal", status)
|
||||
layout.prop(settings, "cutout_mesh_detail")
|
||||
layout.prop(settings, "cutout_inward_axis")
|
||||
|
||||
@@ -31,6 +31,7 @@ from .geometry import (
|
||||
from .shape import (
|
||||
BALLOON_ATTRIBUTE_NAME,
|
||||
BALLOON_CUTOUT_SHAPES,
|
||||
CUTOUT_AXIS_TO_SOURCE,
|
||||
CUTOUT_NODE_GROUP_NAME,
|
||||
CUTOUT_SHAPE_INDICES,
|
||||
DEPTH_CUTOUT_SHAPES,
|
||||
@@ -43,23 +44,38 @@ class NormalMode(StrEnum):
|
||||
OBJECT = "OBJECT"
|
||||
|
||||
|
||||
CUTOUT_AXIS_TO_SOURCE = (
|
||||
(0.0, -1.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 1.0, 0.0),
|
||||
(-1.0, 0.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
class CutoutInwardAxis(StrEnum):
|
||||
NEGATIVE_Z = "NEGATIVE_Z"
|
||||
POSITIVE_X = "POSITIVE_X"
|
||||
|
||||
|
||||
def _cutout_world_matrix(source_matrix, crop_center):
|
||||
def _cutout_world_matrix(source_matrix, crop_center, inward_axis):
|
||||
from mathutils import Matrix
|
||||
|
||||
center_x, center_y = (float(value) for value in crop_center)
|
||||
return (
|
||||
source_matrix
|
||||
@ Matrix.Translation((center_x, center_y, 0.0))
|
||||
@ Matrix(CUTOUT_AXIS_TO_SOURCE)
|
||||
)
|
||||
matrix = source_matrix @ Matrix.Translation((center_x, center_y, 0.0))
|
||||
if CutoutInwardAxis(inward_axis) == CutoutInwardAxis.POSITIVE_X:
|
||||
matrix @= Matrix(CUTOUT_AXIS_TO_SOURCE)
|
||||
return matrix
|
||||
|
||||
|
||||
def orient_object_normal_image(image, inward_axis):
|
||||
"""Convert internal +X Cutout normals to the selected object axes."""
|
||||
if CutoutInwardAxis(inward_axis) == CutoutInwardAxis.POSITIVE_X:
|
||||
return image
|
||||
import numpy as np
|
||||
|
||||
pixels = np.empty(int(image.size[0]) * int(image.size[1]) * 4, dtype=np.float32)
|
||||
image.pixels.foreach_get(pixels)
|
||||
rgba = pixels.reshape((-1, 4))
|
||||
normal = rgba[:, :3] * 2.0 - 1.0
|
||||
rgba[:, :3] = np.column_stack(
|
||||
(-normal[:, 1], normal[:, 2], -normal[:, 0])
|
||||
) * 0.5 + 0.5
|
||||
image.pixels.foreach_set(pixels)
|
||||
image.update()
|
||||
image.pack()
|
||||
return image
|
||||
|
||||
|
||||
def _assign_uv(mesh, uv):
|
||||
@@ -149,11 +165,13 @@ def create_shape_object(
|
||||
depth_metadata=None,
|
||||
normal_image=None,
|
||||
normal_mode=NormalMode.NONE,
|
||||
inward_axis=CutoutInwardAxis.NEGATIVE_Z,
|
||||
color_image,
|
||||
):
|
||||
import numpy as np
|
||||
|
||||
normal_mode = NormalMode(normal_mode)
|
||||
inward_axis = CutoutInwardAxis(inward_axis)
|
||||
bounds = crop_plane_bounds(
|
||||
source_object,
|
||||
content_bounds,
|
||||
@@ -245,6 +263,7 @@ def create_shape_object(
|
||||
mesh_object.matrix_world = _cutout_world_matrix(
|
||||
source_object.matrix_world,
|
||||
crop_center,
|
||||
inward_axis,
|
||||
)
|
||||
mesh_object["anyimage_mesh_shape"] = shape
|
||||
node_group = load_cutout_node_group()
|
||||
@@ -253,6 +272,10 @@ def create_shape_object(
|
||||
modifier.show_group_selector = False
|
||||
for name, value in (
|
||||
("Shape", CUTOUT_SHAPE_INDICES[shape]),
|
||||
(
|
||||
"Inward Axis",
|
||||
0 if inward_axis == CutoutInwardAxis.NEGATIVE_Z else 1,
|
||||
),
|
||||
("Cleanup", cleanup_value),
|
||||
("Smooth Iterations", 0),
|
||||
("Smooth", 1.0),
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
BALLOON_ATTRIBUTE_NAME = "o_balloon"
|
||||
CUTOUT_NODE_GROUP_NAME = "O Image Cutout"
|
||||
CUTOUT_AXIS_TO_SOURCE = (
|
||||
(0.0, -1.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 1.0, 0.0),
|
||||
(-1.0, 0.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
CUTOUT_NODE_GROUP_NAMES = {
|
||||
"SURFACE": "O Image Surface",
|
||||
"BALLOON": "O Image Balloon",
|
||||
|
||||
Reference in New Issue
Block a user