def _diff(self, struct: T.Object, key: str, prop: T.Property, context: Context, diff: Proxy) -> Optional[Delta]: from mixer.blender_data.attributes import diff_attribute must_replace = False data_datablock = struct.data if data_datablock is not None: dirty_vertex_groups = data_datablock.mixer_uuid in context.visit_state.dirty_vertex_groups # Replace the whole Object. Otherwise we would have to merge a DeltaReplace for vertex_groups # and a DeltaUpdate for the remaining items logger.debug(f"_diff: {struct} dirty vertex group: replace") must_replace |= dirty_vertex_groups if not must_replace: # Parenting with ctrl-P generates a Delta with parent, local_matrix and matrix_parent_inverse. # Applying this delta causes a position shift in the parented object. A full replace fixes the problem. # Not that parenting with just updating the parent property in the property panel does not cause # the same problem parent_property = struct.bl_rna.properties["parent"] parent_delta = diff_attribute(struct.parent, "parent", parent_property, self._data["parent"], context) must_replace |= parent_delta is not None if must_replace: diff.load(struct, context) return DeltaReplace(diff) else: return super()._diff(struct, key, prop, context, diff)
def diff(self, datablock: T.ID, key: Union[int, str], datablock_property: T.Property, context: Context) -> Optional[DeltaReplace]: """ Computes the difference between this proxy and its Blender state. """ if datablock is None: return DeltaReplace(DatablockRefProxy()) value = read_attribute(datablock, key, datablock_property, None, context) assert isinstance(value, DatablockRefProxy) if value._datablock_uuid != self._datablock_uuid: return DeltaReplace(value) else: return None
def _diff(self, struct: T.Mesh, key: str, prop: T.Property, context: Context, diff: MeshProxy) -> Optional[Union[DeltaUpdate, DeltaReplace]]: if self.requires_clear_geometry(struct): # If any mesh buffer changes requires a clear geometry on the receiver, the receiver will clear all # buffers, including uv_layers and vertex_colors. # Resend everything diff.load(struct, context) # force ObjectProxy._diff to resend the Vertex groups context.visit_state.dirty_vertex_groups.add(struct.mixer_uuid) return DeltaReplace(diff) else: if prop is not None: context.visit_state.path.append(key) try: # vertex groups are always replaced as a whole mesh_vertex_groups = VertexGroups.from_mesh( struct).to_array_sequence() proxy_vertex_groups: ArrayGroup = self._arrays.get( "vertex_groups", []) if mesh_vertex_groups != proxy_vertex_groups: diff._arrays["vertex_groups"] = mesh_vertex_groups # force Object update. This requires that Object updates are processed later, which seems to be # the order they are listed in Depsgraph.updates context.visit_state.dirty_vertex_groups.add( struct.mixer_uuid) properties = context.synchronized_properties.properties(struct) properties = specifics.conditional_properties( struct, properties) for k, member_property in properties: try: member = getattr(struct, k) except AttributeError: logger.warning("diff: unknown attribute ...") logger.warning( f"... {context.visit_state.display_path()}.{k}") continue proxy_data = self._data.get(k) delta = diff_attribute(member, k, member_property, proxy_data, context) if delta is not None: diff._data[k] = delta finally: if prop is not None: context.visit_state.path.pop() if len(diff._data) or len(diff._arrays): return DeltaUpdate(diff) return None
def diff( self, collection: T.bpy_prop_collection, key: Union[int, str], collection_property: T.Property, context: Context ) -> Optional[Union[DeltaUpdate, DeltaReplace]]: """ Computes the difference between the state of an item tracked by this proxy and its Blender state. This proxy tracks a collection of items indexed by string (e.g Scene.render.views) or int. The result will be a ProxyDiff that contains a Delta item per added, deleted or updated item Args: collection; the collection that must be diffed agains this proxy key: the name of the collection, to record in the visit path collection_property; the property os collection as found in its enclosing object """ sequence = self._sequence if len(sequence) == 0 and len(collection) == 0: return None if specifics.diff_must_replace(collection, sequence, collection_property): # A collection cannot be updated because either: # - some of its members cannot be updated : # SplineBezierPoints has no API to remove points, so Curve.splines cannot be update and must be replaced # - updating the name of members will cause unsolicited renames. # When swapping layers A and B in a GreasePencilLayers, renaming layer 0 into B cause an unsolicited # rename of layer 0 into B.001 # Send a replacement for the whole collection self.load(collection, context) return DeltaReplace(self) else: item_property = collection_property.fixed_type diff = self.__class__() # items from clear_from index cannot be updated, most often because eir type has changed (e.g # ObjectModifier) clear_from = specifics.clear_from(collection, sequence, context) # run a diff for the head, that can be updated in-place for i in range(clear_from): delta = diff_attribute(collection[i], i, item_property, sequence[i], context) if delta is not None: diff._diff_updates.append((i, delta)) if specifics.can_resize(collection, context): # delete the existing tail that cannot be modified diff._diff_deletions = len(sequence) - clear_from # add the new tail for i, item in enumerate(collection[clear_from:], clear_from): value = read_attribute(item, i, item_property, collection, context) diff._diff_additions.append(DeltaAddition(value)) if diff._diff_updates or diff._diff_deletions or diff._diff_additions: return DeltaUpdate(diff) return None
def diff( self, container: Union[T.bpy_prop_collection, T.Struct], key: Union[str, int], prop: T.Property, context: Context, ) -> Optional[DeltaUpdate]: attr = read_attribute(container, key, prop, None, context) if isinstance(attr, NonePtrProxy): return None return DeltaReplace(attr)
def _diff(self, datablock: T.ID, key: Union[int, str], prop: T.Property, context: Context, diff: DatablockProxy) -> Optional[Delta]: key_blocks = datablock.key_blocks key_bocks_property = datablock.bl_rna.properties["key_blocks"] key_blocks_proxy = self._data["key_blocks"] must_replace = specifics.diff_must_replace(key_blocks, key_blocks_proxy._sequence, key_bocks_property) if must_replace: # The Key.key_blocks collection must be replaced, and the receiver must call Object.shape_key_clear(), # causing the removal of the Key datablock. # Ensure that the whole Key data is available to be reloaded after clear() diff.load(datablock, context) return DeltaReplace(diff) else: # this delta is processed by the regular apply return super()._diff(datablock, key, prop, context, diff)
def diff(self, attribute: Set[Any], unused_key: Union[int, str], unused_prop: T.Property, unused_context: Context) -> Optional[Delta]: """ Computes the difference between the state of an item tracked by this proxy and its Blender state. Args: attribute: the set to update (e.g. a the "delimit" attribute of a DecimateModifier instance) unused_key: the key that identifies attribute in parent (e.g "delimit") unused_prop: the Property of attribute as found in its parent attribute unused_context: proxy and visit state """ if set(self.items) == attribute: return None new_set = SetProxy() new_set.items = attribute return DeltaReplace(new_set)