public static UIBenchmarkData Average(params UIBenchmarkData[] input) { return(new UIBenchmarkData() { WorkDone = ConsoleMath.Round(input.Select(input => input.WorkDone).Average()), }); }
private void InitNavKeys() { FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.Escape, null, () => { if (currentContent != null && FocusManager.FocusedControl != null && (FocusManager.FocusedControl == currentContent || (currentContent is ConsolePanel && (currentContent as ConsolePanel).Descendents.Contains(FocusManager.FocusedControl))) ) { currentNavButton?.TryFocus(); } else { AnimatedDialog.Show((dialogHandle) => { var contentBg = RGB.Yellow; var bgCompliment = contentBg.GetCompliment(); var textColor = RGB.Black.CalculateDistanceTo(bgCompliment) < RGB.MaxDistance * .75f ? RGB.Black : bgCompliment; var panel = new ConsolePanel() { Height = 11, Width = ConsoleMath.Round(LayoutRoot.Width * .5f), Background = contentBg }; var label = panel.Add(new Label() { Text = "Press enter to quit or escape to resume".ToConsoleString(textColor, contentBg) }).CenterBoth(); FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.Enter, null, () => Stop(), panel); FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.Escape, null, dialogHandle.CloseDialog, panel); return(panel); }); } }, this); }
public static T FillAndPreserveAspectRatio <T>(this T child, Container parent = null, Thickness?padding = null) where T : ConsoleControl { var effectivePadding = padding.HasValue ? padding.Value : new Thickness(0, 0, 0, 0); return(DoParentTriggeredLayoutAction(child, parent, (c, p) => { if (p.Width == 0 || p.Height == 0) { return; } var aspectRatio = (float)c.Width / c.Height; var newW = p.Width - (effectivePadding.Left + effectivePadding.Right); var newH = ConsoleMath.Round(newW / aspectRatio); if (newH > p.Height) { newH = p.Height; newW = ConsoleMath.Round(newH * aspectRatio); } var newLeft = (p.Width - newW) / 2; var newTop = (p.Height - newH) / 2; c.Bounds = new RectF(newLeft, newTop, newW, newH); })); }
private void UpdateScrollbars() { var contentSize = ScrollableContentSize; if (contentSize.Height <= Height) { verticalScrollbar.Height = 0; verticalScrollbar.CanFocus = false; VerticalScrollUnits = 0; // dangerous because if the observable is ever changed to notify on equal changes then this will cause a stack overflow } else { var verticalPercentageShowing = Height / (double)contentSize.Height; var verticalPercentageScrolled = VerticalScrollUnits / (double)contentSize.Height; var verticalScrollbarHeight = ConsoleMath.Round(Height * verticalPercentageShowing); verticalScrollbar.Height = verticalScrollbarHeight; verticalScrollbar.Y = ConsoleMath.Round(Height * verticalPercentageScrolled); if (verticalScrollbar.Y == Height && verticalPercentageScrolled < 1) { verticalScrollbar.Y--; } else if (verticalScrollbar.Y == 0 && verticalPercentageScrolled > 0) { verticalScrollbar.Y = 1; } verticalScrollbar.CanFocus = true; } if (contentSize.Width <= Width) { horizontalScrollbar.Width = 0; horizontalScrollbar.CanFocus = false; HorizontalScrollUnits = 0; // dangerous because if the observable is ever changed to notify on equal changes then this will cause a stack overflow } else { var horizontalPercentageShowing = Width / (double)contentSize.Width; var horizontalPercentageScrolled = HorizontalScrollUnits / (double)contentSize.Width; horizontalScrollbar.Width = (int)(Width * horizontalPercentageShowing); horizontalScrollbar.X = (int)(Width * horizontalPercentageScrolled); if (verticalScrollbar.X == Width && horizontalPercentageScrolled < 1) { verticalScrollbar.X--; } else if (verticalScrollbar.X == 0 && horizontalPercentageScrolled > 0) { verticalScrollbar.X = 1; } horizontalScrollbar.CanFocus = true; } }
public double Percentile(double p) { lock (samples) { if (isWindowFull == false) { return(float.NaN); } Array.Sort(samples); var i = (int)ConsoleMath.Round((samples.Length - 1) * p); return(samples[i]); } }
private void UpdateContentSize() { var w = (float)Width; var h = w / widthOverHeight; if (h > Height) { h = Height; w = h * widthOverHeight; } content.Width = Math.Min(Width, ConsoleMath.Round(w)); content.Height = Math.Min(Height, ConsoleMath.Round(h)); }
private void FocusChanged() { bool focusedControlIsWithinMe = VisitControlTree((control) => { if (IsExpired || IsExpiring || IsBeingRemoved) { return(false); } return(control is Scrollbar == false && control == Application.FocusManager.FocusedControl); }); if (focusedControlIsWithinMe) { var offset = Application.FocusManager.FocusedControl.CalculateRelativePosition(this); var visibleWindowBounds = new RectF(HorizontalScrollUnits, VerticalScrollUnits, Width, Height); var focusedControlBounds = new RectF(offset.X, offset.Y, Application.FocusManager.FocusedControl.Width, Application.FocusManager.FocusedControl.Height); if (focusedControlBounds.IsAbove(visibleWindowBounds)) { int amount = ConsoleMath.Round(visibleWindowBounds.Top - focusedControlBounds.Top); VerticalScrollUnits -= amount; } if (focusedControlBounds.IsBelow(visibleWindowBounds)) { int amount = ConsoleMath.Round(focusedControlBounds.Bottom - visibleWindowBounds.Bottom); VerticalScrollUnits += amount; } if (focusedControlBounds.IsLeftOf(visibleWindowBounds)) { int amount = ConsoleMath.Round(visibleWindowBounds.Left - focusedControlBounds.Left); HorizontalScrollUnits -= amount; } if (focusedControlBounds.IsRightOf(visibleWindowBounds)) { int amount = ConsoleMath.Round(focusedControlBounds.Right - visibleWindowBounds.Right); HorizontalScrollUnits += amount; } } }
private static async Task AnimateAsyncInternal(AnimatorOptions options) { var animationTime = TimeSpan.FromMilliseconds(options.Duration); if (animationTime == TimeSpan.Zero) { #if DEBUG options.Debug?.Invoke("NoOp animation"); #endif options.Set(options.To); } var numberOfFrames = (float)(ConsoleMath.Round(animationTime.TotalSeconds * TargetFramesPerSecond)); numberOfFrames = Math.Max(numberOfFrames, 2); #if DEBUG options.Debug?.Invoke($"Frames: {numberOfFrames}"); #endif var timeBetweenFrames = TimeSpan.FromMilliseconds(ConsoleMath.Round(animationTime.TotalMilliseconds / numberOfFrames)); #if DEBUG options.Debug?.Invoke($"Time between frames: {timeBetweenFrames.TotalMilliseconds} ms"); #endif var initialValue = options.From; options.Set(initialValue); #if DEBUG options.Debug?.Invoke($"InitialValue: {initialValue}"); #endif var delta = options.To - initialValue; #if DEBUG options.Debug?.Invoke($"Delta: {delta}"); #endif var workSw = Stopwatch.StartNew(); for (float i = 1; i <= numberOfFrames; i++) { var percentageDone = i / numberOfFrames; if (options.EasingFunction != null) { percentageDone = options.EasingFunction(percentageDone); } var scheduledTimeAfterThisFrame = TimeSpan.FromMilliseconds(timeBetweenFrames.TotalMilliseconds * i); var newValue = initialValue + (delta * percentageDone); options.OnSet?.Invoke(percentageDone); options.Set(newValue); #if DEBUG options.Debug?.Invoke($"Set value to {newValue} at percentage {percentageDone}"); #endif var delayTime = options.DelayProvider is WallClockDelayProvider?TimeSpan.FromMilliseconds(Math.Max(0, scheduledTimeAfterThisFrame.TotalMilliseconds - workSw.Elapsed.TotalMilliseconds)) : timeBetweenFrames; #if DEBUG options.Debug?.Invoke($"Delayed for {delayTime.TotalMilliseconds} ms at percentage {percentageDone}"); #endif if (options.IsCancelled != null && options.IsCancelled()) { return; } if (delayTime == TimeSpan.Zero) { await options.YieldAsync(); } else { await options.DelayAsync(delayTime); } } }
protected override async Task Startup() { InitPause(); var random = new Random(100); var camera = LayoutRoot.Add(new Camera() { BigBounds = new RectF(0, 0, 400, 400) }).Fill(); camera.CameraLocation = camera.BigBounds.Center.ToRect(camera.Width, camera.Height).TopLeft; FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.W, null, () => DefaultColliderGroup.SpeedRatio = DefaultColliderGroup.SpeedRatio + .1f, this); FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.S, null, () => DefaultColliderGroup.SpeedRatio = Math.Max(0, DefaultColliderGroup.SpeedRatio - .1f), this); while (true) { var left = camera.Add(new ConsoleControl() { Width = 5, Height = 2, X = ConsoleMath.Round(camera.BigBounds.Center.Left - 50), Y = ConsoleMath.Round(camera.BigBounds.Center.Top), Background = new RGB((byte)random.Next(60, 120), (byte)random.Next(60, 120), (byte)random.Next(60, 120)) }); var right = camera.Add(new ConsoleControl() { Width = 5, Height = 2, X = ConsoleMath.Round(camera.BigBounds.Center.Left + 50), Y = ConsoleMath.Round(camera.BigBounds.Center.Top), Background = new RGB((byte)random.Next(60, 120), (byte)random.Next(60, 120), (byte)random.Next(60, 120)) }); await Task.WhenAll(left.FadeIn(delayProvider: DelayProvider), right.FadeIn(delayProvider: DelayProvider)); var leftV = new Velocity2(left, DefaultColliderGroup) { Bounce = true }; leftV.Speed = 90; leftV.Angle = Angle.Right; var rightV = new Velocity2(right, DefaultColliderGroup) { Bounce = true }; rightV.Speed = 10; rightV.Angle = Angle.Left; FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.UpArrow, null, () => { leftV.SpeedRatio = leftV.SpeedRatio + .1f; }, this); FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.DownArrow, null, () => { leftV.SpeedRatio = Math.Max(0, leftV.SpeedRatio - .1f); }, this); await TaskEx.WhenAny(PauseManager.Delay(5000), leftV.ImpactOccurred.CreateNextFireTask()); await Task.WhenAll(left.FadeOut(duration: 2000, delayProvider: DelayProvider), right.FadeOut(duration: 2000, delayProvider: DelayProvider)); left.Dispose(); right.Dispose(); } camera.BigBounds = default; }
/// <summary> /// Takes the abstract definitions of a row or column and converts them into actual pixel /// values given the current budget (height or width) /// </summary> /// <param name="definitions">the definitions to evaluate</param> /// <param name="budget">the total number of pixels to fill</param> /// <returns>an array with one absolute pixel value per definition</returns> private int[] ConvertDefinitionsIntoAbsolutePixelSizes(List <GridValueDefinition> definitions, int budget) { Dictionary <int, int> results = new Dictionary <int, int>(); var remainingBudget = budget; // handle all pixel and percentage values first, wich can easily be // converted into real pixel values for (var i = 0; i < definitions.Count; i++) { int size; if (definitions[i].Type == GridValueType.Pixels) { size = (int)definitions[i].Value; results.Add(i, size); remainingBudget -= size; } else if (definitions[i].Type == GridValueType.Percentage) { size = ConsoleMath.Round(definitions[i].Value * budget); results.Add(i, size); remainingBudget -= size; } else { // remainder case, do nothing on this pass } } // next make a pass and count the total number of shares // and the total number of remainder definitions double remainderShares = 0; var numberOfRemainders = 0; for (var i = 0; i < definitions.Count; i++) { if (definitions[i].Type == GridValueType.RemainderValue) { remainderShares += definitions[i].Value; numberOfRemainders++; } } // finally, divy out the remainders and account for rounding errors var remainderSum = 0; var remaindersToProcess = numberOfRemainders; for (var i = 0; i < definitions.Count; i++) { if (definitions[i].Type == GridValueType.RemainderValue) { var myShare = definitions[i].Value / remainderShares; var size = ConsoleMath.Round(myShare * remainingBudget); results.Add(i, size); remainderSum += size; remaindersToProcess--; if (remaindersToProcess == 0) { // account for rounding while (remainderSum < remainingBudget) { results[i]++; remainderSum++; } // account for rounding while (remainderSum > remainingBudget) { results[i]--; remainderSum--; } } } } // convert results into an array var ret = new int[definitions.Count]; for (var i = 0; i < ret.Length; i++) { ret[i] = results[i]; } return(ret); }
public async Task <bool> SeekAsync(bool forward, float duration) { if (seekLt != null && seekLt.IsExpired == false) { return(false); } using (seekLt = this.CreateChildLifetime()) { var thisMonth = new DateTime(Options.Year, Options.Month, 1); thisMonth = thisMonth.AddMonths(forward ? 1 : -1); this.Options.Month = thisMonth.Month; this.Options.Year = thisMonth.Year; var lastMonth = thisMonth.AddMonths(-1); var nextMonth = thisMonth.AddMonths(1); var leftDest = CalculateLeftDestination(); var centerDest = CalculateCenterDestination(); var rightDest = CalculateRightDestination(); var tempMonth = !forward ? lastMonth : nextMonth; var temp = ProtectedPanel.Add(new MonthCalendar(new MonthCalendarOptions() { CustomizeContent = Options.CustomizeContent, MinMonth = Options.MinMonth, MaxMonth = Options.MaxMonth, AdvanceMonthBackwardKey = null, AdvanceMonthForwardKey = null, TodayHighlightColor = Options.TodayHighlightColor, Month = tempMonth.Month, Year = tempMonth.Year })); temp.Width = 2; temp.Height = 1; temp.X = !forward ? -temp.Width : Width + temp.Width; temp.Y = ConsoleMath.Round((Height - temp.Height) / 2f); var tempDest = !forward ? leftDest : rightDest; EasingFunction ease = Animator.EaseInOut; var tempAnimation = temp.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, Destination = () => tempDest, Duration = duration, EasingFunction = ease }); if (!forward) { var rightAnimationDest = new RectF(Width + 2, Height / 2, 2, 1); var centerAnimationDest = right.Bounds; var leftAnimationDest = center.Bounds; await Task.WhenAll ( right.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, EasingFunction = ease, Destination = () => rightAnimationDest, Duration = duration }), center.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, EasingFunction = ease, Destination = () => centerAnimationDest, Duration = duration }), left.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, EasingFunction = ease, Destination = () => leftAnimationDest, Duration = duration }), tempAnimation ); right.Dispose(); right = center; center = left; left = temp; } else { var rightAnimationDest = ((ICollider)center).Bounds; var centerAnimationDest = ((ICollider)left).Bounds; var leftAnimationDest = new RectF(-2, Height / 2, 2, 1); await Task.WhenAll ( right.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, EasingFunction = ease, Destination = () => rightAnimationDest, Duration = duration }), center.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, EasingFunction = ease, Destination = () => centerAnimationDest, Duration = duration }), left.AnimateAsync(new ConsoleControlAnimationOptions() { IsCancelled = () => seekLt.IsExpired, EasingFunction = ease, Destination = () => leftAnimationDest, Duration = duration }), tempAnimation ); left.Dispose(); left = center; center = right; right = temp; left.Bounds = leftDest; center.Bounds = centerDest; right.Bounds = rightDest; await Task.Yield(); left.Refresh(); right.Refresh(); center.Refresh(); } return(true); } }
private void InitializeForm() { var labelColumn = Add(new StackPanel() { Orientation = Orientation.Vertical, Margin = 1 }); var valueColumn = Add(new StackPanel() { Orientation = Orientation.Vertical, Margin = 1 }); this.SynchronizeForLifetime(nameof(this.Bounds), () => { var labelColumnWidth = ConsoleMath.Round(this.Width * this.Options.LabelColumnPercentage); var valueColumnWidth = ConsoleMath.Round(this.Width * (1 - this.Options.LabelColumnPercentage)); while (labelColumnWidth + valueColumnWidth > this.Width) { labelColumnWidth--; } while (labelColumnWidth + valueColumnWidth < this.Width) { valueColumnWidth++; } labelColumn.Width = labelColumnWidth; valueColumn.Width = valueColumnWidth; labelColumn.Height = this.Height; valueColumn.Height = this.Height; valueColumn.X = labelColumnWidth; }, this); foreach (var element in this.Options.Elements) { labelColumn.Add(new Label() { Height = 1, Text = element.Label }).FillHorizontally(); element.ValueControl.Height = 1; valueColumn.Add(element.ValueControl); EnsureSizing(element); } this.Options.Elements.Added.SubscribeForLifetime((addedElement) => { var index = this.Options.Elements.IndexOf(addedElement); var label = new Label() { Height = 1, Text = addedElement.Label }; addedElement.ValueControl.Height = 1; labelColumn.Controls.Insert(index, label); label.FillHorizontally(); valueColumn.Controls.Insert(index, addedElement.ValueControl); EnsureSizing(addedElement); }, this); this.Options.Elements.Removed.SubscribeForLifetime((removedElement) => { var index = valueColumn.Controls.IndexOf(removedElement.ValueControl); labelColumn.Controls.RemoveAt(index); valueColumn.Controls.RemoveAt(index); }, this); this.Options.Elements.AssignedToIndex.SubscribeForLifetime((assignment) => throw new NotSupportedException("Index assignments not supported in form elements"), this); }
public ConsoleString GetFormattedSample() { return((ConsoleMath.Round(lastSample) + " %").ToConsoleString(lastSample < 50 ? ConsoleColor.Green : lastSample < 90 ? ConsoleColor.Yellow : ConsoleColor.Red)); }
public Angle RoundAngleToNearest(Angle nearest) => new Angle(((float)ConsoleMath.Round(Value / nearest.Value) * nearest.Value) % 360);
public LocF GetRounded() => new LocF(ConsoleMath.Round(Left), ConsoleMath.Round(Top));
/// <summary> /// Shows a dialog on top of the current ConsoleApp. /// </summary> /// <param name="contentFactory">A callback where you are given a handle that can be used to configure the dialog. /// It also has a method that lets you close the dialog. This callback should return the dialog content.</param> public static async void Show(Func <DialogHandle, Container> contentFactory, AnimatedDialogOptions options = null) { options = options ?? new AnimatedDialogOptions(); options.Parent = options.Parent ?? ConsoleApp.Current.LayoutRoot; using (var dialogLt = new Lifetime()) { var handle = new DialogHandle(); if (options.PushPop) { ConsoleApp.Current.FocusManager.Push(); if (options.AllowEscapeToClose) { ConsoleApp.Current.FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.Escape, null, () => { handle.CloseDialog(); }, dialogLt); } if (options.AllowEnterToClose) { ConsoleApp.Current.FocusManager.GlobalKeyHandlers.PushForLifetime(ConsoleKey.Enter, null, () => { handle.CloseDialog(); }, dialogLt); } dialogLt.OnDisposed(ConsoleApp.Current.FocusManager.Pop); } var content = contentFactory(handle); content.IsVisible = false; var dialogContainer = options.Parent.Add(new BorderPanel(content) { BorderColor = handle.BorderColor, Background = content.Background, Width = 1, Height = 1 }).CenterBoth(); dialogContainer.ZIndex = options.ZIndex; await Forward(300 *options.SpeedPercentage, dialogLt, percentage => dialogContainer.Width = Math.Max(1, ConsoleMath.Round((4 + content.Width) * percentage))); await Forward(200 *options.SpeedPercentage, dialogLt, percentage => dialogContainer.Height = Math.Max(1, ConsoleMath.Round((2 + content.Height) * percentage))); content.IsVisible = true; await handle.CallerLifetime.ToTask(); content.IsVisible = false; await Reverse(150 *options.SpeedPercentage, dialogLt, percentage => dialogContainer.Height = Math.Max(1, (int)Math.Floor((2 + content.Height) * percentage))); await Task.Delay((int)(200 * options.SpeedPercentage)); await Reverse(200 *options.SpeedPercentage, dialogLt, percentage => dialogContainer.Width = Math.Max(1, ConsoleMath.Round((4 + content.Width) * percentage))); dialogContainer.Dispose(); } }
private async void Init() { var options = GetOptionsForArg(); if (args.Mode == ConsoleMode.VirtualTerminal) { ConsoleProvider.Fancy = true; } if (args.Mode == ConsoleMode.Console) { ConsoleProvider.Fancy = false; } var mechanism = ConsoleProvider.Fancy == false ? "System.Console" : "VirtualTerminal"; options.InitTest?.Invoke(); var messagePanel = Add(new ConsolePanel() { Width = 45, Height = 3, Background = ConsoleColor.Red }).CenterBoth(); var messageLabel = messagePanel.Add(new Label() { Text = "Waiting".ToConsoleString(fg: ConsoleColor.Black, bg: ConsoleColor.Red) }).CenterBoth(); var now = DateTime.Now; var paintsNow = ConsoleApp.Current.TotalPaints; while ((DateTime.Now - now).TotalSeconds < 3) { messageLabel.Text = $"{ConsoleApp.Current.TotalPaints- paintsNow} paints using {mechanism}".ToConsoleString(fg: ConsoleColor.Black, bg: ConsoleColor.Red, true); options.OnFrame?.Invoke(); await Task.Yield(); } var animationPanel = Add(new ConsolePanel() { Background = ConsoleColor.Green, Width = 45, Height = 3 }); var centerX = ConsoleMath.Round(Width / 2.0 - animationPanel.Width / 2.0); var targetY = ConsoleMath.Round((Height / 2.0 - animationPanel.Height / 2) - 5.0); animationPanel.X = centerX; animationPanel.Y = Height; var animationLabel = animationPanel.Add(new Label() { Text = "That's all folks".ToBlack(bg: ConsoleColor.Green) }).CenterBoth(); await animationPanel.AnimateAsync(new ConsoleControlAnimationOptions() { Duration = 1000, Destination = () => new RectF(centerX, targetY, animationPanel.Width, animationPanel.Height), }); if (animationPanel.IsExpired == false && animationPanel.Parent?.IsExpired == false) { animationPanel.CenterHorizontally(); } }
internal override void Set(float value) => Setter(ConsoleMath.Round(value));
protected override float RunActual(ConsoleApp app) { int n = 0; app.Invoke(async() => { await Task.Delay(1000); app.LayoutRoot.Background = new RGB(20, 20, 20); var random = new Random(100); var nLabel = app.LayoutRoot.Add(new Label() { ZIndex = 100, Text = $"N = {n}".ToCyan() }).CenterHorizontally().DockToTop(padding: 2); var leftWall = app.LayoutRoot.Add(new ConsoleControl()); var rightWall = app.LayoutRoot.Add(new ConsoleControl()); var topWall = app.LayoutRoot.Add(new ConsoleControl()); var bottomWall = app.LayoutRoot.Add(new ConsoleControl()); var w = 8000; var h = 8000; leftWall.Bounds = app.LayoutRoot.Center().Offset(-w / 2, 0).ToRect(2, h); rightWall.Bounds = app.LayoutRoot.Center().Offset(w / 2, 0).ToRect(2, h); topWall.Bounds = app.LayoutRoot.Center().Offset(0, -h / 2).ToRect(w, 1); bottomWall.Bounds = app.LayoutRoot.Center().Offset(0, h / 2).ToRect(w, 1); var colliderGroup = new ColliderGroup(app); new Velocity2(leftWall, colliderGroup); new Velocity2(rightWall, colliderGroup); new Velocity2(topWall, colliderGroup); new Velocity2(bottomWall, colliderGroup); var slowCount = 0; while (true) { slowCount = colliderGroup.LatestDT < 50 ? 0 : slowCount + 1; if (slowCount == 10) { break; } var el = app.LayoutRoot.Add(new ConsoleControl()); el.Bounds = app.LayoutRoot.Bounds.Center.ToRect(2, 1); el.Background = new RGB((byte)random.Next(60, 120), (byte)random.Next(60, 120), (byte)random.Next(60, 120)); while (app.LayoutRoot.Controls.Where(e => e != el && e.Touches(el)).Any()) { el.Bounds = new RectF(random.Next(0, app.LayoutRoot.Width - 2), random.Next(0, app.LayoutRoot.Height - 1), el.Width, el.Height); } var v = new Velocity2(el, colliderGroup) { Bounce = true }; v.Speed = random.Next(0, 80); v.Angle = random.Next(0, 360); n++; nLabel.Text = $"N = {n}, DT = {ConsoleMath.Round(colliderGroup.LatestDT)}".ToConsoleString(); await Task.Delay(10); } app.Stop(); }); app.Run(); return(n); }