Skip to content

Commit b7ad0c3

Browse files
committed
infinate mandelbrot
1 parent cec051b commit b7ad0c3

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

Examples/UICatalog/Scenarios/MandelbrotImageView.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ internal sealed class MandelbrotImageView : ImageView
1313
private const double DEFAULT_CENTER_Y = 0;
1414
private const double DEFAULT_SPAN = 3;
1515
private const int DEFAULT_ITERATIONS = 80;
16+
private const double PAN_FACTOR = 0.1;
17+
private const double ZOOM_IN_FACTOR = 0.8;
18+
private const double ZOOM_OUT_FACTOR = 1.25;
19+
20+
private Point? _lastDragPosition;
1621

1722
private double _centerX = DEFAULT_CENTER_X;
1823
public double CenterX => _centerX;
@@ -73,6 +78,7 @@ public MandelbrotImageView ()
7378
Arrangement = ViewArrangement.Resizable;
7479
UseRasterGraphics = true;
7580

81+
ReplacePanAndZoomCommands ();
7682
ViewportChanged += (_, _) => RenderMandelbrot ();
7783
}
7884

@@ -117,6 +123,16 @@ protected override bool CenterOnViewportPoint (Point position)
117123
return true;
118124
}
119125

126+
protected override bool OnMouseEvent (Mouse mouse)
127+
{
128+
if (HandleFractalDrag (mouse))
129+
{
130+
return true;
131+
}
132+
133+
return base.OnMouseEvent (mouse);
134+
}
135+
120136
private static int CountIterations (double cx, double cy, int maxIterations)
121137
{
122138
double zx = 0;
@@ -191,6 +207,80 @@ private Size GetPreferredRasterResolution ()
191207
return _sixelSupportResult.Resolution;
192208
}
193209

210+
private bool HandleFractalDrag (Mouse mouse)
211+
{
212+
if (mouse.Position is not { } position)
213+
{
214+
return false;
215+
}
216+
217+
if (mouse.Flags.FastHasFlags (MouseFlags.LeftButtonPressed) && !mouse.Flags.FastHasFlags (MouseFlags.PositionReport))
218+
{
219+
_lastDragPosition = position;
220+
App?.Mouse.GrabMouse (this);
221+
222+
return true;
223+
}
224+
225+
if (mouse.Flags == (MouseFlags.LeftButtonPressed | MouseFlags.PositionReport) && _lastDragPosition is { } lastDragPosition)
226+
{
227+
bool panned = PanMandelbrot (lastDragPosition.X - position.X, lastDragPosition.Y - position.Y);
228+
_lastDragPosition = position;
229+
230+
return panned;
231+
}
232+
233+
if (!mouse.Flags.FastHasFlags (MouseFlags.LeftButtonReleased))
234+
{
235+
return false;
236+
}
237+
238+
_lastDragPosition = null;
239+
240+
if (App is { } && App.Mouse.IsGrabbed (this))
241+
{
242+
App.Mouse.UngrabMouse ();
243+
}
244+
245+
return true;
246+
}
247+
248+
private bool PanMandelbrot (int deltaX, int deltaY)
249+
{
250+
Rectangle viewport = Viewport;
251+
252+
if (viewport.Width <= 0 || viewport.Height <= 0)
253+
{
254+
return false;
255+
}
256+
257+
double spanY = _span * viewport.Height / viewport.Width;
258+
double centerX = _centerX + deltaX * _span * PAN_FACTOR;
259+
double centerY = _centerY + deltaY * spanY * PAN_FACTOR;
260+
SetMandelbrot (centerX, centerY, _span, _maxIterations, true);
261+
262+
return true;
263+
}
264+
265+
private void ReplacePanAndZoomCommands ()
266+
{
267+
AddCommand (Command.ScrollLeft, () => PanMandelbrot (-1, 0));
268+
AddCommand (Command.ScrollRight, () => PanMandelbrot (1, 0));
269+
AddCommand (Command.ScrollUp, () => PanMandelbrot (0, -1));
270+
AddCommand (Command.ScrollDown, () => PanMandelbrot (0, 1));
271+
AddCommand (Command.Home,
272+
() =>
273+
{
274+
ResetMandelbrot ();
275+
276+
return true;
277+
});
278+
AddCommand (Command.ZoomIn, context => ZoomMandelbrot (context, ZOOM_IN_FACTOR));
279+
AddCommand (Command.ZoomOut, context => ZoomMandelbrot (context, ZOOM_OUT_FACTOR));
280+
AddCommand (Command.PageUp, context => ZoomMandelbrot (context, ZOOM_IN_FACTOR));
281+
AddCommand (Command.PageDown, context => ZoomMandelbrot (context, ZOOM_OUT_FACTOR));
282+
}
283+
194284
private void RenderMandelbrot ()
195285
{
196286
Rectangle viewport = Viewport;
@@ -243,4 +333,51 @@ private void SetMandelbrot (double centerX, double centerY, double span, int max
243333
private bool ShouldUseKittyGraphics () =>
244334
_kittyGraphicsSupportResult is { IsSupported: true }
245335
&& App?.Driver?.GetOutput ().UseKittyGraphics == true;
336+
337+
private bool ZoomMandelbrot (ICommandContext? context, double spanMultiplier)
338+
{
339+
Point? anchor = context?.Binding is MouseBinding { MouseEvent.Position: { } mousePosition } ? mousePosition : null;
340+
double nextSpan = Math.Max (MinimumSpan, _span * spanMultiplier);
341+
342+
if (Math.Abs (nextSpan - _span) < double.Epsilon)
343+
{
344+
return false;
345+
}
346+
347+
if (anchor is { } anchorPosition && TryGetMandelbrotPoint (anchorPosition, _span, out double anchorX, out double anchorY))
348+
{
349+
double centerX = anchorX - (anchorX - _centerX) * spanMultiplier;
350+
double centerY = anchorY - (anchorY - _centerY) * spanMultiplier;
351+
SetMandelbrot (centerX, centerY, nextSpan, _maxIterations, true);
352+
353+
return true;
354+
}
355+
356+
SetMandelbrot (_centerX, _centerY, nextSpan, _maxIterations, true);
357+
358+
return true;
359+
}
360+
361+
private bool TryGetMandelbrotPoint (Point position, double span, out double x, out double y)
362+
{
363+
x = _centerX;
364+
y = _centerY;
365+
Rectangle viewport = Viewport;
366+
367+
if (viewport.Width <= 0 || viewport.Height <= 0)
368+
{
369+
return false;
370+
}
371+
372+
if (position.X < 0 || position.Y < 0 || position.X >= viewport.Width || position.Y >= viewport.Height)
373+
{
374+
return false;
375+
}
376+
377+
double spanY = span * viewport.Height / viewport.Width;
378+
x = _centerX + ((position.X + 0.5d) / viewport.Width - 0.5d) * span;
379+
y = _centerY + ((position.Y + 0.5d) / viewport.Height - 0.5d) * spanY;
380+
381+
return true;
382+
}
246383
}

0 commit comments

Comments
 (0)