Skip to content

Instantly share code, notes, and snippets.

@chunibyo-wly
Created May 2, 2023 05:18
Show Gist options
  • Save chunibyo-wly/2d2d03356b351b0e84e5782e6973e292 to your computer and use it in GitHub Desktop.
Save chunibyo-wly/2d2d03356b351b0e84e5782e6973e292 to your computer and use it in GitHub Desktop.
import cairo
import numpy as np
import math
def cb(x1, y1, x4, y4, xc, yc):
ax = x1 - xc
ay = y1 - yc
bx = x4 - xc
by = y4 - yc
q1 = ax * ax + ay * ay
q2 = q1 + ax * bx + ay * by
k2 = (4 / 3) * ((2 * q1 * q2) ** 0.5 - q2) / (ax * by - ay * bx)
x2 = xc + ax - k2 * ay
y2 = yc + ay + k2 * ax
x3 = xc + bx + k2 * by
y3 = yc + by - k2 * bx
return x2, y2, x3, y3
def bezier_circle_center(p0, p1, p2):
"""
计算二阶贝塞尔曲线的圆心坐标
:param P0: 起点
:param P1: 控制点
:param P2: 终点
:return: 圆心坐标
"""
p0 = np.asarray(p0)
p1 = np.asarray(p1)
p2 = np.asarray(p2)
v1, v2 = p0 - p1, p2 - p1
l1, l2 = np.linalg.norm(v1), np.linalg.norm(v2)
# assert is_close(l1, l2) and not is_close(l1, 0)
theta = np.arccos(np.dot(v1 / l1, v2 / l2))
tan_half_theta = np.tan(theta / 2)
r = l1 * tan_half_theta
o2control_distance = np.sqrt(r**2 + l1**2)
control2target_distance = o2control_distance - r
control2target_dir = (v1 + v2) / np.linalg.norm(v1 + v2)
target_point = control2target_dir * control2target_distance + p1
o = control2target_dir * o2control_distance + p1
return (target_point, o)
# creating a SVG surface
# here geek95 is file name & 700, 700 is dimension
with cairo.SVGSurface("geek95.svg", 2000, 2000) as surface:
# creating a cairo context object for SVG surface
# using Context method
context = cairo.Context(surface)
x1, y1 = 0, 0
control = np.asarray((500, 400))
x4, y4 = control[0] * 2, 0
# 二阶
# https://blog.csdn.net/sl8023dxf/article/details/112624927
context.move_to(x1, y1)
C1 = np.asarray([x1, y1])
C4 = np.asarray([x4, y4])
C2 = C1 + 2 / 3 * (control - C1)
C3 = C4 + 2 / 3 * (control - C4)
context.curve_to(C2[0], C2[1], C3[0], C3[1], x4, y4)
context.set_source_rgb(1, 0, 0)
context.set_line_width(10)
context.stroke()
# 三阶
_, (xc, yc) = bezier_circle_center((x1, y1), (control[0], control[1]), (x4, y4))
x2, y2, x3, y3 = cb(x1, y1, x4, y4, xc, yc)
context.move_to(x1, y1)
context.curve_to(x2, y2, x3, y3, x4, y4)
context.set_source_rgb(0, 1, 0)
context.set_line_width(10)
context.stroke()
# 圆弧
# context.move_to(x1, y1)
# context.arc(control[0], 0, control[0], 0, 0.5 * math.pi)
radian = math.atan((y4 - yc) / (x4 - xc))
context.arc(
xc, yc, ((xc - x1) ** 2 + (yc - y1) ** 2) ** 0.5, radian, math.pi - radian
)
context.set_source_rgb(0, 0, 1)
context.set_line_width(5)
context.stroke()
@chunibyo-wly
Copy link
Author

result

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment