Skip to content

Instantly share code, notes, and snippets.

@m13253
Last active November 23, 2020 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m13253/c1f0019a6b2471b5f9e99efffb660677 to your computer and use it in GitHub Desktop.
Save m13253/c1f0019a6b2471b5f9e99efffb660677 to your computer and use it in GitHub Desktop.
Cubic Bézier curve splitting program
#!/usr/bin/env python3
# Copyright (C) 2020 Star Brilliant <coder@poorlab.com>
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
# 0. You just DO WHAT THE FUCK YOU WANT TO.
import sys
import typing
import numpy
def print_help() -> None:
print('Cubic Bézier curve splitting program')
print('Usage: {} x1,y1 x2,y2 x3,y3 x4,y4 split_point_x'.format(sys.argv[0]))
print()
print('If you have a cubic Bézier curve defined by Tikz syntax')
print()
print(' \draw (1,1) .. controls +(1,1) and +(-1,-1) .. (3,1)')
print()
print('or using absolute coordinates,')
print()
print(' \draw (1,1) .. controls (2,2) and (2,0) .. (3,1)')
print()
print('and you want to know what the two segments are if split at x=2.')
print()
print('you can call this program using the following arguments:')
print()
print(' {} 1,1 1,1 -1,-1 3,1 2'.format(sys.argv[0]))
print()
def parse_point(s: str) -> typing.Tuple[float, float]:
fields = str.split(s, ',', 2)
x = float(fields[0])
y = float(fields[1])
return x, y
def main() -> None:
if len(sys.argv) != 6:
print_help()
return
a = numpy.asarray(parse_point(sys.argv[1]))
b = numpy.asarray(parse_point(sys.argv[2]))
c = numpy.asarray(parse_point(sys.argv[3]))
d = numpy.asarray(parse_point(sys.argv[4]))
x = float(sys.argv[5])
b += a
c += d
t_all = numpy.roots(numpy.asarray([
[-1, 3, -3, 1, 0],
[ 3, -6, 3, 0, 0],
[-3, 3, 0, 0, 0],
[ 1, 0, 0, 0, -1],
]).dot(numpy.asarray([a[0], b[0], c[0], d[0], x])))
t = numpy.abs(sorted(t_all, key=lambda x: numpy.abs(x - 0.5))[0])
matrix_t = numpy.asarray([t*t*t, t*t, t, 1])
matrix_abcd = numpy.asarray([a, b, c, d])
b1 = matrix_t.dot(numpy.asarray([
[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[-1, 1, 0, 0],
[ 1, 0, 0, 0],
])).dot(matrix_abcd)
c1 = matrix_t.dot(numpy.asarray([
[ 0, 0, 0, 0],
[ 1, -2, 1, 0],
[-2, 2, 0, 0],
[ 1, 0, 0, 0],
])).dot(matrix_abcd)
mid = matrix_t.dot(numpy.asarray([
[-1, 3, -3, 1],
[ 3, -6, 3, 0],
[-3, 3, 0, 0],
[ 1, 0, 0, 0],
])).dot(matrix_abcd)
b2 = matrix_t.dot(numpy.asarray([
[0, 0, 0, 0],
[0, 1, -2, 1],
[0, -2, 2, 0],
[0, 1, 0, 0],
])).dot(matrix_abcd)
c2 = matrix_t.dot(numpy.asarray([
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, -1, 1],
[0, 0, 1, 0],
])).dot(matrix_abcd)
b1 -= a
c1 -= mid
b2 -= mid
c2 -= d
print('Split at ({:.3f},{:.6f}), t = {:.6f}'.format(mid[0], mid[1], t))
print()
print('\\draw ({:.3f},{:.3f})'.format(a[0], a[1]))
print(' .. controls +({:.3f},{:.3f}) and +({:.3f},{:.3f}) ..'.format(b1[0], b1[1], c1[0], c1[1]))
print(' ({:.3f},{:.3f})'.format(mid[0], mid[1]))
print(' .. controls +({:.3f},{:.3f}) and +({:.3f},{:.3f}) ..'.format(b2[0], b2[1], c2[0], c2[1]))
print(' ({:.3f},{:.3f});'.format(d[0], d[1]))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment