BradyBrenot / huestacean

Philips Hue control app for desktop with screen syncing. C++ with Qt Quick GUI.
http://huestacean.com
Apache License 2.0
566 stars 53 forks source link

"Scenes" that can mix effects and simple light commands #19

Closed BradyBrenot closed 5 years ago

BradyBrenot commented 6 years ago

Requirements

Effects

Triggers

User stories

Cinema mode

I click on my "cinema scene"; some of my lights dim, some turn off, and the light behind my TV enters screen sync mode.

Hockey mode (#20)

I click on "hockey scene"; my side lights turn to my team's color, while the light behind the TV enters a nice cool white. When my team scores, all of my lights pulse red for a few moments before returning to their previous colors.

Dimmer switch

The dimmer switch can activate or deactivate screen sync mode. It can also change the options on the currently-used screen sync mode, e.g. changing the current brightness multiplier.

Design

Glossary

Scene
Combines one or more effects together, it does **not** map directly to a Hue scene.
Effect
Applies a change to *one or more* lights, either instantaneously, or for a set duration, or over an indefinite duration.
Virtual room
Defines a square-shaped space containing one or more lights and one or more spatial effects.
Spatial effect
'Casts light' on a virtual room, influencing the contents of that room. May have a position in the room, or may be 'global', affecting the entire room.
## Spatial effects All effects will now be "spatial effects" ❓ -- this might not be appropriate to every effect type, might not give users sufficient control, and might just be crazy. On the other hand, this allows for any arbitrary number of lights to be set up in a room, to work with any effect, and will allow for neat visualization and fast evaluation even for a very high numbers of lights. Spatial effects don't influence individual lights, but rather apply an influence to an entire space, as defined by a _virtual room_. A virtual room is an imaginary 2D euclidean space with coordinates from [0,0] to [1,1]. Every point in the virtual room has a color, which is influenced by the combined effects in the room If this was visualized, a virtual room might look like this: ![image](https://user-images.githubusercontent.com/632920/38712252-5f639a64-3e98-11e8-9300-3bfcd9e733a6.png) (the circles represent lights; the hexagon represents an Effect; they aren't part of the colour values of the room and are just for visualization) Here, there is an Effect in the middle of the room, which appears to be _casting light_ on the room. There are three lights in the room, labelled **1**, **2**, and **3**. Light **1** is a dark red; light **2** is lighter; light ***3*** has *no colour at all*. Lights take the color of a *single point* in the room, they are not an *area*; effects will need to compensate for this themselves if they need to. ### Screen sync's influence on the room If I were to just straight adapt screen sync for this new setup (imagine this for now, I don't have an image): The center of the room (coordinate 0.5, 0.5) contains the average of the entire screen's image; lights placed here are "whole screen" lights. Left center is set to the average of about the left fifth of the screen; top centre is about the top third of the screen; likewise right and bottom. Interpolate for the remaining values. ### Implementation - Calculation and evaluation I've got all types of ideas popping around my head here. #### Simple implementation ``` for every light for every effect effect.influence(light) ``` - Easy enough to implement - Can add in new, complicated functions easily - May be prohibitively expensive if we get up to the dozens of lights, and uh, Razer hardware includes a hell of a lot of lights, as do the various individually-addressable LED light strips that are around #### Small texture with interpolation Effects "color" a small image. We then use bilinear interpolation to find the light values intermediate points where the lights lie. - This is _similar_ to what Huestacean already does - Use CPU initially. CPU is easy to work with. - Can lean on GPU for the texture scaling, or just do bilinear interp on the CPU #### GPU room texture generator Effects send required set of data to the GPU. Lighting calculation, blending, and interpolation are all performed on the GPU. The GPU creates a texture that represents the room (almost exactly as the image above appears); CPU evaluates points on texture to determine what color to use. - GPU's a bit harder to work with, especially given how green I am RE: rendering - On the other hand, learning is good... - Different effects can render entirely differently, there's lots of flexibility and it could be easy to add new effects once it's rolling ## Colours Final light colours are converted to expected colour space. Effects can use any intermediate colour space, but any interpolation that needs to happen is expected to happen in **CIE Luv LCh**, so their contribution to the light in the _virtual room_ will need to be defined in that space. - Provide functions to convert colour between spaces as needed: - RGB - CIE XYZ and xyY - CIE Luv - CIE Luv LCh - HSLuv
BradyBrenot commented 6 years ago

// Scene is // - A bunch of effects // - Those effects' properties

// Routine is // - One or more scenes and the rooms to apply them to // - A trigger condition // - Schedule, hotkey, button // // A routine can be used to // - Trigger multiple scenes // - Triggers scenes on special events (hotkeys, times, ...)

//Fold 'routine' into the Scene object

// Multiple scenes may be in effect against the same room at the same time (see below for exceptions) // The same scene may affect multiple rooms at the same time.

// Types of scenes // - Background scene - Cut off any layers below them. // - Blend scene - More than one may apply to a given room at one time! // "Background scene" is implied if a scene has certain effects

// Scenes layer with opacity // - Color after applying layer B: layer A's color (1 - layer B's opacity) + layer B's color layer B's opacity // (processing top-down as an optimization)

Update loop

I could complicate this by making it more asynchronous, threading it, whatever, but it's likely unnecessary.