Checking If Two Vectors Are Colinear - Blender Artists Community Home » Collinear Vectors Python » Checking If Two Vectors Are Colinear - Blender Artists Community Maybe your like Collinear Vectors Test Collinear Vs Collinear Collinear Vs Coplanar Collinear Vs Coplanar Difference Collinear Vs Non Collinear Loading Checking if two vectors are colinear Coding Python Support Passion_3D (Passion_3D) March 15, 2009, 2:04pm 1 Basically, colinear vectors are lie on parallel lines, in particular cases - on one and the same line. [More on theory in Google :)] I’ve wrote a script that identifies if two given vectors are colinear. Here it is: def Vectors_colinear(vec1,vec2): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[1],vec2[1]]) D = mat.determinant() # if (D == 0): # This is the perfect situation but in Blender things are NOT perfect! if (abs(D) <= 0.00001): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[2],vec2[2]]) D = mat.determinant() # if (D == 0): if (abs(D) <= 0.00001): mat = Mathutils.Matrix([vec1[1],vec2[1]],[vec1[2],vec2[2]]) D = mat.determinant() # if (D == 0): if (abs(D) <= 0.00001): return True else: return False else: return False else: return False It uses the fact that each determinant constructed by taking any two pairs of coresponding coordinates of the vectors are equal to zero when the vectors are colinear. I needed to accept a very small non-zero tollerance for practical checking with Blender data as the perfect situation (if (D == 0) filtered cases in which differences in angles between the vectors are extremely small (smaller than 1e-006) so that Blender practically never shows you such a difference. In the same time, your script does NOT recognise the pair as being colinear as D is actually NOT exactly equal to zero. THis leads to missing facts while your script investigates your mesh… Regards, rocketship (rocketship) March 15, 2009, 4:25pm 2 I may be misunderstanding the problem you are trying to solve, but wouldn’t it be true that colinear vectors (in the 3d graphics sense) are just uniformly scaled versions of each other? So: vec1.x / vec2.x = vec1.y / vec2.y = vec1.z / vec2.z (accounting for epsilon , that is) RS <Edit> Reading further posts, I don’t think you’re talking about vectors at all, but edges. Sorry. If you are concerned about numerical stability, though, Blender isn’t the tool to be working with. I would suggest checking out CGAL - there are (some) Python bindings for it, and we would all welcome more migius (migius) March 15, 2009, 5:14pm 3 alternative solution from Blender import Mathutils print ' ---START--------------------' threshold = 0.00001 vec1=Mathutils.Vector([0.0,0.0,1.0]) vec2=Mathutils.Vector([0.0,0.0,2.0]) #------------------------------------------- def Vectors_colinear1(vec1,vec2): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[1],vec2[1]]) D = mat.determinant() if (abs(D) <= threshold): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[2],vec2[2]]) D = mat.determinant() if (abs(D) <= threshold): mat = Mathutils.Matrix([vec1[1],vec2[1]],[vec1[2],vec2[2]]) D = mat.determinant() if (abs(D) <= threshold): return True return False #------------------------------------------- def Vectors_colinear2(vec1,vec2): a = 1.0 if abs(vec2[0])>threshold: a = vec1[0] / vec2[0] elif abs(vec2[1])>threshold: a = vec1[1] / vec2[1] elif abs(vec2[2])>threshold: a = vec1[2] / vec2[2] vec2 = vec2 * a if vec1==vec2: return True return False time0 = Blender.sys.time() for l in range(100000): result=Vectors_colinear1(vec1,vec2) print 'result1=', result, ', in %.4f sec.' % (Blender.sys.time()-time0) time0 = Blender.sys.time() for l in range(100000): result=Vectors_colinear2(vec1,vec2) print 'result2=', result, ', in %.4f sec.' % (Blender.sys.time()-time0) theeth (theeth) March 15, 2009, 6:10pm 4 The cross product of collinear vectors is null (6 multiplications, faster than 3 divisions). This is equivalent to the determinant method, but you don’t waste time constructing intermediary matrices. If you want to make sure they point into the same direction, the dot product of two vectors pointing in opposite direction is negative. Martin Passion_3D (Passion_3D) March 16, 2009, 12:06am 5 To rocketship - what you say is absolutely correct and at my first version I tried to determine colinearity by exactly if (vec1.x / vec2.x = vec1.y / vec2.y = vec1.z / vec2.z)… BUT this is nice to use only for non-zero x,y or z on the second vector soooo one needs to check this too => I changed the checking by a matrix operation which runs in ALL cases. Second - you are rignt that I am using the vector colinearity to identify “colinear” edges in a mesh while working on a bigger task… If interested, see here: http://blenderartists.org/forum/showthread.php?t=140970 To migius - Your second option gets the comments above, sorry! But I like your first option very much!!! Perhaps the definition should be def Vectors_colinear1(vec1,vec2,threshold): as the threshold is will NOT be defined otherwise. Any way, accepting thresholds < 0.00001 is useless for practical reasons in BLENDER To theeth - you’re right about the method with using the cross-product procedure but I am sure its first action is constructing the same matrix, right? Firstly, I used the method of checking AngleBetweenVecs(vec1,vec2) function of Mathutils but angles were reported to be something like 180.0000045058 (if you take the 0 na 1 vertex of a square face for the first vector and 2 and 3 vertex - it that order - for the second). So I wasnt sure what colinearity means in Blender as in math the angle should be exatly 180 degrees, humm… I also wasnt sure what will the cross-product function of Mathutils module show… Plus - you cannot avoid comparison with a threshold to check your results as BLENDER objects and calculations prove to be NOT perfect… Redards, theeth (theeth) March 16, 2009, 4:47am 6 Passion_3D: To theeth - you’re right about the method with using the cross-product procedure but I am sure its first action is constructing the same matrix, right? It doesn’t construct the actual matrix object, no (whereas in your case, you’re make three of them). Passion_3D: Firstly, I used the method of checking AngleBetweenVecs(vec1,vec2) function of Mathutils but angles were reported to be something like 180.0000045058 (if you take the 0 na 1 vertex of a square face for the first vector and 2 and 3 vertex - it that order - for the second). So I wasnt sure what colinearity means in Blender as in math the angle should be exatly 180 degrees, humm… The difference from 180 is just imprecision in the cos-1 function. It is to be expected. Passion_3D: I also wasnt sure what will the cross-product function of Mathutils module show… Plus - you cannot avoid comparison with a threshold to check your results as BLENDER objects and calculations prove to be NOT perfect… You can compare the vector resulting from the cross product to your threshold, either component-wise or all at once (dot product of itself for squared length). The first is faster and good enough in this case I’d say. Martin Passion_3D (Passion_3D) March 16, 2009, 7:31am 7 The difference from 180 is just imprecision in the cos-1 function. It is to be expected. I realised that but when checking the angle you need to check with 180 eactly and the check DOES NOT give you “True” but “False”… It doesn’t construct the actual matrix object Math theory says the quickest way to calc colinearity is to construct a matrix of 2 rows, 3 columns and then calculate the three determinants and check them against 0… That’s what I explicitly did. Do you know which method is implemented in Mathutils’ cross-product procedure? Regards, theeth (theeth) March 16, 2009, 10:13am 8 Passion_3D: Math theory says the quickest way to calc colinearity is to construct a matrix of 2 rows, 3 columns and then calculate the three determinants and check them against 0… That’s what I explicitly did. Do you know which method is implemented in Mathutils’ cross-product procedure? It’s basically a call to this C function: void Crossf(float *c, float *a, float *b) { c[0] = a[1] * b[2] - a[2] * b[1]; c[1] = a[2] * b[0] - a[0] * b[2]; c[2] = a[0] * b[1] - a[1] * b[0]; } Which is faster than having to initialize three different Python Matrix objects just to discard them right away. Martin Passion_3D (Passion_3D) March 16, 2009, 10:24am 9 Aaaahh, yes, theeth - this is the wide form of determinants of those 3 sub-matrices… :rolleyes: migius (migius) March 17, 2009, 12:59am 10 Passion_3D: To migius - Your second option gets the comments above, sorry! But I like your first option very much!!! a joke :), the first routine is in fact your determinant method. I’ve just slightly rewritten it to drop some text balast. as the threshold is will NOT be defined otherwise. Any way, accepting thresholds < 0.00001 is useless for practical reasons in BLENDER because of Blender and Python’s floats imperfections, I need threshold as workaround in many places, so i prefer to make it a global constant. Below the profiling code, inclusive theeth’s CrossVector() method. It was quite interesting for me to observe their performance. import Blender from Blender import Mathutils print ' ---START--------------------' #--by Passion_3D--------------------------------- def Vectors_colinear1(vec1,vec2): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[1],vec2[1]]) D = mat.determinant() if D==0: mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[2],vec2[2]]) D = mat.determinant() if D==0: mat = Mathutils.Matrix([vec1[1],vec2[1]],[vec1[2],vec2[2]]) D = mat.determinant() if D==0: return True return False #--by Passion_3D--------------------------------- def fuzzVectors_colinear1(vec1,vec2): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[1],vec2[1]]) D = mat.determinant() if (abs(D) <= threshold): mat = Mathutils.Matrix([vec1[0],vec2[0]],[vec1[2],vec2[2]]) D = mat.determinant() if (abs(D) <= threshold): mat = Mathutils.Matrix([vec1[1],vec2[1]],[vec1[2],vec2[2]]) D = mat.determinant() if (abs(D) <= threshold): return True return False #--by migius-------------------------------------- def Vectors_colinear2(vec1,vec2): a = 1.0 if abs(vec2[0])>threshold: a = vec1[0] / vec2[0] elif abs(vec2[1])>threshold: a = vec1[1] / vec2[1] elif abs(vec2[2])>threshold: a = vec1[2] / vec2[2] vec2 = vec2 * a if vec1==vec2: return True return False #--by migius-------------------------------------- def fuzzVectors_colinear2(vec1,vec2): a = 1.0 if abs(vec2[0])>threshold: a = vec1[0] / vec2[0] elif abs(vec2[1])>threshold: a = vec1[1] / vec2[1] elif abs(vec2[2])>threshold: a = vec1[2] / vec2[2] vec2 = vec2 * a if (vec1-vec2).length < threshold: return True return False #--by theeth-------------------------------------- def Vectors_colinear3(vec1,vec2): if Mathutils.CrossVecs(vec1,vec2).length == 0: return True return False #--by theeth-------------------------------------- def fuzzVectors_colinear3(vec1,vec2): if Mathutils.CrossVecs(vec1,vec2).length < threshold: return True return False #--dummy routine for netto calculation--------------- def dummy(vec1,vec2): a = 2.0 if a <= threshold: return True return False #---------------------------------------- def compareRoutines(vec1,vec2): print ' vec1:', vec1 print 'vec2:', vec2 time0 = Blender.sys.time() for l in range(iter): result=dummy(vec1,vec2) netto_time = Blender.sys.time()-time0 time0 = Blender.sys.time() for l in range(iter): result=Vectors_colinear1(vec1,vec2) print 'determinant =', result, ', %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=fuzzVectors_colinear1(vec1,vec2) print 'determinant fuzzy=', result, ', %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=Vectors_colinear2(vec1,vec2) print 'scaleFactor =', result, ', %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=fuzzVectors_colinear2(vec1,vec2) print 'scaleFactor fuzzy=', result, ', %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=Vectors_colinear3(vec1,vec2) print 'crossProduct =', result, ', %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=fuzzVectors_colinear3(vec1,vec2) print 'crossProduct fuzzy=', result, ', %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=Mathutils.CrossVecs(vec1,vec2).length < threshold print 'crossP one-liner=', result, ', %.4f sec.' % (Blender.sys.time()-time0) #------------------------------------------- threshold = 0.00001 iter = 100000 Vec = Mathutils.Vector vec1, vec2 = Vec([0.5,0.5,0.5]), Vec([-6.0,-6.0,-6.0]) compareRoutines(vec1,vec2) vec1, vec2 = Vec([0.0,0.0,1.0]), Vec([0.0,0.0,-8.0]) compareRoutines(vec1,vec2) vec1, vec2 = Vec([1.0,0.0,0.0]), Vec([-10.0,0.000001,0.0]) compareRoutines(vec1,vec2) vec1: [0.500000, 0.500000, 0.500000](vector) vec2: [-6.000000, -6.000000, -6.000000](vector) determinant = True , 1.5334 sec. determinant fuzzy= True , 1.6600 sec. scaleFactor = True , 0.1426 sec. <---! scaleFactor fuzzy= True , 0.2263 sec. crossProduct = True , 0.1835 sec. crossProduct fuzzy= True , 0.1814 sec. crossP one-liner= True , 0.1715 sec. vec1: [0.000000, 0.000000, 1.000000](vector) vec2: [0.000000, 0.000000, -8.000000](vector) determinant = True , 1.5493 sec. determinant fuzzy= True , 1.6612 sec. scaleFactor = True , 0.2585 sec. scaleFactor fuzzy= True , 0.3486 sec. crossProduct = True , 0.1828 sec. crossProduct fuzzy= True , 0.1820 sec. crossP one-liner= True , 0.1707 sec. vec1: [1.000000, 0.000000, 0.000000](vector) vec2: [-10.000000, 0.000001, 0.000000](vector) <--- fuzzy vector determinant = False , 0.5316 sec. determinant fuzzy= True , 1.6584 sec. scaleFactor = False , 0.1571 sec. scaleFactor fuzzy= True , 0.2288 sec. crossProduct = False , 0.2031 sec. crossProduct fuzzy= True , 0.1812 sec. crossP one-liner= True , 0.1679 sec. <---! My conclusion: for “ideal” vectors: the ScaleFactor method is slightly faster than CrossVector method. for “fuzzy” vectors: the CrossVector (threshold) method is the fastest, so this would be my choice. It doesn’t even need to be a subroutine - as one-liner it works a bit faster. @theeth, in ScaleVector_method, there is effectively only one division and one multiplication. I guess, if C implemented, it could be much faster than CrossVector_method. theeth (theeth) March 17, 2009, 5:31am 11 migius: @theeth, in ScaleVector_method, there is effectively only one division and one multiplication. I guess, if C implemented, it could be much faster than CrossVector_method. Divisions are much slower than multiplications., but yeah, it’d be something worth trying. Also, replacing all calls to length by a dot product saves some time too (square root isn’t too fast). Martin bjornmose (bjornmose) March 17, 2009, 9:06am 12 Passion_3D: Math theory says the quickest way to calc colinearity is to construct a matrix of 2 rows, 3 columns and then calculate the three determinants and check them against 0… This is true for exact colinearity. Which is equivalent to calculating the cross product and checking each component of the result for zero. But if you want to do it fuzzy you have to define what is described by the tolerance. My personal choice in this case would be the angle. If you compare the length of the result vector to some threshold this will give different results with the same angle but varying lengths. ( since len(cross(a,b))) = |a|*|b|sin(alpha) –> making a and b short enough will result in yes even if they are perpendicular ) So the a cheap fuzzy variant for constant angle threshold might be 1 - dot(a,b)/|a||b| < t && 1 - dot(a,b)/|a||b| > 0 yes, the two normalizations are a bitter pill, but inevitable. <edit> i did choose dot() aka ‘inner product’ because it is cheaper than cross() note: dot(a,b) also is |a||b|*cos(angle) and since cos(x) is monotonous on 0 2PI no need to do a acos() for simple threshold </edit> RickyBlender (RickyBlender) March 17, 2009, 10:40am 13 interesting for mechanical or physical things i guess! there would be another interesting way to do it and it would involve transforming the lines into Spherical coordinates whit the angles then compare the angles but not certain if this would be faster but it would show the different angles and a decision can be taken base on theses angles with a threshold too at least you can see the angle if needed easier to understand angles than a matrix representing theses angles! happy blendering macouno (macouno) March 17, 2009, 11:25am 14 How about something ultra basic (sorry if it was mentioned before), you can easily get the .lenght of both vectors, so you know what the length of the vector resulting from (vector1 + (-vector2)) would be if they were colinear. 0 if they’re the same length. Or even… just normalise them before doing that… if it’s 0… done. RickyBlender (RickyBlender) March 17, 2009, 11:30am 15 the lenght does not tell you what the angles are in 3D so not very usefull to know if of equal length ! Salutations theeth (theeth) March 17, 2009, 1:11pm 16 bjornmose: So the a cheap fuzzy variant for constant angle threshold might be 1 - dot(a,b)/|a||b| < t && 1 - dot(a,b)/|a||b| > 0 yes, the two normalizations are a bitter pill, but inevitable. It might be possible to get rid of the square roots by using the squared values. Like this: f = dot(a,b) 1 - f*f/dot(a,a)*dot(b,b) < t*t macouno: How about something ultra basic (sorry if it was mentioned before), you can easily get the .lenght of both vectors, so you know what the length of the vector resulting from (vector1 + (-vector2)) would be if they were colinear. 0 if they’re the same length. Or even… just normalise them before doing that… if it’s 0… done. That wouldn’t work for vectors pointing in opposite directions. It would also be more computationally heavy (two square roots, six divisions). If the vectors are prenormalized however, it’s as easy just to do a dot product and see how close it is to 1 or -1. Martin bjornmose (bjornmose) March 17, 2009, 2:10pm 17 theeth: It might be possible to get rid of the square roots by using the squared values. Like this: f = dot(a,b) 1 - f*f/dot(a,a)*dot(b,b) < t*t Martin brilliant, but why use t*t ? cos(alpha) is as unintuitive as cos(alpha)cos(alpha) so leave it to the caller to sort that out . / squeezing out the last unneeded multiplication */ Another hint: Depending on the application,if you want to care for opposite directions ( angles > 90 degrees ) simply check the sign of f before doing the heavy 2nd line. <edit> i am not really sure with all the pre fetching multiple pipeline processing CPU cores if a sqrt performs worse than a bunch of multiplications in line … not to count the overhead calling functions from python … might even be more efficient to normalize a and b before doing anything else, as you said “it’s as easy just to do a dot product and see how close it is to 1 or -1” … me shrugs </edit> <edit2> i know that it is exposed to python and i would take this one snip from arithb.c /* Return the shortest angle in degrees between the 2 vectors */ float VecAngle2(float *v1, float *v2) { float vec1[3], vec2[3]; VecCopyf(vec1, v1); VecCopyf(vec2, v2); Normalize(vec1); Normalize(vec2); return NormalizedVecAngle2(vec1, vec2)* 180.0/M_PI; } float NormalizedVecAngle2(float *v1, float *v2) { /* this is the same as acos(Inpf(v1, v2)), but more accurate */ if (Inpf(v1, v2) < 0.0f) { float vec[3]; vec[0]= -v2[0]; vec[1]= -v2[1]; vec[2]= -v2[2]; return (float)M_PI - 2.0f*saasin(VecLenf(vec, v1)/2.0f); } else return 2.0f*saasin(VecLenf(v2, v1)/2.0); } “angle in degree” … which gives all the knit picking optimizations to the core developers. </edit2> <edit3> Python exposed like that –> http://www.blender.org/documentation/242PythonDoc/Mathutils-module.html#AngleBetweenVecs not that it is fastest but really accurate, comfortable and supported by the blender team. however … i hope you enjoyed this excursion on basic linear algebra and the difference it makes to go fuzzy. (!@theeth i know that you know ) </edit3> <edit4> @theeht may be we should check how useful a function returning / checking against cos(angle) or the square of it might be ? </edit4> migius (migius) March 18, 2009, 5:46am 18 macouno: …something ultra basic…, you can easily get the .lenght of both vectors, so you know what the length of the vector resulting from (vector1 + (-vector2)) would be if they were colinear. 0 if they’re the same length. Or even… just normalise them before doing that… if it’s 0… done. @macouno: given vectors may be different lenght and orientation, also slightly unprecise comming from blender scene, so we need a threshold tolerance. All this make your “ultra basic” solution quite complicated. migius (migius) March 18, 2009, 7:00am 19 import Blender from Blender.Mathutils import Vector, DotVecs #----comparison DotVecs() and Vector.lenght--------------------------------------- iter = 100000 vec1 = Vector([0.5,0.5,0.5]) print ' iterations = ', iter time0 = Blender.sys.time() for l in range(iter): result=1 netto_time = Blender.sys.time()-time0 #print 'netto time %.4f sec.' % (netto_time) time0 = Blender.sys.time() for l in range(iter): result=DotVecs(vec1,vec1) print 'DotVecs(vec1,vec1) in %.4f sec.' % (Blender.sys.time()-time0-netto_time) time0 = Blender.sys.time() for l in range(iter): result=vec1.length print 'Vector.length in %.4f sec.' % (Blender.sys.time()-time0-netto_time) iterations = 100000 DotVecs(vec1,vec1) in 0.0620 sec. Vector.length in 0.0281 sec. @theeth: interesting: Mathutils.DotVecs() is slower than Mathutils.Vector.length. in vector.c line1070: /* vector.length */ static PyObject *Vector_getLength( VectorObject * self, void *type ) { double dot = 0.0f; int i; for(i = 0; i < self->size; i++){ dot += (self->vec[i] * self->vec[i]); } return PyFloat_FromDouble(sqrt(dot)); } compare to Mathutils.c line 450 //----------------------------------Mathutils.DotVec() ------------------- //calculates the dot product of two vectors PyObject *M_Mathutils_DotVecs(PyObject * self, PyObject * args) { VectorObject *vec1 = NULL, *vec2 = NULL; double dot = 0.0f; int x; if(!PyArg_ParseTuple(args, "O!O!", &vector_Type, &vec1, &vector_Type, &vec2)) return EXPP_ReturnPyObjError(PyExc_TypeError, "Mathutils.DotVecs(): expects (2) vector objects of the same size "); if(vec1->size != vec2->size) return EXPP_ReturnPyObjError(PyExc_AttributeError, "Mathutils.DotVecs(): expects (2) vector objects of the same size "); for(x = 0; x < vec1->size; x++) { dot += vec1->vec[x] * vec2->vec[x]; } return PyFloat_FromDouble(dot); } Passion_3D (Passion_3D) March 18, 2009, 10:23am 20 Well, well, well… Very interesting discussion runs here!!! Sorry for not stopping sooner to tell you what I’ve done (and I will be away some 2 weeks too) but here I am… Reverting to my original procedure <def Vectors_colinear(vec1,vec2)> (with or without using a threshold) which has been nicely modified by migius to reduce repeated code in there and comparing this with cross-product proc shows results of about 2.5 times slower performance of the Vectors_colinear proc… For me Vectors_colinear is more clear and straight-forward, readable and easy to understand what is going on in the meantime and as soon as wasnt sure if cross-product will give the same result I used Vectors_colinear proc. Now I am clear that cross-product gives (mathematically) the same results + this it is faster soooo it’s nicer! I abandoned the method using angle between vectors cause it is more complicated to construct and gives you the same imperfection but in regards to angles. Finally, I’d say that I dont like to use any methods based on length (distance between verts) just because it is ALWAYS a matter of checking if you have or NOT have null-length vectors which is problemating in calculations therefore - the procedure becomes longer and slower… huh… next page → Tag » Collinear Vectors Python (Python) Script To Determine If (x, Y) Coordinates Are Colinear Check If Two Vectors Are Collinear Or Not - GeeksforGeeks Program To Check If Three Points Are Collinear - GeeksforGeeks Find Clusters Of Collinear Points From A Given Set Of Data Points Statistics In Python — Collinearity And Multicollinearity A Python Library To Remove Collinearity | By Gianluca Malato Python Program To Check If Three Points Are Collinear - BTech Geeks Complete Guide To Vectors In Linear Algebra With Implementation ... Vector-shortcuts · PyPI oss — NumPy V1.23 Manual Python Script To Determine If X Y Coordinates Are Colinear Getting ... Types Of Vectors: Collinear And Equal Vectors, Videos ... - Toppr Collinearity | Programming Praxis Python: Test If Two Segments Are Roughly Collinear (on The Same Line)