Unit circle visualizer - OldMoses - 07-28-2022
I have always had an issue remembering what trig function does what. I'm doing a graphic visualization aid so I can finally get my gourd wrapped around it all. I find it easier to absorb this sort of thing if I have a visual aid. It has some math issues to be ironed out, some rather clumsy code and I have more things I'm thinking of adding, but generally is doing what I wanted. I zipped the whole thing since it's relying on a library, but here's the base code.
EDIT: 7-29-2022 Upload changes
8-13-2022 added bolt circle calculator feature
Code: (Select All) 'Unit Circle.bas
_TITLE "Unit Circle Visualizer"
'MAIN MODULE
'Type declarations
TYPE V2
x AS INTEGER
y AS INTEGER
END TYPE
TYPE U2
x AS SINGLE
y AS SINGLE
END TYPE
TYPE object
x AS INTEGER
y AS INTEGER
r AS DOUBLE
END TYPE
'Global variables
DIM SHARED baselayer AS LONG
DIM SHARED ang(24) AS object
'Constants
CONST TwoPI = 2 * _PI
'Display Setup
SCREEN _NEWIMAGE(1024, 512, 32)
WINDOW (-512, 256)-(512, -256)
Draw_Base
Main_Loop
'DATA SECTION
DATA 0,0,"pi/12"
DATA 0,0,"pi/6",0,0,"pi/4"
DATA 0,0,"pi/3",0,0,"5(pi)/12"
DATA -16,0,"pi/2",0,0,"7(pi)/12"
DATA 0,0,"2(pi)/3",0,0,"3(pi)/4"
DATA 0,0,"5(pi)/6",0,0,"11(pi)/12"
DATA 0,8,"(180) pi",0,0,"13(pi)/12"
DATA 0,0,"7(pi)/6",0,0,"5(pi)/4"
DATA 0,0,"4(pi)/3",0,0,"17(pi)/12"
DATA 24,0,"3(pi)/2",0,0,"19(pi)/12"
DATA 0,0,"5(pi)/3",0,0,"7(pi)/4"
DATA 0,0,"11(pi)/6",0,0,"23(pi)/12"
DATA 0,8,"2(pi) (0)"
'SUBROUTINE SECTION
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Bolt_Circ
WINDOW
Dialog_Box "Bolt Circle", 512, 512, 0, &HFF00FF00, &HFFFFFFFF, "c"
_PRINTMODE _KEEPBACKGROUND
LOCATE 4, _SHR(_WIDTH(0), 4) - 6
INPUT "# of holes: ", h%
LOCATE 5, _SHR(_WIDTH(0), 4) - 6
INPUT "radius: ", r!
_PRINTSTRING (_SHR(_WIDTH(0), 1) - 112, 140), "Hole coordinates relative to..."
DIM lb$(6)
lb$(1) = "Center": lb$(2) = "Quad 1": lb$(3) = "Quad 2"
lb$(4) = "Quad 3": lb$(5) = "Quad 4": lb$(6) = "From 1"
ref% = Chs_Key_Button%("c1234f", "h", 160, 6, 70, 32, 10, _SHR(_WIDTH(0), 1), lb$())
WINDOW (-512, 256)-(512, -256)
Clear_MB 1
IF ref% = -1 THEN EXIT SUB
_PUTIMAGE , baselayer
LINE (295, 205)-(500, 168 - (h% * 16) - 48), &HFF7F7FFF, BF
COLOR &HFF010101
_PRINTSTRING (PMAP(300, 0), PMAP(200, 1)), "Bolt Circle Coordinates"
_PRINTSTRING (PMAP(300, 0), PMAP(184, 1)), " X Y"
DIM bolt(h% - 1) AS object
DIM drill(h% - 1) AS U2
maxx! = 0: maxy! = 0
FOR x% = 0 TO UBOUND(bolt)
angle! = ((TwoPI) / h%) * x%
bolt(x%).r = angle!
bolt(x%).x = COS(angle!) * 200
bolt(x%).y = SIN(angle!) * 200
drill(x%).x = CINT(COS(angle!) * (r! * 1000)) / 1000
drill(x%).y = CINT(SIN(angle!) * (r! * 1000)) / 1000
IF x% > 0 THEN
maxx! = maxx! + (-(ABS(drill(x%).x) - maxx!) * (ABS(drill(x%).x) > maxx!))
END IF
maxy! = maxy! + (-(ABS(drill(x%).y) - maxy!) * (ABS(drill(x%).y) > maxy!))
NEXT x%
'find limits for ref%, modify maxx! & maxy! for quad or hole #1
SELECT CASE ref%
CASE IS = 1: maxx! = 0: maxy! = 0 ' no change
CASE IS = 2: maxx! = drill(0).x ' quad 1
CASE IS = 3: maxx! = -maxx! ' quad 2
CASE IS = 4: maxx! = -maxx!: maxy! = -maxy! ' quad 3
CASE IS = 5: maxx! = drill(0).x: maxy! = -maxy! ' quad 4
CASE IS = 6: maxx! = drill(0).x: maxy! = drill(0).y ' subtract hole 1 position
END SELECT
'reference crosshairs
LINE (-512, maxy! / r! * 200)-(1023, maxy! / r! * 200), &HFFFF0000, , 15 'horizontal
LINE (maxx! / r! * 200, 256)-(maxx! / r! * 200, -255), &HFFFF0000, , 15 'vertical
FOR x% = 0 TO UBOUND(bolt)
'apply origin modifier
drill(x%).x = drill(x%).x - maxx!: drill(x%).y = drill(x%).y - maxy!
hole& = _NEWIMAGE(40, 40, 32)
_DEST hole&
CLS
_CLEARCOLOR _RGB(0, 0, 0)
FCirc 20, 20, 19, &HFF00FF00
_PRINTMODE _KEEPBACKGROUND
COLOR &HFF010101
_PRINTSTRING (8, 12), STR$(x% + 1)
_DEST 0
_PUTIMAGE (bolt(x%).x - 20, bolt(x%).y + 20), hole&, 0
_FREEIMAGE hole&
COLOR &HFF010101
_PRINTSTRING (PMAP(300, 0), PMAP(168 - (x% * 16), 1)), STR$(x% + 1)
_PRINTSTRING (PMAP(Align%(348, drill(x%).x), 0), PMAP(168 - (x% * 16), 1)), STR$(drill(x%).x)
_PRINTSTRING (PMAP(Align%(424, drill(x%).y), 0), PMAP(168 - (x% * 16), 1)), STR$(drill(x%).y)
NEXT x%
chrd! = CINT(2 * (r! * 1000) * SIN(angle! / 2)) / 1000
_PRINTSTRING (PMAP(300, 0), PMAP(168 - ((x% + 1) * 16), 1)), "Chord= " + STR$(chrd!)
COLOR &HFFFFFFFF
Press_Click
END SUB 'Bolt_Circ
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Details (var AS object, hand AS _BYTE)
hd$ = Fix_Float(CDBL(_R2D(var.r)), 2) + CHR$(248) + " Properties"
sn$ = LEFT$(Fix_Float$(SIN(var.r), 4), 6)
cs$ = LEFT$(Fix_Float$(COS(var.r), 4), 6)
IF hand THEN
IF var.r = _PI / 2 OR var.r = (3 * _PI) / 2 THEN
tn$ = "undefined"
ELSE
tn$ = LEFT$(Fix_Float$(TAN(var.r), 4), 6)
END IF
ELSE
IF var.x = 0 THEN
tn$ = "undefined"
ELSE
tn$ = LEFT$(Fix_Float$(TAN(var.r), 4), 6)
END IF
END IF
rd$ = LEFT$(Fix_Float$(var.r, 8), 8)
LINE (295, 205)-(500, -100), &HFF7F7FFF, BF
c& = _DEFAULTCOLOR
COLOR &HFF000000
_PRINTMODE _KEEPBACKGROUND
_PRINTSTRING (PMAP(300, 0), PMAP(200, 1)), hd$
_PRINTSTRING (PMAP(300, 0), PMAP(168, 1)), "Sin: " + sn$ 'STR$(SIN(var.r))
_PRINTSTRING (PMAP(300, 0), PMAP(152, 1)), "Cos: " + cs$ 'STR$(COS(var.r))
_PRINTSTRING (PMAP(300, 0), PMAP(136, 1)), "Tan: " + tn$
_PRINTSTRING (PMAP(300, 0), PMAP(112, 1)), "Radians= " + rd$
CIRCLE (0, 0), 200, &H3F00FF00, 0, var.r
Press_Click
COLOR c&
_PRINTMODE _FILLBACKGROUND
END SUB 'Details
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Draw_Base
CLS
'(xpos AS INTEGER, ypos AS INTEGER, xsiz AS INTEGER, ysiz AS INTEGER, label AS STRING, high AS INTEGER, col AS _UNSIGNED LONG)
Con_Blok PMAP(0, 2), PMAP(120, 3), 100, 32, "Enter Angle", 0, &HFF7F7F7F
Con_Blok PMAP(0, 2), PMAP(168, 3), 100, 32, "Enter SIN", 0, &HFFFF3F3F
Con_Blok PMAP(0, 2), PMAP(216, 3), 100, 32, "Enter COS", 0, &HFF3F3FFF
Con_Blok PMAP(924, 2), PMAP(120, 3), 100, 32, "Bolt Circle", 0, &HFF7F7F7F
DO UNTIL i% > 500 ' Grid loop
IF i% = 0 THEN
c& = &H7FFF0000: tk% = 5
DO UNTIL j% > 500 ' Axis tick loop
j% = j% + 20
LINE (-tk%, j%)-(tk%, j%), c& 'vert
LINE (-tk%, -j%)-(tk%, -j%), c& 'vert
LINE (j%, -tk%)-(j%, tk%), c& 'horiz
LINE (-j%, -tk%)-(-j%, tk%), c& 'horiz
LOOP
END IF
c& = &H7F9F9F9F * (i% > 0) - &HFF0F3FFF * (i% = 0)
LINE (i%, -256)-(i%, 256), c&
IF i% > 0 THEN LINE (-i%, -256)-(-i%, 256), c&
LINE (-512, i%)-(512, i%), c&
IF i% > 0 THEN LINE (-512, -i%)-(512, -i%), c&
i% = i% + 100
LOOP ' end: grid loop
CIRCLE (0, 0), 200, &HFFFFFFFF ' unit circle
FOR sp# = _PI / 12 TO TwoPI STEP _PI / 12 ' common angle points & leaders
q% = q% + 1
ang(q%).r = sp#: ang(q%).x = COS(sp#) * 200: ang(q%).y = SIN(sp#) * 200
FCirc ang(q%).x, ang(q%).y, 3, &HFFFF00FF
LINE (COS(sp#) * 20, SIN(sp#) * 20)-(COS(sp#) * 215, SIN(sp#) * 215), &H2FFFFFFF
READ x%, y%, l$
sb% = LEN(l$) * 8 * (SGN(COS(sp#)) < 0)
su% = -16 * (SGN(SIN(sp#)) > 0) ' setup for quad I & II
_PRINTSTRING (PMAP(COS(sp#) * 220 + sb% + x%, 0), PMAP(SIN(sp#) * 220 + su% + y%, 1)), l$
NEXT sp#
_PRINTSTRING (0, 0), "Quadrant II (-, +)"
_PRINTSTRING (0, 496), "Quadrant III (-, -)"
_PRINTSTRING (887, 0), "Quadrant I (+, +)"
_PRINTSTRING (879, 496), "Quadrant IV (+, -)"
_PRINTSTRING (PMAP(-12, 0), PMAP(252, 1)), "(90)"
_PRINTSTRING (PMAP(-20, 0), PMAP(-236, 1)), "(270)"
_PRINTSTRING (0, 416), "SOH:"
_PRINTSTRING (0, 432), "CAH:"
_PRINTSTRING (0, 448), "TOA:"
FOR y% = 416 TO 448 STEP 16
_PRINTSTRING (72, y%), "="
_PRINTSTRING (120, y%), "/"
NEXT y%
_PRINTSTRING (631, 496), "Esc to quit"
'represent (SIN, COS) here
COLOR &HFFFF0000
_PRINTSTRING (160, 0), "+SIN"
_PRINTSTRING (168, 496), "-SIN"
_PRINTSTRING (799, 0), "+SIN"
_PRINTSTRING (791, 496), "-SIN"
_PRINTSTRING (160, 16), "+CSC"
_PRINTSTRING (168, 480), "-CSC"
_PRINTSTRING (799, 16), "+CSC"
_PRINTSTRING (791, 480), "-CSC"
_PRINTSTRING (40, 416), "Sin"
_PRINTSTRING (88, 416), "Opp"
_PRINTSTRING (88, 448), "Opp"
COLOR &HFF0000FF
_PRINTSTRING (200, 0), "-COS"
_PRINTSTRING (208, 496), "-COS"
_PRINTSTRING (839, 0), "+COS"
_PRINTSTRING (831, 496), "+COS"
_PRINTSTRING (200, 16), "-SEC"
_PRINTSTRING (208, 480), "-SEC"
_PRINTSTRING (839, 16), "+SEC"
_PRINTSTRING (831, 480), "+SEC"
_PRINTSTRING (40, 432), "Cos"
_PRINTSTRING (88, 432), "Adj"
_PRINTSTRING (136, 448), "Adj"
COLOR &HFFFF00FF
_PRINTSTRING (240, 0), "-TAN"
_PRINTSTRING (248, 496), "+TAN"
_PRINTSTRING (759, 0), "+TAN"
_PRINTSTRING (751, 496), "-TAN"
_PRINTSTRING (240, 16), "-COT"
_PRINTSTRING (248, 480), "+COT"
_PRINTSTRING (759, 16), "+COT"
_PRINTSTRING (751, 480), "-COT"
_PRINTSTRING (40, 448), "Tan"
COLOR &HFF00FF00
_PRINTSTRING (136, 416), "Hyp"
_PRINTSTRING (136, 432), "Hyp"
COLOR &HFFFFFFFF
baselayer = _COPYIMAGE(0)
END SUB 'Draw_Base
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Main_Loop
DO ' DISPLAY REFRESH LOOP
DO ' INPUT LOOP
DIM AS V2 m, inpnt
DIM inang AS object
CLS
_PUTIMAGE , baselayer
k$ = INKEY$ ' get keypresses- set in% when valid
IF k$ <> "" THEN
IF k$ = CHR$(27) THEN SYSTEM ' exit program
END IF
ms% = MBS ' get mouse clicks
m.x = PMAP(_MOUSEX, 2)
m.y = PMAP(_MOUSEY, 3)
Tracer m
IF ms% AND 1 THEN ' if left click
Clear_MB 1
_AUTODISPLAY
FOR g% = 1 TO 24
IF _HYPOT(ang(g%).x - m.x, ang(g%).y - m.y) < 5 THEN
m.x = ang(g%).x: m.y = ang(g%).y
_PUTIMAGE , baselayer
Tracer m
Details ang(g%), 0
END IF
NEXT g%
IF ABS(_MOUSEX - 50) < 50 THEN
IF ABS(_MOUSEY - 136) < 16 THEN ' Enter angle code
LOCATE 9, 15
INPUT ": ", a!
_PUTIMAGE , baselayer
inang.r = _D2R(a!)
inpnt.x = 200 * COS(inang.r)
inpnt.y = 200 * SIN(inang.r)
Tracer inpnt
Details inang, -1
END IF
IF ABS(_MOUSEY - 184) < 16 THEN ' Enter SIN code
LOCATE 12, 15
INPUT ": ", s!
c! = SQR(1 - s! ^ 2) ' get COS
_PUTIMAGE , baselayer
LOCATE 12, 15
PRINT s!
LOCATE 15, 15
PRINT c!
inpnt.x = 200 * c!
inpnt.y = 200 * s!
Tracer inpnt
LOCATE 9, 15
PRINT _R2D(_ATAN2(inpnt.y, inpnt.x))
Press_Click
END IF
IF ABS(_MOUSEY - 232) < 16 THEN ' Enter COS code
LOCATE 15, 15
INPUT ": ", c!
s! = SQR(1 - c! ^ 2) ' get SIN
_PUTIMAGE , baselayer
LOCATE 15, 15
PRINT c!
LOCATE 12, 15
PRINT s!
inpnt.x = 200 * c!
inpnt.y = 200 * s!
Tracer inpnt
LOCATE 9, 15
PRINT _R2D(_ATAN2(inpnt.y, inpnt.x))
Press_Click
END IF
ELSEIF ABS(_MOUSEX - 999) < 50 THEN ' right side buttons
IF ABS(_MOUSEY - 136) < 16 THEN
Bolt_Circ
END IF
END IF
in% = -1
END IF
'IF ms% AND 2 THEN ' if right click
' Clear_MB 2
' 'right click code here
' in% = -1
'END IF
_DISPLAY
_LIMIT 30
LOOP UNTIL in% ' END: INPUT LOOP
'refresh main program displays
_DISPLAY
LOOP ' END: DISPLAY REFRESH LOOP
END SUB 'Main_Loop
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Tracer (p AS V2)
DIM AS V2 ci, tn
LINE (0, 0)-(p.x, p.y), &H7F7F7F7F ' mouse leader line
ci = p: Vec_Norm ci, 200 ' circle intersection point
LINE (ci.x, 0)-(ci.x, ci.y), &HFFFF0000 ' red sine? line
IF ABS(ci.x) > 15 AND ABS(ci.y) > 15 THEN ' if room draw ortho box
LINE (ci.x - 10 * SGN(ci.x), 0)-(ci.x - 10 * SGN(ci.x), 10 * SGN(ci.y)), &HFFFF0000
LINE (ci.x - 10 * SGN(ci.x), 10 * SGN(ci.y))-(ci.x, 10 * SGN(ci.y)), &HFFFF0000
END IF
LINE (0, ci.y)-(ci.x, ci.y), &HFF0000FF, , 63 ' blue cosine? line
tn = ci: Vec_Orth tn: Vec_Norm tn, 550 ' compute tangent line
LINE (ci.x + tn.x, ci.y + tn.y)-(ci.x - tn.x, ci.y - tn.y), &HFFFF00FF 'purple tangent line
LINE (0, 0)-(ci.x, ci.y), &HFF00FF00 ' green radius line
COLOR &HFFFF0000
_PRINTSTRING (0, 32), "SIN= " + Fix_Float$((ci.y / _HYPOT(ci.x, ci.y)), 6)
COLOR &HFF0000FF
_PRINTSTRING (0, 48), "COS= " + Fix_Float$((ci.x / _HYPOT(ci.x, ci.y)), 6)
COLOR &HFFFF00FF
IF ci.x = 0 THEN
_PRINTSTRING (0, 64), "TAN= undefined"
ELSE
_PRINTSTRING (0, 64), "TAN= " + Fix_Float((ci.y / ci.x), 6)
END IF
COLOR &HFFFFFFFF
IF ci.y < 0 THEN
rad = TwoPI - ABS(_ATAN2(ci.y, ci.x))
ELSE
rad = _ATAN2(ci.y, ci.x)
END IF
_PRINTSTRING (0, 80), "Radians= " + Fix_Float$(rad, 10)
_PRINTSTRING (0, 96), "Degrees= " + Fix_Float$(_R2D(rad), 10)
END SUB 'Tracer
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Vec_Norm (var AS V2, scalar AS INTEGER) 'normalize and scale a 2D vector
mag! = _HYPOT(var.x, var.y)
IF mag! = 0 THEN
var.x = 0: var.y = 0
ELSE
var.x = (var.x / mag!) * scalar
var.y = (var.y / mag!) * scalar
END IF
END SUB 'Vec_Norm
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
SUB Vec_Orth (var AS V2) 'return 2D vector (var) as an orthogonal vector
x% = var.x: y% = var.y
var.x = -y%
var.y = x%
END SUB 'Vec_Orth
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
FUNCTION Fix_Float$ (x##, dec AS INTEGER)
'fix_float is appropriately named as it needs fixing...
bs$ = STR$(x##) ' string of input number x##
ex = INSTR(bs$, "D") + INSTR(bs$, "E")
IF ex <> 0 THEN ' an exponential has been thrown
pwr = VAL(MID$(bs$, ex + 3))
'use pwr to loop pwr-1 0's after a "." then use left$(bs,1)
n$ = " ."
FOR z% = 1 TO pwr - 1
n$ = n$ + "0"
NEXT z%
Fix_Float$ = n$ + LEFT$(_TRIM$(bs$), 1)
ELSE ' a decimal number has been thrown
pnt = INSTR(bs$, ".")
IF pnt = 0 THEN
Fix_Float$ = bs$
ELSE
Fix_Float$ = LEFT$(bs$, pnt + dec)
END IF
END IF
END FUNCTION 'Fix_Float##
'²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
'$include: 'omlib.bm'
RE: Unit circle visualizer - OldMoses - 08-13-2022
A few years back, I had a project to do some jig boring of indexing plates on my tired and ancient milling machine. No CNC, no DRO, just a copy of "Machinery's Handbook" a calculator and some manual handcranks with lots of lash in the screws. It was a laborious job, for an old dude that needs reading glasses. So it occurred to me that this little utility could become a real shop tool with the addition of a bolt circle calculator SUB. It wasn't too challenging, all things considered.
Click on the bolt circle button and a no frills dialog opens querying for the number of holes and the radius of the circle. Then a button click on where the user would prefer the reference position to be. The result is a listing of the holes and their X & Y axis positions from the reference datum, in thousandths. That is followed by a chordal measurement between the hole centers to help with determining what size hole can be drilled without weakening the part. A red dashed crosshair marks the reference point on the screen.
The code and zip is updated in the original post.
|