private nfloat CircleRatio(LiquittableCircle circle, LiquittableCircle other)
        {
            var distance = other.Center.Minus(circle.Center).Length();
            var ratio    = 1.0f - (distance - RadiusThreshold) / (circle.Radius + other.Radius + RadiusThreshold);

            return((nfloat)Math.Min(Math.Max(ratio, 0.0), 1.0));
        }
        private UIBezierPath[] SplitPath(LiquittableCircle circle, LiquittableCircle other, nfloat ratio)
        {
            var p1p2    = CircleConnectedPoint(circle, other, CoreGraphicsExtensions.DegToRad(60));
            var p3p4    = CircleConnectedPoint(other, circle, CoreGraphicsExtensions.DegToRad(60));
            var p1      = p1p2.Item1;
            var p2      = p1p2.Item2;
            var p3      = p3p4.Item1;
            var p4      = p3p4.Item2;
            var crossed = CoreGraphicsExtensions.Intersection(p1, p3, p2, p4);

            if (crossed != null)
            {
                var d1 = CircleConnectedPoint(circle, other, 0).Item1;
                var d2 = CircleConnectedPoint(other, circle, 0).Item1;
                var r  = (ratio - connectThresh) / (AngleThreshold - connectThresh);

                var a1   = d2.Split(crossed.Value, r * r);
                var part = new UIBezierPath();
                part.MoveTo(p1);
                part.AddQuadCurveToPoint(p2, a1);
                part.ClosePath();

                var a2    = d1.Split(crossed.Value, r * r);
                var part2 = new UIBezierPath();
                part2.MoveTo(p3);
                part2.AddQuadCurveToPoint(p4, a2);
                part2.ClosePath();

                return(new[] { part, part2 });
            }
            return(new UIBezierPath[0]);
        }
        private UIBezierPath NormalPath(LiquittableCircle circle, LiquittableCircle other)
        {
            var p1p2    = CircleConnectedPoint(circle, other);
            var p3p4    = CircleConnectedPoint(other, circle);
            var p1      = p1p2.Item1;
            var p2      = p1p2.Item2;
            var p3      = p3p4.Item1;
            var p4      = p3p4.Item2;
            var crossed = CoreGraphicsExtensions.Intersection(p1, p3, p2, p4);

            if (crossed != null)
            {
                var path = new UIBezierPath();
                var r    = CircleRatio(circle, other);
                path.MoveTo(p1);
                var r1   = p2.Mid(p3);
                var r2   = p1.Mid(p4);
                var rate = (1 - r) / (1 - AngleThreshold) * Viscosity;
                var mul  = r1.Mid(crossed.Value).Split(r2, rate);
                var mul2 = r2.Mid(crossed.Value).Split(r1, rate);
                path.AddQuadCurveToPoint(p4, mul);
                path.AddLineTo(p3);
                path.AddQuadCurveToPoint(p2, mul2);
                path.ClosePath();
                return(path);
            }
            return(null);
        }
        private UIBezierPath[] GenerateConnectedPath(LiquittableCircle circle, LiquittableCircle other)
        {
            if (IsConnected(circle, other))
            {
                var ratio = CircleRatio(circle, other);

                if (ratio >= AngleThreshold && ratio <= 1.0f)
                {
                    var path = NormalPath(circle, other);
                    return(path != null ? new[] { path } : null);
                }
                else if (ratio >= 0.0f && ratio < AngleThreshold)
                {
                    return(SplitPath(circle, other, ratio));
                }
                else
                {
                    return(null);
                }
            }
            else
            {
                return(null);
            }
        }
        private Tuple <CGPoint, CGPoint> CircleConnectedPoint(LiquittableCircle circle, LiquittableCircle other)
        {
            var ratio = CircleRatio(circle, other);

            ratio = (ratio + connectThresh) / (1.0f + connectThresh);
            var angle = CoreGraphicsExtensions.PI2 * angleOpen * ratio;

            return(CircleConnectedPoint(circle, other, angle));
        }
        private Tuple <CGPoint, CGPoint> CircleConnectedPoint(LiquittableCircle circle, LiquittableCircle other, nfloat angle)
        {
            var vec    = other.Center.Minus(circle.Center);
            var radian = (nfloat)Math.Atan2(vec.Y, vec.X);
            var p1     = circle.Center.CirclePoint(circle.Radius, radian + angle);
            var p2     = circle.Center.CirclePoint(circle.Radius, radian - angle);

            return(new Tuple <CGPoint, CGPoint>(p1, p2));
        }
        public LiquittableCircle[] Push(LiquittableCircle circle, LiquittableCircle other)
        {
            var paths = GenerateConnectedPath(circle, other);

            if (paths != null)
            {
                var layers = paths.Select(ConstructLayer);
                foreach (var l in layers)
                {
                    layer.AddSublayer(l);
                }
                return(new[] { circle, other });
            }
            return(new LiquittableCircle[0]);
        }
        public void Setup(LiquidFloatingActionButton actionButton)
        {
            AnimateStyle        = actionButton.AnimateStyle;
            ClipsToBounds       = false;
            Layer.MasksToBounds = false;

            engine              = new SimpleCircleLiquidEngine();
            engine.Viscosity    = viscosity;
            engine.Color        = actionButton.Color;
            bigEngine           = new SimpleCircleLiquidEngine();
            bigEngine.Viscosity = viscosity;
            bigEngine.Color     = actionButton.Color;

            baseLiquid                     = new LiquittableCircle();
            baseLiquid.Color               = actionButton.Color;
            baseLiquid.ClipsToBounds       = false;
            baseLiquid.Layer.MasksToBounds = false;
            AddSubview(baseLiquid);
        }
        private bool IsConnected(LiquittableCircle circle, LiquittableCircle other)
        {
            var distance = circle.Center.Minus(other.Center).Length();

            return(distance - circle.Radius - other.Radius < RadiusThreshold);
        }