private TabletopParams CreateFeatureParamsForSection(int section) { int numSections = sectionsProvider.NumSections(); double sectionLengthSeconds = sectionModel.TotalLength.TotalSeconds; //first decide if it has a tabletop at all. //the chance of it being something at all rises from 0% to 100%. double progression = ((float)section + 1) / numSections; // <= 1 //if it's a tabletop: double topLength = randomizer.ProportionAlong(sectionModel.FeatureLengthVariation, progression, sectionModel.MinFeatureLength.TotalSeconds, sectionModel.MaxFeatureLength.TotalSeconds); double maxRampLength = Math.Min(sectionModel.MaxRampLength.TotalSeconds, (sectionLengthSeconds - topLength) / 2); if (sectionModel.MinRampLength.TotalSeconds > maxRampLength) { throw new InvalidOperationException($"MinRampLength must be <= maxRampLength. MinTabletopLength could be too high."); } // could feasibly be MinRampLength at the start of the track. Desirable? Yes, because other parameters constrain the dramaticness at the start. double rampLength = randomizer.ProportionAlong(sectionModel.RampLengthVariation, progression, maxRampLength, sectionModel.MinRampLength.TotalSeconds); // Max is first as shorter ramps are more dramatic (nearer the end of the track) var result = new TabletopParams { RampLength = rampLength, TopLength = topLength, RampsUseSin2 = true }; return(result); }
private void ValidateParams(TabletopParams p) { double sectionLengthSeconds = sectionModel.TotalLength.TotalSeconds; if (p.TopLength < 0) { throw new InvalidOperationException("TopLength must be >= 0"); } if (p.RampLength < 0) { throw new InvalidOperationException("RampLength must be >= 0"); } if (p.TopLength + 2 * p.RampLength > sectionLengthSeconds) { throw new InvalidOperationException("TopLength + 2*RampLength must be <= sectionLengthSeconds"); } }
/* TopLength RampLength * <--------------><-> * y| ________________ * | /ymax \ prefixLength (same both sides) * | / \ <--> * |____/ \____ * |ymin * ________________________________x * <-----------xmax--------------> * the TopFrequency doesn't necessarily have to be greater than baseFrequency - but it must be >0. */ public static double GetY(double x, double xmax, double ymin, double ymax, TabletopParams p) { double prefixLength = (xmax - p.TopLength - 2 * p.RampLength) / 2; //length of the bit at base frequency before the first ramp double dy = ymax - ymin; if (x < prefixLength) { //before the first ramp return(ymin); } else if (x < prefixLength + p.RampLength) { // on the first ('up') ramp double timeAlongRamp = x - prefixLength; double proportionAlongRamp = timeAlongRamp / p.RampLength; double proportionUpRamp = p.RampsUseSin2 ? Math.Pow(Math.Sin(proportionAlongRamp * Math.PI / 2), 2) : proportionAlongRamp; return(ymin + proportionUpRamp * dy); } else if (x <= prefixLength + p.RampLength + p.TopLength) { // on the tabletop return(ymax); } else if (x <= prefixLength + 2 * p.RampLength + p.TopLength) { //on the second ('down') ramp double timeAlongRamp = x - prefixLength - p.RampLength - p.TopLength; double proportionAlongRamp = timeAlongRamp / p.RampLength; double proportionUpRamp = p.RampsUseSin2 ? Math.Pow(Math.Sin(proportionAlongRamp * Math.PI / 2), 2) : proportionAlongRamp; return(ymin + (1 - proportionUpRamp) * dy); } else { //after the second ramp return(ymin); } }