Example #1
0
        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);
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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);
        }
Example #4
0
		//(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;
		}
Example #5
0
        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);
        }
Example #6
0
		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;
						}
					}
				}

			}
		}
Example #7
0
        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);
        }
Example #8
0
        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);
        }
Example #9
0
File: Chase.cs Project: komby/vixen
 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);
     }
 }
Example #10
0
        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);
        }
Example #11
0
        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);
        }
Example #12
0
        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);
        }
Example #13
0
        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);
        }
Example #14
0
        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;
            }
        }
Example #15
0
        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;
                            }
                        }
                    }
                }
            }
        }
Example #16
0
        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;
                            }
                        }
                    }
                }
            }
        }
Example #17
0
File: Spin.cs Project: ctmal/vixen
        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);
        }
Example #18
0
File: Chase.cs Project: komby/vixen
        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);
        }
Example #19
0
        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;
        }
Example #20
0
		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;
						}
					}
				}

			}



		}
Example #21
0
        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;
                        }
                    }
                }

            }
        }
Example #22
0
        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;
                            }
                        }
                    }
                }
            }
        }
Example #23
0
        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);
        }
Example #24
0
        //(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;
        }
Example #25
0
File: Spin.cs Project: stewmc/vixen
		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);
		}
Example #26
0
        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);
        }
Example #27
0
File: Chase.cs Project: komby/vixen
        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;
            }
        }
Example #28
0
        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);
        }
Example #29
0
        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;
        }