2026年7月20日月曜日

How to transfer root bone pose animation to the animation of the entire armature 5.x

 import bpy

import math


from mathutils import Matrix

from bpy_extras import anim_utils



# ============================================================

# Settings

# ============================================================


ROOT_BONE_NAME = "Root"


# If True:

#   Bake a keyframe on every frame between the first and last

#   root bone keyframe.

#   This preserves interpolation more accurately, but creates

#   many additional keyframes.

#

# If False:

#   Process only frames that already contain root bone keys.

#   This behaves similarly to the original version of the script.

BAKE_EVERY_FRAME = False


# Delete the root bone after the transfer is complete.

DELETE_ROOT_BONE = True



# ============================================================

# Blender 5.x / Legacy Action Compatibility

# ============================================================


def get_action_fcurves(animated_id):

    """

    Return all F-Curves from the Action currently assigned

    to the specified animated datablock.


    Blender 4.x and earlier:

        action.fcurves


    Blender 5.x:

        Action Slot

            -> Channelbag

                -> F-Curves

    """


    animation_data = animated_id.animation_data


    if animation_data is None:

        return []


    action = animation_data.action


    if action is None:

        return []


    # Legacy Blender API.

    if hasattr(action, "fcurves"):

        return list(action.fcurves)


    # Blender 5.x layered Action API.

    action_slot = getattr(animation_data, "action_slot", None)


    # Normally animation_data.action_slot is available.

    # As a fallback, use the only slot if the Action has exactly one.

    if action_slot is None:

        slots = getattr(action, "slots", None)


        if slots is not None and len(slots) == 1:

            action_slot = slots[0]


    if action_slot is None:

        print(

            f"Warning: No Action slot was found for "

            f"'{animated_id.name}'."

        )

        return []


    channelbag = anim_utils.action_get_channelbag_for_slot(

        action,

        action_slot

    )


    if channelbag is None:

        return []


    return list(channelbag.fcurves)



def collect_keyframes(fcurves, data_path_prefix=None):

    """

    Collect all unique keyframe times from a list of F-Curves.


    If data_path_prefix is specified, only F-Curves whose

    data paths start with that prefix are included.

    """


    frames = set()


    for fcurve in fcurves:

        if (

            data_path_prefix is not None

            and not fcurve.data_path.startswith(data_path_prefix)

        ):

            continue


        for keyframe in fcurve.keyframe_points:

            frames.add(float(keyframe.co.x))


    return sorted(frames)



def set_scene_frame(scene, frame):

    """

    Set the current timeline position with subframe support,

    then update the dependency graph.

    """


    whole_frame = math.floor(frame)

    subframe = frame - whole_frame


    scene.frame_set(whole_frame, subframe=subframe)

    bpy.context.view_layer.update()



# ============================================================

# Main

# ============================================================


scene = bpy.context.scene


# Store the original timeline position so it can be restored later.

original_frame = scene.frame_current + scene.frame_subframe


# Use the currently active object as the source armature.

armature = bpy.context.active_object


if armature is None or armature.type != 'ARMATURE':

    raise ValueError("Please select an armature object.")


bpy.context.view_layer.objects.active = armature

armature.select_set(True)


# Switch to Object Mode before reading transforms.

if armature.mode != 'OBJECT':

    bpy.ops.object.mode_set(mode='OBJECT')



# ============================================================

# Validate Root Bone

# ============================================================


root_pose_bone = armature.pose.bones.get(ROOT_BONE_NAME)


if root_pose_bone is None:

    raise ValueError(

        f"Root bone '{ROOT_BONE_NAME}' was not found."

    )


root_data_bone = armature.data.bones.get(ROOT_BONE_NAME)


if root_data_bone is None:

    raise ValueError(

        f"Root data bone '{ROOT_BONE_NAME}' was not found."

    )



# ============================================================

# Store Root Bone Rest Transform

# ============================================================


armature_matrix_world = armature.matrix_world.copy()

root_bone_matrix_rest = root_data_bone.matrix_local.copy()


root_bone_matrix_world_rest = (

    armature_matrix_world

    @ root_bone_matrix_rest

)


(

    root_bone_default_loc,

    root_bone_default_rot,

    root_bone_default_scale

) = root_bone_matrix_world_rest.decompose()


inverse_root_bone_default_rot = (

    root_bone_default_rot.inverted()

)



# ============================================================

# Collect IK Targets

# ============================================================


ik_targets = []


for pose_bone in armature.pose.bones:

    for constraint in pose_bone.constraints:


        if constraint.type != 'IK':

            continue


        target = constraint.target


        if target is None:

            continue


        # Only process external IK target objects parented

        # to the selected armature.

        if target.parent != armature:

            continue


        # Prevent duplicate targets from being added.

        if target not in ik_targets:

            ik_targets.append(target)



# ============================================================

# Store Original IK Target World Positions

# ============================================================


ik_target_keyframes = {}


for target in ik_targets:

    target_fcurves = get_action_fcurves(target)

    target_frames = collect_keyframes(target_fcurves)


    ik_target_keyframes[target.name] = {}


    for frame in target_frames:

        set_scene_frame(scene, frame)


        ik_target_keyframes[target.name][frame] = (

            target.matrix_world.translation.copy()

        )



# ============================================================

# Get Armature Action

# ============================================================


animation_data = armature.animation_data


if animation_data is None:

    raise ValueError("Animation data was not found.")


action = animation_data.action


if action is None:

    raise ValueError("No Action is assigned to the armature.")


armature_fcurves = get_action_fcurves(armature)


if not armature_fcurves:

    raise ValueError(

        "No F-Curves were found in the armature Action."

    )



# ============================================================

# Collect Root Bone Keyframes

# ============================================================


root_data_path_prefix = (

    f'pose.bones["{ROOT_BONE_NAME}"]'

)


root_keyframes = collect_keyframes(

    armature_fcurves,

    root_data_path_prefix

)


if not root_keyframes:

    raise ValueError(

        f"No animation keys were found for "

        f"root bone '{ROOT_BONE_NAME}'."

    )


# Optionally bake every integer frame between

# the first and last root bone keyframe.

if BAKE_EVERY_FRAME:

    first_frame = math.floor(min(root_keyframes))

    last_frame = math.ceil(max(root_keyframes))


    sample_frames = [

        float(frame)

        for frame in range(first_frame, last_frame + 1)

    ]


else:

    sample_frames = root_keyframes



# ============================================================

# Sample Root Bone Transforms

#

# At this stage, transforms are only recorded.

# No keys are written to the armature object yet.

#

# This prevents newly inserted armature object keyframes

# from affecting transform evaluation on later frames.

# ============================================================


transform_data = {}


for frame in sample_frames:

    set_scene_frame(scene, frame)


    # PoseBone.matrix is expressed in armature object space.

    root_bone_matrix = root_pose_bone.matrix.copy()


    # Convert the root bone transform to world space.

    global_matrix = (

        armature.matrix_world

        @ root_bone_matrix

    )


    global_loc, global_rot, global_scale = (

        global_matrix.decompose()

    )


    transform_data[frame] = {

        "location": global_loc.copy(),

        "rotation": global_rot.copy(),

        "scale": global_scale.copy(),

    }



# ============================================================

# Bake the Root Bone Transform to the Armature Object

# ============================================================


bpy.ops.object.mode_set(mode='OBJECT')


# Quaternion rotation avoids Euler angle discontinuities

# during the transfer.

armature.rotation_mode = 'QUATERNION'


translation_matrix_to_default = Matrix.Translation(

    -root_bone_default_loc

)


for frame in sample_frames:

    data = transform_data[frame]


    loc = data["location"]

    rot = data["rotation"]

    scale = data["scale"]


    # Remove the root bone's rest rotation.

    corrected_rot = (

        rot

        @ inverse_root_bone_default_rot

    )


    translation_matrix_current = Matrix.Translation(loc)

    rotation_matrix = corrected_rot.to_matrix().to_4x4()


    # Apply the same transform order as the original script:

    #   1. Translate relative to the root bone's rest location.

    #   2. Apply the corrected root bone rotation.

    #   3. Move to the current root bone location.

    final_matrix = (

        translation_matrix_current

        @ rotation_matrix

        @ translation_matrix_to_default

    )


    final_loc, final_rot, _ = final_matrix.decompose()


    armature.location = final_loc

    armature.rotation_quaternion = final_rot

    armature.scale = scale


    armature.keyframe_insert(

        data_path="location",

        frame=frame

    )


    armature.keyframe_insert(

        data_path="rotation_quaternion",

        frame=frame

    )


    armature.keyframe_insert(

        data_path="scale",

        frame=frame

    )



# ============================================================

# Preserve IK Target World Positions

# ============================================================


for target in ik_targets:

    saved_frames = ik_target_keyframes.get(target.name, {})


    for frame, initial_world_position in saved_frames.items():

        set_scene_frame(scene, frame)


        # Convert the stored world-space position back into

        # the target's armature-relative local space using

        # the armature transform at the current frame.

        local_position = (

            armature.matrix_world.inverted()

            @ initial_world_position

        )


        target.location = local_position


        target.keyframe_insert(

            data_path="location",

            frame=frame

        )



# ============================================================

# Delete Root Bone

# ============================================================


if DELETE_ROOT_BONE:

    bpy.context.view_layer.objects.active = armature

    armature.select_set(True)


    bpy.ops.object.mode_set(mode='EDIT')


    edit_root_bone = armature.data.edit_bones.get(

        ROOT_BONE_NAME

    )


    if edit_root_bone is not None:

        armature.data.edit_bones.remove(edit_root_bone)


    bpy.ops.object.mode_set(mode='OBJECT')



# ============================================================

# Restore Original Timeline Position

# ============================================================


set_scene_frame(scene, original_frame)


print(

    f"Finished. Root bone animation has been transferred "

    f"to the armature object '{armature.name}'."

)


2026年7月19日日曜日

Privacy Policy for Living Diorama

Privacy Policy for Living Diorama

Last updated: 19 July 2026

Living Diorama respects your privacy.

Data Collection

Living Diorama does not collect, store, transmit or share any personal information or usage data.

The app does not collect information such as:

  • Names or contact details

  • Location data

  • Device identifiers

  • Usage or interaction data

  • Photos, videos or audio

  • Health or fitness data

  • Purchase information

Interactions with animals, food and other elements within the app are not recorded or transmitted to the developer.

Analytics, Advertising and Tracking

Living Diorama does not use third-party analytics services, advertising networks or tracking technologies.

The app does not track users across apps or websites and does not display personalised advertising.

Third-Party Data Sharing

Living Diorama does not share user data with third parties because the app does not collect user data.

Data Storage and Deletion

Living Diorama does not store personal information on external servers.

As no personal information is collected or retained by the developer, there is no personal data to access, export or delete.

Children’s Privacy

Living Diorama does not knowingly collect personal information from children or any other users.

Changes to This Privacy Policy

This Privacy Policy may be updated if the app’s features or data practices change. Any changes will be published on this page, together with an updated revision date.


2026年7月8日水曜日

Swipe to Collapse - A Quantum Wave Game

 Overview

Swipe to Collapse is a single-player iPhone game and interactive quantum wave simulation.

This app displays a real-time simulation of the Schrödinger equation using compute shaders.

A quantum wave function lives on the grid. Its colour is the phase and its brightness is how likely the particle is to be there (|ψ|²). You play by observing it.

App purpose and target audience

The app lets users play with a quantum wave function. The color represents phase, and the brightness represents probability density, |ψ|². Users interact with the wave by dragging across the field to observe a selected region. When the user releases, the wave function collapses probabilistically according to the Born rule.

The app is designed for users who enjoy physics-inspired games, experimental arcade games, and interactive science visualizations. Its value is to turn abstract quantum concepts such as observation, probability density, phase, wave function collapse, and position/momentum representations into an interactive visual experience.

The app is intended for entertainment and educational exploration. It is not a professional scientific, medical, financial, or regulated-industry tool.

Observe

Drag across the field to outline a region, then let go to measure it. The wave collapses by the Born rule — the particle is either found inside your region (it snaps bright there) or not (that area goes dark). Pick a stage and try it freely before starting a game.

Game

Tap Start for a 30-second round. Green particles roam the field and score continuously — the brighter the wave |ψ| where a particle sits, the faster it scores. Observe (drag) to collapse the wave and pile it onto the green particles. Red particles (stage 2+) drain your score the same way, so keep the wave dark around them. Best 3 scores per stage are saved. Tap × to quit.

Z/pZ Lab — position & momentum together

An experimental mode on the finite set Z/pZ, where position and momentum are both finite and the Fourier transform links them. Two strips show \psi(x) and \hat{\psi}(k) at once — drag either one to observe in that basis. The carpet below charts the wave over time; watch the fractal revivals. Switch p, potential, kinetic term, initial state and more.

2026年7月5日日曜日

Privacy Policy for Swipe to Collapse

 Privacy Policy

Effective Date: July 5, 2026

This Privacy Policy applies to Swipe to Collapse, an iPhone app developed by Junnichi Suko.

No Data Collection

Swipe to Collapse does not collect, store, transmit, or share personal data with the developer or any third party.

The app does not collect or transmit:

  • Name
  • Email address
  • Location data
  • Contacts
  • Photos or videos
  • Audio recordings
  • Device identifiers
  • Advertising identifiers
  • Analytics data
  • User-generated content

Local Game Data

The app may save certain game data locally on your device, such as game progress or high scores.

This data is stored only on your device and is not transmitted to the developer or to any external server.

If you delete the app, this locally stored data may also be deleted.

No Advertising or Tracking

The app does not display advertisements.

The app does not use third-party advertising networks.

The app does not track users across apps, websites, or services.

No Third-Party Analytics

The app does not use third-party analytics or crash reporting services.

Internet Connection

Core gameplay does not require an internet connection.

The app does not send personal information or gameplay data to the developer or to any external server.

Children’s Privacy

The app does not knowingly collect any personal information from children or adults.

Changes to This Privacy Policy

This Privacy Policy may be updated in the future if the app’s features change.

If the app ever begins collecting data, this Privacy Policy will be updated to describe what data is collected, how it is used, and any choices available to users.

Contact

If you have any questions about this Privacy Policy, please contact:

junichistamesi@gmail.com

2026年5月20日水曜日

Spatial Sound Magic Privacy Policy

 Last updated: May 20, 2026

This app does not collect, store, transmit, or share any personal data.

The app uses the microphone, hand tracking, and room interaction features only to provide its core spatial audio experience. Voice input, recorded audio, hand interactions, and room-based effects are processed locally on your device.

If you record audio in the app, the recording is used only within the app experience. Audio recordings are not uploaded to any server, sent to the developer, or shared with third parties.

This app does not use analytics, advertising, tracking technologies, or third-party data collection services.

We do not collect:

  • Personal information
  • Audio data
  • Speech data
  • Room or spatial mapping data
  • Hand tracking data
  • Device identifiers
  • Location data
  • Usage analytics

Because no personal data is collected, there is no personal data for us to sell, share, or disclose.

Spatial Sound Magic

This app turns your voice, hands, walls, and room into a playful spatial musical instrument.

Tap the Show Immersive Space button to enter an interactive spatial sound experience.

When you speak, colorful sound orbs are launched forward into your room (Up to 30 pieces). Each orb changes its color based on the pitch of your voice and its size based on the volume. When a sound orb bounces off the room collision mesh, that part of the room mesh temporarily becomes visible and plays a tone based on the orb’s color.

You can also interact with sound directly using your hands. Touch a sound orb to make it play a tone and disappear. Touch a wall, or use gaze and pinch toward a wall, to make the wall glow and play a pitch based on the height of the touched position.

The app also includes voice recording. Tap the Record button to record your voice(Up to 20 files). From the bottom area of the main menu, you can open the recorded audio list, play back your recordings, or tap the 3D Cube button to visualize a recording above your left palm. The recorded sound becomes a 3D audio sculpture, where thickness represents volume and color represents pitch. Turn your palm downward to detach the sculpture from your hand.

You can play the sculpture like a physical music object. Stroke the sculpture up and down with your left or right fingers, and the touched section will play back its corresponding sound, like pins on a music box or a playback bar moving through audio.

Turn on Comic Mode to transform speech into an American comic-style speech bubble experience. The app recognizes spoken language and launches 3D alphabet characters into space.

In Theremin Mode, your hand position and gestures generate sound in real time. Closing your hand lowers the volume. Theremin Mode can be toggled separately for the left and right hands.