Last active
November 23, 2020 23:35
-
-
Save m13253/c1f0019a6b2471b5f9e99efffb660677 to your computer and use it in GitHub Desktop.
Cubic Bézier curve splitting program
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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