Currently you can select an item in the annotation list on the right hand side of the user interface window, and it is highlighted in the fractal explorer window. But when you have a lot of annotations, trying to find a specific annotation gets tedious. So I want to make it so I can click in the explorer window and have it select the annotation in the list.
I think I can do this by drawing the annotations (using the #CairoGraphics library) into an off-screen buffer with anti-aliasing turned off and annotation ID values as colours. I'll probably draw the rays thicker than 1 pixel in this buffer, otherwise selecting will be a nightmare. I already have one off-screen buffer used for text collision/overlap avoidance (aka dynamic level of detail), I might be able to reuse it. Ray sort order should be by (preperiod+period) (lowest drawn last / on top) to be intuitive, text b-boxes on top.
Add a button to annotate an embedded Julia set automatically, because marking all those points and rays is a bit boring.
Add a number box for hiding rays whose (preperiod+period) is higher than this threshold, as a stop-gap until I figure out how to choose this threshold (or another way to prune rays) automatically.
Add a button to annotate the descendent child bulbs of a component.
Make this button annotate the tuned islands too.
Add rays, namely the periodic pair landing on the root, and pre-periodic pairs pruning the filaments.
✅ first medium part done
❎ hard part needs a completely different approach
❎ second medium part is fairly straightforward, next on the list
Automatic annotation progress update:
✅ child bulbs of a mu-unit
✅ filaments of a mu-unit (done, this post)
❎ child islands of a mu-unit
❎ embedded Julia set filaments (next on the list)
❎ embedded Julia set islands
❎ embedded Julia set hubs
Mu-unit is Robert Munafo's terminology: https://www.mrob.com/pub/muency/muunit.html
Seems to work for doubly-embedded Julia sets, though it gets prohibitively slow without multi-core acceleration (~45mins to wait for the annotations on the attached, when it could take 3mins if all my cores were used in parallel). Image radius is about 1e-14, so not very deep, but tracing all those rays takes long.
Working on an asynchronous task queue with a worker pool now. I want each task to show up in the GUI when it is enqueued, with its own progress bar and cancel button; to be removed from GUI when it is done (when completed, it adds the annotation to the image, unless cancelled).
Ideally I will be able to continue interacting while tasks are running, enqueuing new tasks or even navigating to different locations. It remains to be seen whether I will need a priority system to make rendering more responsive.
Got the asynchronous task list working. Now I can initiate many annotation tasks and continue zooming while they calculate in the background in as many threads as are available.
The overall GUI layout is bad at the moment (space around image, task list goes off screen without scroll bars, stretching the window, progress bars have tiny height) and the only algorithm I've ported to the new thread pool is Ray Out.
But it's a start.
✅ click to select
Mouse clicks in the exploration window can select nearby annotations in the annotation list. Wasn't as hard as expected, implementation was as described at the top of this thread.
https://mathr.co.uk/mandelbrot/2019-10-22_m-perturbator-gtk_click_to_select.mp4 1920x1080p60 2mins 15MB
(also showing light vs dark theme, line dashing patterns, and version string in title bar)
Another idea for a feature: option to filter the annotation list to list only annotations that are visible in the exploration window.
feature idea: fill hyperbolic components
- trace boundary using Newton's method in two complex variables
- adaptive subdivision of visible parts, or
- compute control points for cubic Bezier spline segments so that the curve passes through the desired points
- make sure cusps of cardioids are sharp
- be careful near roots of circles
- pattern fill for low-ink printing
feature idea: fill wakes of ray pairs
- compute lowest iteration count in image (which occurs on its boundary)
- follow lower ray outwards from endpoint P0 to that count, keeping track of the last intersection P1 with image edges
- follow upper ray outwards from endpoint Q0 to that count, keeping track of all intersections with image edges
- find first intersection Q1 of upper ray with image edge anticlockwise from P1
- fill region P0-(along lower ray)-P1-(along image edges)-Q1-(along upper ray)-Q0-(close loop)-P0
- this is so complicated because rays may have multiple segments within the image, and naive filling of the whole ray extent to a fixed large radius at deep zooms will overflow libcairo's number types and explode everything in NaNs
- it may even need to be yet more complicated, considering deep zooms off-centre from spirals, where the above could still overflow: solution may be to compute all intersections with image edges of both rays along with iteration counts at those points and whether the ray is leaving or entering the image, so that they can be ordered semantically, with the direction of drawing at image edges determined by consistency
Turns out it was much simpler to just clamp the potentially huge wake image coordinates to +/-10 in mpfr_t before converting to lower-range double for cairo filling.
The image is roughly +/-1 in that coordinate frame, depending on aspect ratio - clamping may break appearance with very wide images, left a #FIXME note in the code for later.
new features implemented:
✅ wake clipping (draw from narrowest to widest, subtracting each from clip region after drawing, prevents overlap)
✅ pattern fills (select from dropdown combo box in toolbar before activating wake tool)
✅ colour fills (select from colour button in toolbar before activating wake tool)
✅ global toggle of colour/monochrome mode (so you can use colours for screen editing, and turn them off for printing)
more ideas to implement:
❎ medium colour mode with differently coloured pattern fills (bit tricky as cairo patterns have their own colour, so would need to create patterns on the fly instead of once at program startup)
❎ colour strokes (should be easy, just boring plumbing the values through the code)
❎ editing patterns / colours of existing annotations (could be hard, but should be possible to add widgets for each annotation in the annotation list)
❎ better algorithm for "find the other ray of the wake" than "next (anti)clockwise match for (pre)period") (maybe require the two rays to be manually selected in the UI, or use screen-space distance of endpoints as a filter) (see nested red areas in colour images attached)
❎ extending rays of wakes (not sure how to do this, the wake makes a copy of the rays' points at wake creation time)
❎ make the filament tool fill its wakes
The new stuff (wakes, patterns, colours, dark vs light theme, colour vs monochrome theme) isn't serialized yet, and it will be a pain doing it in a backward compatible way (will be annoying if loading old parameters is no longer possible in new versions - I think I already broke it once when adding line dashing or so...).
✅ colour ray strokes
✅ take screen-space distance into account when finding other ray for wakes
✅ editing line dashing / colour / fill pattern of existing annotations (where appropriate; via existing widgets)
✅ three colour levels (monochrome, low colour, full colour; see attached; also works works dark theme)
✅ (de)serialization of everything (might be backward incompatible, breaking old parameters again, but now parameters save version info so should be easier to migrate the format in future)
❎ wakes can only be extended by modifying parameter files in a text editor and reloading from scratch
❎ the toolbar is overflowing making some tool options inaccessible, need to restructure into less spacious menu items
Hometown is adapted from Mastodon, a decentralized social network with no ads, no corporate surveillance, and ethical design.