Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RGB.pulse method #10

Merged
merged 2 commits into from
Jan 11, 2018
Merged

Add RGB.pulse method #10

merged 2 commits into from
Jan 11, 2018

Conversation

islemaster
Copy link

Similar to the LED.pulse() method that runs an animation for a colorless LED, but implemented for a color LED. I had to make more changes than I expected here, I'll annotate them inline.

Tested against Adafruit Circuit Playground Developer Edition in Code.org's App Lab+Maker Toolkit environment, against my local development machine.

@@ -182,7 +182,8 @@ function RGB(opts) {
blue: 255,
intensity: 100,
isAnode: opts.isAnode || false,
interval: null
interval: null,
isRunning: false
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRunning is used to track whether an Animation (requiring more detail that setInterval) is in progress. Copying the pattern used in led.js here.

red: 255,
green: 255,
blue: 255
};
Copy link
Author

@islemaster islemaster Jan 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, state.prev was set during calls to off() so that you could later call on() and restore the LED to its previous color. It was cleared when it was consumed by on() and a default value of white (255,255,255) was encoded in that method.

Since nothing else depended on state.prev, I've added it to the initial component state with its default white value.

I now update it when color() is called, to handle the following case:

rgb.color('red');
rgb.pulse(300);

I'm also not clearing it when on() is called - there's no particular reason to.

It may look like this is redundant with the state.values object, above, but upon further examination the comment on state.values does not accurately describe the way it or the state.red, state.green, state.blue properties are used - they seem to always represent the current interpolated RGB values, not the ones most recently set.

RGB.prototype.pulse = function(duration, callback) {
var state = priv.get(this);

var currentColor = state.prev;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pulse() uses the last color set using color(), saved to state.prev instead of checking the actual current color of the LEDs. This is to avoid the problem where subsequent calls to pulse() would lead to progressively smaller intensity ranges. Consider this case:

color('red');
pulse(300); // Sets up an animation for intensity 0-100
// 150ms passes
pulse(300); // Current intensity is 50, so new animation created from the interpolated color appears to be intensity 0-50.
// 150ms passes
pulse(300); // Current intensity is 50 but appears to be 25, so new animation appears to be intensity 0-25.
// ...and so on, the animation gets dimmer with each iteration.

@@ -573,7 +621,7 @@ RGB.prototype[Animation.normalize] = function(keyFrames) {
*/

RGB.prototype[Animation.render] = function(frames) {
return this.color(frames[0]);
return this.update(frames[0]);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The animation render method here was calling color(), which does a lot of normalization and validation before itself calling update. I've changed this to behave more like the led class, which trusts its animations and calls update directly.


var rgbs = new RGB.Collection([
[1, 2, 3],
[4, 5, 6],
]);

this.color = this.sandbox.stub(RGB.prototype, "color");
rgbs.each(function(rgb) {
this.sandbox.stub(rgb, "write");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to update these tests that stub color to test animations, since the Animation.render method no longer calls color. I can't stub update because it's a read-only property, so I'm stubbing write which is another layer down, and a better representation of the values actually being sent to the device.

@davidsbailey
Copy link
Member

sorry I missed this today, @islemaster . I will take a look in the morning.

@davidsbailey
Copy link
Member

👀 looking...

Copy link
Member

@davidsbailey davidsbailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the slow response, much of this is new to me. I have a few questions inline.

@@ -291,21 +291,36 @@ exports["RGB.Collection"] = {


"Animation.render": function(test) {
test.expect(1);
test.expect(4);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conceptually, what are you expecting to equal 4 here? Are there any docs or other guidance for understanding these test cases?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Johnny-Five tests are written with nodeunit. API docs are inline in that README, here. expect(count) sets the number of assertions the test expects to run. This count is checked when done() is called at the end of the test, and the test failed if there were missing/extra assertions.

this.rgb[Animation.render]([0]);
test.equal(this.color.callCount, 1);
test.expect(2);
// rgb.write() is already wrapped
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is rgb.write() wrapped?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 55, in the setUp method for this block of tests, it's stubbed using sinon:

this.write = this.sandbox.spy(this.rgb, "write");

],
metronomic: true,
loop: true,
easing: "inOutSine",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -77,7 +77,7 @@ board.on("ready", function() {
## License
Copyright (c) 2012, 2013, 2014 Rick Waldron <waldron.rick@gmail.com>
Licensed under the MIT license.
Copyright (c) 2017 The Johnny-Five Contributors
Copyright (c) 2018 The Johnny-Five Contributors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see any changes in this commit besides the copyright year change. Did I miss something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No: The CI setup for this repo regenerates these docs, and complains if it finds differences between what it generated and the committed copies. Due to the year change, it's requiring me to commit updates for all the documentation files to pass CI.

Copy link
Member

@davidsbailey davidsbailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, though still interested in the answers to my questions.

@davidsbailey
Copy link
Member

Awesome, thanks for the answers, Brad! Hopefully this helps grease the rails for the next review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants