private void RenderElement(bool altColor, ref TimeSpan startTime, ref TimeSpan intervalTime, ElementNode element) { EffectIntents result; if ((StaticColor1 && altColor) || StaticColor2 && !altColor) { var level = new SetLevel.SetLevel(); level.TargetNodes = new ElementNode[] { element }; level.Color = altColor ? Color1 : Color2; level.TimeSpan = intervalTime; level.IntensityLevel = altColor ? IntensityLevel1 : IntensityLevel2; result = level.Render(); } else { var pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { element }; pulse.TimeSpan = intervalTime; pulse.ColorGradient = altColor ? ColorGradient1 : ColorGradient2; pulse.LevelCurve = altColor ? Curve1 : Curve2; result = pulse.Render(); } result.OffsetAllCommandsByTime(startTime); _elementData.Add(result); }
private void GenerateStartingStaticPulse(ElementNode target, IIntentNode intentNode, ColorGradient gradient = null) { if (intentNode == null || intentNode.StartTime <= TimeSpan.Zero) { return; } var lightingIntent = intentNode.Intent as LightingIntent; if (lightingIntent != null && lightingIntent.StartValue.Intensity > 0) { var newCurve = new Curve( new PointPairList(new PointPairList(new[] { 0.0, 100 }, new[] { lightingIntent.StartValue.Intensity *100, lightingIntent.StartValue.Intensity *100 }))); var pulse = new Pulse.Pulse { TargetNodes = new[] { target }, LevelCurve = newCurve, ColorGradient = gradient ?? new ColorGradient(lightingIntent.StartValue.FullColor), TimeSpan = intentNode.StartTime }; EffectIntents result = pulse.Render(); _elementData.Add(result); } }
private void RenderElement(Pulse.Pulse pulse, GradientLevelPair gradientLevelPair, TimeSpan startTime, ElementNode element, EffectIntents effectIntents) { pulse.ColorGradient = gradientLevelPair.ColorGradient; pulse.LevelCurve = gradientLevelPair.Curve; pulse.TargetNodes = new[] { element }; var result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); effectIntents.Add(result); }
//(Numbers represent color/curve pairs, rows are elements columns are intervals) //12341234 //23412341 //34123412 //41234123 //An offset of 2 //12341234 //34123412 //12341234 //34123412 //Offset 3 //12341234 //41234123 //23412341 //12341234 // renders the given node to the internal ElementData dictionary. If the given node is // not a element, will recursively descend until we render its elements. private EffectIntents RenderNode(ElementNode node) { EffectIntents effectIntents = new EffectIntents(); int intervals = 1; var gradientLevelItem = 0; var startIndexOffset = 0; //make local hold variables to prevent changes in the middle of rendering. int group = GroupLevel; var skip = IntervalSkipCount; int colorCount = Colors.Count(); //Use a single pulse to do our work, we don't need to keep creating it and then thowing it away making the GC work //hard for no reason. var pulse = new Pulse.Pulse(); if (!EnableStatic) { intervals = Convert.ToInt32(Math.Ceiling(TimeSpan.TotalMilliseconds/Interval)); } var startTime = TimeSpan.Zero; var nodes = node.GetLeafEnumerator(); var intervalTime = intervals == 1 ? TimeSpan : TimeSpan.FromMilliseconds(Interval); pulse.TimeSpan = intervalTime; for (int i = 0; i < intervals; i++) { var elements = nodes.Select((x, index) => new { x, index }) .GroupBy(x => x.index / group, y => y.x); foreach (IGrouping<int, ElementNode> elementGroup in elements) { var glp = Colors[gradientLevelItem]; foreach (var element in elementGroup) { RenderElement(pulse, glp, startTime, element, effectIntents); } gradientLevelItem = ++gradientLevelItem % colorCount; } startIndexOffset = (skip+startIndexOffset) % colorCount; gradientLevelItem = startIndexOffset; startTime += intervalTime; } return effectIntents; }
private void GeneratePulse(ElementNode target, TimeSpan startTime, TimeSpan duration, double currentMovementPosition) { EffectIntents result; Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { target }; pulse.TimeSpan = duration; pulse.LevelCurve = new Curve(PulseCurve); // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; break; case ChaseColorHandling.GradientThroughWholeEffect: double startPos = ((double)startTime.Ticks / (double)TimeSpan.Ticks); double endPos = ((double)(startTime + duration).Ticks / (double)TimeSpan.Ticks); if (startPos < 0.0) { startPos = 0.0; } if (endPos > 1.0) { endPos = 1.0; } pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case ChaseColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(currentMovementPosition / 100.0)); break; } result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); _elementData.Add(result); }
private void RenderNonBurst(CancellationTokenSource tokenSource, IEnumerable<IGrouping<int, ElementNode>> renderNodes) { var pulse = new Pulse.Pulse(); if (renderNodes != null && renderNodes.Any()) { TimeSpan effectTime = TimeSpan.Zero; if (WipeByCount) { int count = 0; double pulseSegment = (TimeSpan.TotalMilliseconds / PassCount) * (PulsePercent / 100); TimeSpan intervalTime = TimeSpan.FromMilliseconds((TimeSpan.TotalMilliseconds - pulseSegment) / (renderNodes.Count() * PassCount)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(pulseSegment); while (count < PassCount) { foreach (var item in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; EffectIntents result; foreach (ElementNode element in item) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; if (element != null) { pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; } count++; } } else { double intervals = (double)PulseTime / (double)renderNodes.Count(); var intervalTime = TimeSpan.FromMilliseconds(intervals); // the calculation above blows up render time/memory as count goes up, try this.. // also fails if intervals is less than half a ms and intervalTime then gets 0 intervalTime = TimeSpan.FromMilliseconds(Math.Max(intervalTime.TotalMilliseconds, 5)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(PulseTime); while (effectTime < TimeSpan) { foreach (var item in renderNodes) { EffectIntents result; if (tokenSource != null && tokenSource.IsCancellationRequested) return; foreach (ElementNode element in item) { if (element != null) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; if (effectTime >= TimeSpan) return; } } } } }
private void DoRendering() { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(2); List<ChannelNode> renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()).ToList(); int targetNodeCount = renderNodes.Count; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets int i = 0; foreach (ChannelNode target in renderNodes) { pulse = new Pulse.Pulse(); pulse.TargetNodes = new ChannelNode[] { target }; pulse.TimeSpan = TimeSpan; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { DefaultLevel * 100, DefaultLevel * 100 })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = new ColorGradient(StaticColor); break; case ChaseColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case ChaseColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt((double)i / (double)targetNodeCount)); break; } pulseData = pulse.Render(); _channelData.Add(pulseData); i++; } // the total chase time TimeSpan chaseTime = TimeSpan.FromMilliseconds(TimeSpan.TotalMilliseconds - PulseOverlap); if (chaseTime.TotalMilliseconds <= 0) chaseTime = TimeSpan.FromMilliseconds(1); // we need to keep track of the channel that is 'under' the curve at a given time, to see if it changes, // and when it does, we make the effect for it then (since it's a variable time pulse). ChannelNode lastTargetedNode = null; TimeSpan lastNodeStartTime = TimeSpan.Zero; // iterate up to and including the last pulse generated for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan; current += increment) { double currentPercentageIntoChase = ((double)current.Ticks / (double)chaseTime.Ticks) * 100.0; double currentMovementPosition = ChaseMovement.GetValue(currentPercentageIntoChase); int currentNodeIndex = (int)((currentMovementPosition / 100.0) * targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount) currentNodeIndex--; if (currentNodeIndex >= targetNodeCount) { VixenSystem.Logging.Warning("Chase effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } ChannelNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) continue; // if the last node targeted wasn't null, we need to make a pulse for it if (lastTargetedNode != null) { GeneratePulse(lastTargetedNode, lastNodeStartTime, current - lastNodeStartTime + TimeSpan.FromMilliseconds(PulseOverlap), currentMovementPosition); } lastTargetedNode = currentNode; lastNodeStartTime = current; // if we've hit the 100% mark of the chase curve, bail (the last one gets generated after) if (currentPercentageIntoChase >= 100.0) break; } // generate the last pulse if (lastTargetedNode != null) { GeneratePulse(lastTargetedNode, lastNodeStartTime, TimeSpan - lastNodeStartTime, 1.0); } _channelData = EffectIntents.Restrict(_channelData, TimeSpan.Zero, TimeSpan); }
private void GeneratePulse(ChannelNode target, TimeSpan startTime, TimeSpan duration, double currentMovementPosition) { EffectIntents result; Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new ChannelNode[] { target }; pulse.TimeSpan = duration; pulse.LevelCurve = new Curve(PulseCurve); // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; break; case ChaseColorHandling.GradientThroughWholeEffect: double startPos = ((double)startTime.Ticks / (double)TimeSpan.Ticks); double endPos = ((double)(startTime + duration).Ticks / (double)TimeSpan.Ticks); if (startPos < 0.0) startPos = 0.0; if (endPos > 1.0) endPos = 1.0; pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case ChaseColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(currentMovementPosition / 100.0)); break; } result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); _channelData.Add(result); }
private void GenerateStartingStaticPulse(ElementNode target, IIntentNode intentNode, ColorGradient gradient=null) { if (intentNode == null || intentNode.StartTime <= TimeSpan.Zero) return; var lightingIntent = intentNode.Intent as LightingIntent; if (lightingIntent!= null && lightingIntent.StartValue.Intensity > 0) { var newCurve = new Curve( new PointPairList(new PointPairList(new[] {0.0, 100}, new[] {lightingIntent.StartValue.Intensity*100, lightingIntent.StartValue.Intensity*100}))); var pulse = new Pulse.Pulse { TargetNodes = new[] {target}, LevelCurve = newCurve, ColorGradient = gradient ?? new ColorGradient(lightingIntent.StartValue.FullColor), TimeSpan = intentNode.StartTime }; EffectIntents result = pulse.Render(); _elementData.Add(result); } }
private void DoRendering() { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(10); List<ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; ElementNode lastTargetedNode = null; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets int i = 0; foreach (ElementNode target in renderNodes) { pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { target }; pulse.TimeSpan = TimeSpan; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { DefaultLevel * 100.0, DefaultLevel * 100.0 })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = new ColorGradient(StaticColor); break; case SpinColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; break; case SpinColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case SpinColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt((double)i / (double)targetNodeCount)); break; } pulseData = pulse.Render(); _elementData.Add(pulseData); i++; } // calculate the pulse time and revolution time exactly (based on the parameters from the data) double revTimeMs = 0; // single revolution time (ms) // figure out the relative length of a individual pulse double pulseConstant = 0; // how much of each pulse is a constant time double pulseFractional = 0; // how much of each pulse is a fraction of a single spin if (PulseLengthFormat == SpinPulseLengthFormat.FixedTime) { pulseConstant = PulseTime; } else if (PulseLengthFormat == SpinPulseLengthFormat.PercentageOfRevolution) { pulseFractional = PulsePercentage / 100.0; } else if (PulseLengthFormat == SpinPulseLengthFormat.EvenlyDistributedAcrossSegments) { pulseFractional = 1.0 / (double)targetNodeCount; } // magic number. (the inaccuracy of interpolating the curve into a position. eg. if we have 5 'positions', then // the curve should really be from 0-80% for the last spin, to make sure the last pulse finishes accurately.) double pulseInterpolationOffset = 1.0 / (double)targetNodeCount; // figure out either the revolution count or time, based on what data we have if (SpeedFormat == SpinSpeedFormat.RevolutionCount) { revTimeMs = (TimeSpan.TotalMilliseconds - pulseConstant) / (RevolutionCount + pulseFractional - pulseInterpolationOffset); } else if (SpeedFormat == SpinSpeedFormat.RevolutionFrequency) { revTimeMs = (1.0 / RevolutionFrequency) * 1000.0; // convert Hz to period ms } else if (SpeedFormat == SpinSpeedFormat.FixedTime) { revTimeMs = RevolutionTime; } double pulTimeMs = pulseConstant + (revTimeMs * pulseFractional); TimeSpan revTimeSpan = TimeSpan.FromMilliseconds(revTimeMs); TimeSpan pulseTimeSpan = TimeSpan.FromMilliseconds(pulTimeMs); // figure out which way we're moving through the elements Curve movement; if (ReverseSpin) movement = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { 100, 0 })); else movement = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { 0, 100 })); // iterate up to and including the last pulse generated // a bit iffy, but stops 'carry over' spins past the end (when there's overlapping spins). But we need to go past // (total - pulse) as the last pulse can often be a bit inaccurate due to the rounding of the increment for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan - pulseTimeSpan + increment; current += increment) { double currentPercentageIntoSpin = ((double)(current.Ticks % revTimeSpan.Ticks) / (double)revTimeSpan.Ticks) * 100.0; double targetElementPosition = movement.GetValue(currentPercentageIntoSpin); int currentNodeIndex = (int)((targetElementPosition / 100.0) * targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount) currentNodeIndex--; if (currentNodeIndex >= targetNodeCount) { VixenSystem.Logging.Warning("Spin effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) continue; // make a pulse for it pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { currentNode }; pulse.TimeSpan = pulseTimeSpan; pulse.LevelCurve = new Curve(PulseCurve); // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; break; case SpinColorHandling.GradientThroughWholeEffect: double startPos = ((double)current.Ticks / (double)TimeSpan.Ticks); double endPos = 1.0; if (TimeSpan - current >= pulseTimeSpan) endPos = ((double)(current + pulseTimeSpan).Ticks / (double)TimeSpan.Ticks); pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); break; case SpinColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case SpinColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(targetElementPosition / 100.0)); break; } pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); lastTargetedNode = currentNode; } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private EffectIntents RenderElement(ElementNode node, double positionWithinGroup, List <IndividualTwinkleDetails> twinkles = null) { if (node == null || node.Element == null) { return(null); } if (twinkles == null) { twinkles = GenerateTwinkleData(); } EffectIntents result = new EffectIntents(); bool discreteColors = ColorModule.isElementNodeDiscreteColored(node); // render the flat 'minimum value' across the entire effect Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { node }; pulse.TimeSpan = TimeSpan; double minPulseValue = MinimumLevel * 100.0; EffectIntents pulseData; // figure out what color gradient to use for the pulse if (MinimumLevel > 0.0) { switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: if (discreteColors) { List <Tuple <Color, float> > colorProportions = ColorGradient.GetDiscreteColorsAndProportionsAt(0); foreach (Tuple <Color, float> colorProportion in colorProportions) { double value = minPulseValue * colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { value, value })); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); result.Add(pulseData); } } else { pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { minPulseValue, minPulseValue })); pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(0)); pulseData = pulse.Render(); result.Add(pulseData); } break; case TwinkleColorHandling.GradientThroughWholeEffect: pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { minPulseValue, minPulseValue })); pulse.ColorGradient = ColorGradient; pulseData = pulse.Render(); result.Add(pulseData); break; case TwinkleColorHandling.StaticColor: pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { minPulseValue, minPulseValue })); pulse.ColorGradient = StaticColorGradient; pulseData = pulse.Render(); result.Add(pulseData); break; case TwinkleColorHandling.ColorAcrossItems: if (discreteColors) { List <Tuple <Color, float> > colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple <Color, float> colorProportion in colorsAtPosition) { double value = minPulseValue * colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { value, value })); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); result.Add(pulseData); } } else { pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { minPulseValue, minPulseValue })); pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulseData = pulse.Render(); result.Add(pulseData); } break; } } // render all the individual twinkles foreach (IndividualTwinkleDetails twinkle in twinkles) { { // make a pulse for it pulse.TargetNodes = new [] { node }; pulse.TimeSpan = twinkle.Duration; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 50, 100 }, new [] { twinkle.curvePoints[0], twinkle.curvePoints[1], twinkle.curvePoints[2] })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); break; case TwinkleColorHandling.GradientThroughWholeEffect: double startPos = ((double)twinkle.StartTime.Ticks / (double)TimeSpan.Ticks); double endPos = ((double)(twinkle.StartTime + twinkle.Duration).Ticks / (double)TimeSpan.Ticks); if (discreteColors) { double range = endPos - startPos; if (range <= 0.0) { Logging.Error("Twinkle: bad range: " + range + " (SP=" + startPos + ", EP=" + endPos + ")"); break; } ColorGradient cg = ColorGradient.GetSubGradientWithDiscreteColors(startPos, endPos); foreach (Color color in cg.GetColorsInGradient()) { Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair point in newCurve.Points) { double effectRelativePosition = startPos + ((point.X / 100.0) * range); double proportion = ColorGradient.GetProportionOfColorAt(effectRelativePosition, color); point.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(color); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } } else { pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } break; case TwinkleColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); break; case TwinkleColorHandling.ColorAcrossItems: if (discreteColors) { List <Tuple <Color, float> > colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple <Color, float> colorProportion in colorsAtPosition) { float proportion = colorProportion.Item2; // scale all levels of the twinkle curve by the proportion that is applicable to this color Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair pointPair in newCurve.Points) { pointPair.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } break; } } } return(result); }
private void DoRendering(CancellationTokenSource tokenSource = null) { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(2); List <ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets if the level is supposed to be nonzero if (DefaultLevel > 0) { int i = 0; foreach (ElementNode target in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } if (target != null && target.Element != null) { bool discreteColors = ColorModule.isElementNodeDiscreteColored(target); pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { target }; pulse.TimeSpan = TimeSpan; double level = DefaultLevel * 100.0; // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case ChaseColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case ChaseColorHandling.ColorAcrossItems: double positionWithinGroup = i / (double)targetNodeCount; if (discreteColors) { List <Tuple <Color, float> > colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple <Color, float> colorProportion in colorsAtPosition) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } double value = level * colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { value, value })); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); _elementData.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); } break; } i++; } } } // the total chase time TimeSpan chaseTime = TimeSpan.FromMilliseconds(TimeSpan.TotalMilliseconds - PulseOverlap); if (chaseTime.TotalMilliseconds <= 0) { chaseTime = TimeSpan.FromMilliseconds(1); } // we need to keep track of the element that is 'under' the curve at a given time, to see if it changes, // and when it does, we make the effect for it then (since it's a variable time pulse). ElementNode lastTargetedNode = null; TimeSpan lastNodeStartTime = TimeSpan.Zero; // iterate up to and including the last pulse generated for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan; current += increment) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } double currentPercentageIntoChase = ((double)current.Ticks / (double)chaseTime.Ticks) * 100.0; double currentMovementPosition = ChaseMovement.GetValue(currentPercentageIntoChase); int currentNodeIndex = (int)((currentMovementPosition / 100.0) * targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount && currentNodeIndex > 0) { currentNodeIndex--; } if (currentNodeIndex >= targetNodeCount) { Logging.Warn( "Chase effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } if (currentNodeIndex < 0) { continue; } ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) { continue; } // if the last node targeted wasn't null, we need to make a pulse for it if (lastTargetedNode != null) { TimeSpan duration = current - lastNodeStartTime + TimeSpan.FromMilliseconds(PulseOverlap); GeneratePulse(lastTargetedNode, lastNodeStartTime, duration , currentMovementPosition); } lastTargetedNode = currentNode; lastNodeStartTime = current; // if we've hit the 100% mark of the chase curve, bail (the last one gets generated after) if (currentPercentageIntoChase >= 100.0) { break; } } // generate the last pulse if (lastTargetedNode != null) { GeneratePulse(lastTargetedNode, lastNodeStartTime, TimeSpan - lastNodeStartTime, 1.0); } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private void DoRendering(CancellationTokenSource tokenSource = null) { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(10); List <ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; //If there are no nodes to render on Exit! if (targetNodeCount == 0) { return; } ElementNode lastTargetedNode = null; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets if nonzero if (DefaultLevel > 0) { int i = 0; foreach (ElementNode target in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } bool discreteColors = ColorModule.isElementNodeDiscreteColored(target); if (target == null) { continue; } if (target != null) { pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { target }; pulse.TimeSpan = TimeSpan; double level = DefaultLevel * 100.0; // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case SpinColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case SpinColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case SpinColorHandling.ColorAcrossItems: double positionWithinGroup = i / (double)targetNodeCount; if (discreteColors) { List <Tuple <Color, float> > colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple <Color, float> colorProportion in colorsAtPosition) { double value = level * colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { value, value })); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); _elementData.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { level, level })); pulseData = pulse.Render(); _elementData.Add(pulseData); } break; } i++; } } } // calculate the pulse time and revolution time exactly (based on the parameters from the data) double revTimeMs = 0; // single revolution time (ms) // figure out the relative length of a individual pulse double pulseConstant = 0; // how much of each pulse is a constant time double pulseFractional = 0; // how much of each pulse is a fraction of a single spin if (PulseLengthFormat == SpinPulseLengthFormat.FixedTime) { pulseConstant = PulseTime; } else if (PulseLengthFormat == SpinPulseLengthFormat.PercentageOfRevolution) { pulseFractional = PulsePercentage / 100.0; } else if (PulseLengthFormat == SpinPulseLengthFormat.EvenlyDistributedAcrossSegments) { pulseFractional = 1.0 / (double)targetNodeCount; } // magic number. (the inaccuracy of interpolating the curve into a position. eg. if we have 5 'positions', then // the curve should really be from 0-80% for the last spin, to make sure the last pulse finishes accurately.) double pulseInterpolationOffset = 1.0 / (double)targetNodeCount; // figure out either the revolution count or time, based on what data we have if (SpeedFormat == SpinSpeedFormat.RevolutionCount) { revTimeMs = (TimeSpan.TotalMilliseconds - pulseConstant) / (RevolutionCount + pulseFractional - pulseInterpolationOffset); } else if (SpeedFormat == SpinSpeedFormat.RevolutionFrequency) { revTimeMs = (1.0 / RevolutionFrequency) * 1000.0; // convert Hz to period ms } else if (SpeedFormat == SpinSpeedFormat.FixedTime) { revTimeMs = RevolutionTime; } double pulTimeMs = pulseConstant + (revTimeMs * pulseFractional); TimeSpan revTimeSpan = TimeSpan.FromMilliseconds(revTimeMs); TimeSpan pulseTimeSpan = TimeSpan.FromMilliseconds(pulTimeMs); // figure out which way we're moving through the elements Curve movement; if (ReverseSpin) { movement = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { 100, 0 })); } else { movement = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { 0, 100 })); } // iterate up to and including the last pulse generated // a bit iffy, but stops 'carry over' spins past the end (when there's overlapping spins). But we need to go past // (total - pulse) as the last pulse can often be a bit inaccurate due to the rounding of the increment for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan - pulseTimeSpan + increment; current += increment) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } double currentPercentageIntoSpin = ((double)(current.Ticks % revTimeSpan.Ticks) / (double)revTimeSpan.Ticks) * 100.0; double targetElementPosition = movement.GetValue(currentPercentageIntoSpin); int currentNodeIndex = (int)((targetElementPosition / 100.0) * targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount) { currentNodeIndex--; } if (currentNodeIndex >= targetNodeCount) { Logging.Warn( "Spin effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) { continue; } bool discreteColors = ColorModule.isElementNodeDiscreteColored(currentNode); // make a pulse for it pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { currentNode }; pulse.TimeSpan = pulseTimeSpan; pulse.LevelCurve = new Curve(PulseCurve); // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); break; case SpinColorHandling.GradientThroughWholeEffect: double startPos = ((double)current.Ticks / (double)TimeSpan.Ticks); double endPos = 1.0; if (TimeSpan - current >= pulseTimeSpan) { endPos = ((double)(current + pulseTimeSpan).Ticks / (double)TimeSpan.Ticks); } if (discreteColors) { double range = endPos - startPos; if (range <= 0.0) { Logging.Error("Spin: bad range: " + range + " (SP=" + startPos + ", EP=" + endPos + ")"); break; } ColorGradient cg = ColorGradient.GetSubGradientWithDiscreteColors(startPos, endPos); foreach (Color color in cg.GetColorsInGradient()) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair point in newCurve.Points) { double effectRelativePosition = startPos + ((point.X / 100.0) * range); double proportion = ColorGradient.GetProportionOfColorAt(effectRelativePosition, color); point.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(color); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } } else { pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } break; case SpinColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); break; case SpinColorHandling.ColorAcrossItems: if (discreteColors) { List <Tuple <Color, float> > colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(targetElementPosition / 100.0); foreach (Tuple <Color, float> colorProportion in colorsAtPosition) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } float proportion = colorProportion.Item2; // scale all levels of the pulse curve by the proportion that is applicable to this color Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair pointPair in newCurve.Points) { pointPair.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(targetElementPosition / 100.0)); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } break; } lastTargetedNode = currentNode; } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private void GeneratePulse(ElementNode target, TimeSpan startTime, TimeSpan duration, double currentMovementPosition) { EffectIntents result = null; Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new[] { target }; pulse.TimeSpan = duration; pulse.LevelCurve = new Curve(PulseCurve); bool discreteColors = ColorModule.isElementNodeDiscreteColored(target); IIntentNode intent; // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { foreach (var iintentNode in result.FirstOrDefault().Value) { GenerateStartingStaticPulse(target, iintentNode); } } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { foreach (var iintentNode in result.FirstOrDefault().Value) { GenerateExtendedStaticPulse(target, iintentNode); } } break; case ChaseColorHandling.GradientThroughWholeEffect: double startPos = (startTime.Ticks / (double)TimeSpan.Ticks); double endPos = ((startTime + duration).Ticks / (double)TimeSpan.Ticks); if (startPos < 0.0) { startPos = 0.0; } if (endPos > 1.0) { endPos = 1.0; } if (discreteColors) { double range = endPos - startPos; if (range <= 0.0) { Logging.Error("Chase: bad range: " + range + " (SP=" + startPos + ", EP=" + endPos + ")"); break; } ColorGradient cg = ColorGradient.GetSubGradientWithDiscreteColors(startPos, endPos); foreach (Color color in cg.GetColorsInGradient()) { Curve newCurve = new Curve(PulseCurve.Points); foreach (PointPair point in newCurve.Points) { double effectRelativePosition = startPos + ((point.X / 100.0) * range); double proportion = ColorGradient.GetProportionOfColorAt(effectRelativePosition, color); point.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(color); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent); } } } else { pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent, ColorGradient.GetSubGradient(0, startPos)); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent, ColorGradient.GetSubGradient(endPos, 1)); } } break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent); } break; case ChaseColorHandling.ColorAcrossItems: if (discreteColors) { List <Tuple <Color, float> > colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(currentMovementPosition / 100.0); foreach (Tuple <Color, float> colorProportion in colorsAtPosition) { float proportion = colorProportion.Item2; // scale all levels of the pulse curve by the proportion that is applicable to this color Curve newCurve = new Curve(PulseCurve.Points); foreach (PointPair pointPair in newCurve.Points) { pointPair.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(colorProportion.Item1); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent); } } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(currentMovementPosition / 100.0)); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent); } } break; } }
private void RenderNonBurst(CancellationTokenSource tokenSource, IEnumerable <IGrouping <int, ElementNode> > renderNodes) { var pulse = new Pulse.Pulse(); if (renderNodes != null && renderNodes.Any()) { TimeSpan effectTime = TimeSpan.Zero; if (WipeByCount) { int count = 0; double pulseSegment = (TimeSpan.TotalMilliseconds / PassCount) * (PulsePercent / 100); TimeSpan intervalTime = TimeSpan.FromMilliseconds((TimeSpan.TotalMilliseconds - pulseSegment) / (renderNodes.Count() * PassCount)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(pulseSegment); while (count < PassCount) { foreach (var item in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } EffectIntents result; foreach (ElementNode element in item) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } if (element != null) { pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; } count++; } } else { double intervals = (double)PulseTime / (double)renderNodes.Count(); var intervalTime = TimeSpan.FromMilliseconds(intervals); // the calculation above blows up render time/memory as count goes up, try this.. // also fails if intervals is less than half a ms and intervalTime then gets 0 intervalTime = TimeSpan.FromMilliseconds(Math.Max(intervalTime.TotalMilliseconds, 5)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(PulseTime); while (effectTime < TimeSpan) { foreach (var item in renderNodes) { EffectIntents result; if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } foreach (ElementNode element in item) { if (element != null) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; if (effectTime >= TimeSpan) { return; } } } } } }
private void RenderBurst(CancellationTokenSource tokenSource, WipeDirection direction) { switch (direction) { case WipeDirection.In: case WipeDirection.Out: break; default: throw new InvalidOperationException("the RenderBurst method should only be called for Wipe Directions In and Out"); break; } var burstNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .Select(s => { var prop = s.Properties.Get(LocationDescriptor._typeId); if (prop != null) { return(new Tuple <ElementNode, int, int, int>(s, ((LocationData)prop.ModuleData).X, ((LocationData)prop.ModuleData).Y, ((LocationData)prop.ModuleData).Z)); } return(new Tuple <ElementNode, int, int, int>(null, -1, -1, -1)); //return null }) .Where(s => s.Item2 > 0) // Ignore the pseudo null values .ToList(); if (!burstNodes.Any()) { return; } var maxX = burstNodes.Max(m => m.Item2); var maxY = burstNodes.Max(m => m.Item3); var minX = burstNodes.Min(m => m.Item2); var minY = burstNodes.Min(m => m.Item3); var Steps = (int)(Math.Max(maxX - minX, maxY - minY) / 2); List <Tuple <int, ElementNode[]> > groups = new List <Tuple <int, ElementNode[]> >(); for (int i = 0; i < Steps; i++) { List <ElementNode> elements = new List <ElementNode>(); var xNodes = burstNodes.Where(x => (x.Item2 == minX + i || x.Item2 == maxX - i) ) .Select(s => s.Item1).ToList(); var yNodes = burstNodes.Where(x => ( x.Item3 == minY + i || x.Item3 == maxY - i) ) .Select(s => s.Item1).ToList(); yNodes.RemoveAll(s => { var prop = s.Properties.Get(LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X < minX + i || ((LocationData)prop.ModuleData).X > maxX - i); } return(false); }); xNodes.RemoveAll(s => { var prop = s.Properties.Get(LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y < minY + i || ((LocationData)prop.ModuleData).Y > maxY - i); } return(false); }); elements.AddRange(yNodes); elements.AddRange(xNodes); groups.Add(new Tuple <int, ElementNode[]>(i, elements.ToArray())); } List <ElementNode[]> renderNodes = new List <ElementNode[]>(); switch (direction) { case WipeDirection.In: renderNodes = groups.OrderBy(o => o.Item1).Select(s => s.Item2).ToList(); break; case WipeDirection.Out: renderNodes = groups.OrderByDescending(o => o.Item1).Select(s => s.Item2).ToList(); break; } var pulse = new Pulse.Pulse(); if (renderNodes != null && renderNodes.Any()) { TimeSpan effectTime = TimeSpan.Zero; if (WipeByCount) { int count = 0; double pulseSegment = (TimeSpan.TotalMilliseconds / PassCount) * (PulsePercent / 100); TimeSpan intervalTime = TimeSpan.FromMilliseconds((TimeSpan.TotalMilliseconds - pulseSegment) / (renderNodes.Count() * PassCount)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(pulseSegment); while (count < PassCount) { foreach (var item in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } EffectIntents result; foreach (ElementNode element in item) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } if (element != null) { pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; } count++; } } else { double intervals = (double)PulseTime / (double)renderNodes.Count(); var intervalTime = TimeSpan.FromMilliseconds(intervals); // the calculation above blows up render time/memory as count goes up, try this.. // also fails if intervals is less than half a ms and intervalTime then gets 0 intervalTime = TimeSpan.FromMilliseconds(Math.Max(intervalTime.TotalMilliseconds, 5)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(PulseTime); while (effectTime < TimeSpan) { foreach (var item in renderNodes) { EffectIntents result; if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } foreach (ElementNode element in item) { if (element != null) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; if (effectTime >= TimeSpan) { return; } } } } } }
private void DoRendering() { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(10); List <ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; ElementNode lastTargetedNode = null; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets int i = 0; foreach (ElementNode target in renderNodes) { if (target != null) { pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { target }; pulse.TimeSpan = TimeSpan; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { DefaultLevel * 100.0, DefaultLevel * 100.0 })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = new ColorGradient(StaticColor); break; case SpinColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; break; case SpinColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case SpinColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt((double)i / (double)targetNodeCount)); break; } pulseData = pulse.Render(); _elementData.Add(pulseData); i++; } } // calculate the pulse time and revolution time exactly (based on the parameters from the data) double revTimeMs = 0; // single revolution time (ms) // figure out the relative length of a individual pulse double pulseConstant = 0; // how much of each pulse is a constant time double pulseFractional = 0; // how much of each pulse is a fraction of a single spin if (PulseLengthFormat == SpinPulseLengthFormat.FixedTime) { pulseConstant = PulseTime; } else if (PulseLengthFormat == SpinPulseLengthFormat.PercentageOfRevolution) { pulseFractional = PulsePercentage / 100.0; } else if (PulseLengthFormat == SpinPulseLengthFormat.EvenlyDistributedAcrossSegments) { pulseFractional = 1.0 / (double)targetNodeCount; } // magic number. (the inaccuracy of interpolating the curve into a position. eg. if we have 5 'positions', then // the curve should really be from 0-80% for the last spin, to make sure the last pulse finishes accurately.) double pulseInterpolationOffset = 1.0 / (double)targetNodeCount; // figure out either the revolution count or time, based on what data we have if (SpeedFormat == SpinSpeedFormat.RevolutionCount) { revTimeMs = (TimeSpan.TotalMilliseconds - pulseConstant) / (RevolutionCount + pulseFractional - pulseInterpolationOffset); } else if (SpeedFormat == SpinSpeedFormat.RevolutionFrequency) { revTimeMs = (1.0 / RevolutionFrequency) * 1000.0; // convert Hz to period ms } else if (SpeedFormat == SpinSpeedFormat.FixedTime) { revTimeMs = RevolutionTime; } double pulTimeMs = pulseConstant + (revTimeMs * pulseFractional); TimeSpan revTimeSpan = TimeSpan.FromMilliseconds(revTimeMs); TimeSpan pulseTimeSpan = TimeSpan.FromMilliseconds(pulTimeMs); // figure out which way we're moving through the elements Curve movement; if (ReverseSpin) { movement = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { 100, 0 })); } else { movement = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { 0, 100 })); } // iterate up to and including the last pulse generated // a bit iffy, but stops 'carry over' spins past the end (when there's overlapping spins). But we need to go past // (total - pulse) as the last pulse can often be a bit inaccurate due to the rounding of the increment for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan - pulseTimeSpan + increment; current += increment) { double currentPercentageIntoSpin = ((double)(current.Ticks % revTimeSpan.Ticks) / (double)revTimeSpan.Ticks) * 100.0; double targetElementPosition = movement.GetValue(currentPercentageIntoSpin); int currentNodeIndex = (int)((targetElementPosition / 100.0) * targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount) { currentNodeIndex--; } if (currentNodeIndex >= targetNodeCount) { VixenSystem.Logging.Warning("Spin effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) { continue; } // make a pulse for it pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { currentNode }; pulse.TimeSpan = pulseTimeSpan; pulse.LevelCurve = new Curve(PulseCurve); // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; break; case SpinColorHandling.GradientThroughWholeEffect: double startPos = ((double)current.Ticks / (double)TimeSpan.Ticks); double endPos = 1.0; if (TimeSpan - current >= pulseTimeSpan) { endPos = ((double)(current + pulseTimeSpan).Ticks / (double)TimeSpan.Ticks); } pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); break; case SpinColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case SpinColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(targetElementPosition / 100.0)); break; } pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); lastTargetedNode = currentNode; } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private void DoRendering(CancellationTokenSource tokenSource = null) { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(2); List<ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets if the level is supposed to be nonzero if (DefaultLevel > 0) { int i = 0; foreach (ElementNode target in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; if (target != null && target.Element != null) { bool discreteColors = ColorModule.isElementNodeDiscreteColored(target); pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] {target}; pulse.TimeSpan = TimeSpan; double level = DefaultLevel*100.0; // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case ChaseColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case ChaseColorHandling.ColorAcrossItems: double positionWithinGroup = i/(double) targetNodeCount; if (discreteColors) { List<Tuple<Color, float>> colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple<Color, float> colorProportion in colorsAtPosition) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; double value = level*colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {value, value})); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); _elementData.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); } break; } i++; } } } // the total chase time TimeSpan chaseTime = TimeSpan.FromMilliseconds(TimeSpan.TotalMilliseconds - PulseOverlap); if (chaseTime.TotalMilliseconds <= 0) chaseTime = TimeSpan.FromMilliseconds(1); // we need to keep track of the element that is 'under' the curve at a given time, to see if it changes, // and when it does, we make the effect for it then (since it's a variable time pulse). ElementNode lastTargetedNode = null; TimeSpan lastNodeStartTime = TimeSpan.Zero; // iterate up to and including the last pulse generated for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan; current += increment) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; double currentPercentageIntoChase = ((double) current.Ticks/(double) chaseTime.Ticks)*100.0; double currentMovementPosition = ChaseMovement.GetValue(currentPercentageIntoChase); int currentNodeIndex = (int) ((currentMovementPosition/100.0)*targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount && currentNodeIndex > 0) currentNodeIndex--; if (currentNodeIndex >= targetNodeCount) { Logging.Warn( "Chase effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } if (currentNodeIndex < 0) continue; ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) continue; // if the last node targeted wasn't null, we need to make a pulse for it if (lastTargetedNode != null) { TimeSpan duration = current - lastNodeStartTime + TimeSpan.FromMilliseconds(PulseOverlap); GeneratePulse(lastTargetedNode, lastNodeStartTime, duration , currentMovementPosition); } lastTargetedNode = currentNode; lastNodeStartTime = current; // if we've hit the 100% mark of the chase curve, bail (the last one gets generated after) if (currentPercentageIntoChase >= 100.0) break; } // generate the last pulse if (lastTargetedNode != null) { GeneratePulse(lastTargetedNode, lastNodeStartTime, TimeSpan - lastNodeStartTime, ChaseMovement.GetValue(100)); } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private EffectIntents RenderElement(ElementNode node, double positionWithinGroup, List<IndividualTwinkleDetails> twinkles = null) { if (node == null) return null; if (twinkles == null) twinkles = GenerateTwinkleData(); EffectIntents result = new EffectIntents(); // render the flat 'minimum value' across the entire effect Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { node }; pulse.TimeSpan = TimeSpan; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { MinimumLevel * 100.0, MinimumLevel * 100.0 })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(0)); break; case TwinkleColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; break; case TwinkleColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case TwinkleColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); break; } EffectIntents pulseData = pulse.Render(); result.Add(pulseData); // render all the individual twinkles foreach (IndividualTwinkleDetails twinkle in twinkles) { { // make a pulse for it pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { node }; pulse.TimeSpan = twinkle.Duration; pulse.LevelCurve = twinkle.TwinkleCurve; // figure out what color gradient to use for the pulse switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; break; case TwinkleColorHandling.GradientThroughWholeEffect: double startPos = ((double)twinkle.StartTime.Ticks / (double)TimeSpan.Ticks); double endPos = ((double)(twinkle.StartTime + twinkle.Duration).Ticks / (double)TimeSpan.Ticks); pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); break; case TwinkleColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case TwinkleColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); break; } pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } } return result; }
private void RenderBurst(CancellationTokenSource tokenSource, WipeDirection direction) { switch (direction) { case WipeDirection.In: case WipeDirection.Out: break; default: throw new InvalidOperationException("the RenderBurst method should only be called for Wipe Directions In and Out"); break; } var burstNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .Select(s => { var prop = s.Properties.Get(LocationDescriptor._typeId); if (prop != null) { return new Tuple<ElementNode, int, int, int>(s, ((LocationData)prop.ModuleData).X, ((LocationData)prop.ModuleData).Y, ((LocationData)prop.ModuleData).Z); } return new Tuple<ElementNode, int, int, int>(null, -1, -1, -1); //return null }) .Where(s => s.Item2 > 0) // Ignore the pseudo null values .ToList(); if (!burstNodes.Any()) return; var maxX = burstNodes.Max(m => m.Item2); var maxY = burstNodes.Max(m => m.Item3); var minX = burstNodes.Min(m => m.Item2); var minY = burstNodes.Min(m => m.Item3); var Steps = (int)(Math.Max(maxX - minX, maxY - minY) / 2); List<Tuple<int, ElementNode[]>> groups = new List<Tuple<int, ElementNode[]>>(); for (int i = 0; i < Steps; i++) { List<ElementNode> elements = new List<ElementNode>(); var xNodes = burstNodes.Where(x => (x.Item2 == minX + i || x.Item2 == maxX - i) ) .Select(s => s.Item1).ToList(); var yNodes = burstNodes.Where(x => ( x.Item3 == minY + i || x.Item3 == maxY - i) ) .Select(s => s.Item1).ToList(); yNodes.RemoveAll(s => { var prop = s.Properties.Get(LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X < minX + i || ((LocationData)prop.ModuleData).X > maxX - i; } return false; }); xNodes.RemoveAll(s => { var prop = s.Properties.Get(LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y < minY + i || ((LocationData)prop.ModuleData).Y > maxY - i; } return false; }); elements.AddRange(yNodes); elements.AddRange(xNodes); groups.Add(new Tuple<int, ElementNode[]>(i, elements.ToArray())); } List<ElementNode[]> renderNodes = new List<ElementNode[]>(); switch (direction) { case WipeDirection.In: renderNodes = groups.OrderBy(o => o.Item1).Select(s => s.Item2).ToList(); break; case WipeDirection.Out: renderNodes = groups.OrderByDescending(o => o.Item1).Select(s => s.Item2).ToList(); break; } var pulse = new Pulse.Pulse(); if (renderNodes != null && renderNodes.Any()) { TimeSpan effectTime = TimeSpan.Zero; if (WipeByCount) { int count = 0; double pulseSegment = (TimeSpan.TotalMilliseconds / PassCount) * (PulsePercent / 100); TimeSpan intervalTime = TimeSpan.FromMilliseconds((TimeSpan.TotalMilliseconds - pulseSegment) / (renderNodes.Count() * PassCount)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(pulseSegment); while (count < PassCount) { foreach (var item in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; EffectIntents result; foreach (ElementNode element in item) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; if (element != null) { pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; } count++; } } else { double intervals = (double)PulseTime / (double)renderNodes.Count(); var intervalTime = TimeSpan.FromMilliseconds(intervals); // the calculation above blows up render time/memory as count goes up, try this.. // also fails if intervals is less than half a ms and intervalTime then gets 0 intervalTime = TimeSpan.FromMilliseconds(Math.Max(intervalTime.TotalMilliseconds, 5)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(PulseTime); while (effectTime < TimeSpan) { foreach (var item in renderNodes) { EffectIntents result; if (tokenSource != null && tokenSource.IsCancellationRequested) return; foreach (ElementNode element in item) { if (element != null) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; pulse.TargetNodes = new ElementNode[] { element }; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; if (effectTime >= TimeSpan) return; } } } } }
protected override void _PreRender(CancellationTokenSource tokenSource = null) { _elementData = new EffectIntents(); IEnumerable<IGrouping<int, ElementNode>> renderNodes = null; var enumerator = TargetNodes.SelectMany(x => x.GetLeafEnumerator()); var b = enumerator; switch (_data.Direction) { case WipeDirection.Up: renderNodes = TargetNodes .SelectMany(x => x.GetLeafEnumerator()) .OrderByDescending(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y; } else return 1; }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X; } else return 1; }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y; } else return 1; }) .Distinct(); break; case WipeDirection.Down: renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .OrderBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y; } else return 1; }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X; } else return 1; }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y; } else return 1; }) .Distinct(); break; case WipeDirection.Right: renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .OrderBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X; } else return 1; }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y; } else return 1; }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X; } else return 1; }) .Distinct(); break; case WipeDirection.Left: renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .OrderByDescending(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X; } return 1; }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).Y; } return 1; }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return ((LocationData)prop.ModuleData).X; } return 1; }) .Distinct(); break; default: break; } if (renderNodes != null && renderNodes.Count()>0) { TimeSpan effectTime = TimeSpan.Zero; if (WipeByCount) { int count = 0; double pulseSegment = (TimeSpan.TotalMilliseconds / PassCount) * (PulsePercent / 100) ; TimeSpan intervalTime = TimeSpan.FromMilliseconds((TimeSpan.TotalMilliseconds - pulseSegment) / (renderNodes.Count() * PassCount)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(pulseSegment); while (count < PassCount) { foreach (var item in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; EffectIntents result; foreach (ElementNode element in item) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; if (element != null) { var pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { element }; pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; } count++; } } else { double intervals = (double)PulseTime / (double)renderNodes.Count(); var intervalTime = TimeSpan.FromMilliseconds(intervals); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(PulseTime); while (effectTime < TimeSpan) { foreach (var item in renderNodes) { EffectIntents result; if (tokenSource != null && tokenSource.IsCancellationRequested) return; foreach (ElementNode element in item) { if (element != null) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; var pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { element }; pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; if (effectTime >= TimeSpan) return; } } } } }
protected override void _PreRender(CancellationTokenSource tokenSource = null) { _elementData = new EffectIntents(); IEnumerable <IGrouping <int, ElementNode> > renderNodes = null; var enumerator = TargetNodes.SelectMany(x => x.GetLeafEnumerator()); var b = enumerator; switch (_data.Direction) { case WipeDirection.Up: renderNodes = TargetNodes .SelectMany(x => x.GetLeafEnumerator()) .OrderByDescending(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y); } else { return(1); } }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X); } else { return(1); } }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y); } else { return(1); } }) .Distinct(); break; case WipeDirection.Down: renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .OrderBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y); } else { return(1); } }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X); } else { return(1); } }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y); } else { return(1); } }) .Distinct(); break; case WipeDirection.Right: renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .OrderBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X); } else { return(1); } }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y); } else { return(1); } }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X); } else { return(1); } }) .Distinct(); break; case WipeDirection.Left: renderNodes = TargetNodes.SelectMany(x => x.GetLeafEnumerator()) .OrderByDescending(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X); } return(1); }) .ThenBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).Y); } return(1); }) .GroupBy(x => { var prop = x.Properties.Get(VixenModules.Property.Location.LocationDescriptor._typeId); if (prop != null) { return(((LocationData)prop.ModuleData).X); } return(1); }) .Distinct(); break; default: break; } if (renderNodes != null && renderNodes.Count() > 0) { TimeSpan effectTime = TimeSpan.Zero; if (WipeByCount) { int count = 0; double pulseSegment = (TimeSpan.TotalMilliseconds / PassCount) * (PulsePercent / 100); TimeSpan intervalTime = TimeSpan.FromMilliseconds((TimeSpan.TotalMilliseconds - pulseSegment) / (renderNodes.Count() * PassCount)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(pulseSegment); while (count < PassCount) { foreach (var item in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } EffectIntents result; foreach (ElementNode element in item) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } if (element != null) { var pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { element }; pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; } count++; } } else { double intervals = (double)PulseTime / (double)renderNodes.Count(); var intervalTime = TimeSpan.FromMilliseconds(intervals); // the calculation above blows up render time/memory as count goes up, try this.. // also fails if intervals is less than half a ms and intervalTime then gets 0 intervalTime = TimeSpan.FromMilliseconds(Math.Max(intervalTime.TotalMilliseconds, 50)); TimeSpan segmentPulse = TimeSpan.FromMilliseconds(PulseTime); while (effectTime < TimeSpan) { foreach (var item in renderNodes) { EffectIntents result; if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } foreach (ElementNode element in item) { if (element != null) { if (tokenSource != null && tokenSource.IsCancellationRequested) { return; } var pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { element }; pulse.TimeSpan = segmentPulse; pulse.ColorGradient = _data.ColorGradient; pulse.LevelCurve = _data.Curve; result = pulse.Render(); result.OffsetAllCommandsByTime(effectTime); _elementData.Add(result); } } effectTime += intervalTime; if (effectTime >= TimeSpan) { return; } } } } } }
private EffectIntents RenderElement(ElementNode node, double positionWithinGroup, List <IndividualTwinkleDetails> twinkles = null) { if (node == null) { return(null); } if (twinkles == null) { twinkles = GenerateTwinkleData(); } EffectIntents result = new EffectIntents(); // render the flat 'minimum value' across the entire effect Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { node }; pulse.TimeSpan = TimeSpan; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { MinimumLevel * 100.0, MinimumLevel * 100.0 })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(0)); break; case TwinkleColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; break; case TwinkleColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case TwinkleColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); break; } EffectIntents pulseData = pulse.Render(); result.Add(pulseData); // render all the individual twinkles foreach (IndividualTwinkleDetails twinkle in twinkles) { { // make a pulse for it pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { node }; pulse.TimeSpan = twinkle.Duration; pulse.LevelCurve = twinkle.TwinkleCurve; // figure out what color gradient to use for the pulse switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; break; case TwinkleColorHandling.GradientThroughWholeEffect: double startPos = ((double)twinkle.StartTime.Ticks / (double)TimeSpan.Ticks); double endPos = ((double)(twinkle.StartTime + twinkle.Duration).Ticks / (double)TimeSpan.Ticks); pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); break; case TwinkleColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case TwinkleColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); break; } pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } } return(result); }
private void DoRendering(CancellationTokenSource tokenSource = null) { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(10); List<ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; //If there are no nodes to render on Exit! if (targetNodeCount == 0) return; ElementNode lastTargetedNode = null; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets if nonzero if (DefaultLevel > 0) { int i = 0; foreach (ElementNode target in renderNodes) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; bool discreteColors = ColorModule.isElementNodeDiscreteColored(target); if (target == null) continue; if (target != null) { pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] {target}; pulse.TimeSpan = TimeSpan; double level = DefaultLevel*100.0; // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case SpinColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case SpinColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); break; case SpinColorHandling.ColorAcrossItems: double positionWithinGroup = i/(double) targetNodeCount; if (discreteColors) { List<Tuple<Color, float>> colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple<Color, float> colorProportion in colorsAtPosition) { double value = level*colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {value, value})); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); _elementData.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {level, level})); pulseData = pulse.Render(); _elementData.Add(pulseData); } break; } i++; } } } // calculate the pulse time and revolution time exactly (based on the parameters from the data) double revTimeMs = 0; // single revolution time (ms) // figure out the relative length of a individual pulse double pulseConstant = 0; // how much of each pulse is a constant time double pulseFractional = 0; // how much of each pulse is a fraction of a single spin if (PulseLengthFormat == SpinPulseLengthFormat.FixedTime) { pulseConstant = PulseTime; } else if (PulseLengthFormat == SpinPulseLengthFormat.PercentageOfRevolution) { pulseFractional = PulsePercentage/100.0; } else if (PulseLengthFormat == SpinPulseLengthFormat.EvenlyDistributedAcrossSegments) { pulseFractional = 1.0/(double) targetNodeCount; } // magic number. (the inaccuracy of interpolating the curve into a position. eg. if we have 5 'positions', then // the curve should really be from 0-80% for the last spin, to make sure the last pulse finishes accurately.) double pulseInterpolationOffset = 1.0/(double) targetNodeCount; // figure out either the revolution count or time, based on what data we have if (SpeedFormat == SpinSpeedFormat.RevolutionCount) { revTimeMs = (TimeSpan.TotalMilliseconds - pulseConstant)/ (RevolutionCount + pulseFractional - pulseInterpolationOffset); } else if (SpeedFormat == SpinSpeedFormat.RevolutionFrequency) { revTimeMs = (1.0/RevolutionFrequency)*1000.0; // convert Hz to period ms } else if (SpeedFormat == SpinSpeedFormat.FixedTime) { revTimeMs = RevolutionTime; } double pulTimeMs = pulseConstant + (revTimeMs*pulseFractional); TimeSpan revTimeSpan = TimeSpan.FromMilliseconds(revTimeMs); TimeSpan pulseTimeSpan = TimeSpan.FromMilliseconds(pulTimeMs); // figure out which way we're moving through the elements Curve movement; if (ReverseSpin) movement = new Curve(new PointPairList(new double[] {0, 100}, new double[] {100, 0})); else movement = new Curve(new PointPairList(new double[] {0, 100}, new double[] {0, 100})); // iterate up to and including the last pulse generated // a bit iffy, but stops 'carry over' spins past the end (when there's overlapping spins). But we need to go past // (total - pulse) as the last pulse can often be a bit inaccurate due to the rounding of the increment for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan - pulseTimeSpan + increment; current += increment) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; double currentPercentageIntoSpin = ((double) (current.Ticks%revTimeSpan.Ticks)/(double) revTimeSpan.Ticks)*100.0; double targetElementPosition = movement.GetValue(currentPercentageIntoSpin); int currentNodeIndex = (int) ((targetElementPosition/100.0)*targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount) currentNodeIndex--; if (currentNodeIndex >= targetNodeCount) { Logging.Warn( "Spin effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) continue; bool discreteColors = ColorModule.isElementNodeDiscreteColored(currentNode); // make a pulse for it pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] {currentNode}; pulse.TimeSpan = pulseTimeSpan; pulse.LevelCurve = new Curve(PulseCurve); // figure out what color gradient to use for the pulse switch (ColorHandling) { case SpinColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); break; case SpinColorHandling.GradientThroughWholeEffect: double startPos = ((double) current.Ticks/(double) TimeSpan.Ticks); double endPos = 1.0; if (TimeSpan - current >= pulseTimeSpan) endPos = ((double)(current + pulseTimeSpan).Ticks / (double)TimeSpan.Ticks); if (discreteColors) { double range = endPos - startPos; if (range <= 0.0) { Logging.Error("Spin: bad range: " + range + " (SP=" + startPos + ", EP=" + endPos + ")"); break; } ColorGradient cg = ColorGradient.GetSubGradientWithDiscreteColors(startPos, endPos); foreach (Color color in cg.GetColorsInGradient()) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair point in newCurve.Points) { double effectRelativePosition = startPos + ((point.X / 100.0) * range); double proportion = ColorGradient.GetProportionOfColorAt(effectRelativePosition, color); point.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(color); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } } else { pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } break; case SpinColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); break; case SpinColorHandling.ColorAcrossItems: if (discreteColors) { List<Tuple<Color, float>> colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(targetElementPosition / 100.0); foreach (Tuple<Color, float> colorProportion in colorsAtPosition) { if (tokenSource != null && tokenSource.IsCancellationRequested) return; float proportion = colorProportion.Item2; // scale all levels of the pulse curve by the proportion that is applicable to this color Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair pointPair in newCurve.Points) { pointPair.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(targetElementPosition/100.0)); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(current); _elementData.Add(pulseData); } break; } lastTargetedNode = currentNode; } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private void DoRendering() { //TODO: get a better increment time. doing it every X ms is..... shitty at best. TimeSpan increment = TimeSpan.FromMilliseconds(2); List <ElementNode> renderNodes = GetNodesToRenderOn(); int targetNodeCount = renderNodes.Count; Pulse.Pulse pulse; EffectIntents pulseData; // apply the 'background' values to all targets int i = 0; foreach (ElementNode target in renderNodes) { pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { target }; pulse.TimeSpan = TimeSpan; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 100 }, new double[] { DefaultLevel * 100, DefaultLevel * 100 })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = new ColorGradient(StaticColor); break; case ChaseColorHandling.GradientThroughWholeEffect: pulse.ColorGradient = ColorGradient; break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = new ColorGradient(StaticColor); break; case ChaseColorHandling.ColorAcrossItems: pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt((double)i / (double)targetNodeCount)); break; } pulseData = pulse.Render(); _elementData.Add(pulseData); i++; } // the total chase time TimeSpan chaseTime = TimeSpan.FromMilliseconds(TimeSpan.TotalMilliseconds - PulseOverlap); if (chaseTime.TotalMilliseconds <= 0) { chaseTime = TimeSpan.FromMilliseconds(1); } // we need to keep track of the element that is 'under' the curve at a given time, to see if it changes, // and when it does, we make the effect for it then (since it's a variable time pulse). ElementNode lastTargetedNode = null; TimeSpan lastNodeStartTime = TimeSpan.Zero; // iterate up to and including the last pulse generated for (TimeSpan current = TimeSpan.Zero; current <= TimeSpan; current += increment) { double currentPercentageIntoChase = ((double)current.Ticks / (double)chaseTime.Ticks) * 100.0; double currentMovementPosition = ChaseMovement.GetValue(currentPercentageIntoChase); int currentNodeIndex = (int)((currentMovementPosition / 100.0) * targetNodeCount); // on the off chance we hit the 100% mark *exactly*... if (currentNodeIndex == targetNodeCount) { currentNodeIndex--; } if (currentNodeIndex >= targetNodeCount) { VixenSystem.Logging.Warning("Chase effect: rendering, but the current node index is higher or equal to the total target nodes."); continue; } ElementNode currentNode = renderNodes[currentNodeIndex]; if (currentNode == lastTargetedNode) { continue; } // if the last node targeted wasn't null, we need to make a pulse for it if (lastTargetedNode != null) { GeneratePulse(lastTargetedNode, lastNodeStartTime, current - lastNodeStartTime + TimeSpan.FromMilliseconds(PulseOverlap), currentMovementPosition); } lastTargetedNode = currentNode; lastNodeStartTime = current; // if we've hit the 100% mark of the chase curve, bail (the last one gets generated after) if (currentPercentageIntoChase >= 100.0) { break; } } // generate the last pulse if (lastTargetedNode != null) { GeneratePulse(lastTargetedNode, lastNodeStartTime, TimeSpan - lastNodeStartTime, 1.0); } _elementData = EffectIntents.Restrict(_elementData, TimeSpan.Zero, TimeSpan); }
private void GeneratePulse(ElementNode target, TimeSpan startTime, TimeSpan duration, double currentMovementPosition) { EffectIntents result = null; Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new[] {target}; pulse.TimeSpan = duration; pulse.LevelCurve = new Curve(PulseCurve); bool discreteColors = ColorModule.isElementNodeDiscreteColored(target); IIntentNode intent; // figure out what color gradient to use for the pulse switch (ColorHandling) { case ChaseColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { foreach (var iintentNode in result.FirstOrDefault().Value) { GenerateStartingStaticPulse(target, iintentNode); } } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { foreach (var iintentNode in result.FirstOrDefault().Value) { GenerateExtendedStaticPulse(target, iintentNode); } } break; case ChaseColorHandling.GradientThroughWholeEffect: double startPos = (startTime.Ticks/(double) TimeSpan.Ticks); double endPos = ((startTime + duration).Ticks/(double) TimeSpan.Ticks); if (startPos < 0.0) startPos = 0.0; if (endPos > 1.0) endPos = 1.0; if (discreteColors) { double range = endPos - startPos; if (range <= 0.0) { Logging.Error("Chase: bad range: " + range + " (SP=" + startPos + ", EP=" + endPos + ")"); break; } ColorGradient cg = ColorGradient.GetSubGradientWithDiscreteColors(startPos, endPos); foreach (Color color in cg.GetColorsInGradient()) { Curve newCurve = new Curve(PulseCurve.Points); foreach (PointPair point in newCurve.Points) { double effectRelativePosition = startPos + ((point.X / 100.0) * range); double proportion = ColorGradient.GetProportionOfColorAt(effectRelativePosition, color); point.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(color); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent, new ColorGradient(color)); } if(result.Count>0) _elementData.Add(result); if (ExtendPulseToEnd && result.Count>0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent, new ColorGradient(color)); } } } else { pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent, ColorGradient.GetSubGradient(0, startPos)); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent, ColorGradient.GetSubGradient(endPos,1)); } } break; case ChaseColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent); } break; case ChaseColorHandling.ColorAcrossItems: if (discreteColors) { List<Tuple<Color, float>> colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(currentMovementPosition / 100.0); foreach (Tuple<Color, float> colorProportion in colorsAtPosition) { float proportion = colorProportion.Item2; // scale all levels of the pulse curve by the proportion that is applicable to this color Curve newCurve = new Curve(PulseCurve.Points); foreach (PointPair pointPair in newCurve.Points) { pointPair.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(colorProportion.Item1); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent, new ColorGradient(colorProportion.Item1)); } if(result.Count>0)_elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent, new ColorGradient(colorProportion.Item1)); } } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(currentMovementPosition/100.0)); result = pulse.Render(); result.OffsetAllCommandsByTime(startTime); if (ExtendPulseToStart && result.Count > 0) { intent = result.FirstOrDefault().Value.FirstOrDefault(); GenerateStartingStaticPulse(target, intent); } _elementData.Add(result); if (ExtendPulseToEnd && result.Count > 0) { intent = result.FirstOrDefault().Value.LastOrDefault(); GenerateExtendedStaticPulse(target, intent); } } break; } }
private void RenderElement(bool altColor, ref TimeSpan startTime, ref System.TimeSpan intervalTime, ref LightingValue? lightingValue, ElementNode element) { EffectIntents result; if ((StaticColor1 && altColor) || StaticColor2 && !altColor) { var level = new SetLevel.SetLevel(); level.TargetNodes = new ElementNode[] { element }; level.Color = altColor ? Color1 : Color2; level.TimeSpan = intervalTime; result = level.Render(); } else { var pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] { element }; pulse.TimeSpan = intervalTime; pulse.ColorGradient = altColor ? ColorGradient1 : ColorGradient2; pulse.LevelCurve = altColor ? Curve1 : Curve2; result = pulse.Render(); } result.OffsetAllCommandsByTime(startTime); _elementData.Add(result); }
private EffectIntents RenderElement(ElementNode node, double positionWithinGroup, List<IndividualTwinkleDetails> twinkles = null) { if (node == null || node.Element == null) return null; if (twinkles == null) twinkles = GenerateTwinkleData(); EffectIntents result = new EffectIntents(); bool discreteColors = ColorModule.isElementNodeDiscreteColored(node); // render the flat 'minimum value' across the entire effect Pulse.Pulse pulse = new Pulse.Pulse(); pulse.TargetNodes = new ElementNode[] {node}; pulse.TimeSpan = TimeSpan; double minPulseValue = MinimumLevel * 100.0; EffectIntents pulseData; // figure out what color gradient to use for the pulse if (MinimumLevel > 0.0) { switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: if (discreteColors) { List<Tuple<Color, float>> colorProportions = ColorGradient.GetDiscreteColorsAndProportionsAt(0); foreach (Tuple<Color, float> colorProportion in colorProportions) { double value = minPulseValue * colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {value, value})); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); result.Add(pulseData); } } else { pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {minPulseValue, minPulseValue})); pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(0)); pulseData = pulse.Render(); result.Add(pulseData); } break; case TwinkleColorHandling.GradientThroughWholeEffect: pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {minPulseValue, minPulseValue})); pulse.ColorGradient = ColorGradient; pulseData = pulse.Render(); result.Add(pulseData); break; case TwinkleColorHandling.StaticColor: pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {minPulseValue, minPulseValue})); pulse.ColorGradient = StaticColorGradient; pulseData = pulse.Render(); result.Add(pulseData); break; case TwinkleColorHandling.ColorAcrossItems: if (discreteColors) { List<Tuple<Color, float>> colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple<Color, float> colorProportion in colorsAtPosition) { double value = minPulseValue * colorProportion.Item2; pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {value, value})); pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); result.Add(pulseData); } } else { pulse.LevelCurve = new Curve(new PointPairList(new double[] {0, 100}, new double[] {minPulseValue, minPulseValue})); pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulseData = pulse.Render(); result.Add(pulseData); } break; } } // render all the individual twinkles foreach (IndividualTwinkleDetails twinkle in twinkles) { { // make a pulse for it pulse.TargetNodes = new [] {node}; pulse.TimeSpan = twinkle.Duration; pulse.LevelCurve = new Curve(new PointPairList(new double[] { 0, 50, 100 }, new [] { twinkle.curvePoints[0], twinkle.curvePoints[1], twinkle.curvePoints[2] })); // figure out what color gradient to use for the pulse switch (ColorHandling) { case TwinkleColorHandling.GradientForEachPulse: pulse.ColorGradient = ColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); break; case TwinkleColorHandling.GradientThroughWholeEffect: double startPos = ((double) twinkle.StartTime.Ticks/(double) TimeSpan.Ticks); double endPos = ((double) (twinkle.StartTime + twinkle.Duration).Ticks/(double) TimeSpan.Ticks); if (discreteColors) { double range = endPos - startPos; if (range <= 0.0) { Logging.Error("Twinkle: bad range: " + range + " (SP=" + startPos + ", EP=" + endPos + ")"); break; } ColorGradient cg = ColorGradient.GetSubGradientWithDiscreteColors(startPos, endPos); foreach (Color color in cg.GetColorsInGradient()) { Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair point in newCurve.Points) { double effectRelativePosition = startPos + ((point.X / 100.0) * range); double proportion = ColorGradient.GetProportionOfColorAt(effectRelativePosition, color); point.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(color); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } } else { pulse.ColorGradient = ColorGradient.GetSubGradient(startPos, endPos); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } break; case TwinkleColorHandling.StaticColor: pulse.ColorGradient = StaticColorGradient; pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); break; case TwinkleColorHandling.ColorAcrossItems: if (discreteColors) { List<Tuple<Color, float>> colorsAtPosition = ColorGradient.GetDiscreteColorsAndProportionsAt(positionWithinGroup); foreach (Tuple<Color, float> colorProportion in colorsAtPosition) { float proportion = colorProportion.Item2; // scale all levels of the twinkle curve by the proportion that is applicable to this color Curve newCurve = new Curve(pulse.LevelCurve.Points); foreach (PointPair pointPair in newCurve.Points) { pointPair.Y *= proportion; } pulse.LevelCurve = newCurve; pulse.ColorGradient = new ColorGradient(colorProportion.Item1); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } } else { pulse.ColorGradient = new ColorGradient(ColorGradient.GetColorAt(positionWithinGroup)); pulseData = pulse.Render(); pulseData.OffsetAllCommandsByTime(twinkle.StartTime); result.Add(pulseData); } break; } } } return result; }