Как предотвратить рисование дополнительной линии между текстом и фигурой?

Я пытаюсь привязать PyCairo к графической библиотеке Cairo. Я столкнулся с проблемой при смешивании текста и дуг. В моем коде я рисую круг, затем текст рядом с ним в цикле, который рисует 10 кругов и связанный с ними текст.

введите здесь описание изображения

При рисовании текста с помощью ctx.show_text() текущая позиция устанавливается в точку продвижения (место, где должен быть написан следующий символ). Проблема заключается в том, что линия рисуется между текущей точкой и первой точкой последующего круга.

Единственная возможность, которую я вижу, - это использовать ctx.move_to() для перемещения текущей точки в начальную точку дуги перед рисованием круга. В данном случае это не большая проблема, потому что первую точку можно легко вычислить, и у меня уже есть функция для преобразования полярных координат (угол 0 и расстояние = радиус) в декартовы координаты, необходимые для move_to(). Тем не менее, я бы предпочел этого не делать, так как другие случаи могут быть не столь очевидными.

Интересно, я что-то упустил и есть ли другая возможность, например, команда, которая удалит текущую точку перед рисованием круга. Кроме того, проблема, по-видимому, связана только с рисованием текста, потому что, если текст не рисуется, нет линии, соединяющей отдельные маленькие кружки, хотя они тоже рисуются с использованием нескольких команд:

введите здесь описание изображения

Почему добавление текста создает нежелательные линии? Как я могу это решить?

import math
import cairo

def polar_to_cart(cx, cy, angle, dist):
    x = cx + math.cos(angle) * dist
    y = cy - math.sin(angle) * dist
    return x,y

cx,cy = 150,150
two_pi = 2 * math.pi
radius = 100

# 300x300 px surface
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 2*cx, 2*cy)
ctx = cairo.Context(surface)

# Draw large circle
ctx.set_source_rgb(0,0,0)
ctx.set_line_width(1)
ctx.arc(cx, cy, radius, 0, two_pi)
ctx.stroke()

# Draw small circles
n = 10
for i in range(n):
    # Center location
    x,y = polar_to_cart(cx, cy, two_pi / n * i, radius)

    # Draw an empty circle at x,y
    ctx.arc(x, y, 10, 0, two_pi)    
    ctx.set_source_rgb(1,0,0)
    ctx.set_line_width(1)
    ctx.stroke()

    # Name location (bbox center)
    name = str(i)
    xbbc,ybbc = polar_to_cart(cx, cy, two_pi / n * i, radius + 25)

    # Equivalent reference point location
    x_bearing, y_bearing, width, height = ctx.text_extents(name)[:4]
    xrp = xbbc - width/2 - x_bearing
    yrp = ybbc - height/2 - y_bearing

    # Draw name
    ctx.set_source_rgb(0,0,1)
    ctx.select_font_face("Tahoma", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
    ctx.set_font_size(16)
    ctx.move_to(xrp,yrp)
    ctx.show_text(name)

surface.write_to_png('_temp.png')

person mins    schedule 26.03.2021    source источник


Ответы (1)


Как поясняется в документации по cairo_show_text,

После этого вызова текущая точка перемещается в исходную точку, где следующий глиф будет помещен в той же последовательности. То есть текущая точка будет в исходной точке конечного глифа, смещенного на его опережающие значения. Это позволяет легко отображать одну логическую строку с несколькими вызовами cairo_show_text().

Это означает, что после отрисовки текста текущий путь состоит из одной точки. Метод ctx.arc добавляется к текущему пути, что в данном случае означает соединение этой точки с первой точкой окружности прямой линией. Это нежелательная красная линия.

Чтобы исправить это, вы можете заменить вызов ctx.show_text(name) на

    ctx.text_path(name)
    ctx.fill()

в котором ctx.fill() будет неявно очищать текущий путь после выполнения заливки. (ctx.stroke() вместо этого рисовал контур шрифта.)

Кроме того, вы можете добавить

   ctx.new_path()

после вызова ctx.show_text(name) для очистки пути, но это немного менее элегантно.

person user3840170    schedule 26.03.2021
comment
Читая полную документацию, я вижу, что new_sub_path() также можно использовать. Эта другая возможность не требует вызова операции (например, fill()) перед ее использованием. Вызов new_sub_path() особенно полезен, когда новый подпуть начинается с одного из вызовов Context.arc(). Это упрощает задачу, так как больше не нужно вручную вычислять начальные координаты дуги для вызова Context.move_to(). Это больше похоже на перо вверх, движение, перо вниз. Похожий вопрос - person mins; 27.03.2021