/// <summary> /// This is used to fetch the color for the light the given time /// </summary> /// <param name="timeNow">The current time</param> /// <param name="nextUpdateTime">The time that the light will next change</param> /// <returns>The color</returns> public Color Update(int timeNow, ref int nextUpdateTime) { for (; ;) { // Keep on with the current color until the transition if (timeNow <= transitionStart_ms) { nextUpdateTime = transitionStart_ms; return(currentFrame.color); } // Of we are into the next frame if (timeNow >= nextFrame.triggerTime_ms) { currentFrame = nextFrame; nextFrame = null; // make transition; continue; } // Compute the transitory color var dT = timeNow - transitionStart_ms; var color = currentFrame.color; nextUpdateTime = timeNow + 30; return(Color.FromArgb(255, (byte)(color.R + dR * dT), (byte)(color.G + dG * dT), (byte)(color.B + dB * dT))); } }
/// <summary> /// This is used to translate a backpack lights animation struct into /// the internal format used for animation /// </summary> /// <param name="bl">The backpack lights struct</param> /// <param name="Front">The front LED animation frame</param> /// <param name="Middle">The middle LED animation frame</param> /// <param name="Back">The back LED animation frame</param> /// <returns>duration</returns> static uint From(Anki.VectorAnim.BackpackLights bl, out LightFrame Front, out LightFrame Middle, out LightFrame Back) { // Ignore alpha var frontColor = Color.FromArgb(255, (byte)(255 * bl.Front(0)), (byte)(255 * bl.Front(1)), (byte)(255 * bl.Front(2))); var middleColor = Color.FromArgb(255, (byte)(255 * bl.Middle(0)), (byte)(255 * bl.Middle(1)), (byte)(255 * bl.Middle(2))); var backColor = Color.FromArgb(255, (byte)(255 * bl.Back(0)), (byte)(255 * bl.Back(1)), (byte)(255 * bl.Back(2))); var triggerTime_ms = bl.TriggerTimeMs; var durationTime_ms = bl.DurationTimeMs; // Convert each of these to their own frame Front = new LightFrame(triggerTime_ms, durationTime_ms, frontColor); Middle = new LightFrame(triggerTime_ms, durationTime_ms, middleColor); Back = new LightFrame(triggerTime_ms, durationTime_ms, backColor); return(triggerTime_ms + durationTime_ms); }
/// <summary> /// This sets up the structure to transition from one color to the next in the time frame /// </summary> /// <param name="from">The starting color frame</param> /// <param name="to">The light frame to transtion to</param> public void Make(LightFrame from, LightFrame to) { // The amount of time it will take to go from the current light // to the next one float transitionDuration_ms = to.triggerTime_ms - (from.triggerTime_ms + from.duration_ms); if (transitionDuration_ms < 1) { return; } // Compute the change in color by millisecond steps dR = (to.color.R - from.color.R) / transitionDuration_ms; dG = (to.color.G - from.color.G) / transitionDuration_ms; dB = (to.color.B - from.color.B) / transitionDuration_ms; }
/// <summary> /// Looks up the pattern to play the cube lights in given the trigger name /// </summary> /// <param name="triggerName">The animation trigger name</param> /// <returns>A sequence of light patterns to display</returns> public IReadOnlyList <LightsPattern> CubeLightsForTrigger(string triggerName) { // See if the description is already loaded var ret = CubeLightsCache(triggerName); if (null != ret) { return((IReadOnlyList <LightsPattern>)ret); } // look up name for the file if (!cubeTriggerName2Filename.TryGetValue(triggerName, out var partialName)) { return(null); } var path = CubeLightsPath(partialName); // Get the text for the file var text = File.ReadAllText(path); // Get it in a convenient form // Decode the JSON file. The format varies a little between Cozmo and Vector CubeLightSequence[] topLevel; if (AssetsType.Vector == AssetsType) { topLevel = JSONDeserializer.Deserialize <CubeLightSequence[]>(text); } else { // Cozmo has more level of indirection var tmp = JSONDeserializer.Deserialize <Dictionary <string, CubeLightSequence[]> >(text); // TODO: check the number of keys topLevel = tmp[partialName]; } // Convert it into the final internal form var sequences = new List <LightsPattern>(); foreach (var t in topLevel) { // Create something to hold info about this part of the sequence var s = new LightsPattern(); s.name = t.patternDebugName; s.canBeOverridden = t.canBeOverridden; s.duration_ms = t.duration_ms; var lightKeyFrames = new List <LightFrame> [4]; s.lightKeyFrames = lightKeyFrames; sequences.Add(s); // Convert the patterns for the lights var lights = t.pattern; for (var idx = 0; idx < 4; idx++) { // Start the trigger time with the offset for the light uint triggerTime = (uint)lights.offset[idx]; // Create the on frame first var rgba = lights.onColors[idx]; // Ignore alpha var color = Color.FromArgb(255, rgba[0], rgba[1], rgba[2]); var lightFrame = new LightFrame(triggerTime, (uint)lights.onPeriod_ms[idx], color); var a = new List <LightFrame> { lightFrame }; // Compute the trigger time of the off period triggerTime += (uint)(lights.onPeriod_ms[idx] + lights.transitionOnPeriod_ms[idx]); // Create the off frame next rgba = lights.offColors[idx]; // Ignore alpha color = Color.FromArgb(255, rgba[0], rgba[1], rgba[2]); lightFrame = new LightFrame(triggerTime, (uint)lights.offPeriod_ms[idx], color); triggerTime += (uint)(lights.offPeriod_ms[idx] + lights.transitionOffPeriod_ms[idx]); a.Add(lightFrame); var duration = triggerTime - lights.offset[idx]; // Put these key frames for the light in lightKeyFrames[idx] = a; } } var seq = sequences.ToArray(); // cache it CubeLightsCache(triggerName, seq); // return it return(seq); }
/// <summary> /// Looks up the pattern to play the backpack lights in given the trigger name /// </summary> /// <param name="triggerName">The animation trigger name</param> /// <returns>A sequence of light patterns to display</returns> public IReadOnlyList <LightsPattern> BackpackLightsForTrigger(string triggerName) { // See if the description is already loaded var ret = BackpackLightsCache(triggerName); if (null != ret) { return(ret); } // look up name for the file if (!backpackTriggerName2Filename.TryGetValue(triggerName, out var partialName)) { return(null); } var path = BackpackLightsPath(partialName); // Get the text for the file var text = File.ReadAllText(path); // Get it in a convenient form var JSONOptions = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true, IgnoreNullValues = true }; var lights = JsonSerializer.Deserialize <BackpackLights>(text, JSONOptions); var numLights = AssetsType.Cozmo == AssetsType?4:3; var lightKeyFrames = new List <LightFrame> [numLights]; // Convert it into the final internal form for (var idx = 0; idx < numLights; idx++) { // Start the trigger time with the offset for the light uint triggerTime = (uint)lights.offset[idx]; // Create the on frame first var rgba = lights.onColors[idx]; // Ignore alpha var color = Color.FromArgb(255, (byte)(255 * rgba[0]), (byte)(255 * rgba[1]), (byte)(255 * rgba[2])); var lightFrame = new LightFrame(triggerTime, (uint)lights.onPeriod_ms[idx], color); var a = new List <LightFrame> { lightFrame }; // Compute the trigger time of the off period triggerTime += (uint)(lights.onPeriod_ms[idx] + lights.transitionOnPeriod_ms[idx]); // Create the off frame next rgba = lights.offColors[idx]; // Ignore alpha color = Color.FromArgb(255, (byte)(255 * rgba[0]), (byte)(255 * rgba[1]), (byte)(255 * rgba[2])); lightFrame = new LightFrame(triggerTime, (uint)lights.offPeriod_ms[idx], color); triggerTime += (uint)(lights.offPeriod_ms[idx] + lights.transitionOffPeriod_ms[idx]); var duration = triggerTime - lights.offset[idx]; a.Add(lightFrame); // Put these key frames for the light in lightKeyFrames[idx] = a; } var s = new LightsPattern(); s.lightKeyFrames = lightKeyFrames; // Convert it back to an array var sequences = new LightsPattern[1] { s }; // cache it BackpackLightsCache(triggerName, sequences); // return it return(sequences); }