Give the chip a box it fits in, and a spin that settles

The chip window was 60 x 44 with the curl tumbling about its centre. The curl
reaches 24.2 units from that pivot once the stroke is counted, so a half height
of 22 clipped the top and bottom off it on every turn.

The box is now 52 x 58 with the pivot at (26, 33), measured off the actual
Bezier curve rather than its control points. The shadow sits at 15.1 below the
pivot, which is where the curl comes to rest, so the chip lands on its own
shadow instead of floating above it.

The endless rotation is gone. Each bat now adds one or two whole turns in the
direction it was hit and coasts to a stop, so the resting silhouette is always
the same one, the one the shadow was placed under.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
JulianandClaude Opus 5 committed 2026-09-11 07:12:11 +02:00
1 parent 6b9553f6d4
commit f6ecf0ddbf
6 files changed
+49 -33

No files matched your search

+3
View File
@@ -7,6 +7,9 @@ QtObject {
// holds the whiskers, the tail and the glyphs that float above the head. // holds the whiskers, the tail and the glyphs that float above the head.
readonly property real aspect: 106 / 120 readonly property real aspect: 106 / 120
// Chip.qml draws in a 52 x 58 box, large enough to hold the tumbling curl.
readonly property real chipAspect: 58 / 52
readonly property var moodNames: ["walking", "running", "sitting", "grooming", "stretching", readonly property var moodNames: ["walking", "running", "sitting", "grooming", "stretching",
"sleeping", "loving", "alert", "chasing", "batting", "drinking"] "sleeping", "loving", "alert", "chasing", "batting", "drinking"]
+29 -20
View File
@@ -3,27 +3,41 @@ import QtQuick
import QtQuick.Shapes import QtQuick.Shapes
// A curled steel chip, the kind that comes off a milling cutter. // A curled steel chip, the kind that comes off a milling cutter.
// Design space is 60 x 44 units. //
// The box is 52 x 58 and the curl tumbles about (26, 33). Sizing rules, all in
// design units, all measured off the actual Bezier curve rather than its
// control points:
// * farthest point of the curl from the pivot is 24.2 including the stroke,
// so the box has to give 24.2 in every direction or spinning clips it
// * at rest the curl reaches 15.1 below the pivot, which is where the shadow
// goes, so it comes to rest sitting on its own shadow
// * spins are whole turns, so the resting silhouette is always the same one
Item { Item {
id: chip id: chip
property int kick: 0 // Degrees, driven from outside. Each bat adds most of a turn and the
// Behavior lets it coast to a stop.
property real spin: 0
readonly property real unit: width / 60 readonly property real unit: width / 52
implicitWidth: 60 implicitWidth: 52
implicitHeight: 44 implicitHeight: 58
Behavior on spin {
NumberAnimation { duration: 750; easing.type: Easing.OutCubic }
}
Item { Item {
width: 60 width: 52
height: 44 height: 58
transformOrigin: Item.TopLeft transformOrigin: Item.TopLeft
scale: chip.unit scale: chip.unit
Rectangle { // ground shadow Rectangle { // ground shadow, under where the curl comes to rest
x: 14; y: 36 x: 9; y: 48
width: 32; height: 6 width: 26; height: 4
radius: 3 radius: 2
color: "#000000" color: "#000000"
opacity: 0.18 opacity: 0.18
antialiasing: true antialiasing: true
@@ -31,8 +45,10 @@ Item {
Item { Item {
id: curl id: curl
x: 30; y: 22 x: 26; y: 33
width: 0; height: 0 width: 0
height: 0
rotation: chip.spin
Shape { Shape {
x: -30; y: -22 x: -30; y: -22
@@ -58,13 +74,6 @@ Item {
PathCubic { control1X: 5; control1Y: 19; control2X: 16; control2Y: 8; x: 30; y: 12 } PathCubic { control1X: 5; control1Y: 19; control2X: 16; control2Y: 8; x: 30; y: 12 }
} }
} }
RotationAnimation on rotation {
running: true
loops: Animation.Infinite
from: 0; to: 360
duration: 2600
}
} }
} }
} }
+3 -3
View File
@@ -10,10 +10,10 @@ Window {
required property CatController cat required property CatController cat
required property AppSettings settings required property AppSettings settings
readonly property int chipSize: Math.round(settings.catSize * 0.45) readonly property int chipSize: Math.round(settings.catSize * 0.46)
width: chipSize width: chipSize
height: Math.round(chipSize * 0.73) height: Math.round(chipSize * CatDefs.chipAspect)
x: cat.chipX - width / 2 x: cat.chipX - width / 2
y: Screen.desktopAvailableHeight - height - settings.bottomMargin y: Screen.desktopAvailableHeight - height - settings.bottomMargin
color: "transparent" color: "transparent"
@@ -29,6 +29,6 @@ Window {
Chip { Chip {
anchors.fill: parent anchors.fill: parent
kick: win.cat.chipKick spin: win.cat.chipSpin
} }
} }
+3 -3
View File
@@ -59,10 +59,10 @@ Window {
Chip { Chip {
x: win.cat.chipX - width / 2 x: win.cat.chipX - width / 2
y: win.height - height - win.settings.bottomMargin y: win.height - height - win.settings.bottomMargin
width: Math.round(win.settings.catSize * 0.45) width: Math.round(win.settings.catSize * 0.46)
height: Math.round(width * 0.73) height: Math.round(width * CatDefs.chipAspect)
visible: win.cat.chipVisible visible: win.cat.chipVisible
kick: win.cat.chipKick spin: win.cat.chipSpin
Behavior on x { Behavior on x {
NumberAnimation { duration: 320; easing.type: Easing.OutQuad } NumberAnimation { duration: 320; easing.type: Easing.OutQuad }
+7 -3
View File
@@ -179,6 +179,8 @@ void CatController::tossChip()
setChipX(fromLeft ? margin : m_stageWidth - margin); setChipX(fromLeft ? margin : m_stageWidth - margin);
m_batsLeft = rng->bounded(2, 5); m_batsLeft = rng->bounded(2, 5);
m_chipSpin = fromLeft ? 360.0 : -360.0; // it rolls in already turning
emit chipSpinChanged();
m_chipVisible = true; m_chipVisible = true;
emit chipVisibleChanged(); emit chipVisibleChanged();
@@ -223,9 +225,6 @@ void CatController::endCoffee()
void CatController::batChip() void CatController::batChip()
{ {
auto *rng = QRandomGenerator::global(); auto *rng = QRandomGenerator::global();
++m_chipKick;
emit chipKickChanged();
const qreal distance = rng->bounded(140, 380); const qreal distance = rng->bounded(140, 380);
qreal next = m_chipX + (m_facingRight ? distance : -distance); qreal next = m_chipX + (m_facingRight ? distance : -distance);
const qreal edge = 40.0; const qreal edge = 40.0;
@@ -235,6 +234,11 @@ void CatController::batChip()
} }
setChipX(next); setChipX(next);
// Whole turns in the direction it was hit, so it always lands on the same
// silhouette, right on top of its shadow. The QML side lets it coast.
m_chipSpin += (m_facingRight ? 1 : -1) * 360.0 * rng->bounded(1, 3);
emit chipSpinChanged();
if (--m_batsLeft <= 0) { if (--m_batsLeft <= 0) {
endChase(); endChase();
holdMood(Mood::Sitting, 2500); // pleased with itself holdMood(Mood::Sitting, 2500); // pleased with itself
+4 -4
View File
@@ -29,7 +29,7 @@ class CatController : public QObject
Q_PROPERTY(bool chipVisible READ chipVisible NOTIFY chipVisibleChanged) Q_PROPERTY(bool chipVisible READ chipVisible NOTIFY chipVisibleChanged)
Q_PROPERTY(qreal chipX READ chipX NOTIFY chipXChanged) Q_PROPERTY(qreal chipX READ chipX NOTIFY chipXChanged)
Q_PROPERTY(int chipKick READ chipKick NOTIFY chipKickChanged) Q_PROPERTY(qreal chipSpin READ chipSpin NOTIFY chipSpinChanged)
Q_PROPERTY(bool cupVisible READ cupVisible NOTIFY cupVisibleChanged) Q_PROPERTY(bool cupVisible READ cupVisible NOTIFY cupVisibleChanged)
Q_PROPERTY(qreal cupX READ cupX NOTIFY cupXChanged) Q_PROPERTY(qreal cupX READ cupX NOTIFY cupXChanged)
@@ -68,7 +68,7 @@ public:
bool chipVisible() const { return m_chipVisible; } bool chipVisible() const { return m_chipVisible; }
qreal chipX() const { return m_chipX; } qreal chipX() const { return m_chipX; }
int chipKick() const { return m_chipKick; } qreal chipSpin() const { return m_chipSpin; }
bool cupVisible() const { return m_cupVisible; } bool cupVisible() const { return m_cupVisible; }
qreal cupX() const { return m_cupX; } qreal cupX() const { return m_cupX; }
@@ -90,7 +90,7 @@ signals:
void lookChanged(); void lookChanged();
void chipVisibleChanged(); void chipVisibleChanged();
void chipXChanged(); void chipXChanged();
void chipKickChanged(); void chipSpinChanged();
void cupVisibleChanged(); void cupVisibleChanged();
void cupXChanged(); void cupXChanged();
@@ -130,7 +130,7 @@ private:
bool m_chipVisible = false; bool m_chipVisible = false;
qreal m_chipX = 0; qreal m_chipX = 0;
int m_chipKick = 0; qreal m_chipSpin = 0;
int m_batsLeft = 0; int m_batsLeft = 0;
bool m_cupVisible = false; bool m_cupVisible = false;