Author Topic: Parametric Clock  (Read 4326 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Parametric Clock
« on: November 21, 2020, 12:16:29 pm »
Alright, this is prolly the last clock I'll make for a while. You've seen the idea twice this week already from me alone, with plenty of others doing their clock things. Tis the season I guess.

So this clock draws a "face" (subject to change), and then draws the "hands" in real time. The hour hand is always anchored at the center of the window, and points as an hour hand does. The minute hand is tied to the end of the hour hand, but still pivots to follow the orientation of a standard clock, pointing to the correct minute. So too does the second hand, but this attaches to the end of the minute hand. In so-called "12-hour Mode", this ends up looking like a regular clock as shown.

12hour.png

Pay close attention to the background, or so-called clock face. I discussed heavily (on a nearby earth rotations thread) why the looping structure only has 11-fold symmetry on a 12-hour clock face, which I hope is clear by now. The way I generate the clock face is like this: Before the main loop, start with a black background and run the clock through an entire cycle, only plotting the "second" hand. Keep that image as the background, you can see plenty of "nodes" where the second hand overlaps itself at different times. Needless to say, the minute hand traces along the nodes.

With this model, we can play with non-12-hour clocks. Suppose we maintain the idea that there are 60 minutes per hour, and 60 seconds per minute, but let the number of hours on the clock vary. Of course, the most familiar version of this is 24-hour military time:

24hour.png

Fair enough, but how about non-typical clocks? Indeed, it all works. Using the keyboard (LEFT/RIGHT arrows), I can select from many modes, for instance, a 5-hour clock:

5hour.png

Oh yeah, since you boys (bplus, vince) were talking cardioid clocks, set a two-hour mode for a true cardioid:

cardioid.png

If you go all the way down to a one-hour cycle, you basically just have a 60-minute timer. The hour hand and minute hand lock phase:

onehour.png

... And of course, the code:

EDIT: UPARROW/DOWNARROW enable/disable stopwatch mode.
EDIT2: Random colors, removed 24-mode

Code: QB64: [Select]
  1.  
  2. _TITLE "Parametric Clock"
  3.  
  4. DIM SHARED MainScreen AS LONG
  5. DIM SHARED BackScreen AS LONG
  6. MainScreen = _NEWIMAGE(600, 600, 32)
  7. BackScreen = _NEWIMAGE(600, 600, 32)
  8. SCREEN MainScreen
  9.  
  10.  
  11. pi = 4 * ATN(1)
  12. phi = (1 + SQR(5)) / 2
  13.  
  14. TYPE TimeValue
  15.     Hour AS INTEGER
  16.     Minute AS INTEGER
  17.     Second AS DOUBLE
  18.  
  19. TYPE Vector
  20.     x AS DOUBLE
  21.     y AS DOUBLE
  22.  
  23. TYPE ClockHand
  24.     Center AS Vector
  25.     HandPosition AS Vector
  26.     Length AS DOUBLE
  27.     Angle AS DOUBLE
  28.     Shade AS _UNSIGNED LONG
  29.  
  30. DIM SHARED TheTime AS TimeValue
  31. DIM SHARED HourHand AS ClockHand
  32. DIM SHARED MinuteHand AS ClockHand
  33. DIM SHARED SecondHand AS ClockHand
  34.  
  35. DIM SHARED ModeList(12) AS INTEGER
  36. DIM SHARED TimeShift AS DOUBLE
  37. TimeShift = 0
  38.  
  39. HourHand.Center.x = 0
  40. HourHand.Center.y = 0
  41. HourHand.Length = 150
  42. SecondHand.Length = HourHand.Length / (phi ^ 2)
  43. MinuteHand.Length = HourHand.Length / (phi)
  44. HourHand.Shade = _RGBA(200, 50, 50, 255)
  45. MinuteHand.Shade = _RGBA(65, 105, 225, 255)
  46. SecondHand.Shade = _RGBA(255, 165, 0, 255)
  47.  
  48. CALL InitializeModes
  49. Mode = 12
  50.  
  51. CALL PrepareClockface(0)
  52.     CALL KeyProcess
  53.     CALL UpdateTime(TIMER + TimeShift)
  54.     CALL UpdateClock
  55.     CLS
  56.     _PUTIMAGE (0, 0)-(_WIDTH, _HEIGHT), BackScreen, MainScreen, (0, 0)-(_WIDTH, _HEIGHT)
  57.     CALL DrawModeList
  58.     CALL DrawClockHands
  59.     CALL DrawDigitalClock
  60.     _DISPLAY
  61.     _LIMIT 60
  62.  
  63.  
  64. SUB InitializeModes
  65.     DIM k AS INTEGER
  66.     FOR k = 1 TO 12
  67.         ModeList(k) = k
  68.     NEXT
  69.  
  70. SUB PrepareClockface (col AS _UNSIGNED LONG)
  71.     DIM k AS LONG
  72.     DIM p AS DOUBLE
  73.     p = RND
  74.     IF (col = 0) THEN col = _RGBA(255 * p, 255 * RND * 155, 255 * (1 - p), 30)
  75.     _DEST BackScreen
  76.     CLS
  77.     CALL ccircle(0, 0, HourHand.Length, HourHand.Shade)
  78.     FOR k = 0 TO (Mode * 3600) - (1) STEP (1)
  79.         CALL UpdateTime(k)
  80.         CALL UpdateClock
  81.         CALL lineSmooth(SecondHand.Center.x, SecondHand.Center.y, SecondHand.HandPosition.x, SecondHand.HandPosition.y, col)
  82.     NEXT
  83.     FOR k = 0 TO (Mode * 3600) - (3600) STEP (3600)
  84.         CALL UpdateTime(k)
  85.         CALL UpdateClock
  86.         CALL ccircle(HourHand.HandPosition.x, HourHand.HandPosition.y, 6, HourHand.Shade)
  87.         CALL ccirclefill(HourHand.HandPosition.x, HourHand.HandPosition.y, 5, _RGBA(0, 0, 0, 255))
  88.     NEXT
  89.     _DEST MainScreen
  90.  
  91. SUB KeyProcess
  92.     IF (_KEYDOWN(19200) = -1) THEN
  93.         CALL DecreaseMode
  94.         CALL PrepareClockface(0)
  95.         _DELAY .1 * 1 / Mode
  96.     END IF
  97.     IF (_KEYDOWN(19712) = -1) THEN
  98.         CALL IncreaseMode
  99.         CALL PrepareClockface(0)
  100.         _DELAY .1 * 1 / Mode
  101.     END IF
  102.     IF (_KEYDOWN(20480) = -1) THEN
  103.         TimeShift = 0
  104.     END IF
  105.     IF (_KEYDOWN(18432) = -1) THEN
  106.         TimeShift = -TIMER
  107.     END IF
  108.     _KEYCLEAR
  109.  
  110. SUB UpdateTime (t AS DOUBLE)
  111.     IF (t < 3600) THEN
  112.         TheTime.Hour = Mode
  113.     ELSE
  114.         TheTime.Hour = t \ 3600
  115.         t = t - TheTime.Hour * 3600
  116.     END IF
  117.     TheTime.Hour = TheTime.Hour MOD Mode
  118.     IF (TheTime.Hour = 0) THEN TheTime.Hour = Mode
  119.     TheTime.Minute = t \ 60
  120.     t = t - TheTime.Minute * 60
  121.     TheTime.Second = t
  122.  
  123. SUB UpdateClock
  124.     HourHand.Angle = -((TheTime.Hour + (TheTime.Minute / 60) + (TheTime.Second / 3600)) / Mode) * 2 * pi + (pi / 2)
  125.     MinuteHand.Angle = -((TheTime.Minute / 60) + (TheTime.Second / 3600)) * 2 * pi + (pi / 2)
  126.     SecondHand.Angle = -(TheTime.Second / 60) * 2 * pi + (pi / 2)
  127.     HourHand.HandPosition.x = HourHand.Center.x + HourHand.Length * COS(HourHand.Angle)
  128.     HourHand.HandPosition.y = HourHand.Center.y + HourHand.Length * SIN(HourHand.Angle)
  129.     MinuteHand.Center.x = HourHand.HandPosition.x
  130.     MinuteHand.Center.y = HourHand.HandPosition.y
  131.     MinuteHand.HandPosition.x = MinuteHand.Center.x + MinuteHand.Length * COS(MinuteHand.Angle)
  132.     MinuteHand.HandPosition.y = MinuteHand.Center.y + MinuteHand.Length * SIN(MinuteHand.Angle)
  133.     SecondHand.Center.x = MinuteHand.HandPosition.x
  134.     SecondHand.Center.y = MinuteHand.HandPosition.y
  135.     SecondHand.HandPosition.x = SecondHand.Center.x + SecondHand.Length * COS(SecondHand.Angle)
  136.     SecondHand.HandPosition.y = SecondHand.Center.y + SecondHand.Length * SIN(SecondHand.Angle)
  137.  
  138. SUB DrawModeList
  139.     DIM k AS INTEGER
  140.     FOR k = 1 TO UBOUND(ModeList)
  141.         IF (Mode = k) THEN
  142.             COLOR _RGBA(255, 255, 0, 255), _RGBA(0, 0, 255, 255)
  143.         ELSE
  144.             COLOR _RGBA(100, 100, 100, 255), _RGBA(0, 0, 0, 0)
  145.         END IF
  146.         _PRINTSTRING ((4 + 5 * k) * 8, _HEIGHT - (1) * 16), LTRIM$(RTRIM$(STR$(ModeList(k))))
  147.     NEXT
  148.     COLOR _RGBA(200, 200, 0, 255), _RGBA(0, 0, 0, 0)
  149.     _PRINTSTRING ((4 + 1) * 8, _HEIGHT - (1) * 16), ">"
  150.     _PRINTSTRING ((4 + 5 * (UBOUND(ModeList) + 1)) * 8, _HEIGHT - (1) * 16), "<"
  151.  
  152. SUB IncreaseMode
  153.     IF (Mode < 12) THEN
  154.         Mode = Mode + 1
  155.     ELSE
  156.         Mode = 1
  157.     END IF
  158.  
  159. SUB DecreaseMode
  160.     IF (Mode = 1) THEN
  161.         Mode = 12
  162.     ELSE
  163.         Mode = Mode - 1
  164.     END IF
  165.  
  166. SUB DrawClockHands
  167.     DIM k AS DOUBLE
  168.     DIM ctmp AS _UNSIGNED LONG
  169.     FOR k = 0 TO 1 STEP .01
  170.         ctmp = ColorMix(_RGBA(0, 0, 0, 255), HourHand.Shade, k)
  171.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), k * _ALPHA32(ctmp))
  172.         CALL ccirclefill(HourHand.Center.x + (k * HourHand.Length) * COS(HourHand.Angle), HourHand.Center.y + (k * HourHand.Length) * SIN(HourHand.Angle), k * 4, ctmp)
  173.     NEXT
  174.     FOR k = 0 TO 1 STEP .01
  175.         ctmp = ColorMix(HourHand.Shade, MinuteHand.Shade, k)
  176.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  177.         CALL ccirclefill(MinuteHand.Center.x + (k * MinuteHand.Length) * COS(MinuteHand.Angle), MinuteHand.Center.y + (k * MinuteHand.Length) * SIN(MinuteHand.Angle), 4 - 1 * k, ctmp)
  178.     NEXT
  179.     FOR k = 0 TO 1 STEP .005
  180.         ctmp = ColorMix(MinuteHand.Shade, SecondHand.Shade, k)
  181.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  182.         CALL ccirclefill(SecondHand.Center.x + (k * SecondHand.Length) * COS(SecondHand.Angle), SecondHand.Center.y + (k * SecondHand.Length) * SIN(SecondHand.Angle), 3 * (1 - k), ctmp)
  183.     NEXT
  184.     CALL ccircle(HourHand.HandPosition.x, HourHand.HandPosition.y, 5, _RGBA(255, 255, 255, 255))
  185.     CALL ccircle(MinuteHand.HandPosition.x, MinuteHand.HandPosition.y, 3, _RGBA(255, 255, 255, 255))
  186.  
  187. SUB DrawDigitalClock
  188.     DIM t AS STRING
  189.     COLOR _RGBA(200, 200, 0, 255), _RGBA(0, 0, 0, 0)
  190.     t = TIME$
  191.     LOCATE 1, (_WIDTH / 8) / 2 - LEN(t) / 2
  192.     PRINT t
  193.  
  194. FUNCTION ColorMix~& (Shade1 AS _UNSIGNED LONG, Shade2 AS _UNSIGNED LONG, param AS DOUBLE)
  195.     ColorMix~& = _RGB32((1 - param) * _RED32(Shade1) + param * _RED32(Shade2), (1 - param) * _GREEN32(Shade1) + param * _GREEN32(Shade2), (1 - param) * _BLUE32(Shade1) + param * _BLUE32(Shade2))
  196.  
  197. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  198.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  199.  
  200. SUB cline (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
  201.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  202.  
  203. SUB ccircle (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  204.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  205.  
  206. SUB ccirclefill (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  207.     CALL CircleFill(_WIDTH / 2 + x1, -y1 + _HEIGHT / 2, rad, col)
  208.  
  209. SUB CircleFill (CX AS INTEGER, CY AS INTEGER, R AS INTEGER, C AS _UNSIGNED LONG)
  210.     ' CX = center x coordinate
  211.     ' CY = center y coordinate
  212.     '  R = radius
  213.     '  C = fill color
  214.     DIM Radius AS INTEGER, RadiusError AS INTEGER
  215.     DIM X AS INTEGER, Y AS INTEGER
  216.     Radius = ABS(R)
  217.     RadiusError = -Radius
  218.     X = Radius
  219.     Y = 0
  220.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  221.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  222.     WHILE X > Y
  223.         RadiusError = RadiusError + Y * 2 + 1
  224.         IF RadiusError >= 0 THEN
  225.             IF X <> Y + 1 THEN
  226.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  227.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  228.             END IF
  229.             X = X - 1
  230.             RadiusError = RadiusError - X * 2
  231.         END IF
  232.         Y = Y + 1
  233.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  234.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  235.     WEND
  236.  
  237. SUB lineSmooth (x0, y0, x1, y1, c AS _UNSIGNED LONG)
  238.     'translated by FellippeHeitor from
  239.     'https://en.wikipedia.org/w/index.php?title=Xiaolin_Wu%27s_line_algorithm&oldid=852445548
  240.     'correction alpha channel by STxAxTIC (11/2020)
  241.  
  242.     DIM plX AS INTEGER, plY AS INTEGER, plI
  243.  
  244.     DIM steep AS _BYTE
  245.     steep = ABS(y1 - y0) > ABS(x1 - x0)
  246.  
  247.     IF steep THEN
  248.         SWAP x0, y0
  249.         SWAP x1, y1
  250.     END IF
  251.  
  252.     IF x0 > x1 THEN
  253.         SWAP x0, x1
  254.         SWAP y0, y1
  255.     END IF
  256.  
  257.     DIM dx, dy, gradient
  258.     dx = x1 - x0
  259.     dy = y1 - y0
  260.     gradient = dy / dx
  261.  
  262.     IF dx = 0 THEN
  263.         gradient = 1
  264.     END IF
  265.  
  266.     'handle first endpoint
  267.     DIM xend, yend, xgap, xpxl1, ypxl1
  268.     xend = _ROUND(x0)
  269.     yend = y0 + gradient * (xend - x0)
  270.     xgap = (1 - ((x0 + .5) - INT(x0 + .5)))
  271.     xpxl1 = xend 'this will be used in the main loop
  272.     ypxl1 = INT(yend)
  273.     IF steep THEN
  274.         plX = ypxl1
  275.         plY = xpxl1
  276.         plI = (1 - (yend - INT(yend))) * xgap
  277.         GOSUB plot
  278.  
  279.         plX = ypxl1 + 1
  280.         plY = xpxl1
  281.         plI = (yend - INT(yend)) * xgap
  282.         GOSUB plot
  283.     ELSE
  284.         plX = xpxl1
  285.         plY = ypxl1
  286.         plI = (1 - (yend - INT(yend))) * xgap
  287.         GOSUB plot
  288.  
  289.         plX = xpxl1
  290.         plY = ypxl1 + 1
  291.         plI = (yend - INT(yend)) * xgap
  292.         GOSUB plot
  293.     END IF
  294.  
  295.     DIM intery
  296.     intery = yend + gradient 'first y-intersection for the main loop
  297.  
  298.     'handle second endpoint
  299.     DIM xpxl2, ypxl2
  300.     xend = _ROUND(x1)
  301.     yend = y1 + gradient * (xend - x1)
  302.     xgap = ((x1 + .5) - INT(x1 + .5))
  303.     xpxl2 = xend 'this will be used in the main loop
  304.     ypxl2 = INT(yend)
  305.     IF steep THEN
  306.         plX = ypxl2
  307.         plY = xpxl2
  308.         plI = (1 - (yend - INT(yend))) * xgap
  309.         GOSUB plot
  310.  
  311.         plX = ypxl2 + 1
  312.         plY = xpxl2
  313.         plI = (yend - INT(yend)) * xgap
  314.         GOSUB plot
  315.     ELSE
  316.         plX = xpxl2
  317.         plY = ypxl2
  318.         plI = (1 - (yend - INT(yend))) * xgap
  319.         GOSUB plot
  320.  
  321.         plX = xpxl2
  322.         plY = ypxl2 + 1
  323.         plI = (yend - INT(yend)) * xgap
  324.         GOSUB plot
  325.     END IF
  326.  
  327.     'main loop
  328.     DIM x
  329.     IF steep THEN
  330.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  331.             plX = INT(intery)
  332.             plY = x
  333.             plI = (1 - (intery - INT(intery)))
  334.             GOSUB plot
  335.  
  336.             plX = INT(intery) + 1
  337.             plY = x
  338.             plI = (intery - INT(intery))
  339.             GOSUB plot
  340.  
  341.             intery = intery + gradient
  342.         NEXT
  343.     ELSE
  344.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  345.             plX = x
  346.             plY = INT(intery)
  347.             plI = (1 - (intery - INT(intery)))
  348.             GOSUB plot
  349.  
  350.             plX = x
  351.             plY = INT(intery) + 1
  352.             plI = (intery - INT(intery))
  353.             GOSUB plot
  354.  
  355.             intery = intery + gradient
  356.         NEXT
  357.     END IF
  358.  
  359.     EXIT SUB
  360.  
  361.     plot:
  362.     ' Change to regular PSET for standard coordinate orientation.
  363.     CALL cpset(plX, plY, _RGB32(_RED32(c), _GREEN32(c), _BLUE32(c), plI * _ALPHA32(c)))
  364.     RETURN
  365.  
« Last Edit: November 21, 2020, 10:36:13 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Parametric Clock
« Reply #1 on: November 22, 2020, 09:20:55 pm »
I don't know if jointed or joined arm clocks will every catch on but I do like the clock face design!

Here is my version of 12 hours clock with 4 regular analog so you can compare angles:
Code: QB64: [Select]
  1. _TITLE "Jointed arms clock with 4 regular analog to compare angles" 'b+ 2020-11-22
  2. ' inspsired by STx Parametric clock specially the faces  https://www.qb64.org/forum/index.php?topic=3277.msg125579#msg125579
  3. ' I wish to see what a large circle joint at center would look like, first can I get similar face? yes sorta
  4.  
  5. CONST xmax = 710, ymax = 710, CX = xmax / 2, CY = ymax / 2, hhr0 = 20, hhr1 = 10, mhr1 = 5, shr1 = 3, hh = 200, mh = 100, sh = 50
  6. SCREEN _NEWIMAGE(xmax, ymax, 32)
  7. _DELAY .25
  8. face& = _NEWIMAGE(_WIDTH, _HEIGHT, 32)
  9. ' face
  10. FOR vi = 1 TO 43200
  11.     h = vi / 3600
  12.     hha = h / 12 * _PI(2) - _PI(.5)
  13.     mha = (h - INT(h)) * _PI(2) - _PI(.5) '  + (vi MOD 60) / 3600 ? add or not seems better without
  14.     sha = (vi MOD 60) / 60 * _PI(2) - _PI(.5)
  15.     hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  16.     mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  17.     shx = mhx + sh * COS((sha)): shy = mhy + sh * SIN((sha))
  18.     PSET (shx, shy), &HFFFF0000
  19.     PSET (mhx, mhy), &HFF008800
  20. CIRCLE (CX, CY), hh, &HFF0000BB
  21. FOR a = 0 TO _PI(2) STEP _PI(1 / 6)
  22.     fcirc CX + hh * COS(a), CY + hh * SIN(a), 3, &HFF0000BB
  23. _PUTIMAGE , 0, face&
  24. ' clock hands
  25. FOR vi = 1 TO 43200
  26.     _PUTIMAGE , face&, 0
  27.     h = VAL(MID$(TIME$, 1, 2))
  28.     IF h > 12 THEN h = h - 12
  29.     m = VAL(MID$(TIME$, 4, 2))
  30.     s = VAL(MID$(TIME$, 7, 2))
  31.     hha = (h + (m / 60)) / 12 * _PI(2) - _PI(.5)
  32.     mha = (m / 60 + (s / 3600)) * _PI(2) - _PI(.5)
  33.     sha = s / 60 * _PI(2) - _PI(.5)
  34.     hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  35.     mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  36.     shx = mhx + sh * COS((sha)): shy = mhy + sh * SIN((sha))
  37.     drawPully CX, CY, hhr0, hhx, hhy, hhr1, &H88FFFFFF '&HFF8888BB
  38.     drawPully hhx, hhy, hhr1, mhx, mhy, mhr1, &H88FFFFFF ' &HFF8808888
  39.     drawPully mhx, mhy, mhr1, shx, shy, shr1, &H88FFFFFF '&HFFFF4444
  40.     clock 65, 65, 60
  41.     clock _WIDTH - 65, 65, 60
  42.     clock 65, _HEIGHT - 65, 60
  43.     clock _WIDTH - 65, _HEIGHT - 65, 60
  44.     _DISPLAY
  45.     _LIMIT 30
  46.  
  47. SUB fcirc (CX AS INTEGER, CY AS INTEGER, R AS INTEGER, C AS _UNSIGNED LONG)
  48.     DIM Radius AS INTEGER, RadiusError AS INTEGER
  49.     DIM X AS INTEGER, Y AS INTEGER
  50.     Radius = ABS(R): RadiusError = -Radius: X = Radius: Y = 0
  51.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  52.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  53.     WHILE X > Y
  54.         RadiusError = RadiusError + Y * 2 + 1
  55.         IF RadiusError >= 0 THEN
  56.             IF X <> Y + 1 THEN
  57.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  58.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  59.             END IF
  60.             X = X - 1
  61.             RadiusError = RadiusError - X * 2
  62.         END IF
  63.         Y = Y + 1
  64.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  65.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  66.     WEND
  67.  
  68. SUB drawPully (x1, y1, r1, x2, y2, r2, c AS _UNSIGNED LONG)
  69.     DIM a, a1, a2, x3, x4, x5, x6, y3, y4, y5, y6
  70.     a = _ATAN2(y2 - y1, x2 - x1)
  71.     a1 = a + _PI(1 / 2)
  72.     a2 = a - _PI(1 / 2)
  73.     x3 = x1 + r1 * COS(a1): y3 = y1 + r1 * SIN(a1)
  74.     x4 = x1 + r1 * COS(a2): y4 = y1 + r1 * SIN(a2)
  75.     x5 = x2 + r2 * COS(a1): y5 = y2 + r2 * SIN(a1)
  76.     x6 = x2 + r2 * COS(a2): y6 = y2 + r2 * SIN(a2)
  77.     LINE (x3, y3)-(x5, y5), c
  78.     LINE (x4, y4)-(x6, y6), c
  79.     CIRCLE (x1, y1), r1, c
  80.     CIRCLE (x2, y2), r2, c
  81.  
  82. ' for regular analog
  83. SUB clock (x, y, r)
  84.     FOR a = 0 TO 359 STEP 6
  85.         IF a MOD 30 = 0 THEN r1 = 1 / 30 * r ELSE r1 = 1 / 75 * r
  86.         CIRCLE (x + r * COS(_D2R(a)), y + r * SIN(_D2R(a))), r1
  87.         PAINT (x + r * COS(_D2R(a)), y + r * SIN(_D2R(a))), _RGB32(100, 100, 100), _RGB32(255, 255, 255)
  88.     NEXT
  89.     IF VAL(LEFT$(TIME$, 2)) + (VAL(MID$(TIME$, 4, 2)) / 60) >= 12 THEN hrs = VAL(LEFT$(TIME$, 2)) + (VAL(MID$(TIME$, 4, 2)) / 60) - 12 ELSE hrs = VAL(LEFT$(TIME$, 2)) + (VAL(MID$(TIME$, 4, 2)) / 60)
  90.     ftri x + 1 / 15 * r * COS(VAL(MID$(TIME$, 4, 2)) * _PI(1 / 30) - _PI(1 / 2) + _PI(1 / 2)), y + 1 / 15 * r * SIN(VAL(MID$(TIME$, 4, 2)) * _PI(1 / 30) - _PI(1 / 2) + _PI(1 / 2)), x + 1 / 15 * r * COS(VAL(MID$(TIME$, 4, 2)) * _PI(1 / 30) - _PI(1 / 2) - _PI(1 / 2)), y + 1 / 15 * r * SIN(VAL(MID$(TIME$, 4, 2)) * _PI(1 / 30) - _PI(1 / 2) - _PI(1 / 2)), x + r * COS(VAL(MID$(TIME$, 4, 2)) * _PI(1 / 30) - _PI(1 / 2)), y + r * SIN(VAL(MID$(TIME$, 4, 2)) * _PI(1 / 30) - _PI(1 / 2)), _RGB32(255, 0, 0)
  91.     ftri x + 1 / 10 * r * COS(hrs * _PI(1 / 6) - _PI(1 / 2) + _PI(1 / 2)), y + 1 / 10 * r * SIN(hrs * _PI(1 / 6) - _PI(1 / 2) + _PI(1 / 2)), x + 1 / 10 * r * COS(hrs * _PI(1 / 6) - _PI(1 / 2) - _PI(1 / 2)), y + 1 / 10 * r * SIN(hrs * _PI(1 / 6) - _PI(1 / 2) - _PI(1 / 2)), x + 2 / 3 * r * COS(hrs * _PI(1 / 6) - _PI(1 / 2)), y + 2 / 3 * r * SIN(hrs * _PI(1 / 6) - _PI(1 / 2)), _RGB32(0, 0, 255)
  92.     LINE (x, y)-(x + r * COS(VAL(RIGHT$(TIME$, 2)) * _PI(1 / 30) - _PI(1 / 2)), y + r * SIN(VAL(RIGHT$(TIME$, 2)) * _PI(1 / 30) - _PI(1 / 2))), _RGB32(255, 255, 0)
  93.     CIRCLE (x, y), 1 / 10 * r, _RGB32(255, 255, 255)
  94.     PAINT (x + 1 / 75 * r, y + 1 / 75 * r), _RGB32(100, 100, 100), _RGB32(255, 255, 255)
  95.     CIRCLE (x, y), 1 / 30 * r, _RGB32(0, 0, 0)
  96.  
  97. SUB ftri (x1, y1, x2, y2, x3, y3, K AS _UNSIGNED LONG)
  98.     a& = _NEWIMAGE(1, 1, 32)
  99.     _DEST a&
  100.     PSET (0, 0), K
  101.     _DEST 0
  102.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, 0)-(0, 0), a& TO(x1, y1)-(x2, y2)-(x3, y3)
  103.     _FREEIMAGE a& '<<< this is important!
  104.  
  105.  

 
Jointed arms clock.PNG

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Parametric Clock
« Reply #2 on: November 22, 2020, 09:41:59 pm »
Say, I like that "pulley" system for the clock hand display. My next tweak was going to have more definitive clock hands and that was gonna basically be it. Nice.

If I end up ripping the pulley code I'll return your inspiration credit!

Thanks for playing with it.
You're not done when it works, you're done when it's right.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Parametric Clock
« Reply #3 on: November 22, 2020, 11:01:35 pm »
Yes, the pulley was a nice touch.

My code came out a bit shorter (calls for anti-aliased lines):

Code: QB64: [Select]
  1. SUB DrawPulley (x1, y1, rad1, x2, y2, rad2, col AS _UNSIGNED LONG)
  2.     'Inspiration credit: {(bplus)(qb64.org)(2020-11-22)}
  3.     DIM ang AS DOUBLE
  4.     ang = _ATAN2(y2 - y1, x2 - x1) + pi / 2
  5.     CALL lineSmooth(x1 + rad1 * COS(ang), y1 + rad1 * SIN(ang), x2 + rad2 * COS(ang), y2 + rad2 * SIN(ang), col)
  6.     CALL lineSmooth(x1 - rad1 * COS(ang), y1 - rad1 * SIN(ang), x2 - rad2 * COS(ang), y2 - rad2 * SIN(ang), col)
  7.     CALL ccircle(x1, y1, rad1, col)
  8.     CALL ccircle(x2, y2, rad2, col)

Next is full code, then a screenshot:

Code: QB64: [Select]
  1.  
  2. _TITLE "Parametric Clock"
  3.  
  4. DIM SHARED MainScreen AS LONG
  5. DIM SHARED BackScreen AS LONG
  6. MainScreen = _NEWIMAGE(600, 600, 32)
  7. BackScreen = _NEWIMAGE(600, 600, 32)
  8. SCREEN MainScreen
  9.  
  10.  
  11. pi = 4 * ATN(1)
  12. phi = (1 + SQR(5)) / 2
  13.  
  14. TYPE TimeValue
  15.     Hour AS INTEGER
  16.     Minute AS INTEGER
  17.     Second AS DOUBLE
  18.  
  19. TYPE Vector
  20.     x AS DOUBLE
  21.     y AS DOUBLE
  22.  
  23. TYPE ClockHand
  24.     Center AS Vector
  25.     HandPosition AS Vector
  26.     Length AS DOUBLE
  27.     Angle AS DOUBLE
  28.     Shade AS _UNSIGNED LONG
  29.  
  30. DIM SHARED TheTime AS TimeValue
  31. DIM SHARED HourHand AS ClockHand
  32. DIM SHARED MinuteHand AS ClockHand
  33. DIM SHARED SecondHand AS ClockHand
  34.  
  35. DIM SHARED ModeList(12) AS INTEGER
  36. DIM SHARED TimeShift AS DOUBLE
  37. TimeShift = 0
  38.  
  39. HourHand.Center.x = 0
  40. HourHand.Center.y = 0
  41. HourHand.Length = 150
  42. SecondHand.Length = HourHand.Length / (phi ^ 2)
  43. MinuteHand.Length = HourHand.Length / (phi)
  44. HourHand.Shade = _RGBA(200, 50, 50, 255)
  45. MinuteHand.Shade = _RGBA(65, 105, 225, 255)
  46. SecondHand.Shade = _RGBA(255, 165, 0, 255)
  47.  
  48. CALL InitializeModes
  49. Mode = 12
  50.  
  51. CALL PrepareClockface(1)
  52.     CALL KeyProcess
  53.     CALL UpdateTime(TIMER + TimeShift)
  54.     CALL UpdateClock
  55.     CALL DrawEverything
  56.     _KEYCLEAR
  57.     _LIMIT 60
  58.  
  59.  
  60. SUB InitializeModes
  61.     DIM k AS INTEGER
  62.     FOR k = 1 TO 12
  63.         ModeList(k) = k
  64.     NEXT
  65.  
  66. SUB PrepareClockface (metric AS INTEGER)
  67.     DIM p AS DOUBLE
  68.     DIM q AS LONG
  69.     _DEST BackScreen
  70.     CLS
  71.     CALL ccircle(0, 0, HourHand.Length, HourHand.Shade)
  72.     p = RND
  73.     FOR q = 0 TO ((Mode * 3600) - (metric)) STEP (metric)
  74.         CALL UpdateTime(q)
  75.         CALL UpdateClock
  76.         CALL lineSmooth(SecondHand.Center.x, SecondHand.Center.y, SecondHand.HandPosition.x, SecondHand.HandPosition.y, _RGBA(255 * p, 255 * RND * 155, 255 * (1 - p), 30))
  77.     NEXT
  78.     FOR q = 0 TO ((Mode * 3600) - (3600)) STEP (3600)
  79.         CALL UpdateTime(q)
  80.         CALL UpdateClock
  81.         CALL ccircle(HourHand.HandPosition.x, HourHand.HandPosition.y, 6, HourHand.Shade)
  82.         CALL ccirclefill(HourHand.HandPosition.x, HourHand.HandPosition.y, 5, _RGBA(0, 0, 0, 255))
  83.     NEXT
  84.     _DEST MainScreen
  85.  
  86. SUB KeyProcess
  87.     IF (_KEYDOWN(19200) = -1) THEN
  88.         CALL DecreaseMode
  89.         CALL PrepareClockface(1)
  90.         _DELAY .1
  91.     END IF
  92.     IF (_KEYDOWN(19712) = -1) THEN
  93.         CALL IncreaseMode
  94.         CALL PrepareClockface(1)
  95.         _DELAY .1
  96.     END IF
  97.     IF (_KEYDOWN(20480) = -1) THEN
  98.         TimeShift = 0
  99.         CALL PrepareClockface(1 + INT(RND * 5))
  100.         _DELAY .1
  101.     END IF
  102.     IF (_KEYDOWN(18432) = -1) THEN
  103.         TimeShift = -TIMER
  104.     END IF
  105.  
  106. SUB UpdateTime (t AS DOUBLE)
  107.     IF (t < 3600) THEN
  108.         TheTime.Hour = Mode
  109.     ELSE
  110.         TheTime.Hour = t \ 3600
  111.         t = t - TheTime.Hour * 3600
  112.     END IF
  113.     TheTime.Hour = TheTime.Hour MOD Mode
  114.     IF (TheTime.Hour = 0) THEN TheTime.Hour = Mode
  115.     TheTime.Minute = t \ 60
  116.     t = t - TheTime.Minute * 60
  117.     TheTime.Second = t
  118.  
  119. SUB UpdateClock
  120.     HourHand.Angle = -((TheTime.Hour + (TheTime.Minute / 60) + (TheTime.Second / 3600)) / Mode) * 2 * pi + (pi / 2)
  121.     MinuteHand.Angle = -((TheTime.Minute / 60) + (TheTime.Second / 3600)) * 2 * pi + (pi / 2)
  122.     SecondHand.Angle = -(TheTime.Second / 60) * 2 * pi + (pi / 2)
  123.     HourHand.HandPosition.x = HourHand.Center.x + HourHand.Length * COS(HourHand.Angle)
  124.     HourHand.HandPosition.y = HourHand.Center.y + HourHand.Length * SIN(HourHand.Angle)
  125.     MinuteHand.Center.x = HourHand.HandPosition.x
  126.     MinuteHand.Center.y = HourHand.HandPosition.y
  127.     MinuteHand.HandPosition.x = MinuteHand.Center.x + MinuteHand.Length * COS(MinuteHand.Angle)
  128.     MinuteHand.HandPosition.y = MinuteHand.Center.y + MinuteHand.Length * SIN(MinuteHand.Angle)
  129.     SecondHand.Center.x = MinuteHand.HandPosition.x
  130.     SecondHand.Center.y = MinuteHand.HandPosition.y
  131.     SecondHand.HandPosition.x = SecondHand.Center.x + SecondHand.Length * COS(SecondHand.Angle)
  132.     SecondHand.HandPosition.y = SecondHand.Center.y + SecondHand.Length * SIN(SecondHand.Angle)
  133.  
  134. SUB DrawEverything
  135.     CLS
  136.     _PUTIMAGE (0, 0)-(_WIDTH, _HEIGHT), BackScreen, MainScreen, (0, 0)-(_WIDTH, _HEIGHT)
  137.     CALL DrawModeList
  138.     CALL DrawClockHands
  139.     CALL DrawDigitalClock
  140.     _DISPLAY
  141.  
  142. SUB DrawModeList
  143.     DIM k AS INTEGER
  144.     FOR k = 1 TO UBOUND(ModeList)
  145.         IF (Mode = k) THEN
  146.             COLOR _RGBA(255, 255, 0, 255), _RGBA(0, 0, 255, 255)
  147.         ELSE
  148.             COLOR _RGBA(100, 100, 100, 255), _RGBA(0, 0, 0, 0)
  149.         END IF
  150.         _PRINTSTRING ((4 + 5 * k) * 8, _HEIGHT - (1) * 16), LTRIM$(RTRIM$(STR$(ModeList(k))))
  151.     NEXT
  152.     COLOR _RGBA(200, 200, 0, 255), _RGBA(0, 0, 0, 0)
  153.     _PRINTSTRING ((4 + 1) * 8, _HEIGHT - (1) * 16), ">"
  154.     _PRINTSTRING ((4 + 5 * (UBOUND(ModeList) + 1)) * 8, _HEIGHT - (1) * 16), "<"
  155.  
  156. SUB IncreaseMode
  157.     IF (Mode < 12) THEN
  158.         Mode = Mode + 1
  159.     ELSE
  160.         Mode = 1
  161.     END IF
  162.  
  163. SUB DecreaseMode
  164.     IF (Mode = 1) THEN
  165.         Mode = 12
  166.     ELSE
  167.         Mode = Mode - 1
  168.     END IF
  169.  
  170. SUB DrawClockHands
  171.     DIM k AS DOUBLE
  172.     DIM ctmp AS _UNSIGNED LONG
  173.     FOR k = 0 TO 1 STEP .01
  174.         ctmp = ColorMix(_RGBA(0, 0, 255, 255), HourHand.Shade, k)
  175.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), k * _ALPHA32(ctmp))
  176.         CALL ccirclefill(HourHand.Center.x + (k * HourHand.Length) * COS(HourHand.Angle), HourHand.Center.y + (k * HourHand.Length) * SIN(HourHand.Angle), k * 10, ctmp)
  177.     NEXT
  178.     FOR k = 0 TO 1 STEP .01
  179.         ctmp = ColorMix(HourHand.Shade, MinuteHand.Shade, k)
  180.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  181.         CALL ccirclefill(MinuteHand.Center.x + (k * MinuteHand.Length) * COS(MinuteHand.Angle), MinuteHand.Center.y + (k * MinuteHand.Length) * SIN(MinuteHand.Angle), 10 - 1 * k, ctmp)
  182.     NEXT
  183.     FOR k = 0 TO 1 STEP .005
  184.         ctmp = ColorMix(MinuteHand.Shade, SecondHand.Shade, k)
  185.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  186.         CALL ccirclefill(SecondHand.Center.x + (k * SecondHand.Length) * COS(SecondHand.Angle), SecondHand.Center.y + (k * SecondHand.Length) * SIN(SecondHand.Angle), 9 * (1 - k), ctmp)
  187.     NEXT
  188.  
  189.     CALL DrawPulley(HourHand.Center.x, HourHand.Center.x, 0, HourHand.HandPosition.x, HourHand.HandPosition.y, 10, _RGBA(255, 255, 255, 255))
  190.     CALL DrawPulley(HourHand.HandPosition.x, HourHand.HandPosition.y, 10, MinuteHand.HandPosition.x, MinuteHand.HandPosition.y, 9, _RGBA(255, 255, 255, 255))
  191.     CALL DrawPulley(MinuteHand.HandPosition.x, MinuteHand.HandPosition.y, 9, SecondHand.HandPosition.x, SecondHand.HandPosition.y, 0, _RGBA(255, 255, 255, 255))
  192.  
  193.  
  194. SUB DrawDigitalClock
  195.     DIM t AS STRING
  196.     COLOR _RGBA(200, 200, 0, 255), _RGBA(0, 0, 0, 0)
  197.     t = TIME$
  198.     LOCATE 1, (_WIDTH / 8) / 2 - LEN(t) / 2
  199.     PRINT t
  200.  
  201. FUNCTION ColorMix~& (Shade1 AS _UNSIGNED LONG, Shade2 AS _UNSIGNED LONG, param AS DOUBLE)
  202.     ColorMix~& = _RGB32((1 - param) * _RED32(Shade1) + param * _RED32(Shade2), (1 - param) * _GREEN32(Shade1) + param * _GREEN32(Shade2), (1 - param) * _BLUE32(Shade1) + param * _BLUE32(Shade2))
  203.  
  204. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  205.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  206.  
  207. SUB cline (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
  208.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  209.  
  210. SUB ccircle (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  211.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  212.  
  213. SUB ccirclefill (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  214.     CALL CircleFill(_WIDTH / 2 + x1, -y1 + _HEIGHT / 2, rad, col)
  215.  
  216. SUB CircleFill (CX AS INTEGER, CY AS INTEGER, R AS INTEGER, C AS _UNSIGNED LONG)
  217.     ' CX = center x coordinate
  218.     ' CY = center y coordinate
  219.     '  R = radius
  220.     '  C = fill color
  221.     DIM Radius AS INTEGER, RadiusError AS INTEGER
  222.     DIM X AS INTEGER, Y AS INTEGER
  223.     Radius = ABS(R)
  224.     RadiusError = -Radius
  225.     X = Radius
  226.     Y = 0
  227.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  228.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  229.     WHILE X > Y
  230.         RadiusError = RadiusError + Y * 2 + 1
  231.         IF RadiusError >= 0 THEN
  232.             IF X <> Y + 1 THEN
  233.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  234.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  235.             END IF
  236.             X = X - 1
  237.             RadiusError = RadiusError - X * 2
  238.         END IF
  239.         Y = Y + 1
  240.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  241.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  242.     WEND
  243.  
  244. SUB DrawPulley (x1, y1, rad1, x2, y2, rad2, col AS _UNSIGNED LONG)
  245.     'Inspiration credit: {(bplus)(qb64.org)(2020-11-22)}
  246.     DIM ang AS DOUBLE
  247.     ang = _ATAN2(y2 - y1, x2 - x1) + pi / 2
  248.     CALL lineSmooth(x1 + rad1 * COS(ang), y1 + rad1 * SIN(ang), x2 + rad2 * COS(ang), y2 + rad2 * SIN(ang), col)
  249.     CALL lineSmooth(x1 - rad1 * COS(ang), y1 - rad1 * SIN(ang), x2 - rad2 * COS(ang), y2 - rad2 * SIN(ang), col)
  250.     CALL ccircle(x1, y1, rad1, col)
  251.     CALL ccircle(x2, y2, rad2, col)
  252.  
  253. SUB lineSmooth (x0, y0, x1, y1, c AS _UNSIGNED LONG)
  254.     'translated by FellippeHeitor from
  255.     'https://en.wikipedia.org/w/index.php?title=Xiaolin_Wu%27s_line_algorithm&oldid=852445548
  256.     'correction alpha channel by STxAxTIC (11/2020)
  257.  
  258.     DIM plX AS INTEGER, plY AS INTEGER, plI
  259.  
  260.     DIM steep AS _BYTE
  261.     steep = ABS(y1 - y0) > ABS(x1 - x0)
  262.  
  263.     IF steep THEN
  264.         SWAP x0, y0
  265.         SWAP x1, y1
  266.     END IF
  267.  
  268.     IF x0 > x1 THEN
  269.         SWAP x0, x1
  270.         SWAP y0, y1
  271.     END IF
  272.  
  273.     DIM dx, dy, gradient
  274.     dx = x1 - x0
  275.     dy = y1 - y0
  276.     gradient = dy / dx
  277.  
  278.     IF dx = 0 THEN
  279.         gradient = 1
  280.     END IF
  281.  
  282.     'handle first endpoint
  283.     DIM xend, yend, xgap, xpxl1, ypxl1
  284.     xend = _ROUND(x0)
  285.     yend = y0 + gradient * (xend - x0)
  286.     xgap = (1 - ((x0 + .5) - INT(x0 + .5)))
  287.     xpxl1 = xend 'this will be used in the main loop
  288.     ypxl1 = INT(yend)
  289.     IF steep THEN
  290.         plX = ypxl1
  291.         plY = xpxl1
  292.         plI = (1 - (yend - INT(yend))) * xgap
  293.         GOSUB plot
  294.  
  295.         plX = ypxl1 + 1
  296.         plY = xpxl1
  297.         plI = (yend - INT(yend)) * xgap
  298.         GOSUB plot
  299.     ELSE
  300.         plX = xpxl1
  301.         plY = ypxl1
  302.         plI = (1 - (yend - INT(yend))) * xgap
  303.         GOSUB plot
  304.  
  305.         plX = xpxl1
  306.         plY = ypxl1 + 1
  307.         plI = (yend - INT(yend)) * xgap
  308.         GOSUB plot
  309.     END IF
  310.  
  311.     DIM intery
  312.     intery = yend + gradient 'first y-intersection for the main loop
  313.  
  314.     'handle second endpoint
  315.     DIM xpxl2, ypxl2
  316.     xend = _ROUND(x1)
  317.     yend = y1 + gradient * (xend - x1)
  318.     xgap = ((x1 + .5) - INT(x1 + .5))
  319.     xpxl2 = xend 'this will be used in the main loop
  320.     ypxl2 = INT(yend)
  321.     IF steep THEN
  322.         plX = ypxl2
  323.         plY = xpxl2
  324.         plI = (1 - (yend - INT(yend))) * xgap
  325.         GOSUB plot
  326.  
  327.         plX = ypxl2 + 1
  328.         plY = xpxl2
  329.         plI = (yend - INT(yend)) * xgap
  330.         GOSUB plot
  331.     ELSE
  332.         plX = xpxl2
  333.         plY = ypxl2
  334.         plI = (1 - (yend - INT(yend))) * xgap
  335.         GOSUB plot
  336.  
  337.         plX = xpxl2
  338.         plY = ypxl2 + 1
  339.         plI = (yend - INT(yend)) * xgap
  340.         GOSUB plot
  341.     END IF
  342.  
  343.     'main loop
  344.     DIM x
  345.     IF steep THEN
  346.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  347.             plX = INT(intery)
  348.             plY = x
  349.             plI = (1 - (intery - INT(intery)))
  350.             GOSUB plot
  351.  
  352.             plX = INT(intery) + 1
  353.             plY = x
  354.             plI = (intery - INT(intery))
  355.             GOSUB plot
  356.  
  357.             intery = intery + gradient
  358.         NEXT
  359.     ELSE
  360.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  361.             plX = x
  362.             plY = INT(intery)
  363.             plI = (1 - (intery - INT(intery)))
  364.             GOSUB plot
  365.  
  366.             plX = x
  367.             plY = INT(intery) + 1
  368.             plI = (intery - INT(intery))
  369.             GOSUB plot
  370.  
  371.             intery = intery + gradient
  372.         NEXT
  373.     END IF
  374.  
  375.     EXIT SUB
  376.  
  377.     plot:
  378.     ' Change to regular PSET for standard coordinate orientation.
  379.     CALL cpset(plX, plY, _RGB32(_RED32(c), _GREEN32(c), _BLUE32(c), plI * _ALPHA32(c)))
  380.     RETURN
pulley.png
* pulley.png (Filesize: 380.79 KB, Dimensions: 600x625, Views: 100)
You're not done when it works, you're done when it's right.

FellippeHeitor

  • Guest
Re: Parametric Clock
« Reply #4 on: November 22, 2020, 11:05:41 pm »
Gorgeous work, STx. I'll take it as a work of art, as the whole philosophy/logic goes over my head, but it's gorgeous to look at nonetheless.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Parametric Clock
« Reply #5 on: November 23, 2020, 07:12:24 am »
Facelift:
Code: QB64: [Select]
  1. _TITLE "Jointed arms clock 2" 'b+ 2020-11-22
  2. ' inspsired by STx Parametric clock specially the faces  https://www.qb64.org/forum/index.php?topic=3277.msg125579#msg125579
  3. ' I wish to see what a large circle joint at center would look like, first can I get similar face? yes sorta
  4. ' 2020-11-23 More work on clock face, drawPully less LOC
  5.  
  6. CONST xmax = 710, ymax = 710, CX = xmax / 2, CY = ymax / 2, hhr0 = 20, hhr1 = 10, mhr1 = 5, shr1 = 0, hh = 180, mh = 110, sh = 36
  7. SCREEN _NEWIMAGE(xmax, ymax, 32)
  8. _DELAY .25
  9. face& = _NEWIMAGE(_WIDTH, _HEIGHT, 32)
  10. FOR vi = 1 TO 43200
  11.     h = vi / 3600
  12.     hha = h / 12 * _PI(2) - _PI(.5)
  13.     mha = (h - INT(h)) * _PI(2) - _PI(.5)
  14.     sha = (vi MOD 60) / 60 * _PI(2) - _PI(.5)
  15.     hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  16.     mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  17.     shx = mhx + sh * COS((sha)): shy = mhy + sh * SIN((sha))
  18.     drawPully mhx, mhy, mhr1, shx, shy, shr1, &H2300FF88
  19. _PUTIMAGE , 0, face&
  20.     _PUTIMAGE , face&, 0
  21.     h = VAL(MID$(TIME$, 1, 2))
  22.     IF h > 12 THEN h = h - 12
  23.     m = VAL(MID$(TIME$, 4, 2))
  24.     s = VAL(MID$(TIME$, 7, 2))
  25.     hha = (h + (m / 60)) / 12 * _PI(2) - _PI(.5)
  26.     mha = (m / 60 + (s / 3600)) * _PI(2) - _PI(.5)
  27.     sha = s / 60 * _PI(2) - _PI(.5)
  28.     hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  29.     mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  30.     shx = mhx + sh * COS((sha)): shy = mhy + sh * SIN((sha))
  31.     drawPully CX, CY, hhr0, hhx, hhy, hhr1, &HFFFFFFFF
  32.     drawPully hhx, hhy, hhr1, mhx, mhy, mhr1, &HFFFFFFFF
  33.     drawPully mhx, mhy, mhr1, shx, shy, shr1, &HFFFFFFFF
  34.     _DISPLAY
  35.     _LIMIT 30
  36.  
  37. SUB drawPully (x1, y1, r1, x2, y2, r2, c AS _UNSIGNED LONG) ' drawPully with less LOC, thanks STx
  38.     a = _ATAN2(y2 - y1, x2 - x1) + _PI(.5)
  39.     LINE (x1 + r1 * COS(a), y1 + r1 * SIN(a))-(x2 + r2 * COS(a), y2 + r2 * SIN(a)), c
  40.     LINE (x1 + r1 * COS(a - _PI), y1 + r1 * SIN(a - _PI))-(x2 + r2 * COS(a - _PI), y2 + r2 * SIN(a - _PI)), c
  41.     CIRCLE (x1, y1), r1, c
  42.     CIRCLE (x2, y2), r2, c
  43.  
Jointed arms clock 2.PNG


EDIT: dang I was doing clock in a FOR loop!
EDIT 2: drawPully with less LOC!
« Last Edit: November 23, 2020, 08:42:56 am by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Parametric Clock
« Reply #6 on: November 23, 2020, 09:17:47 am »
Thanks for the kind complements @Fellippe, you are QB64's gentleman!

@bplus:
Something that may have seen like an esoteric decision was the choice of clock hand length, and why I need the golden ratio in my code. Alas, it's so the second and minute hands can stack up to perfectly cover the hour hand (almost shown below).

Code: QB64: [Select]
  1. SecondHand.Length = HourHand.Length / (phi ^ 2)
  2. MinuteHand.Length = HourHand.Length / (phi)
gold1.png
* gold1.png (Filesize: 568.62 KB, Dimensions: 600x625, Views: 110)
gold2.png
* gold2.png (Filesize: 563.83 KB, Dimensions: 600x625, Views: 119)
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Parametric Clock
« Reply #7 on: November 23, 2020, 10:13:46 am »
Quote
Alas, it's so the second and minute hands can stack up to perfectly cover the hour hand (almost shown below).

Oh cool! But do you need Golden Ratio? Just If Hour hand = 3 * Seconds and Minute hand = 2 * Seconds Then Hour = Minute + Second

Well anyway, I am catching up with modes and color changes. Pick modes by single digit key press or mouse click the o'clock:
Code: QB64: [Select]
  1. _TITLE "Jointed arms clock #3: Any changes color, digits change hours: 0 = 12 or click mouse at 1, 2, 3... o'clock" 'b+ started 2020-11-22
  2. ' inspsired by STx Parametric clock specially the faces  https://www.qb64.org/forum/index.php?topic=3277.msg125579#msg125579
  3. ' I wish to see what a large circle joint at center would look like, first can I get similar face? yes sorta
  4. ' 2020-11-23 More work on clock face, less LOC for drawPully, add modes and color changes
  5.  
  6. CONST xmax = 710, ymax = 710, CX = xmax / 2, CY = ymax / 2, hhr0 = 20, hhr1 = 10, mhr1 = 5, shr1 = 0, hh = 180, mh = 110, sh = 36
  7. DIM SHARED face AS LONG, mode AS LONG
  8. SCREEN _NEWIMAGE(xmax, ymax, 32)
  9. _DELAY .25
  10. face = _NEWIMAGE(_WIDTH, _HEIGHT, 32)
  11. makeAFace
  12.     k$ = INKEY$
  13.     IF LEN(k$) THEN
  14.         IF INSTR("0123456789", k$) THEN mode = VAL(k$)
  15.         makeAFace
  16.     END IF
  17.         a = _R2D(_ATAN2(_MOUSEY - CY, _MOUSEX - CX)) + 90 + 15
  18.         IF a < 0 THEN a = a + 360
  19.         IF a > 360 THEN a = a - 360
  20.         a = INT(a / 30)
  21.         IF a >= 0 AND a <= 12 THEN mode = a: makeAFace
  22.     END IF
  23.     _PUTIMAGE , face&, 0
  24.     h = VAL(MID$(TIME$, 1, 2)) MOD mode
  25.     m = VAL(MID$(TIME$, 4, 2))
  26.     s = VAL(MID$(TIME$, 7, 2))
  27.     hha = (h + (m / 60)) / mode * _PI(2) - _PI(.5)
  28.     mha = (m / 60 + (s / 3600)) * _PI(2) - _PI(.5)
  29.     sha = s / 60 * _PI(2) - _PI(.5)
  30.     hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  31.     mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  32.     shx = mhx + ((12 - mode) * 3 + sh) * COS((sha)): shy = mhy + ((12 - mode) * 3 + sh) * SIN((sha))
  33.     drawPully CX, CY, hhr0, hhx, hhy, hhr1, &HFFFFFFFF
  34.     drawPully hhx, hhy, hhr1, mhx, mhy, mhr1, &HFFFFFFFF
  35.     drawPully mhx, mhy, mhr1, shx, shy, shr1, &HFFFFFFFF
  36.     _DISPLAY
  37.     _LIMIT 30
  38.  
  39. SUB drawPully (x1, y1, r1, x2, y2, r2, c AS _UNSIGNED LONG)
  40.     a = _ATAN2(y2 - y1, x2 - x1) + _PI(.5)
  41.     LINE (x1 + r1 * COS(a), y1 + r1 * SIN(a))-(x2 + r2 * COS(a), y2 + r2 * SIN(a)), c
  42.     LINE (x1 + r1 * COS(a - _PI), y1 + r1 * SIN(a - _PI))-(x2 + r2 * COS(a - _PI), y2 + r2 * SIN(a - _PI)), c
  43.     CIRCLE (x1, y1), r1, c
  44.     CIRCLE (x2, y2), r2, c
  45.  
  46. SUB makeAFace
  47.     DIM colr AS _UNSIGNED LONG
  48.     colr = _RGB32((RND < .5) * -1 * (RND * 128 + 127), RND * 128 + 127, (RND < .5) * -1 * (RND * 128 + 127), &H23)
  49.     IF mode = 0 THEN mode = 12
  50.     CLS
  51.     FOR vi = 1 TO mode * 3600
  52.         h = vi / 3600
  53.         hha = h / mode * _PI(2) - _PI(.5)
  54.         mha = (h - INT(h)) * _PI(2) - _PI(.5)
  55.         sha = (vi MOD 60) / 60 * _PI(2) - _PI(.5)
  56.         hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  57.         mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  58.         shx = mhx + ((12 - mode) * 3 + sh) * COS((sha)): shy = mhy + ((12 - mode) * 3 + sh) * SIN((sha))
  59.         drawPully mhx, mhy, mhr1, shx, shy, shr1, colr
  60.     NEXT
  61.     _PUTIMAGE , 0, face&
  62.  

 
Jointed arms clock 3.PNG


Edit: half of 30 is 15 (12 clock hour angles), not 18, fixed and title with instructions now fits title bar.

« Last Edit: November 23, 2020, 03:14:30 pm by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Parametric Clock
« Reply #8 on: November 23, 2020, 10:36:20 am »
Good question, the golden mean isn't needed if we create only a few hands - I guess this choice is a relic from an earlier version that had a millisecond hand too, and getting all hands to match up was, well, a perfect application of the golden mean. Now yeah, I guess as long as length(second)+length(minute)=length(hour), one can use any values. I would need a fourth hand to make it "necessary".

EDIT: Ah, nice work with the mode switching! A wild __cardioid clock__ appears!

EDIT EDIT: Best answer goes to bplus for getting deeply engaged in the issue. Nice tight code too.
« Last Edit: November 23, 2020, 11:34:16 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Parametric Clock
« Reply #9 on: November 24, 2020, 12:38:10 pm »
Good question, the golden mean isn't needed if we create only a few hands - I guess this choice is a relic from an earlier version that had a millisecond hand too, and getting all hands to match up was, well, a perfect application of the golden mean. Now yeah, I guess as long as length(second)+length(minute)=length(hour), one can use any values. I would need a fourth hand to make it "necessary".

EDIT: Ah, nice work with the mode switching! A wild __cardioid clock__ appears!

EDIT EDIT: Best answer goes to bplus for getting deeply engaged in the issue. Nice tight code too.

Wow thanks, coming from you that means allot! But I am still catching up to your colored clock hands, last night I started work and rework on a number of tools to attempt that FX of color transitioning. BTW I found trouble with my version of arc trying to draw the shape of a pully without the circle in inner part. I hope to transition from transparent center hour hand out to solid minute hand join/joint. And we haven't even thought about those straight line edges on pully. Yeah, deeply engaged.

Marked as best answer by STxAxTIC on November 25, 2020, 09:50:05 am

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Parametric Clock
« Reply #10 on: November 24, 2020, 09:56:26 pm »
One more time with new hands:
Code: QB64: [Select]
  1. _TITLE "Jointed arms clock #4: Any changes color, digits change hours: 0 = 12 or click mouse at 1, 2, 3... o'clock" 'b+ started 2020-11-22
  2. ' inspsired by STx Parametric clock specially the faces  https://www.qb64.org/forum/index.php?topic=3277.msg125579#msg125579
  3. ' I wish to see what a large circle joint at center would look like, first can I get similar face? yes sorta
  4. ' 2020-11-23 More work on clock face, less LOC for drawPully, add modes and color changes
  5. ' 2020-11-24 add stuff to make different arms
  6.  
  7. CONST xmax = 710, ymax = 710, CX = xmax / 2, CY = ymax / 2, hhr0 = 20, hhr1 = 10, mhr1 = 5, shr1 = 0, hh = 180, mh = 110, sh = 36
  8. DIM SHARED face AS LONG, mode AS LONG, colr AS _UNSIGNED LONG, hourHand&, minHand&, secHand&
  9. SCREEN _NEWIMAGE(xmax, ymax, 32)
  10. _DELAY .25
  11. face = _NEWIMAGE(_WIDTH, _HEIGHT, 32)
  12. makeAFace
  13.     k$ = INKEY$
  14.     IF LEN(k$) THEN
  15.         IF INSTR("0123456789", k$) THEN mode = VAL(k$)
  16.         makeAFace
  17.     END IF
  18.         a = _R2D(_ATAN2(_MOUSEY - CY, _MOUSEX - CX)) + 90 + 15
  19.         IF a < 0 THEN a = a + 360
  20.         IF a > 360 THEN a = a - 360
  21.         a = INT(a / 30)
  22.         IF a >= 0 AND a <= 12 THEN mode = a: makeAFace
  23.     END IF
  24.     _PUTIMAGE , face&, 0
  25.     h = VAL(MID$(TIME$, 1, 2)) MOD mode
  26.     m = VAL(MID$(TIME$, 4, 2))
  27.     s = VAL(MID$(TIME$, 7, 2))
  28.     hha = (h + (m / 60)) / mode * _PI(2) - _PI(.5)
  29.     mha = (m / 60 + (s / 3600)) * _PI(2) - _PI(.5)
  30.     sha = s / 60 * _PI(2) - _PI(.5)
  31.     hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  32.     mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  33.     shx = mhx + ((12 - mode) * 3 + sh) * COS((sha)): shy = mhy + ((12 - mode) * 3 + sh) * SIN((sha))
  34.     'drawPully CX, CY, hhr0, hhx, hhy, hhr1, &HFFFFFFFF
  35.     'drawPully hhx, hhy, hhr1, mhx, mhy, mhr1, &HFFFFFFFF
  36.     'drawPully mhx, mhy, mhr1, shx, shy, shr1, &HFFFFFFFF
  37.     RotoZoom3 CX, CY, hourHand&, 1, 1, hha
  38.     RotoZoom3 hhx, hhy, minHand&, 1, 1, mha
  39.     RotoZoom3 mhx, mhy, secHand&, 1, 1, sha
  40.     _DISPLAY
  41.     _LIMIT 30
  42.  
  43. SUB drawPully (x1, y1, r1, x2, y2, r2, c AS _UNSIGNED LONG)
  44.     a = _ATAN2(y2 - y1, x2 - x1) + _PI(.5)
  45.     LINE (x1 + r1 * COS(a), y1 + r1 * SIN(a))-(x2 + r2 * COS(a), y2 + r2 * SIN(a)), c
  46.     LINE (x1 + r1 * COS(a - _PI), y1 + r1 * SIN(a - _PI))-(x2 + r2 * COS(a - _PI), y2 + r2 * SIN(a - _PI)), c
  47.     CIRCLE (x1, y1), r1, c
  48.     CIRCLE (x2, y2), r2, c
  49.  
  50. SUB makeAFace
  51.     colr = _RGB32((RND < .5) * -1 * (RND * 128 + 127), RND * 128 + 127, (RND < .5) * -1 * (RND * 128 + 127), &H23)
  52.     IF mode = 0 THEN mode = 12
  53.     CLS
  54.     FOR vi = 1 TO mode * 3600
  55.         h = vi / 3600
  56.         hha = h / mode * _PI(2) - _PI(.5)
  57.         mha = (h - INT(h)) * _PI(2) - _PI(.5)
  58.         sha = (vi MOD 60) / 60 * _PI(2) - _PI(.5)
  59.         hhx = CX + hh * COS((hha)): hhy = CY + hh * SIN((hha))
  60.         mhx = hhx + mh * COS((mha)): mhy = hhy + mh * SIN((mha))
  61.         shx = mhx + ((12 - mode) * 3 + sh) * COS((sha)): shy = mhy + ((12 - mode) * 3 + sh) * SIN((sha))
  62.         drawPully mhx, mhy, mhr1, shx, shy, shr1, colr
  63.     NEXT
  64.     _PUTIMAGE , 0, face&
  65.     'arms look better with the draw color for the face on the edges, it hides raggity border edges.
  66.     ' otherwise we could just draw these once at the beginning of program.
  67.     makeArmImage hourHand&, hh, hhr0, hhr1, &HFFFFFFFF, &H88000000
  68.     makeArmImage minHand&, mh, hhr1, mhr1, &HFFFFFFFF, &H88000000
  69.     makeArmImage secHand&, sh, mhr1, shr1, &HFFFFFFFF, &H88000000
  70.  
  71. SUB makeArmImage (hdl&, length, r1, r2, c1 AS _UNSIGNED LONG, c2 AS _UNSIGNED LONG)
  72.     ' intend to use this with rotozoom so have to make image rotate-able in middle
  73.     ' arm image starts big in middle and points right to smaller radius r2
  74.     ' hdl& image handle to use
  75.     ' length  run of origins of half circles
  76.     ' c1 is color on left in middle = bigger joint , c2 is color on right
  77.     DIM width, height, wd2, hd2, x1, y1, x2, y2, a
  78.     width = 2 * (r2 + length) + 2: height = 2 * r1 + 2: wd2 = width / 2: hd2 = height / 2
  79.     hdl& = _NEWIMAGE(width + 2, height + 2, 32)
  80.     _DEST hdl&
  81.     _SOURCE hdl&
  82.     x1 = wd2: y1 = hd2: x2 = wd2 + length: y2 = hd2: a = _PI(.5)
  83.     LINE (x1 + r1 * COS(a), y1 + r1 * SIN(a))-(x2 + r2 * COS(a), y2 + r2 * SIN(a)), colr
  84.     LINE (x1 + r1 * COS(a - _PI), y1 + r1 * SIN(a - _PI))-(x2 + r2 * COS(a - _PI), y2 + r2 * SIN(a - _PI)), colr
  85.     arc x1, y1, r1, _PI(.5), _PI(1.5), colr
  86.     arc x2, y2, r2, _PI(1.5), _PI(.5), colr
  87.     paint4 x1, y1, c1, c2
  88.     _DEST 0
  89.     _SOURCE 0
  90.  
  91. 'use radians
  92. SUB arc (x, y, r, raBegin, raEnd, c AS _UNSIGNED LONG) ' updated 2020-11-24
  93.     ' raStart is first angle clockwise from due East = 0 degrees
  94.     ' arc will start drawing there and clockwise until raStop angle reached
  95.     'x, y origin, r = radius, c = color
  96.     DIM raStart, raStop, dStart, dStop, al, a
  97.  
  98.     ' Last time I tried to use this SUB it hung the program, possible causes:
  99.     ' Make sure raStart and raStop are between 0 and 2pi.
  100.     ' This sub does not have to be recursive, use GOSUB to do drawing to execute arc in one call.
  101.  
  102.     'make copies before changing
  103.     raStart = raBegin: raStop = raEnd
  104.     WHILE raStart < 0: raStart = raStart + _PI(2): WEND
  105.     WHILE raStart >= _PI(2): raStart = raStart - _PI(2): WEND
  106.     WHILE raStop < 0: raStop = raStop + _PI(2): WEND
  107.     WHILE raStop >= _PI(2): raStop = raStop - _PI(2): WEND
  108.  
  109.     IF raStop < raStart THEN
  110.         dStart = raStart: dStop = _PI(2) - .00001
  111.         GOSUB drawArc
  112.         dStart = 0: dStop = raStop
  113.         GOSUB drawArc
  114.     ELSE
  115.         dStart = raStart: dStop = raStop
  116.         GOSUB drawArc
  117.     END IF
  118.     EXIT SUB
  119.     drawArc: ' I am going back to lines instead of pset
  120.     al = 2 * _PI * r * (dStop - dStart) / _PI(2)
  121.     FOR a = dStart TO dStop STEP 1 / al
  122.         IF a > dStart THEN LINE (lastx, lasty)-(x + r * COS(a), y + r * SIN(a)), c
  123.         lastx = x + r * COS(a): lasty = y + r * SIN(a)
  124.     NEXT
  125.     RETURN
  126.  
  127. SUB cAnalysis (c AS _UNSIGNED LONG, outRed, outGrn, outBlu, outAlp)
  128.     outRed = _RED32(c): outGrn = _GREEN32(c): outBlu = _BLUE32(c): outAlp = _ALPHA32(c)
  129.  
  130. FUNCTION Ink~& (c1 AS _UNSIGNED LONG, c2 AS _UNSIGNED LONG, fr##)
  131.     DIM R1, G1, B1, A1, R2, G2, B2, A2
  132.     cAnalysis c1, R1, G1, B1, A1
  133.     cAnalysis c2, R2, G2, B2, A2
  134.     Ink~& = _RGB32(R1 + (R2 - R1) * fr##, G1 + (G2 - G1) * fr##, B1 + (B2 - B1) * fr##, A1 + (A2 - A1) * fr##)
  135.  
  136. SUB paint4 (x0, y0, c1 AS _UNSIGNED LONG, c2 AS _UNSIGNED LONG) ' needs max, min functions
  137.     DIM fillColor AS _UNSIGNED LONG, W, H, parentF, tick, ystart, ystop, xstart, xstop, x, y
  138.     fillColor = POINT(x0, y0)
  139.     'PRINT fillColor
  140.     W = _WIDTH - 1: H = _HEIGHT - 1
  141.     DIM temp(W, H)
  142.     temp(x0, y0) = 1: parentF = 1
  143.     PSET (x0, y0), Ink~&(c1, c2, ABS((y0 - _HEIGHT / 2) / (_HEIGHT / 2)))
  144.     WHILE parentF = 1
  145.         parentF = 0: tick = tick + 1
  146.         ystart = max(y0 - tick, 0): ystop = min(y0 + tick, H)
  147.         y = ystart
  148.         WHILE y <= ystop
  149.             xstart = max(x0 - tick, 0): xstop = min(x0 + tick, W)
  150.             x = xstart
  151.             WHILE x <= xstop
  152.                 IF POINT(x, y) = fillColor AND temp(x, y) = 0 THEN
  153.                     IF temp(max(0, x - 1), y) THEN
  154.                         temp(x, y) = 1: parentF = 1: PSET (x, y), Ink~&(c1, c2, ABS((y - _HEIGHT / 2) / (_HEIGHT / 2)))
  155.                     ELSEIF temp(min(x + 1, W), y) THEN
  156.                         temp(x, y) = 1: parentF = 1: PSET (x, y), Ink~&(c1, c2, ABS((y - _HEIGHT / 2) / (_HEIGHT / 2)))
  157.                     ELSEIF temp(x, max(y - 1, 0)) THEN
  158.                         temp(x, y) = 1: parentF = 1: PSET (x, y), Ink~&(c1, c2, ABS((y - _HEIGHT / 2) / (_HEIGHT / 2)))
  159.                     ELSEIF temp(x, min(y + 1, H)) THEN
  160.                         temp(x, y) = 1: parentF = 1: PSET (x, y), Ink~&(c1, c2, ABS((y - _HEIGHT / 2) / (_HEIGHT / 2)))
  161.                     END IF
  162.                 END IF
  163.                 x = x + 1
  164.             WEND
  165.             y = y + 1
  166.         WEND
  167.     WEND
  168.  
  169. FUNCTION min (n1, n2)
  170.     IF n1 > n2 THEN min = n2 ELSE min = n1
  171.  
  172. FUNCTION max (n1, n2)
  173.     IF n1 < n2 THEN max = n2 ELSE max = n1
  174.  
  175. ' Description:
  176. ' Started from a mod of Galleon's in Wiki that both scales and rotates an image.
  177. ' This version scales the x-axis and y-axis independently allowing rotations of image just by changing X or Y Scales
  178. ' making this tightly coded routine a very powerful and versatile image tool.
  179. SUB RotoZoom3 (X AS LONG, Y AS LONG, Image AS LONG, xScale AS SINGLE, yScale AS SINGLE, radianRotation AS SINGLE)
  180.     ' This assumes you have set your drawing location with _DEST or default to screen.
  181.     ' X, Y - is where you want to put the middle of the image
  182.     ' Image - is the handle assigned with _LOADIMAGE
  183.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  184.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  185.     ' radianRotation is the Angle in Radian units to rotate the image
  186.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  187.     '       and saves a little time converting from degree.
  188.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  189.  
  190.     DIM px(3) AS SINGLE: DIM py(3) AS SINGLE ' simple arrays for x, y to hold the 4 corners of image
  191.     DIM W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  192.     W& = _WIDTH(Image&): H& = _HEIGHT(Image&)
  193.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  194.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  195.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  196.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  197.     sinr! = SIN(-radianRotation): cosr! = COS(-radianRotation) ' rotation helpers
  198.     FOR i& = 0 TO 3 ' calc new point locations with rotation and zoom
  199.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  200.         px(i&) = x2&: py(i&) = y2&
  201.     NEXT
  202.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  203.     _MAPTRIANGLE _SEAMLESS(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  204.  
  205.  

BTW paint4 is filling with a midrange color between two colors based on location in screen.

 
Jointed arms clock 4.PNG
« Last Edit: November 24, 2020, 10:02:43 pm by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Parametric Clock
« Reply #11 on: November 24, 2020, 11:49:03 pm »
Had to raise the bar.

Behold, the tenth-second hand (I hate it).

Press uparrow to increase time
Press downarrow to decrease time
Press left/right arrow to change mode (not new)
Press Space for stopwatch mode
Press R to reset to normal time

Also got rid of TIME$ dependence completely, all TIMER now.

Code: QB64: [Select]
  1.  
  2. _TITLE "Parametric Clock"
  3.  
  4. DIM SHARED MainScreen AS LONG
  5. DIM SHARED BackScreen AS LONG
  6. MainScreen = _NEWIMAGE(600, 600, 32)
  7. BackScreen = _NEWIMAGE(600, 600, 32)
  8. SCREEN MainScreen
  9.  
  10.  
  11. pi = 4 * ATN(1)
  12. phi = (1 + SQR(5)) / 2
  13.  
  14. TYPE TimeValue
  15.     Hour AS INTEGER
  16.     Minute AS INTEGER
  17.     Second AS DOUBLE
  18.     TenthSecond AS DOUBLE
  19.  
  20. TYPE Vector
  21.     x AS DOUBLE
  22.     y AS DOUBLE
  23.  
  24. TYPE ClockHand
  25.     Center AS Vector
  26.     HandPosition AS Vector
  27.     Length AS DOUBLE
  28.     Angle AS DOUBLE
  29.     Shade AS _UNSIGNED LONG
  30.  
  31. DIM SHARED TheTime AS TimeValue
  32. DIM SHARED HourHand AS ClockHand
  33. DIM SHARED MinuteHand AS ClockHand
  34. DIM SHARED SecondHand AS ClockHand
  35. DIM SHARED TenthSecondHand AS ClockHand
  36.  
  37. DIM SHARED ModeList(12) AS INTEGER
  38. DIM SHARED TimeShift AS DOUBLE
  39. TimeShift = 0
  40.  
  41. HourHand.Center.x = 0
  42. HourHand.Center.y = 0
  43. HourHand.Length = 150
  44. MinuteHand.Length = HourHand.Length / (phi)
  45. SecondHand.Length = HourHand.Length / (phi ^ 2)
  46. TenthSecondHand.Length = HourHand.Length / (phi ^ 3)
  47. HourHand.Shade = _RGBA(200, 50, 50, 255)
  48. MinuteHand.Shade = _RGBA(65, 105, 225, 255)
  49. SecondHand.Shade = _RGBA(255, 165, 0, 255)
  50. TenthSecondHand.Shade = _RGBA(138, 43, 226, 255)
  51.  
  52. CALL InitializeModes
  53. Mode = 12
  54.  
  55. CALL PrepareClockface(1)
  56.     CALL KeyProcess
  57.     CALL UpdateTime(TIMER + TimeShift)
  58.     CALL UpdateClock
  59.     CALL DrawEverything
  60.     _KEYCLEAR
  61.     _LIMIT 60
  62.  
  63.  
  64. SUB InitializeModes
  65.     DIM k AS INTEGER
  66.     FOR k = 1 TO 12
  67.         ModeList(k) = k
  68.     NEXT
  69.  
  70. SUB PrepareClockface (metric AS INTEGER)
  71.     DIM p AS DOUBLE
  72.     DIM q AS LONG
  73.     _DEST BackScreen
  74.     CLS
  75.     CALL ccircle(0, 0, HourHand.Length, HourHand.Shade)
  76.     p = RND
  77.     FOR q = 0 TO ((Mode * 3600) - (metric)) STEP (metric)
  78.         CALL UpdateTime(q)
  79.         CALL UpdateClock
  80.         CALL lineSmooth(SecondHand.Center.x, SecondHand.Center.y, SecondHand.HandPosition.x, SecondHand.HandPosition.y, _RGBA(255 * p, 255 * RND * 155, 255 * (1 - p), 30))
  81.     NEXT
  82.     FOR q = 0 TO ((Mode * 3600) - (3600)) STEP (3600)
  83.         CALL UpdateTime(q)
  84.         CALL UpdateClock
  85.         CALL ccircle(HourHand.HandPosition.x, HourHand.HandPosition.y, 6, HourHand.Shade)
  86.         CALL ccirclefill(HourHand.HandPosition.x, HourHand.HandPosition.y, 5, _RGBA(0, 0, 0, 255))
  87.     NEXT
  88.     _DEST MainScreen
  89.  
  90. SUB KeyProcess
  91.     IF (_KEYDOWN(32) = -1) THEN ' Space
  92.         TimeShift = -TIMER
  93.     END IF
  94.     IF ((_KEYDOWN(114) = -1) OR (_KEYDOWN(84) = -1)) THEN ' r or R
  95.         TimeShift = 0
  96.     END IF
  97.     IF (_KEYDOWN(19200) = -1) THEN ' Leftarrow
  98.         CALL DecreaseMode
  99.         CALL PrepareClockface(1)
  100.         _DELAY .1
  101.     END IF
  102.     IF (_KEYDOWN(19712) = -1) THEN ' Rightarrow
  103.         CALL IncreaseMode
  104.         CALL PrepareClockface(1)
  105.         _DELAY .1
  106.     END IF
  107.     IF (_KEYDOWN(18432) = -1) THEN
  108.         TimeShift = TimeShift + 60 ' Uparrow
  109.     END IF
  110.     IF (_KEYDOWN(20480) = -1) THEN ' Downarrow
  111.         TimeShift = TimeShift - 60
  112.     END IF
  113.  
  114. SUB UpdateTime (z AS DOUBLE)
  115.     DIM t AS DOUBLE
  116.     t = z
  117.     TheTime.Hour = INT(t / 3600)
  118.     t = t - TheTime.Hour * 3600
  119.     TheTime.Hour = TheTime.Hour MOD Mode
  120.     IF (TheTime.Hour = 0) THEN TheTime.Hour = Mode
  121.     TheTime.Minute = INT(t / 60)
  122.     t = t - TheTime.Minute * 60
  123.     TheTime.Second = t
  124.     TheTime.TenthSecond = TheTime.Second - INT(TheTime.Second)
  125.  
  126. SUB UpdateClock
  127.     HourHand.Angle = -((TheTime.Hour + (TheTime.Minute / 60) + (TheTime.Second / 3600)) / Mode) * 2 * pi + (pi / 2)
  128.     MinuteHand.Angle = -((TheTime.Minute / 60) + (TheTime.Second / 3600)) * 2 * pi + (pi / 2)
  129.     SecondHand.Angle = -(TheTime.Second / 60) * 2 * pi + (pi / 2)
  130.     TenthSecondHand.Angle = -(TheTime.TenthSecond) * 2 * pi + (pi / 2)
  131.  
  132.     HourHand.HandPosition.x = HourHand.Center.x + HourHand.Length * COS(HourHand.Angle)
  133.     HourHand.HandPosition.y = HourHand.Center.y + HourHand.Length * SIN(HourHand.Angle)
  134.     MinuteHand.Center.x = HourHand.HandPosition.x
  135.     MinuteHand.Center.y = HourHand.HandPosition.y
  136.     MinuteHand.HandPosition.x = MinuteHand.Center.x + MinuteHand.Length * COS(MinuteHand.Angle)
  137.     MinuteHand.HandPosition.y = MinuteHand.Center.y + MinuteHand.Length * SIN(MinuteHand.Angle)
  138.     SecondHand.Center.x = MinuteHand.HandPosition.x
  139.     SecondHand.Center.y = MinuteHand.HandPosition.y
  140.     SecondHand.HandPosition.x = SecondHand.Center.x + SecondHand.Length * COS(SecondHand.Angle)
  141.     SecondHand.HandPosition.y = SecondHand.Center.y + SecondHand.Length * SIN(SecondHand.Angle)
  142.  
  143.     TenthSecondHand.Center.x = SecondHand.HandPosition.x
  144.     TenthSecondHand.Center.y = SecondHand.HandPosition.y
  145.     TenthSecondHand.HandPosition.x = TenthSecondHand.Center.x + TenthSecondHand.Length * COS(TenthSecondHand.Angle)
  146.     TenthSecondHand.HandPosition.y = TenthSecondHand.Center.y + TenthSecondHand.Length * SIN(TenthSecondHand.Angle)
  147.  
  148.  
  149. SUB DrawEverything
  150.     CLS
  151.     _PUTIMAGE (0, 0)-(_WIDTH, _HEIGHT), BackScreen, MainScreen, (0, 0)-(_WIDTH, _HEIGHT)
  152.     CALL DrawModeList
  153.     CALL DrawHUD
  154.     CALL DrawClockHands
  155.     CALL DrawDigitalClock
  156.     _DISPLAY
  157.  
  158. SUB DrawModeList
  159.     DIM k AS INTEGER
  160.     FOR k = 1 TO UBOUND(ModeList)
  161.         IF (Mode = k) THEN
  162.             COLOR _RGBA(255, 255, 0, 255), _RGBA(0, 0, 255, 255)
  163.         ELSE
  164.             COLOR _RGBA(100, 100, 100, 255), _RGBA(0, 0, 0, 0)
  165.         END IF
  166.         _PRINTSTRING ((4 + 5 * k) * 8, _HEIGHT - (1) * 16), LTRIM$(RTRIM$(STR$(ModeList(k))))
  167.     NEXT
  168.     COLOR _RGBA(200, 200, 0, 255), _RGBA(0, 0, 0, 0)
  169.     _PRINTSTRING ((4 + 1) * 8, _HEIGHT - (1) * 16), ">"
  170.     _PRINTSTRING ((4 + 5 * (UBOUND(ModeList) + 1)) * 8, _HEIGHT - (1) * 16), "<"
  171.  
  172. SUB IncreaseMode
  173.     IF (Mode < 12) THEN
  174.         Mode = Mode + 1
  175.     ELSE
  176.         Mode = 1
  177.     END IF
  178.  
  179. SUB DecreaseMode
  180.     IF (Mode = 1) THEN
  181.         Mode = 12
  182.     ELSE
  183.         Mode = Mode - 1
  184.     END IF
  185.  
  186. SUB DrawClockHands
  187.     DIM k AS DOUBLE
  188.     DIM ctmp AS _UNSIGNED LONG
  189.     DIM SeedLength AS DOUBLE
  190.     SeedLength = 12
  191.     FOR k = 0 TO 1 STEP .01
  192.         ctmp = ColorMix(_RGBA(0, 0, 255, 255), HourHand.Shade, k)
  193.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), k * _ALPHA32(ctmp))
  194.         CALL ccirclefill(HourHand.Center.x + (k * HourHand.Length) * COS(HourHand.Angle), HourHand.Center.y + (k * HourHand.Length) * SIN(HourHand.Angle), k * SeedLength, ctmp)
  195.     NEXT
  196.     FOR k = 0 TO 1 STEP .01
  197.         ctmp = ColorMix(HourHand.Shade, MinuteHand.Shade, k)
  198.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  199.         CALL ccirclefill(MinuteHand.Center.x + (k * MinuteHand.Length) * COS(MinuteHand.Angle), MinuteHand.Center.y + (k * MinuteHand.Length) * SIN(MinuteHand.Angle), SeedLength * (1 - k / phi), ctmp)
  200.     NEXT
  201.     FOR k = 0 TO 1 STEP .005
  202.         ctmp = ColorMix(MinuteHand.Shade, SecondHand.Shade, k)
  203.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  204.         CALL ccirclefill(SecondHand.Center.x + (k * SecondHand.Length) * COS(SecondHand.Angle), SecondHand.Center.y + (k * SecondHand.Length) * SIN(SecondHand.Angle), (SeedLength * (1 - 1 / phi)) * (1 - k), ctmp)
  205.     NEXT
  206.     FOR k = 0 TO 1 STEP .005
  207.         ctmp = ColorMix(SecondHand.Shade, TenthSecondHand.Shade, k)
  208.         ctmp = _RGBA(_RED32(ctmp), _GREEN32(ctmp), _BLUE32(ctmp), _ALPHA32(ctmp))
  209.         CALL ccirclefill(TenthSecondHand.Center.x + (k * TenthSecondHand.Length) * COS(TenthSecondHand.Angle), TenthSecondHand.Center.y + (k * TenthSecondHand.Length) * SIN(TenthSecondHand.Angle), (SeedLength * (1 - 1 / phi)) * (1 - k), ctmp)
  210.     NEXT
  211.  
  212.     CALL DrawPulley(HourHand.Center.x, HourHand.Center.x, 0, HourHand.HandPosition.x, HourHand.HandPosition.y, SeedLength + 2, _RGBA(255, 255, 255, 255))
  213.     CALL DrawPulley(HourHand.HandPosition.x, HourHand.HandPosition.y, SeedLength + 2, MinuteHand.HandPosition.x, MinuteHand.HandPosition.y, (SeedLength * (1 - 1 / phi)) + 1, _RGBA(255, 255, 255, 255))
  214.     CALL DrawPulley(MinuteHand.HandPosition.x, MinuteHand.HandPosition.y, (SeedLength * (1 - 1 / phi)) + 1, SecondHand.HandPosition.x, SecondHand.HandPosition.y, 0, _RGBA(255, 255, 255, 255))
  215.     CALL DrawPulley(SecondHand.HandPosition.x, SecondHand.HandPosition.y, (SeedLength * (1 - 1 / phi)) + 1, TenthSecondHand.HandPosition.x, TenthSecondHand.HandPosition.y, 0, _RGBA(255, 255, 255, 255))
  216.  
  217. SUB DrawDigitalClock
  218.     DIM t AS STRING
  219.     COLOR _RGBA(200, 200, 0, 255), _RGBA(0, 0, 0, 0)
  220.     DIM h AS STRING
  221.     DIM m AS STRING
  222.     DIM s AS STRING
  223.     DIM n AS STRING
  224.     h = LTRIM$(RTRIM$(STR$(TheTime.Hour)))
  225.     IF LEN(h) = 1 THEN h = "0" + h
  226.     m = LTRIM$(RTRIM$(STR$(TheTime.Minute)))
  227.     IF LEN(m) = 1 THEN m = "0" + m
  228.     s = LTRIM$(RTRIM$(STR$(INT(TheTime.Second))))
  229.     IF LEN(s) = 1 THEN s = "0" + s
  230.     n = LTRIM$(RTRIM$(STR$((INT(10 * TheTime.TenthSecond)))))
  231.     t = h + ":" + m + ":" + s + ":" + n
  232.     LOCATE 1, (_WIDTH / 8) / 2 - LEN(t) / 2
  233.     PRINT t
  234.  
  235. SUB DrawHUD
  236.     COLOR _RGBA(0, 200, 200, 255), _RGBA(0, 0, 0, 0)
  237.     LOCATE 1, 2: PRINT "SPACE = Stopwatch"
  238.     LOCATE 2, 2: PRINT "    R = Reset"
  239.     LOCATE 1, 59: PRINT "UpArrow = Time +"
  240.     LOCATE 2, 59: PRINT "DnArrow = Time -"
  241.  
  242. FUNCTION ColorMix~& (Shade1 AS _UNSIGNED LONG, Shade2 AS _UNSIGNED LONG, param AS DOUBLE)
  243.     ColorMix~& = _RGB32((1 - param) * _RED32(Shade1) + param * _RED32(Shade2), (1 - param) * _GREEN32(Shade1) + param * _GREEN32(Shade2), (1 - param) * _BLUE32(Shade1) + param * _BLUE32(Shade2))
  244.  
  245. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  246.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  247.  
  248. SUB cline (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
  249.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  250.  
  251. SUB ccircle (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  252.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  253.  
  254. SUB ccirclefill (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  255.     CALL CircleFill(_WIDTH / 2 + x1, -y1 + _HEIGHT / 2, rad, col)
  256.  
  257. SUB CircleFill (CX AS INTEGER, CY AS INTEGER, R AS INTEGER, C AS _UNSIGNED LONG)
  258.     ' CX = center x coordinate
  259.     ' CY = center y coordinate
  260.     '  R = radius
  261.     '  C = fill color
  262.     DIM Radius AS INTEGER, RadiusError AS INTEGER
  263.     DIM X AS INTEGER, Y AS INTEGER
  264.     Radius = ABS(R)
  265.     RadiusError = -Radius
  266.     X = Radius
  267.     Y = 0
  268.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  269.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  270.     WHILE X > Y
  271.         RadiusError = RadiusError + Y * 2 + 1
  272.         IF RadiusError >= 0 THEN
  273.             IF X <> Y + 1 THEN
  274.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  275.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  276.             END IF
  277.             X = X - 1
  278.             RadiusError = RadiusError - X * 2
  279.         END IF
  280.         Y = Y + 1
  281.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  282.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  283.     WEND
  284.  
  285. SUB DrawPulley (x1 AS DOUBLE, y1 AS DOUBLE, rad1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, rad2 AS DOUBLE, col AS _UNSIGNED LONG)
  286.     'Inspiration credit: {(bplus)(qb64.org)(2020-11-22)}
  287.     DIM ang AS DOUBLE
  288.     ang = _ATAN2(y2 - y1, x2 - x1) + pi / 2
  289.     CALL lineSmooth(x1 + rad1 * COS(ang), y1 + rad1 * SIN(ang), x2 + rad2 * COS(ang), y2 + rad2 * SIN(ang), col)
  290.     CALL lineSmooth(x1 - rad1 * COS(ang), y1 - rad1 * SIN(ang), x2 - rad2 * COS(ang), y2 - rad2 * SIN(ang), col)
  291.     CALL ccircle(x1, y1, rad1, col)
  292.     CALL ccircle(x2, y2, rad2, col)
  293.  
  294. SUB lineSmooth (x0, y0, x1, y1, c AS _UNSIGNED LONG)
  295.     'Inspiration credit: {(FellippeHeitor)(qb64.org)(2020)}
  296.     '                    {https://en.wikipedia.org/w/index.php?title=Xiaolin_Wu%27s_line_algorithm&oldid=852445548}
  297.     'Edit: {(STxAxTIC)(2020-11-20)(Correction to alpha channel.)}
  298.  
  299.     DIM plX AS INTEGER, plY AS INTEGER, plI
  300.  
  301.     DIM steep AS _BYTE
  302.     steep = ABS(y1 - y0) > ABS(x1 - x0)
  303.  
  304.     IF steep THEN
  305.         SWAP x0, y0
  306.         SWAP x1, y1
  307.     END IF
  308.  
  309.     IF x0 > x1 THEN
  310.         SWAP x0, x1
  311.         SWAP y0, y1
  312.     END IF
  313.  
  314.     DIM dx, dy, gradient
  315.     dx = x1 - x0
  316.     dy = y1 - y0
  317.     gradient = dy / dx
  318.  
  319.     IF dx = 0 THEN
  320.         gradient = 1
  321.     END IF
  322.  
  323.     'handle first endpoint
  324.     DIM xend, yend, xgap, xpxl1, ypxl1
  325.     xend = _ROUND(x0)
  326.     yend = y0 + gradient * (xend - x0)
  327.     xgap = (1 - ((x0 + .5) - INT(x0 + .5)))
  328.     xpxl1 = xend 'this will be used in the main loop
  329.     ypxl1 = INT(yend)
  330.     IF steep THEN
  331.         plX = ypxl1
  332.         plY = xpxl1
  333.         plI = (1 - (yend - INT(yend))) * xgap
  334.         GOSUB plot
  335.  
  336.         plX = ypxl1 + 1
  337.         plY = xpxl1
  338.         plI = (yend - INT(yend)) * xgap
  339.         GOSUB plot
  340.     ELSE
  341.         plX = xpxl1
  342.         plY = ypxl1
  343.         plI = (1 - (yend - INT(yend))) * xgap
  344.         GOSUB plot
  345.  
  346.         plX = xpxl1
  347.         plY = ypxl1 + 1
  348.         plI = (yend - INT(yend)) * xgap
  349.         GOSUB plot
  350.     END IF
  351.  
  352.     DIM intery
  353.     intery = yend + gradient 'first y-intersection for the main loop
  354.  
  355.     'handle second endpoint
  356.     DIM xpxl2, ypxl2
  357.     xend = _ROUND(x1)
  358.     yend = y1 + gradient * (xend - x1)
  359.     xgap = ((x1 + .5) - INT(x1 + .5))
  360.     xpxl2 = xend 'this will be used in the main loop
  361.     ypxl2 = INT(yend)
  362.     IF steep THEN
  363.         plX = ypxl2
  364.         plY = xpxl2
  365.         plI = (1 - (yend - INT(yend))) * xgap
  366.         GOSUB plot
  367.  
  368.         plX = ypxl2 + 1
  369.         plY = xpxl2
  370.         plI = (yend - INT(yend)) * xgap
  371.         GOSUB plot
  372.     ELSE
  373.         plX = xpxl2
  374.         plY = ypxl2
  375.         plI = (1 - (yend - INT(yend))) * xgap
  376.         GOSUB plot
  377.  
  378.         plX = xpxl2
  379.         plY = ypxl2 + 1
  380.         plI = (yend - INT(yend)) * xgap
  381.         GOSUB plot
  382.     END IF
  383.  
  384.     'main loop
  385.     DIM x
  386.     IF steep THEN
  387.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  388.             plX = INT(intery)
  389.             plY = x
  390.             plI = (1 - (intery - INT(intery)))
  391.             GOSUB plot
  392.  
  393.             plX = INT(intery) + 1
  394.             plY = x
  395.             plI = (intery - INT(intery))
  396.             GOSUB plot
  397.  
  398.             intery = intery + gradient
  399.         NEXT
  400.     ELSE
  401.         FOR x = xpxl1 + 1 TO xpxl2 - 1
  402.             plX = x
  403.             plY = INT(intery)
  404.             plI = (1 - (intery - INT(intery)))
  405.             GOSUB plot
  406.  
  407.             plX = x
  408.             plY = INT(intery) + 1
  409.             plI = (intery - INT(intery))
  410.             GOSUB plot
  411.  
  412.             intery = intery + gradient
  413.         NEXT
  414.     END IF
  415.  
  416.     EXIT SUB
  417.  
  418.     plot:
  419.     ' Change to regular PSET for standard coordinate orientation.
  420.     CALL cpset(plX, plY, _RGB32(_RED32(c), _GREEN32(c), _BLUE32(c), plI * _ALPHA32(c)))
  421.     RETURN
tenth.png
* tenth.png (Filesize: 387.01 KB, Dimensions: 600x625, Views: 86)
You're not done when it works, you're done when it's right.