IOS: преобразование вида в цилиндрическую форму

С кварцевым 2D мы можем преобразовать наши представления на оси x, y и z.

В некоторых случаях мы могли бы даже заставить их смотреть 3D, изменяя значения матриц.

Мне было интересно, можно ли преобразовать представление в форму цилиндра, как на следующем рисунке?

enter image description here

Пожалуйста, проигнорируйте верхнюю часть цилиндра. Мне более любопытно узнать, возможно ли бы, чтобы деформация UIView была похожа на сторону цилиндра, как на изображении.

Возможно ли только использование кварцевых 2D, слоев и преобразований (не OpenGL)? Если нет, возможно ли хотя бы нарисовать его в CGContext, чтобы сделать представление таким же?

Ответ 1

Вы определенно не можете сделать это с помощью преобразования. Что вы можете сделать, так это создать свой UIView вне экрана, получить контекст для представления, получить изображение из него и затем сопоставить изображение с новым изображением с использованием нелинейного отображения.

Итак:

  • Создание контекста изображения с помощью UIGraphicsBeginImageContext()
  • Отобразить вид там, с view.layer.renderInContext()
  • Получить изображение результата с помощью CGBitmapContextCreateImage()
  • Напишите функцию отображения, которая принимает координаты экрана x/y и отображает их в координаты на цилиндре.
  • Создайте новое изображение размера экрана и вызовите отображение функция для копирования пикселей из источника в пункт назначения.
  • Нарисуйте растровое изображение адресата на экране.

Ни один из этих шагов не является особенно сложным, и вы можете придумать различные способы упрощения. Например, вы можете просто визуализировать полосы исходного вида, компенсируя координату Y, основанную на координатах круга, если вы не используете преобразования перспективы.

Если вы хотите, чтобы представление фактически было интерактивным, вам нужно будет сделать преобразование в обратном направлении при обработке событий касания.

Ответ 2

Нет, вы не можете сгибать представление с помощью преобразования.

Преобразование может управлять только четырьмя углами представления, поэтому независимо от того, что вы делаете, он все равно будет плоскостью.

Ответ 3

Я понимаю, что это выходит за рамки Quartz2D... Вы можете попробовать добавить SceneKit.

  • Получить изображение представления через UIGraphicsBeginImageContext(), view.layer.renderInContext(), CGBitmapContextCreateImage().
  • Создайте материал SCNM с диффузным свойством, установленным на изображение вашего вида.
  • Создайте SCNCylinder и примените к нему материал.
  • Добавьте цилиндр в SCNScene.
  • Создайте SCNView и установите его сцену.
  • Добавьте SCNView в иерархию вашего представления.

Ответ 4

Ссылка: Используя OpenGL ES 2.0 с iOS, как мне нарисовать цилиндр между двумя точками?

Я также использовал один и тот же код для одного из моих проектов:

Отметьте это, где указано, чтобы нарисовать форму конуса; он устарел, но после адаптации алгоритма он работает.

См. приведенный ниже код для решения. Self представляет сетку и содержит вершины, индексы и т.д.

- (instancetype)initWithOriginRadius:(CGFloat)originRadius
                   atOriginPoint:(GLKVector3)originPoint
                    andEndRadius:(CGFloat)endRadius
                      atEndPoint:(GLKVector3)endPoint
                   withPrecision:(NSInteger)precision
                        andColor:(GLKVector4)color
{
self = [super init];

if (self) {
    // normal pointing from origin point to end point
    GLKVector3 normal = GLKVector3Make(originPoint.x - endPoint.x,
                                       originPoint.y - endPoint.y,
                                       originPoint.z - endPoint.z);

    // create two perpendicular vectors - perp and q
    GLKVector3 perp = normal;
    if (normal.x == 0 && normal.z == 0) {
        perp.x += 1;
    } else {
        perp.y += 1;
    }

    // cross product
    GLKVector3 q = GLKVector3CrossProduct(perp, normal);
    perp = GLKVector3CrossProduct(normal, q);

    // normalize vectors
    perp = GLKVector3Normalize(perp);
    q = GLKVector3Normalize(q);

    // calculate vertices
    CGFloat twoPi = 2 * PI;        
    NSInteger index = 0;
    for (NSInteger i = 0; i < precision + 1; i++) {
        CGFloat theta = ((CGFloat) i) / precision * twoPi; // go around circle and get points

        // normals
        normal.x = cosf(theta) * perp.x + sinf(theta) * q.x;
        normal.y = cosf(theta) * perp.y + sinf(theta) * q.y;
        normal.z = cosf(theta) * perp.z + sinf(theta) * q.z;

        AGLKMeshVertex meshVertex;
        AGLKMeshVertexDynamic colorVertex;

        // top vertex
        meshVertex.position.x = endPoint.x + endRadius * normal.x;
        meshVertex.position.y = endPoint.y + endRadius * normal.y;
        meshVertex.position.z = endPoint.z + endRadius * normal.z;
        meshVertex.normal = normal;
        meshVertex.originalColor = color;

        // append vertex
        [self appendVertex:meshVertex];

        // append color vertex
        colorVertex.colors = color;
        [self appendColorVertex:colorVertex];

        // append index
        [self appendIndex:index++];

        // bottom vertex
        meshVertex.position.x = originPoint.x + originRadius * normal.x;
        meshVertex.position.y = originPoint.y + originRadius * normal.y;
        meshVertex.position.z = originPoint.z + originRadius * normal.z;
        meshVertex.normal = normal;
        meshVertex.originalColor = color;

        // append vertex
        [self appendVertex:meshVertex];

        // append color vertex
        [self appendColorVertex:colorVertex];

        // append index
        [self appendIndex:index++];
    }

    // draw command
    [self appendCommand:GL_TRIANGLE_STRIP firstIndex:0 numberOfIndices:self.numberOfIndices materialName:@""];
}

return self;
}