private TransformerControl CreateTransformer(TextBox t1, int gw, int gh, int gx, int gy)
        {
            var RB = new Rectangle
            {
                Width = gw,
                Height = gh,
                Fill = Brushes.Red,
                Opacity = 0.1,
                Cursor = Cursors.Hand,
            }.AttachTo(this).MoveTo(gx, gy);

            var GB = new Rectangle
            {
                Width = gw,
                Height = gh,
                Fill = Brushes.Green,
                Opacity = 0.1,
                Cursor = Cursors.Hand,
            }.AttachTo(this).MoveTo(gx, gy);

            var g0 = new Rectangle
            {
                Width = gw,
                Height = gh,
                Fill = Brushes.Purple,
                Opacity = 0.2
            }.AttachTo(this.Shadow).MoveTo(gx, gy);

            var g1 = new Image
            {
                Width = gw,
                Height = gh,
                Source = new Avalon.Images.d().Source,
                Opacity = 0.7,
            }.AttachTo(this).MoveTo(gx, gy);

            var g2 = new Image
            {
                Width = gw,
                Height = gh,
                Source = new Avalon.Images.d().Source,
                Opacity = 0.7,
            }.AttachTo(this).MoveTo(gx, gy);

            var R = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.Red,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx, gy);

            var G = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.Green,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx, gy);

            var M = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.Magenta,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx, gy);

            var Y = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.Yellow,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx, gy);

            var Orange = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.Orange,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx + gw / 2, gy + gh / 2 - 8);

            // rotation origin
            var Black = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.Black,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx + gw / 2, gy + gh / 2);

            // rotation handler
            var White = new Rectangle
            {
                Width = 8,
                Height = 8,
                Fill = Brushes.White,
                Cursor = Cursors.Hand,
                RenderTransform = new TranslateTransform(-4, -4)
            }.AttachTo(this).MoveTo(gx + gw, gy + gh / 2);


            var OrangeSelfAverage = true;



            Action Update =
                delegate
                {
                    var w = new StringBuilder();

                    w.AppendLine("Magneta:");
                    w.AppendLine(ApplyMatrix(RB, GB, R, G, M, g2, gx, gy, gw, gh));
                    w.AppendLine("Yellow:");
                    w.AppendLine(ApplyMatrix(RB, GB, R, G, Y, g1, gx, gy, gw, gh));

                    if (t1 != null)
                        t1.Text = w.ToString();

                    if (OrangeSelfAverage)
                    {
                        var args = new[]
						{
					
							new { x = Canvas.GetLeft(R), y = Canvas.GetTop(R) },
							new { x = Canvas.GetLeft(G), y = Canvas.GetTop(G) },
							new { x = Canvas.GetLeft(M), y = Canvas.GetTop(M) },
							new { x = Canvas.GetLeft(Y), y = Canvas.GetTop(Y) }
						};

                        var _x = args.Average(k => k.x);
                        var _y = args.Average(k => k.y);
                        Orange.MoveTo(
                            _x,
                            _y - 8
                        );
                    }
                };

            Action Reset =
                delegate
                {
                    M.MoveTo(gx, gy);
                    R.MoveTo(gx + gw, gy);
                    G.MoveTo(gx, gy + gh);
                    Y.MoveTo(gx + gw, gy + gh);
                };

            AsMovableByMouse(R, Update);
            AsMovableByMouse(G, Update);
            AsMovableByMouse(M, Update);
            AsMovableByMouse(Y, Update);

            var RotationInfo = new { x = 0.0, y = 0.0 };

            RotationInfo = null;

            Action UpdateRotation =
                delegate
                {
                    var p = new
                    {
                        White = new { x = Canvas.GetLeft(White), y = Canvas.GetTop(White) },
                        Black = new { x = Canvas.GetLeft(Black), y = Canvas.GetTop(Black) },
                        R = new { x = Canvas.GetLeft(R), y = Canvas.GetTop(R) },
                        G = new { x = Canvas.GetLeft(G), y = Canvas.GetTop(G) },
                        M = new { x = Canvas.GetLeft(M), y = Canvas.GetTop(M) },
                        Y = new { x = Canvas.GetLeft(Y), y = Canvas.GetTop(Y) }
                    };

                    if (RotationInfo == null)
                    {
                        RotationInfo = p.White;
                        return;
                    }

                    // we now need 
                    // - previous distance and rotation
                    // - current distance and rotation

                    var q = new
                    {
                        o = new { x = RotationInfo.x - p.Black.x, y = RotationInfo.y - p.Black.y },
                        n = new { x = p.White.x - p.Black.x, y = p.White.y - p.Black.y },

                        R = new { x = p.Black.x - p.R.x, y = p.Black.y - p.R.y },
                        G = new { x = p.Black.x - p.G.x, y = p.Black.y - p.G.y },
                        M = new { x = p.Black.x - p.M.x, y = p.Black.y - p.M.y },
                        Y = new { x = p.Black.x - p.Y.x, y = p.Black.y - p.Y.y },
                    };

                    Func<double, double, double> GetLength =
                        (x, y) =>
                        {
                            return new vec2(x, y).Length;
                        };

                    var a = new
                    {
                        o = new { z = GetLength(q.o.x, q.o.y), a = new Point(q.o.x, q.o.y).GetRotation() },
                        n = new { z = GetLength(q.n.x, q.n.y), a = new Point(q.n.x, q.n.y).GetRotation() },

                        R = new { z = -GetLength(q.R.x, q.R.y), a = new Point(q.R.x, q.R.y).GetRotation() },
                        G = new { z = -GetLength(q.G.x, q.G.y), a = new Point(q.G.x, q.G.y).GetRotation() },
                        M = new { z = -GetLength(q.M.x, q.M.y), a = new Point(q.M.x, q.M.y).GetRotation() },
                        Y = new { z = -GetLength(q.Y.x, q.Y.y), a = new Point(q.Y.x, q.Y.y).GetRotation() },
                    };

                    var n = new { z = a.n.z / a.o.z, a = a.n.a - a.o.a };

                    if (n.z == 1)
                        if (n.a == 0)
                        {
                            RotationInfo = p.White;
                            return;
                        }


                    R.MoveTo(
                        p.Black.x + Math.Cos(a.R.a + n.a) * a.R.z * n.z,
                        p.Black.y + Math.Sin(a.R.a + n.a) * a.R.z * n.z
                    );

                    G.MoveTo(
                        p.Black.x + Math.Cos(a.G.a + n.a) * a.G.z * n.z,
                        p.Black.y + Math.Sin(a.G.a + n.a) * a.G.z * n.z
                    );

                    M.MoveTo(
                        p.Black.x + Math.Cos(a.M.a + n.a) * a.M.z * n.z,
                        p.Black.y + Math.Sin(a.M.a + n.a) * a.M.z * n.z
                    );

                    Y.MoveTo(
                        p.Black.x + Math.Cos(a.Y.a + n.a) * a.Y.z * n.z,
                        p.Black.y + Math.Sin(a.Y.a + n.a) * a.Y.z * n.z
                    );

                    Update();

                    RotationInfo = p.White;
                };

            AsMovableByMouse(Black, null);
            AsMovableByMouse(White, UpdateRotation);

            var TranslationInfo = new { x = 0.0, y = 0.0 };

            TranslationInfo = null;

            Action ShowVisuals =
                delegate
                {
                    g0.Show();
                    GB.Show();
                    RB.Show();

                    Orange.Opacity = 1;

                    var e = new[] { M, R, G, Y, White, Black };

                    foreach (var k in e)
                    {
                        k.Show();

                        k.BringToFront();
                    }
                };

            Action UpdateTranslation =
                delegate
                {
                    ShowVisuals();


                    var p = new
                    {
                        Orange = new { x = Canvas.GetLeft(Orange), y = Canvas.GetTop(Orange) },

                        White = new { x = Canvas.GetLeft(White), y = Canvas.GetTop(White) },
                        Black = new { x = Canvas.GetLeft(Black), y = Canvas.GetTop(Black) },
                        R = new { x = Canvas.GetLeft(R), y = Canvas.GetTop(R) },
                        G = new { x = Canvas.GetLeft(G), y = Canvas.GetTop(G) },
                        M = new { x = Canvas.GetLeft(M), y = Canvas.GetTop(M) },
                        Y = new { x = Canvas.GetLeft(Y), y = Canvas.GetTop(Y) }
                    };

                    if (TranslationInfo == null)
                    {
                        TranslationInfo = p.Orange;
                        return;
                    }

                    var q = new
                    {
                        x = p.Orange.x - TranslationInfo.x,
                        y = p.Orange.y - TranslationInfo.y,
                    };

                    if (q.x == 0)
                        if (q.y == 0)
                        {
                            TranslationInfo = p.Orange;
                            return;
                        }

                    RotationInfo = null;

                    R.MoveTo(p.R.x + q.x, p.R.y + q.y);
                    G.MoveTo(p.G.x + q.x, p.G.y + q.y);
                    Y.MoveTo(p.Y.x + q.x, p.Y.y + q.y);
                    M.MoveTo(p.M.x + q.x, p.M.y + q.y);
                    Black.MoveTo(p.Black.x + q.x, p.Black.y + q.y);
                    White.MoveTo(p.White.x + q.x, p.White.y + q.y);

                    Update();

                    TranslationInfo = p.Orange;
                };

            var OrangeDrag = AsMovableByMouse(Orange, UpdateTranslation);

            OrangeDrag.Enter = () => OrangeSelfAverage = false;
            OrangeDrag.Exit = () => OrangeSelfAverage = true;

            Reset();
            UpdateRotation();
            Update();

            var rr = new TransformerControl
            {
                SetOpacity =
                    Opacity =>
                    {
                        g1.Opacity = Opacity;
                        g2.Opacity = Opacity;
                    },
                SetSource =
                    src =>
                    {
                        g1.Source = src;
                        g2.Source = src;
                    },
                HideMirror =
                    delegate
                    {
                        Y.Opacity = 0.2;
                        g1.Hide();
                    },
                HideVisuals =
                    delegate
                    {
                        g0.Hide();
                        GB.Hide();
                        RB.Hide();

                        Orange.Opacity = 0.05;

                        var e = new[] { M, R, G, Y, White, Black };

                        foreach (var k in e)
                        {
                            k.Hide();
                        }

                    },
                SetBounds =
                    args =>
                    {
                        var e = new[] { M, R, G, Y };

                        for (int i = 0; i < args.Length; i++)
                        {
                            e[i].MoveTo(args[i].X, args[i].Y);
                        }

                        var _x = args.Average(k => k.X);
                        var _y = args.Average(k => k.Y);
                        Orange.MoveTo(
                            _x,
                            _y - 8
                        );


                        RotationInfo = null;

                        Update();
                    },
                //g2 = g2
            };


            return rr;
        }