McRogueFace/tests/cookbook/features/demo_rotation.py

454 lines
14 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Rotation Demo - Transform rotation and origin points
Interactive controls:
Left/Right: Rotate selected element
Up/Down: Adjust rotation speed
1-4: Select element to rotate
O: Cycle origin point
R: Reset all rotations
A: Toggle auto-rotation
ESC: Exit demo
Rotation properties:
- rotation: Angle in degrees
- origin: (x, y) rotation center point
- rotate_with_camera: For Grid entities
"""
import mcrfpy
import sys
class RotationDemo:
def __init__(self):
self.scene = mcrfpy.Scene("rotation_demo")
self.ui = self.scene.children
self.elements = []
self.selected = 0
self.auto_rotate = False
self.rotation_speed = 45 # degrees per second
self.origin_index = 0
self.setup()
def setup(self):
"""Build the demo scene."""
# Background
bg = mcrfpy.Frame(
pos=(0, 0),
size=(1024, 768),
fill_color=mcrfpy.Color(15, 15, 20)
)
self.ui.append(bg)
# Title
title = mcrfpy.Caption(
text="Rotation Demo",
pos=(512, 30),
font_size=28,
fill_color=mcrfpy.Color(255, 255, 255)
)
title.outline = 2
title.outline_color = mcrfpy.Color(0, 0, 0)
self.ui.append(title)
# Create rotatable elements
self._create_frame_demo(100, 120)
self._create_sprite_demo(400, 120)
self._create_caption_demo(700, 120)
self._create_origin_demo(250, 450)
# Control panel
self._create_control_panel()
# Instructions
instr = mcrfpy.Caption(
text="Left/Right: Rotate | 1-4: Select | O: Cycle origin | A: Auto-rotate | R: Reset | ESC: Exit",
pos=(50, 730),
font_size=14,
fill_color=mcrfpy.Color(120, 120, 120)
)
self.ui.append(instr)
# Start update timer for auto-rotation
mcrfpy.Timer("rotation_update", self._update, 16) # ~60fps
def _create_frame_demo(self, x, y):
"""Create rotating frame demo."""
# Label
label = mcrfpy.Caption(
text="1. Frame Rotation",
pos=(x + 100, y - 20),
font_size=16,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(label)
# Rotatable frame
frame = mcrfpy.Frame(
pos=(x, y),
size=(200, 200),
fill_color=mcrfpy.Color(80, 100, 140),
outline_color=mcrfpy.Color(120, 150, 200),
outline=3
)
# Add child content
inner = mcrfpy.Frame(
pos=(50, 50),
size=(100, 100),
fill_color=mcrfpy.Color(100, 120, 160),
outline=1
)
frame.children.append(inner)
content = mcrfpy.Caption(
text="Rotates!",
pos=(100, 85),
font_size=14,
fill_color=mcrfpy.Color(255, 255, 255)
)
frame.children.append(content)
self.ui.append(frame)
self.elements.append(('Frame', frame))
# Rotation indicator
angle_label = mcrfpy.Caption(
text="0.0°",
pos=(x + 100, y + 220),
font_size=14,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(angle_label)
self.frame_angle_label = angle_label
def _create_sprite_demo(self, x, y):
"""Create rotating sprite demo (using Frame as placeholder)."""
# Label
label = mcrfpy.Caption(
text="2. Sprite Rotation",
pos=(x + 100, y - 20),
font_size=16,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(label)
# Use a frame to simulate sprite (actual sprite would need texture)
sprite_frame = mcrfpy.Frame(
pos=(x, y),
size=(200, 200),
fill_color=mcrfpy.Color(100, 140, 80),
outline_color=mcrfpy.Color(150, 200, 120),
outline=3
)
# Arrow pattern to show rotation direction
arrow_v = mcrfpy.Caption(
text="^",
pos=(100, 40),
font_size=48,
fill_color=mcrfpy.Color(255, 255, 255)
)
sprite_frame.children.append(arrow_v)
sprite_label = mcrfpy.Caption(
text="UP",
pos=(100, 100),
font_size=24,
fill_color=mcrfpy.Color(255, 255, 255)
)
sprite_frame.children.append(sprite_label)
self.ui.append(sprite_frame)
self.elements.append(('Sprite', sprite_frame))
angle_label = mcrfpy.Caption(
text="0.0°",
pos=(x + 100, y + 220),
font_size=14,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(angle_label)
self.sprite_angle_label = angle_label
def _create_caption_demo(self, x, y):
"""Create rotating caption demo."""
# Label
label = mcrfpy.Caption(
text="3. Caption Rotation",
pos=(x + 100, y - 20),
font_size=16,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(label)
# Background for visibility
caption_bg = mcrfpy.Frame(
pos=(x, y),
size=(200, 200),
fill_color=mcrfpy.Color(40, 40, 50),
outline_color=mcrfpy.Color(80, 80, 100),
outline=1
)
self.ui.append(caption_bg)
# Rotatable caption
caption = mcrfpy.Caption(
text="Rotating Text!",
pos=(x + 100, y + 100),
font_size=24,
fill_color=mcrfpy.Color(255, 200, 100)
)
caption.outline = 2
caption.outline_color = mcrfpy.Color(0, 0, 0)
self.ui.append(caption)
self.elements.append(('Caption', caption))
angle_label = mcrfpy.Caption(
text="0.0°",
pos=(x + 100, y + 220),
font_size=14,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(angle_label)
self.caption_angle_label = angle_label
def _create_origin_demo(self, x, y):
"""Create demo showing different origin points."""
# Label
label = mcrfpy.Caption(
text="4. Origin Point Demo (press O to cycle)",
pos=(x + 200, y - 20),
font_size=16,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(label)
# Large frame to show origin effects
origin_frame = mcrfpy.Frame(
pos=(x, y),
size=(400, 200),
fill_color=mcrfpy.Color(140, 80, 100),
outline_color=mcrfpy.Color(200, 120, 150),
outline=3
)
# Origin marker
origin_marker = mcrfpy.Frame(
pos=(0, 0),
size=(10, 10),
fill_color=mcrfpy.Color(255, 255, 0),
outline=0
)
origin_frame.children.append(origin_marker)
self.origin_marker = origin_marker
# Origin name
origin_label = mcrfpy.Caption(
text="Origin: Top-Left",
pos=(200, 85),
font_size=16,
fill_color=mcrfpy.Color(255, 255, 255)
)
origin_frame.children.append(origin_label)
self.origin_name_label = origin_label
self.ui.append(origin_frame)
self.elements.append(('Origin Demo', origin_frame))
self.origin_frame = origin_frame
# Origin positions to cycle through
self.origins = [
("Top-Left", (0, 0)),
("Top-Center", (200, 0)),
("Top-Right", (400, 0)),
("Center-Left", (0, 100)),
("Center", (200, 100)),
("Center-Right", (400, 100)),
("Bottom-Left", (0, 200)),
("Bottom-Center", (200, 200)),
("Bottom-Right", (400, 200)),
]
angle_label = mcrfpy.Caption(
text="0.0°",
pos=(x + 200, y + 220),
font_size=14,
fill_color=mcrfpy.Color(150, 150, 150)
)
self.ui.append(angle_label)
self.origin_angle_label = angle_label
def _create_control_panel(self):
"""Create control panel showing current state."""
panel = mcrfpy.Frame(
pos=(750, 450),
size=(220, 180),
fill_color=mcrfpy.Color(30, 30, 40),
outline_color=mcrfpy.Color(60, 60, 80),
outline=1
)
self.ui.append(panel)
panel_title = mcrfpy.Caption(
text="Status",
pos=(110, 10),
font_size=16,
fill_color=mcrfpy.Color(200, 200, 200)
)
panel.children.append(panel_title)
self.selected_label = mcrfpy.Caption(
text="Selected: Frame",
pos=(15, 40),
font_size=14,
fill_color=mcrfpy.Color(150, 150, 150)
)
panel.children.append(self.selected_label)
self.speed_label = mcrfpy.Caption(
text="Speed: 45°/sec",
pos=(15, 65),
font_size=14,
fill_color=mcrfpy.Color(150, 150, 150)
)
panel.children.append(self.speed_label)
self.auto_label = mcrfpy.Caption(
text="Auto-rotate: Off",
pos=(15, 90),
font_size=14,
fill_color=mcrfpy.Color(200, 100, 100)
)
panel.children.append(self.auto_label)
def _update(self, runtime):
"""Update loop for auto-rotation."""
if self.auto_rotate:
dt = 0.016 # Approximately 16ms per frame
for name, element in self.elements:
try:
element.rotation = (element.rotation + self.rotation_speed * dt) % 360
except AttributeError:
pass
# Update angle labels
self._update_angle_labels()
def _update_angle_labels(self):
"""Update the angle display labels."""
labels = [self.frame_angle_label, self.sprite_angle_label,
self.caption_angle_label, self.origin_angle_label]
for i, (name, element) in enumerate(self.elements):
if i < len(labels):
try:
labels[i].text = f"{element.rotation:.1f}°"
except AttributeError:
labels[i].text = "N/A"
def _cycle_origin(self):
"""Cycle to the next origin point."""
self.origin_index = (self.origin_index + 1) % len(self.origins)
name, pos = self.origins[self.origin_index]
# Update origin
try:
self.origin_frame.origin = pos
self.origin_name_label.text = f"Origin: {name}"
# Move marker to show origin position
self.origin_marker.x = pos[0] - 5
self.origin_marker.y = pos[1] - 5
except AttributeError:
pass
def on_key(self, key, state):
"""Handle keyboard input."""
if state != "start":
return
if key == "Escape":
sys.exit(0)
elif key == "Left":
# Rotate left (counter-clockwise)
name, element = self.elements[self.selected]
try:
element.rotation = (element.rotation - 15) % 360
except AttributeError:
pass
elif key == "Right":
# Rotate right (clockwise)
name, element = self.elements[self.selected]
try:
element.rotation = (element.rotation + 15) % 360
except AttributeError:
pass
elif key == "Up":
self.rotation_speed = min(180, self.rotation_speed + 15)
self.speed_label.text = f"Speed: {self.rotation_speed}°/sec"
elif key == "Down":
self.rotation_speed = max(15, self.rotation_speed - 15)
self.speed_label.text = f"Speed: {self.rotation_speed}°/sec"
elif key in ("Num1", "Num2", "Num3", "Num4"):
self.selected = int(key[-1]) - 1
if self.selected < len(self.elements):
self.selected_label.text = f"Selected: {self.elements[self.selected][0]}"
elif key == "O":
self._cycle_origin()
elif key == "A":
self.auto_rotate = not self.auto_rotate
if self.auto_rotate:
self.auto_label.text = "Auto-rotate: On"
self.auto_label.fill_color = mcrfpy.Color(100, 200, 100)
else:
self.auto_label.text = "Auto-rotate: Off"
self.auto_label.fill_color = mcrfpy.Color(200, 100, 100)
elif key == "R":
# Reset all rotations
for name, element in self.elements:
try:
element.rotation = 0
except AttributeError:
pass
self.origin_index = 0
name, pos = self.origins[0]
try:
self.origin_frame.origin = pos
self.origin_name_label.text = f"Origin: {name}"
self.origin_marker.x = pos[0] - 5
self.origin_marker.y = pos[1] - 5
except AttributeError:
pass
def activate(self):
"""Activate the demo scene."""
self.scene.on_key = self.on_key
mcrfpy.current_scene = self.scene
def main():
"""Run the rotation demo."""
demo = RotationDemo()
demo.activate()
# Headless mode: capture screenshot and exit
try:
if mcrfpy.headless_mode():
from mcrfpy import automation
# Rotate elements for visual interest
for name, element in demo.elements:
try:
element.rotation = 15
except AttributeError:
pass
mcrfpy.Timer("screenshot", lambda rt: (
automation.screenshot("screenshots/features/rotation_demo.png"),
sys.exit(0)
), 200)
except AttributeError:
pass
if __name__ == "__main__":
main()