Mac OS 贝塞尔曲线画气泡

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    NSBezierPath*    bezier = [NSBezierPath bezierPath];
    //bezier = [self makeRectPath:bezier withRect:NSMakeRect(50, 50, 200, 400) corner:20];
    
    //bezier = [self makeBubble:bezier withBubbleRect:NSMakeRect(300, 50, 200, 40) corner:15];
    bezier = [self makeBubble:bezier withBubbleRect:NSMakeRect(10, 0, self.bounds.size.width-10, self.bounds.size.height-10) corner:8 atPostion:1];

    [[NSColor greenColor]set];
    [bezier fill];

    [[NSColor redColor]set];
    [bezier stroke];
}

/*
    支持微调气泡
 atPostion:是指小尖角位置,左上 = 1,左下 = 2,右上 = 3,右下 = 4
 */
- (NSBezierPath *)makeBubble:(NSBezierPath *) bezierPath
              withBubbleRect:(NSRect)rect
                      corner:(CGFloat)corner
                   atPostion:(NSInteger)pos
{
    //基本偏角40度,正好是引出小三解的位置
    CGFloat baseAngle = 40;
    
    CGFloat cornetOffWidth  = 10;
//    CGFloat cornetOffHeight = 5;
    
    //注angle只设定三个值0, 10,40
    /*
        0: 说明现在小三角相对于矩开来说,已看不出来了,这里直接用直角
        10:说明此时的小三解效果效好
        40:说明此时的矩开和小三角太小,需要调到40度有有效果。
        这三个值会自动的根据外部 corner 参数进行调整
     */
    CGFloat angle = 10;
    
    BOOL midCorner = NO;
    
    //矩形的宽和高
    CGFloat rw = CGRectGetWidth(rect);
    CGFloat rh = CGRectGetHeight(rect);
    
    //最小长度
    CGFloat minDiameter = rw > rh ? rh : rw;

    CGFloat radius = 0;//半径
    if (corner > 0)
    {
        radius = corner > minDiameter / 2 ? minDiameter / 2 : corner;
    }
    //最小长度与圆角比
    CGFloat rate = radius / minDiameter;

    //在下角坐标,
    NSPoint leftBottom      = NSMakePoint(CGRectGetMinX(rect), CGRectGetMinY(rect));
    NSPoint leftBottomX     = NSMakePoint(leftBottom.x + radius, leftBottom.y);
    NSPoint leftBottomY     = NSMakePoint(leftBottom.x, leftBottom.y + radius);
    
    NSPoint rightBottom     = NSMakePoint(CGRectGetMaxX(rect), CGRectGetMinY(rect));
    NSPoint rightBottomX    = NSMakePoint(rightBottom.x - radius, rightBottom.y);
    NSPoint rightBottomY    = NSMakePoint(rightBottom.x, rightBottom.y + radius);
    
    NSPoint rightTop = NSMakePoint(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
    NSPoint rightTopX = NSMakePoint(rightTop.x - radius, rightTop.y);
    NSPoint rightTopY = NSMakePoint(rightTop.x, rightTop.y - radius);
    
    
    NSPoint leftTop = NSMakePoint(CGRectGetMinX(rect), CGRectGetMaxY(rect));
    NSPoint leftTopX = NSMakePoint(leftTop.x + radius, leftTop.y);
    NSPoint leftTopY = NSMakePoint(leftTop.x, leftTop.y - radius);


    //最小高度在30以下的,只需要在左或右画三解即可
    if (minDiameter < 30 )
    {
        if (rate > 0.33 && rate <= 0.5)
        {
            angle = 40;
        }
        else //在中间画直角
        {
            angle = 0;
            midCorner = YES;
        }
    }
    else if (minDiameter >= 30 && minDiameter <= 50)
    {
        if (rate > 0.2 && rate < 0.4)
        {
            angle = 40;
        }
        else if (rate >= 0.4 && rate <= 0.5)
        {
            angle = 10; //直角
        }
        else
        {
            angle = 0;
        }
    }
    else // 50 以上的
    {
        if (corner >=10 && corner <= 15)
        {
            angle = 40;
        }
        else if (corner < 10)
        {
            angle = 0; //直角
        }
    }

    //A,B点引线出来产生尖角的结点(即绘制的起始点)
    NSPoint ABNodepoint = NSMakePoint(0, 0);
    NSPoint Apoint = NSMakePoint(0, 0);
    NSPoint Bpoint = NSMakePoint(0, 0);
    
    //上半部分
    CGFloat autoOffUpHeight = 0;
    //下半部份
    CGFloat autoOffDownHeight = 0;
    
    switch (pos) {
        case 2: //左下
        {
            if ((angle == 10) || (angle == 40)) {
                ABNodepoint = NSMakePoint(leftBottom.x - cornetOffWidth, leftBottom.y);
            }
            else
            {
                if (rh <= 30)
                {
                    autoOffDownHeight = (rh- 2*radius - 10) / 2;
                    autoOffUpHeight = autoOffDownHeight;
                    Apoint = NSMakePoint(leftTopY.x,leftTopY.y - autoOffUpHeight);
                    Bpoint = NSMakePoint(leftBottomY.x,leftBottomY.y + autoOffDownHeight);
                }
                else
                {
                    autoOffDownHeight = (rh- 2*radius) / 30;//30 :1 的比例进行(按1:29比例平分高度)
                    autoOffDownHeight = autoOffDownHeight > 10 ? 10 : autoOffDownHeight;
                    autoOffUpHeight = (rh- 2*radius) - autoOffDownHeight;
                    
                    Apoint = NSMakePoint(leftTopY.x,leftTopY.y - autoOffUpHeight + 10);
                    Bpoint = NSMakePoint(leftBottomY.x,leftBottomY.y + autoOffDownHeight);
                }
                
                ABNodepoint = NSMakePoint(leftBottom.x - cornetOffWidth, (Apoint.y + Bpoint.y) / 2);
            }
        }
            break;
        case 3: //右上
        {
            if ((angle == 10) || (angle == 40)) {
                ABNodepoint = NSMakePoint(rightTop.x + cornetOffWidth, rightTop.y);
            }
            else
            {
                if (rh <=30)
                {
                    autoOffDownHeight = (rh- 2*radius - 10) / 2;
                    autoOffUpHeight = autoOffDownHeight;
                    Apoint = NSMakePoint(rightTopY.x,rightTopY.y - autoOffDownHeight);
                    Bpoint = NSMakePoint(rightBottomY.x,rightBottomY.y + autoOffUpHeight);
                }
                else
                {
                    autoOffUpHeight = (rh- 2*radius) / 30;//30 :1 的比例进行(按1:29比例平分高度)
                    autoOffUpHeight = autoOffUpHeight > 10 ? 10 : autoOffUpHeight;
                    autoOffDownHeight = (rh- 2*radius) - autoOffUpHeight;
                    
                    Apoint = NSMakePoint(rightTopY.x,rightTopY.y - autoOffUpHeight );
                    Bpoint = NSMakePoint(rightBottomY.x,rightBottomY.y + autoOffDownHeight - 10);
                }
                
                ABNodepoint = NSMakePoint(rightTopY.x + cornetOffWidth, (Apoint.y + Bpoint.y) / 2);
            }
        }
            break;
        case 4: //右下
        {
            if ((angle == 10) || (angle == 40)) {
                ABNodepoint = NSMakePoint(rightBottom.x + cornetOffWidth, rightBottom.y);
            }
            else //在直线上绘角
            {
                if (rh <= 30)
                {
                    autoOffDownHeight = (rh- 2*radius - 10) / 2;
                    autoOffUpHeight = autoOffDownHeight;
                    Apoint = NSMakePoint(rightTopY.x,rightTopY.y - autoOffUpHeight);
                    Bpoint = NSMakePoint(rightBottomY.x,rightBottomY.y + autoOffDownHeight);
                }
                else
                {
                    autoOffDownHeight = (rh- 2*radius) / 30;//30 :1 的比例进行(按1:29比例平分高度)
                    autoOffDownHeight = autoOffDownHeight > 10 ? 10 : autoOffDownHeight;
                    autoOffUpHeight = (rh- 2*radius) - autoOffDownHeight;
                    
                    Apoint = NSMakePoint(rightTopY.x,rightTopY.y - autoOffUpHeight + 10);
                    Bpoint = NSMakePoint(rightBottomY.x,rightBottomY.y + autoOffDownHeight);
                }
                
                ABNodepoint = NSMakePoint(rightBottomY.x + cornetOffWidth, (Apoint.y + Bpoint.y) / 2);
            }

        }
            break;
            
        default: //左上
        {
            if ((angle == 10) || (angle == 40))
            {
                ABNodepoint = NSMakePoint(leftTop.x - cornetOffWidth, leftTop.y);
            }
            else
            {
                if (rh <=30)
                {
                    autoOffDownHeight = (rh- 2*radius - 10) / 2;
                    autoOffUpHeight = autoOffDownHeight;
                    Apoint = NSMakePoint(leftTopY.x,leftTopY.y - autoOffDownHeight);
                    Bpoint = NSMakePoint(leftBottomY.x,leftBottomY.y + autoOffUpHeight);
                }
                else
                {
                    autoOffUpHeight = (rh- 2*radius) / 30;//30 :1 的比例进行(按1:29比例平分高度)
                    autoOffUpHeight = autoOffUpHeight > 10 ? 10 : autoOffUpHeight;
                    autoOffDownHeight = (rh- 2*radius) - autoOffUpHeight;
                    
                    Apoint = NSMakePoint(leftTopY.x,leftTopY.y - autoOffUpHeight );
                    Bpoint = NSMakePoint(leftBottomY.x,leftBottomY.y + autoOffDownHeight - 10);
                }
                
                ABNodepoint = NSMakePoint(leftTop.x - cornetOffWidth, (Apoint.y + Bpoint.y) / 2);
            }
        }
            break;
    }

    NSPoint rightTopCenter =NSMakePoint(CGRectGetMaxX(rect) - radius,CGRectGetMaxY(rect) - radius);
    NSPoint leftTopCenter =NSMakePoint(CGRectGetMinX(rect) + radius,CGRectGetMaxY(rect) - radius);
    NSPoint leftBottomCenter =NSMakePoint(CGRectGetMinX(rect) + radius,CGRectGetMinY(rect) + radius);
    NSPoint rightBottomCenter =NSMakePoint(CGRectGetMaxX(rect) - radius,CGRectGetMinY(rect) + radius);
    
    //A点引出点的位置
    CGFloat Ax = radius * cos((90 - baseAngle - angle)*M_PI/180);
    CGFloat Ay = radius * sin((90 - baseAngle - angle)*M_PI/180);
    
    //B点引出点的位置
    CGFloat Bx = radius * cos(baseAngle*M_PI/180);
    CGFloat By = radius * sin(baseAngle*M_PI/180);
    
    //右下角缺口位置的A,B点坐标
//    NSPoint rBottomApoint = NSMakePoint(rightBottomX.x + Ax,rightBottomY.y - Ay);//(闭环结点)
    NSPoint rBottomBpoint = NSMakePoint(rightBottomX.x + By,rightBottomY.y - Bx);
    
    //右上角缺口位置A,B点坐标
//    NSPoint rTopApoint = NSMakePoint(rightTopX.x + By,rightTopY.y + Bx);//(闭环结点)
    NSPoint rTopBpoint = NSMakePoint(rightTopX.x + Ax,rightTopY.y + Ay);
    
    //左下角缺口位置A,B点坐标
    NSPoint lBottomApoint = NSMakePoint(leftBottomX.x - Ax,leftBottomY.y - Ay);
//    NSPoint lBottomBpoint = NSMakePoint(leftBottomX.x - By,leftBottomY.y - By);//(闭环结点)
    
    //左上角缺口位置A,B点坐标
    NSPoint lTopApoint = NSMakePoint(leftTopX.x - By,leftTopY.y + Bx);
//    NSPoint lTopBpoint = NSMakePoint(leftTopX.x - Ax,leftTopY.y + Ay);//(闭环结点)

    
    //按顺时针顺序进行绘制
    switch (pos) {
        case 2:
        {
            //绘制启始点
            [bezierPath moveToPoint:ABNodepoint];
            
            if (angle != 0)
            {
                [bezierPath lineToPoint:lBottomApoint];
                
                [bezierPath appendBezierPathWithArcWithCenter:leftBottomCenter radius:radius
                                                   startAngle:-(90 + baseAngle + angle)
                                                     endAngle:180
                                                    clockwise:YES];
            }
            else
            {
                [bezierPath lineToPoint:Apoint];
            }
            //左上半圆
            [self drawLeftTopPart:leftTopCenter withRadius:radius addToBezierPath:bezierPath];
            //右上半圆
            [self drawRightTopPart:rightTopCenter withRadius:radius addToBezierPath:bezierPath];
            //右下半圆
            [self drawRightBottomPart:rightBottomCenter withRadius:radius addToBezierPath:bezierPath];
            
            if (angle != 0)
            {
                [bezierPath appendBezierPathWithArcWithCenter:leftBottomCenter radius:radius
                                                   startAngle:-90
                                                     endAngle:-(90 + baseAngle)
                                                    clockwise:YES];
            }
            else
            {
                //左下半圆
                [self drawLeftBottomPart:leftBottomCenter withRadius:radius addToBezierPath:bezierPath];
                [bezierPath lineToPoint:Bpoint];
            }
        }
            break;
        case 3:
        {
            //绘制启始点
            [bezierPath moveToPoint:ABNodepoint];
            
            if (angle != 0)
            {
                [bezierPath lineToPoint:rTopBpoint];
                //绘制右上角缺角B点上半部的圆解部分
                [bezierPath appendBezierPathWithArcWithCenter:rightTopCenter radius:radius
                                                   startAngle:(90 - baseAngle - angle)
                                                     endAngle:0
                                                    clockwise:YES];
            }
            else
            {
                [bezierPath lineToPoint:Bpoint];
            }
            //右下半圆
            [self drawRightBottomPart:rightBottomCenter withRadius:radius addToBezierPath:bezierPath];
            //左下半圆
            [self drawLeftBottomPart:leftBottomCenter withRadius:radius addToBezierPath:bezierPath];
            //左上半圆
            [self drawLeftTopPart:leftTopCenter withRadius:radius addToBezierPath:bezierPath];
            
            if (angle != 0)
            {
                [bezierPath appendBezierPathWithArcWithCenter:rightTopCenter radius:radius
                                                   startAngle:90
                                                     endAngle:(90-baseAngle)
                                                    clockwise:YES];
            }
            else
            {
                //右上半圆
                [self drawRightTopPart:rightTopCenter withRadius:radius addToBezierPath:bezierPath];
                [bezierPath lineToPoint:Apoint];
            }
        }
            break;
        case 4:
        {
            //绘制启始点
            [bezierPath moveToPoint:ABNodepoint];
            
            if (angle != 0)
            {
                //绘到缺角B坐标点
                [bezierPath lineToPoint:rBottomBpoint];
                //绘制右下角缺角B点下半部的圆解部分
                [bezierPath appendBezierPathWithArcWithCenter:rightBottomCenter radius:radius
                                                   startAngle:-(90-baseAngle)//-50
                                                     endAngle:-90
                                                    clockwise:YES];
            }
            else
            {
                [bezierPath lineToPoint:Bpoint];
                [self drawRightBottomPart:rightBottomCenter withRadius:radius addToBezierPath:bezierPath];
            }
            
            //左下半圆
            [self drawLeftBottomPart:leftBottomCenter withRadius:radius addToBezierPath:bezierPath];
            //左上半圆
            [self drawLeftTopPart:leftTopCenter withRadius:radius addToBezierPath:bezierPath];
            //右上半圆
            [self drawRightTopPart:rightTopCenter withRadius:radius addToBezierPath:bezierPath];
            
            if (angle != 0)
            {
                //绘制右下角缺角A点上半部的圆解部分
                [bezierPath appendBezierPathWithArcWithCenter:rightBottomCenter radius:radius
                                                   startAngle:0
                                                     endAngle:- (90 - baseAngle - angle)//40
                                                    clockwise:YES];
            }
            else
            {
                [bezierPath lineToPoint:Apoint];
            }
        }
            break;
        default:
        {
            //绘制启始点
            [bezierPath moveToPoint:ABNodepoint];
            if (angle != 0)
            {
                //绘到缺角B坐标点
                [bezierPath lineToPoint:lTopApoint];
                //绘制右下角缺角B点下半部的圆解部分
                [bezierPath appendBezierPathWithArcWithCenter:leftTopCenter radius:radius
                                                   startAngle:(90+baseAngle)
                                                     endAngle:90
                                                    clockwise:YES];
            }
            else
            {
                [bezierPath lineToPoint:Apoint];
                //左上半圆
                [self drawLeftTopPart:leftTopCenter withRadius:radius addToBezierPath:bezierPath];
            }
            
            //右上半圆
            [self drawRightTopPart:rightTopCenter withRadius:radius addToBezierPath:bezierPath];
            //右下半圆
            [self drawRightBottomPart:rightBottomCenter withRadius:radius addToBezierPath:bezierPath];
            //左下半圆
            [self drawLeftBottomPart:leftBottomCenter withRadius:radius addToBezierPath:bezierPath];
            
            if (angle != 0)
            {
                [bezierPath appendBezierPathWithArcWithCenter:leftTopCenter radius:radius
                                                   startAngle:180
                                                     endAngle:(90+baseAngle+angle)
                                                    clockwise:YES];
            }
            else
            {
                [bezierPath lineToPoint:Bpoint];
            }
        }
            break;
    }
    
    [bezierPath closePath];

    return bezierPath;
}

//根据绘制点进行组合
//左下半圆
- (void)drawLeftBottomPart:(NSPoint) cornerCenter withRadius:(CGFloat)radius addToBezierPath:(NSBezierPath *) bezierPath
{
    
    [bezierPath appendBezierPathWithArcWithCenter:cornerCenter radius:radius
                                       startAngle:-90
                                         endAngle:-180
                                        clockwise:YES];
}

//左上半圆
- (void)drawLeftTopPart:(NSPoint) cornerCenter withRadius:(CGFloat)radius addToBezierPath:(NSBezierPath *) bezierPath
{
    
    [bezierPath appendBezierPathWithArcWithCenter:cornerCenter radius:radius
                                       startAngle:180 //or -180
                                         endAngle:90  // or - 270
                                        clockwise:YES];
}

//右上半圆
- (void)drawRightTopPart:(NSPoint) cornerCenter withRadius:(CGFloat)radius addToBezierPath:(NSBezierPath *) bezierPath
{
    [bezierPath appendBezierPathWithArcWithCenter:cornerCenter radius:radius
                                       startAngle:90
                                         endAngle:0
                                        clockwise:YES];
}

//右下半圆
- (void)drawRightBottomPart:(NSPoint) cornerCenter withRadius:(CGFloat)radius addToBezierPath:(NSBezierPath *) bezierPath
{
    [bezierPath appendBezierPathWithArcWithCenter:cornerCenter radius:radius
                                       startAngle:0
                                         endAngle:270
                                        clockwise:YES];
}

Demo-贝塞尔曲线

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄权浩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值