/
piliko.py
1938 lines (1710 loc) · 65.2 KB
/
piliko.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# very, very 'rought draft' implementation of some Rational Geometry
# codes. the type system has not been thought out carefully at all.
# See README.md
# Rational Geometry tries to stick to Rational numbers, here
# we use python's "Fraction" type to represent rationals.
# Rational Geometry was discovered and developed by Norman J Wildberger.
# This code is not affiliated with him nor endorsed by him in any way.
# See the README.md file for info.
# todo
# simplify code
# use actual test suite
# deal with /0 and other problems
# implement non-implemented stuff
# make function pointers shorter code, better code
# put 'of' in func names
# what is omega inverse?
# code design philosophy
# work backwards from usage to code. what would i want to type?
# do i have to rememeber tech details or can i type whatever?
# (example: triangle from lines, from points, from linesegs)
# (example: spread( x, y ) ---> allow x to be lines, vectors, etc.
# the code can determine which is appropriate by itself.
from fractions import Fraction
def rat( x, y ):
return Fraction( x, y )
def abso( x ):
if x<0: return -x
else: return x
def sqr( x ):
return x*x
def sum( *args ):
tot = Fraction(0)
for arg in args:
tot += arg
return tot
def avg( *args ):
return Fraction(sum(*args),len(args))
def sign( x ):
if x<0: return -1
return 1
def checktype( typename, arg ):
return isinstance( arg, typename )
def checktypes( typename, *args ): # are all args of a given type?
for i in range(len(args)):
#if not checktype( typename, args[i] ): return False
if not isinstance( args[i], typename ): return False
return True
# return true if all arguments are either int or Fraction
def checkrationals( *args ):
for i in range(len(args)):
if checktypes( Fraction, args[i]):
pass
elif checktypes( int, args[i] ):
pass
elif checktypes( long, args[i]):
pass
elif checktypes( tuple, args[i] ):
for a in args[i]:
if not checkrationals( a ): return False
elif checktypes( str, args[i] ):
for a in args[i]:
if not checkrationals( Fraction(a) ): return False
else:
return False
return True
def crash_if_nonrationals( *args ):
if not checkrationals( *args ):
raise Exception("Rationals only please")
def bitcount(*args):
tot=0
if checkrationals( *args ):
for a in args:
tot+=Fraction(a).numerator.bit_length()
tot+=Fraction(a).denominator.bit_length()
return tot
else: raise Exception("Rationals only please")
bit_count=bitcount
class point:
def __init__(self, *args):
if checktypes(point,*args):
self.x=args[0].x
self.y=args[0].y
if (hasattr(args[0],'z')): self.z=args[0].z
elif checktypes(vector,*args):
self.x=args[0][0]
self.y=args[0][1]
if (hasattr(args[0],'z')): self.z=args[0][2]
elif checkrationals( *args ):
self.x,self.y=args[0],args[1]
if (len(args)==3): self.z=args[2]
elif checktypes( list,*args ) and len(args[0])==2:
self.x,self.y=args[0][0],args[0][1]
elif checktypes( list,*args ) and len(args[0])==3:
self.x,self.y,self.z=args[0][0],args[0][1],args[0][2]
else: raise Exception( 'cant build point from'+str(args))
def __str__( self ):
return point_txt(self)
def __repr__( self ):
return point_txt(self)
def __getitem__( self, i ):
if i==0: return self.x
if i==1: return self.y
if i==2: return self.z
def __add__( self, p):
v=vector(self)+vector(p)
return point(v)
def __sub__( self, p):
v=vector(self)-vector(p)
return point(v)
def __eq__(self, p):
if p==None: return False
if hasattr(p,'z') and hasattr(self,'z'):
return self.x==p.x and self.y==p.y and self.z==p.z
if hasattr(p,'z') and not hasattr(self,'z'): return False
if hasattr(self,'z') and not hasattr(p,'z'): return False
return self.x==p.x and self.y==p.y
class vector:
def __init__( self, *args ):
if checktypes( point,*args ) and len(args)==1:
p = args[0]
elif checktypes( complex,*args ) and len(args)==1:
p = point(args[0].x,args[0].y)
else:
p = point( *args )
self.x,self.y=p.x,p.y
if hasattr(p,'z'): self.z=p.z
def __str__( self ):
return vector_txt(self)
def __add__( self, v):
newx = self.x + v.x
newy = self.y + v.y
if hasattr(v,'z'):
newz = self.z + v.z
return vector(newx, newy, newz)
return vector(newx, newy)
def __sub__( self, v ):
negv = vector( -v.x, -v.y )
if hasattr(v,'z'): negv.z = -v.z
return self + negv
def __mul__( self, *args ):
if checkrationals(*args) and len(args)==1:
scalar = args[0]
newv = vector( self.x * scalar, self.y * scalar )
if hasattr(self,'z'): newv.z = self.z * scalar
return newv
elif checktypes(point,*args) and len(args)==1:
p = args[0]
nx,ny = self.x * p.x, self.y * p.y
if hasattr(self,'z'): nz = self.z * p.z
return point( nx, ny, nz )
else: raise Exception("unknown multiplication type for vector")
def __div__( self, *args ):
if checkrationals(*args) and len(args)==1:
return self * Fraction(1,args[0])
def __rmul__( self, scalar ):
return self * scalar
def __neg__( self ):
return self * -1
def dot( self, v ):
x1,y1,x2,y2 = self.x,self.y,v.x,v.y
p = x1*x2+y1*y2
if hasattr(v,'z') and hasattr(self,'z'):
z1,z2 = self.z,v.z
p += z1*z2
return p
def cross( self, v ):
ax,ay,az,bx,by,bz=self.x,self.y,self.z,v.x,v.y,v.z
nx = wedge( ay, az, by, bz )
ny = wedge( ax, az, bx, bz )
nz = wedge( ax, ay, bx, by )
return vector( nx, -ny, nz )
def perpendicular( self, v ):
return perpendicular( self, v )
def parallel( self, v ):
return parallel( self, v )
def __getitem__( self, i ):
if i==0: return self.x
if i==1: return self.y
if i==2: return self.z
# very simple complex numbers. the form is as follows:
# x + y * i.
# i is the square root of negative one
# x and y are rationals. they also represent coordinates.
class complex:
def __init__( self, *args ):
if checkrationals(*args) and len(args)==2:
self.x,self.y = args[0],args[1]
elif checktypes(complex,*args) and len(args)==1:
self.x,self.y=args[0].x,args[0].y
else: raise Exception('complex needs x,y for x+yi')
def __str__( self ): return complex_txt(self)
def __repr__( self ): return complex_txt(self)
def __add__( self, c ): return complex(self.x+c.x,self.y+c.y)
def __neg__( self ): return complex(-self.x,-self.y)
def __sub__( self, c ): return complex(self.x-c.x,self.y-c.y)
def __div__( self, *args ):
if checkrationals(*args) and len(args)==1:
return complex( Fraction(self.x,args[0]), Fraction(self.y,args[0]) )
else: raise Exception("unknown division for complex number")
def __mul__( self, *args ):
if checkrationals(*args) and len(args)==1:
return complex( self.x*args[0], self.y*args[0] )
elif checktypes(complex,*args) and len(args)==1:
a,b,c,d = self.x,self.y,args[0].x,args[0].y
return complex(a*c-b*d, a*d+b*c) # (a+bi)(c+di)
else: raise Exception("unknown multiplication for complex number")
def __rmul__( self, scalar ): return self * scalar
def sqrt( self ):
x, y = self.x,self.y
# x + yi => ( xoo + yoo i )( xoo + yoo i )
# xoo*yoo*2 = y, xoo^2-yoo^2 = x
# yoo = y / (2*xoo)
# xoo^2 - ( y/(2*xoo) )^2 = x
# xoo^4 - y^2/4 = x * xoo^2
# xoo^2=q
# q^2 - x * q - y^2/4 = 0
# quadratic formula, solve q
# q = [ -(-x) +/- sqrt( x*x - 4*1*-y^2/4 ) ] / 2*1
# q = [ x +/- sqrt( x^2 + y^2 ) ]
# xoo = +/- sqrt(q)
# yoo = y / (2*xoo)
qa = Fraction( x + babylonian_square_root( x*x + y*y ), 2 )
qb = Fraction( x - babylonian_square_root( x*x + y*y ), 2 )
#print 'qa,qb',qa,qb
xooa = babylonian_square_root( abs(qa) )
xoob = babylonian_square_root( abs(qb) )
#print 'xoo',xooa,xoob
yooa = Fraction( y, 2*xooa )
yoob = Fraction( y, 2*xoob )
return complex( xooa, yooa ), complex( xoob, yoob )
class bivector:
def __init__( self, *args ):
if checktypes( vector, *args ):
self.v1,self.v2=args[0], args[1]
else: raise Exception('not implemented')
def __str__( self ):
return bivector_txt(self)
def __add__( self, bv ):
return
def __sub__( self, bv ):
raise Exception('not implemented')
def __mul__( self, scalar ):
return bivector( self.v1 * scalar , self.v2 )
# could use self.v1, self.v2 * scalar
def __rmul__( self, scalar ):
return self.__mul__(scalar)
def dot( self, v ):
raise Exception('not implemented')
def perpendicular( self, bv ):
raise Exception('not implemented')
def parallel( self, bv ):
raise Exception('not implemented')
def value( self ):
# note, det v2, v2 = -1 * det v1, v2
return determinant( self.v1, self.v2 )
def __getitem__( self, i ):
if i==0: return self.v1
if i==1: return self.v2
# determinant, aka wedge, aka signed area of paralellogram formed by 2 vectors
def wedge( *args ):
if checktypes(point,*args) and len(args)==2:
x1,y1,x2,y2=args[0].x,args[0].y,args[1].x,args[1].y
elif checkrationals(*args) and len(args)==4:
x1,y1,x2,y2=args[0],args[1],args[2],args[3]
elif checktypes(vector,*args) and len(args)==2:
x1,y1,x2,y2=args[0].x,args[0].y,args[1].x,args[1].y
else: raise Exception('dont understand input to wedge()'+str(args))
return x1*y2-x2*y1
# line formula here is ax + by + c = 0
class line:
def __init__( self, *args ):
if checktypes(point,*args) and len(args)==2:
self.init_from_points( args[0], args[1] )
elif checktypes(list,*args) and len(args)==2:
if len(args[0])==2 and len(args[1])==2:
p1 = point(args[0][0],args[0][1])
p2 = point(args[1][0],args[1][1])
self.init_from_points( p1, p2 )
else: raise Exception('line needs pts or rationals a,b,c')
elif checktypes(tuple,*args) and len(args)==2:
if len(args[0])==2 and len(args[1])==2:
p1 = point(args[0][0],args[0][1])
p2 = point(args[1][0],args[1][1])
self.init_from_points( p1, p2 )
else: raise Exception('line needs pts or rationals a,b,c')
elif checkrationals(*args) and len(args)==3:
self.a,self.b,self.c=args[0],args[1],args[2]
elif checkrationals(*args) and len(args)==4:
p1=point(args[0],args[1])
p2=point(args[2],args[3])
self.init_from_points( p1, p2 )
else:
raise Exception('line needs pts or rationals a,b,c')
def init_from_points( self, p1, p2 ):
# for any p collinear to p1,p2, area of
# paralello-gram formed by vector v1 (p2-p) and
# vector v2 (p1-p) = 0. thus v1 wedge v2 == 0.
# expand, and a,b,c become obvious.
# (same theory as building plane from pts)
pdiff = p2-p1
self.a,self.b = pdiff.y,-pdiff.x
self.c = -wedge( p1, p2 )
def __str__( self ): return line_txt( self )
def __repr__( self ): return line_txt( self )
def __getitem__( self, i ):
if i==0: return self.a
if i==1: return self.b
if i==2: return self.c
# plane formula here is ax + by + cz + d = 0
# Theory+Problems of Vector Analysis, Murray R Spiegel, 1959
# Schaum Publishing, New York.
# For any p coplanar to p1,p2,p3, area of paralellapiped
# formed by vector (p2-p), vector (p1-p), and
# vector(p3-p) = 0. thus v1 dot v2 cross v3 = 0. expand
# and a,b,c become obvious
#
# | x-x1 y-y1 z-z1|
#det |x2-x1 y2-y1 z2-z1| = 0 --> x[(y2-y1)(z3-z1)-(y3-y1)(z2-z1)] + y[ ... = 0
# |x3-x1 y3-y1 z3-z1| a=(y2-y1)(z3-z1)-(y3-y1)(z2-z1) b=... c=...
#
#On Orientation:
#
#An interesting note. Given 3 points, M,N,P, if you feed them in the
#reverse order, like P,N,M, then the resulting a,b,c,d will be 'flipped'
#in sign. For example
#
#M,N,P = [0,0,1],[1,0,1],[0,1,1]
#plane(M,N,P) -> <0:0:1:-1>
#plane(P,N,M) -> <0:0:-1:1>
#
class plane:
def __init__( self, *args ):
if checktypes(point,*args) and len(args)==3:
p1,p2,p3 = args[0],args[1],args[2]
a,b,c,d=self.findequation(p1.x,p1.y,p1.z,p2.x,p2.y,p2.z,p3.x,p3.y,p3.z)
elif checkrationals(*args) and len(args)==4:
a,b,c,d=args[0],args[1],args[2],args[3]
else:
raise Exception('not implemented. plane needs pts or rationals a,b,c')
self.a,self.b,self.c,self.d=a,b,c,d
def findequation(self,x1,y1,z1,x2,y2,z2,x3,y3,z3 ):
x21,x31 = x2-x1, x3-x1
y21,y31 = y2-y1, y3-y1
z21,z31 = z2-z1, z3-z1
a = y21*z31-z21*y31
b = z21*x31-x21*z31
c = x21*y31-y21*x31
dx = x1*( z21*y31 - y21*z31 )
dy = y1*( x21*z31 - z21*x31 )
dz = z1*( y21*x31 - x21*y31 )
d = sum(dx,dy,dz)
return a,b,c,d
def __str__( self ):
return plane_txt( self )
def __getitem__( self, i ):
if i==0: return self.a
if i==1: return self.b
if i==2: return self.c
if i==3: return self.d
class lineseg:
def __init__( self, *args ):
if checktypes(point,*args) and len(args)==2:
self.p0,self.p1=args[0],args[1]
elif checkrationals(*args) and len(args)==4:
self.p0 = point(args[0],args[1])
self.p1 = point(args[2],args[3])
else: raise Exception('lineseg needs 2 points')
def __str__( self ):
return lineseg_txt( self )
def quadrance( self ):
return quadrance( self.p0, self.p1 )
def __getitem__( self, i ):
if i==0: return self.p0
if i==1: return self.p1
line_seg=lineseg
class triangle:
l0,l1,l2=None,None,None
p0,p1,p2=None,None,None
ls0,ls1,ls2=None,None,None
q0,q1,q2=None,None,None
s0,s1,s2=[None]*3
def __init__( self, *args ):
if checktypes( line, *args ) and len(args)==3:
self.init_from_lines( args[0],args[1],args[2] )
elif checktypes( point, *args ) and len(args)==3:
self.init_from_points( args[0],args[1],args[2] )
elif checktypes( vector, *args ) and len(args)==3:
self.init_from_points( point(args[0]),point(args[1]),point(args[2]) )
elif checkrationals( *args ) and len(args)==6:
p1=point(args[0],args[1])
p2=point(args[2],args[3])
p3=point(args[4],args[5])
self.init_from_points( p1, p2, p3 )
elif checkrationals( *args ) and len(args)==9:
p1=point(args[0],args[1],args[2])
p2=point(args[3],args[4],args[5])
p3=point(args[6],args[7],args[8])
self.init_from_points( p1, p2, p3 )
elif checktypes( triangle, *args ):
self.init_from_points( args[0][0],args[0][1],args[0][2] )
elif checktypes( list, *args ):
if len(args)==3 and len(args[0])==2 and len(args[1])==2 and len(args[2])==2:
p1 = point(args[0][0],args[0][1])
p2 = point(args[1][0],args[1][1])
p3 = point(args[2][0],args[2][1])
self.init_from_points( p1, p2, p3 )
elif len(args)==1:
t=triangle(*args[0])
self.init_from_points( t[0],t[1],t[2])
elif checktypes( tuple, *args ):
if len(args)==3 and len(args[0])==2 and len(args[1])==2 and len(args[2])==2:
p1 = point(args[0][0],args[0][1])
p2 = point(args[1][0],args[1][1])
p3 = point(args[2][0],args[2][1])
self.init_from_points( p1, p2, p3 )
else:
raise Exception('dont know how to build triangle'+str(args))
def init_from_lines( self, l0, l1, l2 ):
p0,p1,p2 = meet(l1,l2),meet(l0,l2),meet(l0,l1)
self.init_from_points( p0, p1, p2 )
def init_from_points( self, p0, p1, p2 ):
#l0,l1,l2 = line(p0,p1),line(p1,p2),line(p2,p0)
#ls0,ls1,ls2 = lineseg(p1,p2),lineseg(p0,p2),lineseg(p0,p1)
#q0,q1,q2=quadrance(ls0),quadrance(ls1),quadrance(ls2)
#s0,s1,s2=spread(l1,l2),spread(l0,l2),spread(l0,l1)
#self.l0,self.l1,self.l2=l0,l1,l2
self.p0,self.p1,self.p2=p0,p1,p2
#self.ls0,self.ls1,self.ls2=ls0,ls1,ls2
#self.q0,self.q1,self.q2=q0,q1,q2
#self.s0,self.s1,self.s2=s0,s1,s2
def __str__( self ):
return triangle_txt( self )
def __getitem__( self, i ):
if i==0: return self.p0
if i==1: return self.p1
if i==2: return self.p2
def __setitem__( self, i, value ):
if i==0: self.p0 = value
if i==1: self.p1 = value
if i==2: self.p2 = value
# Circles in Rational Geometry
#
# The first thing to think about is that we don't store the radius.
# Radius is not always rational, even on a circle that has two rational
# points. Instead store the squared radius, or 'radial quadrance', which
# is always rational between two rational points. For example, consider a
# circle centered at 0,0 with the rational point 1,1. It has an
# irrational radius of sqrt(2).
#
# in general, the hope is to avoid functions that require the use of
# radius. but in cases where we do need it, like plotting a graphical
# representation of the circle, we Approximate.
#
# What does this mean for rational points on the circle? Ah. A circle
# with irrational radius might not have infinitely many rational points
# on it, like a circle with rational radius does. But of course, the
# circle with rational radius has Radial Quadrance that is a perfect
# square, like 9 or 81/25. And there are infinitely many perfect squares
# So... our rational radius circles are rather plentiful in the plane
# are they not? But still, unless we confine ourselves to some highly
# restricted set of points in the plane, we will deal with irrational
# radius Circles. And so there might not be a nice rational
# paramterization of a circle with irrational radius? Or am I missing
# something?
#
# As an aside, it's a fun fact that if you take a rational triangle you
# get a rational circumcenter, but the circumradius might be irrational
# --> but you still get three rational points on it! Another aside: Note
# that for example, given two circles, radius e and pi, both with
# rational centers, we as a species don't know if they can be tangent or
# not. This is the state of affairs of modern mathematics. So many
# questions still to ponder!
#
# OK. So. How do we draw a circle though? We approximate the radius
# using a rational number and a square root algorithm, like that used in
# ancient Mesopotamia / Iraq / Babylon. Ideally we could draw a 'fuzzy
# line' for irrational radius circles but .. it's beyond my graphics
# system at the moment. Anyways. We find the apporximate rational
# radius, and then use a rational paramterization of the circle to build
# a rational approximation of the irrational radius circle.
#
#
# The next thing to think about is that in Chromogeometry, the shape of
# Red and Green circles is not a circle, it's a hyperbola. Red is
# x^2-y^2=radius^2=radial_quadrance.
#
# Note that the quadrance for a circle in this particular computer code
# doesn't have a color tied to it, kind of like the quadrance of the
# formulas like triple-quad dont have a color tied to them. Whether a
# circle is blue, red, or green is a matter of interpretation when it
# comes time to draw it or use it somewhere within this computer code
# package. . . the circle itself only has a center point and a radial
# quadrance, not a 'color'.
#
# So take these three circles. They have a center, 0,0 and a radial
# quadrance, 1, but there are three chromogeometry interpretations:
#
# x^2+y^2=1 x^2-y^2=1 2xy=1 blue, red, green
#
# They form one circle, one rectangluar hyperbola, and one 'sideways'
# hyperbola... But if you draw them, they appear to be tangent. Where?
# x=1,y=0 satisfies both blue and red formulas. So the blue circle touches
# the red circle (hyperbola) there. And green? x=1,y=1 satisfies both
# blue and green formulas, so the blue circle touches the green circle
# (hyperbola) there.
#
# How about green and red? y=1/2x, x^2-(1/2x)^2=1, x^4-. . uhm yeah.
# Plug it into Wolfram Alpha (TM) and you will see if/where Red and
# Green touch.
#
#
# Now... here is the clever bit. Note that Red Quadrance between points
# can be negative! That means that our red circles can have negative
# radial quadrance. But then what is the radius? Normally its
# sqrt(Quadrance). How can you 'approximate' sqrt(-7) with the
# Babylonian's algorithm? You can't. The root is what we moderns call an
# Imaginary Number.
#
# But hold on... I can draw a rational point, like 3,4, that has a
# negative Red Radial Quadrance between itself and 0,0. 3*3-4*4 = 9-16 =
# -7. What's the big deal? I can just draw the point at 3,4, thats not
# imaginary.
#
# Ah. But the 'shape' of the red circle then becomes 'flipped' over the
# line x=y (the 'red null line' / red axis). And so, you can have two
# red circles, one with radial quadrance 7 and another with -7, and they
# will not intersect! Instead, one will be the 'flipped' hyperbola of
# the other over the x=y line. One will have a irrational radius,
# sqrt(7), the other will have an imaginary irrational radius, sqrt(-7).
# If you draw both, you get a sort of 'x' shape with four 'nubs' coming in
# towards the origin.
#
# Lastly note that in addition to radial quadrance, we store squared
# curvature... curvature being the inverse of radius (1/radius).
#
# Note that this means we can have a circle with 'infinite radius'. We
# dont actually store infinity, instead we store curvature as 0 and
# radius as 'None'. And when radius is 0, we have 'infinite curvature' (None)
#
# So if Red and Green circles can have negative radial quadrance, and thus
# imaginary radius... what about blue circles?
#
# Interesting you should ask. No, they can't, if you consider the form
# of the blue quadrance equation. x^2+y^2=Q, then Q is never less than
# 0, unless x and/or y -itself- is imaginary. However... note that the
# radius itself can be negative... and that x and y will still satisfy
# the equation. But what is 'negative radius'???? What is the point of that?
#
# Ok. Let's say you are a Princess of Bohemia and you write letters to
# Renee Descartes. Lets say he tells you that with three 'kissing
# circles', you can find there are two possible choices for a fourth
# 'kisser'. In Algebra,
#
# 2*(k1^2+k2^2+k3^2+k4^2)=(k1+k2+k3+k4)^2 where k = curvature = 1/radius
#
# If you are given k1,k2,k3, then k4 is sqrt(some mess)... but recall that
# sqrt() can have two answers! Negative and Positive! If, by chance, the
# equation reduced to this:
#
# (k4-1)(k4+1/3)=0
#
# then k4 would have to be 1 or -1/3, meaning radius has to be 1 or -3,
# giving us radial quadrance of 1 or 9. Note that our points, x and y,
# can satisfy the blue quadrance formula even with a negative radius.
#
# But if radius is distance, how could x and y give a 'negative'
# distance? Maybe we can imagine that instead of distance, we have
# 'displacement', or a sort of 'vector' x,y, to our rational points on
# the circle, and it has a 'negative' magnitude? For example the vector
# (3,4) could be multiplied by -1, to give (-3,-4). The circle formed by
# these points is basically an 'inversion' of the ordinary circle,
# inversion through the origin 0,0. It looks exactly the same, it has
# all the same points eventually, but it is still there, a sort of twin
# shadow of the ordinary circle, right on top of it.
#
# Lastly, what about 'infinite radius', where curvature is zero?
#
# Recall the actualy full equation for a conic section
#
# ax^2+by^2+cx+dy+exy+f=0
#
# In that case, the curvature is 0 so the circle is a line.
# Therefore a=0,b=0,e=0, and then c,d,f become coefficients of a line equation:
#
# cx+dy+f = 0
#
#in that case, the circle will contain 'equation coefficients'.
#
class circle:
def __init__(self, *args):
if (len(args)<2): raise Exception('need center x,y and radial quadrance')
if checkrationals(*args) and len(args)>=3:
p = point(args[0],args[1])
self.init_from_point_and_radial_q( p, args[2] )
elif checktypes(point,args[0]) and checktypes(int,args[1]):
self.init_from_point_and_radial_q( args[0], args[1] )
elif checktypes(int,args[0]) and checktypes(point,args[1]):
self.init_from_point_and_radial_q( args[1], args[0] )
elif checktypes(point,args[0]) and checktypes(Fraction,args[1]):
self.init_from_point_and_radial_q( args[0], args[1] )
elif checktypes(Fraction,args[0]) and checktypes(point,args[1]):
self.init_from_point_and_radial_q( args[1], args[0] )
elif checktypes(tuple,args[0]) and checkrationals(args[1]):
p = point(args[0][0],args[0][1])
self.init_from_point_and_radial_q( p, args[1] )
elif checktypes(list,args[0]) and checkrationals(args[1]):
p = point(args[0][0],args[0][1])
self.init_from_point_and_radial_q( p, args[1] )
else: raise Exception('need center x,y and radial quadrance')
def init_from_point_and_radial_q( self, p, rq ):
self.center = p
self.radial_quadrance = rq
if (rq!=0): self.curvature_quadrance = Fraction(1,rq)
else: self.curvature_quadrance = None
# def init_curvature_zero( self,
def __str__( self ):
return circle_txt(self)
def __repr__( self ):
return circle_txt(self)
class sphere:
def __init__(self, *args):
if (len(args)<2): raise Exception('need center x,y,z and radial quadrance')
if checkrationals(*args) and len(args)>=4:
p = point(args[0],args[1],args[2])
self.init_from_point_and_radial_q( p, args[3] )
elif checktypes(point,args[0]) and checktypes(int,args[1]):
self.init_from_point_and_radial_q( args[0], args[1] )
elif checktypes(int,args[0]) and checktypes(point,args[1]):
self.init_from_point_and_radial_q( args[1], args[0] )
elif checktypes(point,args[0]) and checktypes(Fraction,args[1]):
self.init_from_point_and_radial_q( args[0], args[1] )
elif checktypes(Fraction,args[0]) and checktypes(point,args[1]):
self.init_from_point_and_radial_q( args[1], args[0] )
elif checktypes(tuple,args[0]) and checkrationals(args[1]):
p = point(args[0][0],args[0][1],args[0][2])
self.init_from_point_and_radial_q( p, args[1] )
elif checktypes(list,args[0]) and checkrationals(args[1]):
p = point(args[0][0],args[0][1],args[0][2])
self.init_from_point_and_radial_q( p, args[1] )
else: raise Exception('need center x,y,z and radial quadrance')
def init_from_point_and_radial_q( self, p, rq ):
self.center = p
self.radial_quadrance = rq
if (rq!=0): self.curvature_quadrance = Fraction(1,rq)
else: self.curvature_quadrance = None
# def init_curvature_zero( self,
def __str__( self ):
return sphere_txt(self)
def __repr__( self ):
return sphere_txt(self)
class quaternion:
def __init__(self,t,v):
self.t,self.v=t,v
######## formulas, functions, theorems, operations
#### determinant of vectors
## assume rows of matrix = vectors
## note there is no matrix definition here
def determinant( *args ):
if checktypes(vector,*args):
pass
elif checktypes(point,*args):
v1,v2=vector(args[0]),vector(args[1])
else:
raise Exception('this determinant() needs vector input as rows')
result = None
if len(args)<=1: raise Exception('not implemented')
if len(args)==2:
v1,v2 = args[0],args[1]
x1,y1,x2,y2 = v1.x,v1.y,v2.x,v2.y
result = x1*y2 - x2*y1
if len(args)==3:
v1,v2,v3 = args[0],args[1],args[2]
a,b,c = v1.x, v1.y, v1.z
d,e,f = v2.x, v2.y, v2.z
g,h,i = v3.x, v3.y, v3.z
result = a*e*i - a*f*h + b*f*g - b*d*i + c*d*h - c*e*g
if len(args)>3: raise Exception('not implemented')
return result
################# nullity
def is_null_point( A ):
v1 = vector(A.x,A.y)
if v1.dot(v1)==0: return True
return False
def is_null_vector( v ):
if v1.dot(v1)==0: return True
return False
def is_null_line_from_points( p1, p2 ):
v1 = vector(p1)
v2 = vector(p2)
if is_null_vector( v2-v1 ): return True
return False
def is_null_line( l ):
a,b,c=l.a,l.b,l.c
# ax+by+c = 0 ---> y = ( -c -ax ) / b
# ax+by+c = 0 ---> x = ( -c -by ) / a
if b!=0:
p1x = 0
p1y = Fraction( -l.c -l.a * p1x , l.b )
p2x = 1
p2y = Fraction( -l.c -l.a * p2x , l.b )
elif a!=0:
p1y = 0
p1x = Fraction( -l.c -l.b * p1y , l.a )
p2y = 1
p2x = Fraction( -l.c -l.b * p2y , l.a )
else:
raise Exception('dont know what to do with line, a&b = 0')
A1=point(p1x,p1y)
A2=point(p2x,p2y)
v1=vector(A1)
v2=vector(A2)
if is_null_vector( v2-v1 ): return True
return False
def is_null_triangle( t ):
if is_null_line( t.l0 ): return True
if is_null_line( t.l1 ): return True
if is_null_line( t.l2 ): return True
return False
def is_null( *args ):
if checktype( line, args[0] ): return is_null_line( args[0] )
if checktype( triangle, args[0] ): return is_null_triangle( args[0] )
if checktype( point, args[0] ): return is_null_point( args[0] )
if checktype( vector, args[0] ): return is_null_vector( args[0] )
if len(args)>1:
if checktype( point, args[0] ) and checktype(point,args[1]):
return is_null_line_from_points( args[0], args[1] )
################# altitudes
# note, line is 3 Rationals: [a:b:c] where ax + by + c = 0
def blue_altitude_line_point( l, A ):
a,b,c = l.a,l.b,l.c
x0,y0 = A.x,A.y
return line(b,-a,-b*x0+a*y0)
def red_altitude_line_point( l, A ):
a,b,c = l.a,l.b,l.c
x0,y0 = A.x,A.y
return line(b,a,-b*x0-a*y0)
def green_altitude_line_point( l, A ):
a,b,c = l.a,l.b,l.c
x0,y0 = A.x,A.y
return line(a,-b,-a*x0+b*y0)
def blue_altitude( *args ):
if len(args<2): raise Exception('need line and point')
if checktypes(line, args[0]) and checktypes(point, args[1]):
l,A = args[0],args[1]
elif checktypes(point, args[0]) and checktypes(line, args[1]):
l,A = args[1],args[0]
return blue_altitude( l, A )
def red_altitude( *args ):
if len(args<2): raise Exception('need line and point')
if checktypes(line, args[0]) and checktypes(point, args[1]):
l,A = args[0],args[1]
elif checktypes(point, args[0]) and checktypes(line, args[1]):
l,A = args[1],args[0]
else:
raise Exception('need line and point')
return red_altitude( l, A )
def green_altitude( *args ):
if len(args<2): raise Exception('need line and point')
if checktypes(line, args[0]) and checktypes(point, args[1]):
l,A = args[0],args[1]
elif checktypes(point, args[0]) and checktypes(line, args[1]):
l,A = args[1],args[0]
else:
raise Exception('need line and point')
return green_altitude( l, A )
def blue_foot( line0, point0 ):
a,b,c=line0.a,line0.b,line0.c
x0,y0=point0.x,point0.y
fx=Fraction(sqr(b)*x0-a*b*y0-a*c,blueq(a,b))
fy=Fraction(-a*b*x0+sqr(a)*y0-b*c,blueq(a,b))
return point(fx,fy)
def red_foot( line0, point0 ):
a,b,c=line0.a,line0.b,line0.c
x0,y0=point0.x,point0.y
fx=Fraction(-sqr(b)*x0-a*b*y0-a*c,redq(a,b))
fy=Fraction(a*b*x0+sqr(a)*y0+b*c,redq(a,b))
return point(fx,fy)
def green_foot( line0, point0 ):
a,b,c=line0.a,line0.b,line0.c
x0,y0=point0.x,point0.y
fx=Fraction(a*x0-b*y0-c,2*a)
fy=Fraction(-a*x0+b*y0-c,2*b)
return point(fx,fy)
###### perpendicular
# Question - can there be multiple perpendiculars for one vector?
# answer - yes... do we return both though?
def blue_find_perpendicular_vector( v1 ):
return vector(-v1.y,v1.x)
def red_find_perpendicular_vector( v1 ):
return vector(v1.y,v1.x)
def green_find_perpendicular_vector( v1 ):
return vector(-v1.x,v1.y)
find_perpendicular_vector=blue_find_perpendicular_vector
def blue_find_perpendicular_line( l1 ):
return line(-l1.b,l1.a,l1.c)
def red_find_perpendicular_line( l1 ):
return line(l1.b,l1.a,l1.c)
def green_find_perpendicular_line( l1 ):
return line(-l1.a,l1.b,l1.c)
find_perpendicular_line=blue_find_perpendicular_line
def blue_is_perpendicular_for_vectors( v1, v2 ):
return v1.x*v2.x + v1.y*v2.y == 0
def red_is_perpendicular_for_vectors( v1, v2 ):
return v1.x*v2.x - v1.y*v2.y == 0
def green_is_perpendicular_for_vectors( v1, v2 ):
return v1.x*v2.y + v1.y*v2.x == 0
def blue_is_perpendicular_for_linesegs( ls1, ls2 ):
v1,v2=vector(ls1[1]-ls1[0]),vector(ls2[1]-ls2[0])
return blue_is_perpendicular_for_vectors(v1,v2)
def red_is_perpendicular_for_linesegs( ls1, ls2 ):
v1,v2=vector(ls1[1]-ls1[0]),vector(ls2[1]-ls2[0])
return red_is_perpendicular_for_vectors(v1,v2)
def green_is_perpendicular_for_linesegs( ls1, ls2 ):
v1,v2=vector(ls1[1]-ls1[0]),vector(ls2[1]-ls2[0])
return green_is_perpendicular_for_vectors(v1,v2)
def blue_is_perpendicular_for_lines( l1,l2 ):
return l1.a*l2.a+l1.b*l2.b==0
def red_is_perpendicular_for_lines( l1,l2 ):
return l1.a*l2.a-l1.b*l2.b==0
def green_is_perpendicular_for_lines( l1,l2 ):
return l1.a*l2.b+l1.b*l2.a==0
def blue_is_perpendicular( *args ):
if checktypes(line,*args) and len(args)==2:
return blue_is_perpendicular_for_lines(args[0],args[1])
elif checktypes(lineseg,*args) and len(args)==2:
return blue_is_perpendicular_for_linesegs(args[0],args[1])
elif checktypes(vector,*args) and len(args)==2:
return blue_is_perpendicular_for_vectors(args[0],args[1])
else: raise Exception('unknown args to blue_is_perpendicular',args)
def red_is_perpendicular( *args ):
if checktypes(line,*args) and len(args)==2:
return red_is_perpendicular_for_lines(args[0],args[1])
elif checktypes(lineseg,*args) and len(args)==2:
return red_is_perpendicular_for_linesegs(args[0],args[1])
elif checktypes(vector,*args) and len(args)==2:
return red_is_perpendicular_for_vectors(args[0],args[1])
else: raise Exception('unknown args to red_is_perpendicular',args)
def green_is_perpendicular( *args ):
if checktypes(line,*args) and len(args)==2:
return green_is_perpendicular_for_lines(args[0],args[1])
elif checktypes(lineseg,*args) and len(args)==2:
return green_is_perpendicular_for_linesegs(args[0],args[1])
elif checktypes(vector,*args) and len(args)==2:
return green_is_perpendicular_for_vectors(args[0],args[1])
else: raise Exception('unknown args to green_is_perpendicular',args)
def is_blue_perpendicular( *args ): return blue_is_perpendicular( *args )
def is_red_perpendicular( *args ): return red_is_perpendicular( *args )
def is_green_perpendicular( *args ): return green_is_perpendicular( *args )
########## parallel
def red_parallel_vectors( v1, v2 ):
raise Exception("not implemented ")
def green_parallel_vectors( v1, v2 ):
raise Exception("not implemented ")
def blue_parallel_vectors( v1, v2 ):
return v1.x*v2.y - v2.x*v1.y == 0
def red_parallel_linesegs( v1, v2 ):
raise Exception("not implemented ")
def green_parallel_linesegs( v1, v2 ):
raise Exception("not implemented ")
def blue_parallel_linesegs( v1, v2 ):
raise Exception("not implemented ")
def red_parallel_lines( v1, v2 ):
raise Exception("not implemented ")
def green_parallel_lines( v1, v2 ):
raise Exception("not implemented ")
def blue_parallel_lines( v1, v2 ):
raise Exception("not implemented ")
def parallel( *args, **kwargs ):
if 'color' in kwargs.keys(): color=kwargs['color']
else: color='blue'
if color=='red':
parallel_vectors = red_parallel_vectors
parallel_lines = red_parallel_lines
parallel_linesegs = red_parallel_linesegs
elif color == 'green':
parallel_vectors = green_parallel_vectors
parallel_lines = green_parallel_lines
parallel_linesegs = green_parallel_linesegs
elif color == 'blue':
parallel_vectors = blue_parallel_vectors
parallel_lines = blue_parallel_lines
parallel_linesegs = blue_parallel_linesegs
if isinstance(args[0],point): # and isinstance(args[1],point):
raise Exception( "not implemented")
if isinstance(args[0],line) and isinstance(args[1],line):
return parallel_lines( args[0], args[1] )
if isinstance(args[0],lineseg) and isinstance(args[1],lineseg):
return parallel_linesegs( args[0], args[1] )
if isinstance(args[0],vector) and isinstance(args[1],vector):
return parallel_vectors( args[0], args[1] )
return None
############### quadrance
def red_quadrance_points( p1, p2 ):
if hasattr(p2,'z') and hasattr(p1,'z'):
raise Exception(" 3d red quadrance not implemented ")
return sqr( p2.x-p1.x ) - sqr( p2.y-p1.y )
def green_quadrance_points( p1, p2 ):
if hasattr(p2,'z') and hasattr(p1,'z'):
raise Exception(" 3d green quadrance not implemented ")
return 2*( p2.x-p1.x ) * ( p2.y-p1.y )
def blue_quadrance_points( p1, p2 ):
q = sqr( p2.x-p1.x ) + sqr( p2.y-p1.y )
if hasattr(p2,'z') and hasattr(p1,'z'): q += sqr( p2.z-p1.z )
return q
def blue_quadrance_coords(x1,y1,x2,y2):
return sqr(x1-x2)+sqr(y2-y1)
def red_quadrance_coords(x1,y1,x2,y2):
return sqr(x1-x2)-sqr(y2-y1)