// The animated picture, independent of Plasma so it can be rendered
// and tested on its own (tools/moire-preview.qml does).
//
// Motion is a handful of slow sines at unrelated periods, so it never
// visibly repeats. It is advanced here in JavaScript, in double
// precision, and handed to the shader as positions and a wrapped
// phase: the shader holds no clock that could lose precision.
import QtQuick 2.15
Item {
id: field
// "moire", "halftone", "both" (moire over halftone), "bokeh", or
// "alternate": fading between fadeFrom and fadeTo, which are any of
// the others, every few minutes.
property string pattern: "both"
property string fadeFrom: "moire"
property string fadeTo: "halftone"
property int bokehLights: 14
// Multiplies how fast everything drifts; 0 holds the picture still.
property real speed: 1
property int frameRate: 30
// "NEIAM" in the bottom right, in the ink colour.
property bool wordmark: false
property color base: "#661e1e"
property color deep: "#470005"
property color ink: "#fdf2c6"
property color tone: "#b6797a"
// Theme changes arrive once a minute at most; ease into them.
Behavior on base { ColorAnimation { duration: 4000 } }
Behavior on deep { ColorAnimation { duration: 4000 } }
Behavior on ink { ColorAnimation { duration: 4000 } }
Behavior on tone { ColorAnimation { duration: 4000 } }
// Seconds of motion so far, scaled by speed. A fresh start picks a
// random point in the motion so two screens don't mirror each other.
property real clock: Math.random() * 10000
readonly property real aspect: height > 0 ? width / height : 16 / 9
// How much of each layer a pattern shows.
function layers(name) {
return {
moire: name === "moire" || name === "both" ? 1 : 0,
halftone: name === "halftone" || name === "both" ? 1 : 0,
bokeh: name === "bokeh" ? 1 : 0,
}
}
function amount(layer) {
if (pattern !== "alternate")
return layers(pattern)[layer]
var t = 0.5 + 0.5 * sweep(360, 0)
return layers(fadeFrom)[layer] * (1 - t) + layers(fadeTo)[layer] * t
}
function sweep(period, phase) {
return Math.sin(2 * Math.PI * (clock / period + phase))
}
ShaderEffect {
anchors.fill: parent
fragmentShader: "field.frag.qsb"
property size size: Qt.size(field.width, field.height)
// The hub, where the pattern is strongest, wanders around the
// right of centre; the ring centres circle it at a breathing
// distance, which walks the moire's beat around the rings.
property point hub: Qt.point(0.62 * field.aspect + 0.05 * field.sweep(97, 0),
0.46 + 0.04 * field.sweep(131, 0.3))
readonly property real spread: 0.03 * (1 + 0.35 * field.sweep(71, 0.1))
readonly property real turn: 2 * Math.PI * field.clock / 53
property point ringA: Qt.point(hub.x - spread * Math.cos(turn) / 2,
hub.y - spread * Math.sin(turn) / 2)
property point ringB: Qt.point(hub.x + spread * Math.cos(turn) / 2,
hub.y + spread * Math.sin(turn) / 2)
property real reach: 0.95 + 0.06 * field.sweep(43, 0.7)
property real period: 1 / 110
property real cell: 1 / 70
property real angle: 0.26 + 0.03 * field.sweep(211, 0.2)
property real wave: (2 * Math.PI * field.clock / 16) % (2 * Math.PI)
// One bokeh cycle is ten minutes; the fastest disc climbs the
// screen three times in it.
property real drift: (field.clock / 600) % 1
// Amounts ease so a pattern change fades rather than cuts; while
// alternating they are already continuous, so easing would lag.
property real moire: field.amount("moire")
property real halftone: field.amount("halftone")
property real bokeh: field.amount("bokeh")
property real lights: field.bokehLights
Behavior on moire { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } }
Behavior on halftone { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } }
Behavior on bokeh { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } }
property color base: field.base
property color deep: field.deep
property color ink: field.ink
property color tone: field.tone
}
// B612 Mono comes from fonts-b612 / firstyear-b612-fonts, which the
// package depends on.
Text {
visible: field.wordmark
anchors { right: parent.right; bottom: parent.bottom; margins: field.height * 0.04 }
text: "NEIAM"
color: field.ink
font { family: "B612 Mono"; bold: true; pixelSize: Math.max(1, Math.round(field.height * 0.12)) }
// Sit the capitals, not the line box, on the margin.
anchors.bottomMargin: field.height * 0.04 - fontMetrics.descent
FontMetrics { id: fontMetrics; font: parent.font }
}
Timer {
running: field.speed > 0 && field.visible
repeat: true
interval: Math.round(1000 / Math.max(1, field.frameRate))
onTriggered: field.clock += field.speed * interval / 1000
}
}
neiam
/neiam-devices
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
5.3 KiB · text
History
bd286fb