在 NSView OSX 上使用 Core Graphics 绘制速度表

Drawing a Speedometer with Core Graphics on OSX in NSView

我正在尝试在 OSX 上使用 Core Graphics 绘制速度计的元素。我几乎明白了,但在仪表内部的中心刻度上需要一点帮助。这是我正在尝试做的事情的图像:

这是我目前得到的图像:

我知道如何绘制圆环以及如何基于仪表中心绘制线段,如下所示:

- (void)drawOuterGaugeRingsInRect:(CGContextRef)contextRef rect:(NSRect)rect {


    CGContextSetLineWidth(contextRef,self.gaugeRingWidth);

    CGContextSetStrokeColorWithColor(contextRef, [MyColors SpeedGaugeOuterRingGray].CGColor);
    CGFloat startRadians = 0;
    CGFloat endRadians =  M_PI*2;
    CGFloat radius = self.bounds.size.width/2 - 5;

    CGContextAddArc(contextRef, CGRectGetMidX(rect),CGRectGetMidY(rect),radius,startRadians,endRadians,YES);
    //Render the outer gauge
    CGContextStrokePath(contextRef);

    //Draw the inner gauge ring.
    radius -= self.gaugeRingWidth;
    CGContextSetStrokeColorWithColor(contextRef, [MyColors SpeedGaugeInnerRingGray].CGColor);
    CGContextAddArc(contextRef, CGRectGetMidX(rect),CGRectGetMidY(rect),radius,startRadians,endRadians,YES);

    //Render the inner gauge
    CGContextStrokePath(contextRef);

    radius -= self.gaugeRingWidth;

    //Draw and fill the gauge background

    CGContextSetFillColorWithColor(contextRef, [MyColors SpeedGaugeCenterFillBlack ].CGColor);
    CGContextSetStrokeColorWithColor(contextRef, [MyColors SpeedGaugeCenterFillBlack].CGColor);
    CGContextAddArc(contextRef, CGRectGetMidX(rect),CGRectGetMidY(rect),radius,startRadians,endRadians,YES);

    //Render and fill the gauge background
    CGContextDrawPath(contextRef, kCGPathFillStroke);


    /*BLUE CIRCULAR DIAL */
    //Prepare to draw the blue circular dial.
    radius -= self.gaugeRingWidth/2;

    //Adjust gauge ring width
    CGContextSetLineWidth(contextRef,self.gaugeRingWidth/2);

    CGContextSetStrokeColorWithColor(contextRef, [MyColors SpeedGaugeBlue].CGColor);

    CGFloat startingRadians = [MyMathHelper degressToRadians:135];
    CGFloat endingRadians = [MyMathHelper degressToRadians:45];


    CGContextAddArc(contextRef, CGRectGetMidX(rect),CGRectGetMidY(rect),radius,startingRadians,endingRadians,NO);

    //Render the blue gauge line
    CGContextStrokePath(contextRef);
}

上面的代码是在我的NSView

中的drawRect:方法中调用的

关键部分是这里的代码:

- (void)drawInnerDividerLines:(CGContextRef)context rect:(NSRect)rect {

    CGFloat centerX = CGRectGetMidX(rect);
    CGFloat centerY = CGRectGetMidY(rect);


    CGContextSetLineWidth       (context, 3.0);
    CGContextSetRGBStrokeColor  (context, 37.0/255.0, 204.0/255.0, 227.0/255.0, 0.5);



    CGFloat destinationX = centerX + (centerY * (cos((135)*(M_PI/180))));
    CGFloat destinationY = centerY + (centerX * (sin((135)*(M_PI/180))));

    NSPoint destinationPoint = NSMakePoint(destinationX, destinationY);


    CGContextMoveToPoint(context, centerX, centerY);

    CGContextAddLineToPoint(context, destinationPoint.x, destinationPoint.y);

    CGContextStrokePath(context);

}

我明白这是怎么回事,但我要解决的问题是绘制小线,从向视图中心点延伸的内部蓝线开始,但不要一直画下去到中心。我有点不确定如何修改数学和绘图逻辑来实现这一点。这是我基于核心图形绘图角度的单位圆。

我试图解决的主要问题是:

  1. 如何将淡蓝色内线的正确起点定义为每个仪表刻度的起点。现在,我正在绘制从仪表中心到边缘的整条线。

  2. 如何控制刻度规的长度,因为它指向蓝线上原点的中心。

任何能为我指明解决此问题的正确方向的提示或建议,我们将不胜感激。

Any tips or advice that would point in me in the right direction to solve this would be appreciated.

在你的圆周上给定一个点,绕圆心成一定角度可以形成一个直角三角形,半径是斜边,另外两条边平行于x轴和y轴(忽略暂时是退化的情况,其中点位于 0、90、180 或 270 度)。鉴于使用 sin & cos 公式(记得学校的 SOHCAHTOA)和一些基本数学,您可以计算点的坐标,并使用它绘制从中心到点的半径。

"tick"标记的终点正好位于不同半径的圆上,所以同样的数学运算会给你终点,你可以画勾。您只需要确定这些圆圈的半径,即刻度线端点沿原始半径的距离。

HTH

我推荐使用向量。您可以通过计算找到给定角度的圆上任意点的直线:

dirX = cos(angle);
dirY = sin(angle);
startPt.x = center.x + innerRadius * dirX;
startPt.y = center.y + innerRadius * dirY;
endPt.x = center.x + outerRadius * dirX;
endPt.y = center.y + outerRadius * dirY;

然后您可以在 startPtendPt 之间画一条线。

另一种避免三角函数的方法是旋转变换矩阵,只画一条垂直线或水平线。

// A vertical "line" of width "width" along the y axis at x==0.
NSRect tick = NSMakeRect(-width / 2.0, innerRadius, width, outerRadius - innerRadius);
NSAffineTransform* xform = [NSAffineTransform transform];
// Move the x and y axes to the center of your speedometer so rotation happens around it
[xform translateXBy:center.x yBy:center.y];
// Rotate the coordinate system so that straight up is actually at your speedometer's 0
[xform rotateByDegrees:135];
[xform concat];
// Create a new transform to rotate back around for each tick.
xform = [NSAffineTransform transform];
[xform rotateByDegrees:-270.0 / numTicks];
for (int i = 0; i < numTicks; i++)
{
    NSRectFill(tick);
   [xform concat];
}

您可能希望将其包装在 [NSGraphicsContext saveGraphicsState][NSGraphicsContext restoreGraphicsState] 中,以便在您完成后恢复转换矩阵。

如果你想要两种不同的刻度线(主要和次要),那么有两个不同的矩形和 select 一个基于 i % 10 == 0 或其他。也许也可以切换颜色。等等