在 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);
}
我明白这是怎么回事,但我要解决的问题是绘制小线,从向视图中心点延伸的内部蓝线开始,但不要一直画下去到中心。我有点不确定如何修改数学和绘图逻辑来实现这一点。这是我基于核心图形绘图角度的单位圆。
我试图解决的主要问题是:
如何将淡蓝色内线的正确起点定义为每个仪表刻度的起点。现在,我正在绘制从仪表中心到边缘的整条线。
如何控制刻度规的长度,因为它指向蓝线上原点的中心。
任何能为我指明解决此问题的正确方向的提示或建议,我们将不胜感激。
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;
然后您可以在 startPt
和 endPt
之间画一条线。
另一种避免三角函数的方法是旋转变换矩阵,只画一条垂直线或水平线。
// 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
或其他。也许也可以切换颜色。等等
我正在尝试在 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);
}
我明白这是怎么回事,但我要解决的问题是绘制小线,从向视图中心点延伸的内部蓝线开始,但不要一直画下去到中心。我有点不确定如何修改数学和绘图逻辑来实现这一点。这是我基于核心图形绘图角度的单位圆。
我试图解决的主要问题是:
如何将淡蓝色内线的正确起点定义为每个仪表刻度的起点。现在,我正在绘制从仪表中心到边缘的整条线。
如何控制刻度规的长度,因为它指向蓝线上原点的中心。
任何能为我指明解决此问题的正确方向的提示或建议,我们将不胜感激。
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;
然后您可以在 startPt
和 endPt
之间画一条线。
另一种避免三角函数的方法是旋转变换矩阵,只画一条垂直线或水平线。
// 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
或其他。也许也可以切换颜色。等等