我试图在视图中绘制一个像这样的三角形(一个UIView,一个NSView):
我的第一个想法是CoreGraphics,但我找不到任何可以帮助我在任意颜色的三个点之间绘制渐变的信息。
任何帮助?
谢谢!
答案 0 :(得分:8)
实际上,CoreGraphics非常简单。下面你可以找到呈现给定三角形的代码,但首先让我们考虑如何解决这个问题。
想象一个边长 w 的等边三角形。所有三个角度都等于60度:
每个角度代表一个像素的组成部分:红色,绿色或蓝色。
让我们分析顶角附近像素中绿色成分的强度:
像素越接近角度,它所具有的分量越强,反之亦然。在这里,我们可以将主要目标分解为较小的目标:
要解决第一个任务,我们将使用CoreGraphics位图上下文。每个像素每个8位长有4个分量。这意味着组件值可能在0到255之间变化。第四个组件是alpha通道,并且将始终等于最大值 - 255.以下是如何为顶角插值的示例:
现在我们需要考虑如何计算组件的价值。
首先,让我们为每个角度定义主色:
现在让我们在三角形上选择坐标为(x,y)的任意点 A :
接下来,我们从与红色成分相关的角度画一条线,它穿过 A ,直到它与三角形的另一侧相交:
如果我们找到 d 和 c 它们的商将等于组件的标准化值,那么可以很容易地计算出值:
找到两点之间距离的公式很简单:
我们可以轻松找到 d 的距离,但不能找到 c 的距离,因为我们没有交点的坐标。实际上并不是那么难。我们只需要为通过 A 的行构建line equations,并在描述三角形的另一侧并找到他们的intersection:
有交点我们可以应用距离公式Distance Formula http://www.sciweavers.org/tex2img.php?eq=%5Csqrt%7B(x_%7B2%7D%20-%20x_%7B1%7D)%5E2%20(y_%7B2%7D%20-%20y_%7B1%7D)%5E2%7D&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0/来找到 c ,最后计算当前点的组件值。 Component value http://www.sciweavers.org/tex2img.php?eq=componentValue%20=%20%5Cfrac%7Bd%7D%7Bc%7D%20*%20255&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0/
同样的流量适用于其他组件。
以下是实现上述概念的代码:
+ (UIImage *)triangleWithSideLength:(CGFloat)sideLength {
return [self triangleWithSideLength:sideLength scale:[UIScreen mainScreen].scale];
}
+ (UIImage *)triangleWithSideLength:(CGFloat)sideLength
scale:(CGFloat)scale {
UIImage *image = nil;
CGSize size = CGSizeApplyAffineTransform((CGSize){sideLength, sideLength * sin(M_PI / 3)}, CGAffineTransformMakeScale(scale, scale));
size_t const numberOfComponents = 4;
size_t width = ceilf(size.width);
size_t height = ceilf(size.height);
size_t realBytesPerRow = width * numberOfComponents;
size_t alignedBytesPerRow = (realBytesPerRow + 0xFF) & ~0xFF;
size_t alignedPixelsPerRow = alignedBytesPerRow / numberOfComponents;
CGContextRef ctx = CGBitmapContextCreate(NULL,
width,
height,
8,
alignedBytesPerRow,
CGColorSpaceCreateDeviceRGB(),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
char *data = CGBitmapContextGetData(ctx);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int edge = ceilf((height - i) / sqrt(3));
if (j < edge || j > width - edge) {
continue;
}
CGFloat redNormalized = 0;
CGFloat greenNormalized = 0;
CGFloat blueNormalized = 0;
CGPoint currentTrianglePoint = (CGPoint){j / scale, (height - i) / scale};
[self calculateCurrentValuesAtGiventPoint:currentTrianglePoint
sideLength:sideLength
sideOne:&redNormalized
sideTwo:&greenNormalized
sideThree:&blueNormalized];
int32_t red = redNormalized * 0xFF;
int32_t green = greenNormalized * 0xFF;
int32_t blue = blueNormalized * 0xFF;
char *pixel = data + (j + i * alignedPixelsPerRow) * numberOfComponents;
*pixel = red;
*(pixel + 1) = green;
*(pixel + 2) = blue;
*(pixel + 3) = 0xFF;
}
}
CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
image = [[UIImage alloc] initWithCGImage:cgImage];
CGContextRelease(ctx);
CGImageRelease(cgImage);
return image;
}
+ (void)calculateCurrentValuesAtGiventPoint:(CGPoint)point
sideLength:(CGFloat)length
sideOne:(out CGFloat *)sideOne
sideTwo:(out CGFloat *)sideTwo
sideThree:(out CGFloat *)sideThree {
CGFloat height = sin(M_PI / 3) * length;
if (sideOne != NULL) {
// Side one is at 0, 0
CGFloat currentDistance = sqrt(point.x * point.x + point.y * point.y);
if (currentDistance != 0) {
CGFloat a = point.y / point.x;
CGFloat b = 0;
CGFloat c = -height / (length / 2);
CGFloat d = 2 * height;
CGPoint intersection = (CGPoint){(d - b) / (a - c), (a * d - c * b) / (a - c)};
CGFloat currentH = sqrt(intersection.x * intersection.x + intersection.y * intersection.y);
*sideOne = 1 - currentDistance / currentH;
} else {
*sideOne = 1;
}
}
if (sideTwo != NULL) {
// Side two is at w, 0
CGFloat currentDistance = sqrt(pow((point.x - length), 2) + point.y * point.y);
if (currentDistance != 0) {
CGFloat a = point.y / (point.x - length);
CGFloat b = height / (length / 2);
CGFloat c = a * -point.x + point.y;
CGFloat d = b * -length / 2 + height;
CGPoint intersection = (CGPoint){(d - c) / (a - b), (a * d - b * c) / (a - b)};
CGFloat currentH = sqrt(pow(length - intersection.x, 2) + intersection.y * intersection.y);
*sideTwo = 1 - currentDistance / currentH;
} else {
*sideTwo = 1;
}
}
if (sideThree != NULL) {
// Side three is at w / 2, w * sin60 degrees
CGFloat currentDistance = sqrt(pow((point.x - length / 2), 2) + pow(point.y - height, 2));
if (currentDistance != 0) {
float dy = point.y - height;
float dx = (point.x - length / 2);
if (fabs(dx) > FLT_EPSILON) {
CGFloat a = dy / dx;
CGFloat b = 0;
CGFloat c = a * -point.x + point.y;
CGFloat d = 0;
CGPoint intersection = (CGPoint){(d - c) / (a - b), (a * d - b * c) / (a - b)};
CGFloat currentH = sqrt(pow(length / 2 - intersection.x, 2) + pow(height - intersection.y, 2));
*sideThree = 1 - currentDistance / currentH;
} else {
*sideThree = 1 - currentDistance / height;
}
} else {
*sideThree = 1;
}
}
}
以下是此代码生成的三角形图像: