Taking a break from the ZX81 to play with something more colourful: #retrocomputing
For completeness, here's an Electron version. Pro: no colour clash. Con: had to write my own circle-drawing routine. Pro: in BBC BASIC, you can do this as a PROC. #retrocomputing
The Electron is not a fast computer (the Spectrum was significantly quicker; I could probably close the gap in this case by writing the circle drawing in assembler rather than BASIC.) #retrocomputing
No assembly required: just increasing the step to 0.3 radians (up from 0.1) matches the Spectrum without a noticeable loss of circularity at that size. #retrocomputing
@robhague Oh, no, no, no! You're supposed to use a lovely little extension of Bresenham's to draw a perfect circle almost instantaneously. I'm shocked you didn't put the effort in to do that. :)
@sgf I know; terrible lack of diligence. I just couldn't recall the details of that (and wanted something quick).
@sgf So I took the bait and tried it out (in an emulator this time, not the real hardware), and the results were interesting. If you stick with BASIC, the Bresenham's version seems to be a lot slower, presumably because of all the calls to PLOT. The result is more accurate, but the differences are fairly minimal.
(This is assuming I've not made a mistake with the implementation, which as I just dashed it off in a few minute is quite possible.)
https://bbcmic.ro/#%7B%22v%22%3A1%2C%22program%22%3A%22MODE%202%5Cn%5CnREM%20Midpoint%2FBresenham%5CnTIME%3D0%5CnPROCcircleB%28512%2C512%2C256%29%5CnPRINT%20TIME%5Cn%5CnREM%20Trig%20%28step%200.1%29%5CnGCOL%200%2C1%5CnTIME%3D0%5CnPROCcircleT%28512%2C512%2C256%2C%200.1%29%5CnPRINT%20TIME%5Cn%5CnREM%20Trig%20%28step%200.3%29%5CnGCOL%200%2C2%5CnTIME%3D0%5CnPROCcircleT%28512%2C512%2C256%2C%200.3%29%5CnPRINT%20TIME%5Cn%5CnEND%5Cn%5CnDEF%20PROCcircleB%28cx%2Ccy%2Cr%29%5Cnt1%20%3D%20r%2F16%5Cnx%20%3D%20r%5Cny%20%3D%200%5CnREPEAT%5CnPLOT%2069%2Ccx%2Bx%2Ccy%2By%5CnPLOT%2069%2Ccx-x%2Ccy%2By%5CnPLOT%2069%2Ccx%2Bx%2Ccy-y%5CnPLOT%2069%2Ccx-x%2Ccy-y%5CnPLOT%2069%2Ccx%2By%2Ccy%2Bx%5CnPLOT%2069%2Ccx-y%2Ccy%2Bx%5CnPLOT%2069%2Ccx%2By%2Ccy-x%5CnPLOT%2069%2Ccx-y%2Ccy-x%5Cny%20%3D%20y%20%2B%201%5Cnt1%20%3D%20t1%20%2B%20y%5Cnt2%20%3D%20t1%20-%20x%5CnIF%20t2%20%3E%3D%200%20THEN%20t1%20%3D%20t2%3A%20x%20%3D%20x%20-%201%5CnUNTIL%20x%3Dy%5CnENDPROC%5Cn%5CnDEF%20PROCcircleT%28cx%2Ccy%2Cr%2Cs%29%5Cntau%3DPI*2%2B0.3%5CnMOVE%20cx%2Ccy%2Br%5CnFOR%20t%3D0%20TO%20tau%20STEP%20s%5CnDRAW%20cx%2BSIN%28t%29*r%2C%20cy%2BCOS%28t%29*r%5CnNEXT%5CnENDPROC%22%7D
@robhague Wow! As always, when optimizing, real-world performance beats theory...
That's fascinating, and pretty unexpected to my mind. In terms of PLOT being slow, I wonder if this is doing all the Bresenham maths in FP when the line-drawer can just use assembly-optimized integers?
Sorry to geek-snipe you, I hope it was a good diversion!
@sgf The integer point is a good one — I forgot that it didn't default to that. Adding % to all the variables speeds it up a by about a second, but still around half of the naive one.
Another issue I realised is that it's working in coordinate space rather than pixel space, so it's drawing something like four times as many pixels as it needs to. That's a more involved change, but I might give it a go if I get a moment...
@sgf As another data point, the Acorn Graphics Extension ROM does it in about 100ms: https://bbcmic.ro/#%7B%22v%22%3A1%2C%22program%22%3A%22MODE%202%5Cn%5CnREM%20Midpoint%2FBresenham%5CnTIME%3D0%5CnPROCcircleB%28512%2C512%2C256%29%5CnPRINT%20TIME%5Cn%5CnREM%20Trig%20%28step%200.1%29%5CnGCOL%200%2C1%5CnTIME%3D0%5CnPROCcircleT%28512%2C512%2C256%2C%200.1%29%5CnPRINT%20TIME%5Cn%5CnREM%20Acorn%20Graphics%20Extension%20ROM%5CnGCOL%200%2C2%5CnTIME%3D0%5CnMOVE%20512%2C512%5CnPLOT%20%2695%2C768%2C512%5CnPRINT%20TIME%5Cn%5CnEND%5Cn%5CnDEF%20PROCcircleB%28cx%25%2Ccy%25%2Cr%25%29%5Cnt1%25%20%3D%20r%25%2F16%5Cnx%25%20%3D%20r%25%5Cny%25%20%3D%200%5CnREPEAT%5CnPLOT%2069%2Ccx%25%2Bx%25%2Ccy%25%2By%25%5CnPLOT%2069%2Ccx%25-x%25%2Ccy%25%2By%25%5CnPLOT%2069%2Ccx%25%2Bx%25%2Ccy%25-y%25%5CnPLOT%2069%2Ccx%25-x%25%2Ccy%25-y%25%5CnPLOT%2069%2Ccx%25%2By%25%2Ccy%25%2Bx%25%5CnPLOT%2069%2Ccx%25-y%25%2Ccy%25%2Bx%25%5CnPLOT%2069%2Ccx%25%2By%25%2Ccy%25-x%25%5CnPLOT%2069%2Ccx%25-y%25%2Ccy%25-x%25%5Cny%25%20%3D%20y%25%20%2B%201%5Cnt1%25%20%3D%20t1%25%20%2B%20y%25%5Cnt2%25%20%3D%20t1%25%20-%20x%25%5CnIF%20t2%25%20%3E%3D%200%20THEN%20t1%25%20%3D%20t2%25%3A%20x%25%20%3D%20x%25%20-%201%5CnUNTIL%20x%25%3Dy%25%5CnENDPROC%5Cn%5CnDEF%20PROCcircleT%28cx%25%2Ccy%25%2Cr%25%2Cs%29%5Cntau%3DPI*2%2B0.3%5CnMOVE%20cx%25%2Ccy%25%2Br%25%5CnFOR%20t%3D0%20TO%20tau%20STEP%20s%5CnDRAW%20cx%25%2BSIN%28t%29*r%25%2C%20cy%25%2BCOS%28t%29*r%25%5CnNEXT%5CnENDPROC%5Cn%5CnDEF%20PROCcircleB2%28cx%25%2Ccy%25%2Cr%25%29%5Cnt1%25%20%3D%20r%25%2F16%5Cnx%25%20%3D%20r%25%5Cny%25%20%3D%200%5CnREPEAT%5CnPLOT%2069%2Ccx%25%2Bx%25%2Ccy%25%2By%25%5CnPLOT%2069%2Ccx%25-x%25%2Ccy%25%2By%25%5CnPLOT%2069%2Ccx%25%2Bx%25%2Ccy%25-y%25%5CnPLOT%2069%2Ccx%25-x%25%2Ccy%25-y%25%5CnPLOT%2069%2Ccx%25%2By%25%2Ccy%25%2Bx%25%5CnPLOT%2069%2Ccx%25-y%25%2Ccy%25%2Bx%25%5CnPLOT%2069%2Ccx%25%2By%25%2Ccy%25-x%25%5CnPLOT%2069%2Ccx%25-y%25%2Ccy%25-x%25%5Cny%25%20%3D%20y%25%20%2B%201%5Cnt1%25%20%3D%20t1%25%20%2B%20y%25%5Cnt2%25%20%3D%20t1%25%20-%20x%25%5CnIF%20t2%25%20%3E%3D%200%20THEN%20t1%25%20%3D%20t2%25%3A%20x%25%20%3D%20x%25%20-%201%5CnUNTIL%20x%25%3Dy%25%5CnENDPROC%22%7D
@sgf Moving to pixel space wasn't as complex as I thought, with a big caveat: MODE 2 pixels aren't actually square, so we're still drawing some pixels twice. Nevertheless, this version is now faster than the trig one.
@robhague I'll call that a win! I hope you've enjoyed doing this as much as I've enjoyed reading it. :)
@sgf I tinkered with it a bit more on and off this morning, and managed to knock another 20% or so off by moving the PLOTs into assembly. It's an enjoyably constrained problem — thanks for sniping. Might have to the it into a blog post.