Refactor 2022 #8

At one extreme you have the repeating pattern in wall paper, at the other extreme there is the total randomness of white noise. Both extremes are boring. (Although, back in the day, you could tune your television to a frequency between broadcast stations and enjoy a hypnotic and continuously morphing display of white noise.)

There is a lot of room between those two extremes, here are I few I feel are close to the middle.

Refactor 2022 #7

The marble or wood grain surfaces initially seem uniform. But then under scrutiny one sees a random pattern. I am fascinated by that boundary between random and not-random. I have explored that space this several times in this blog, and will do it again.

Nature abounds with good examples. I am not trying to reproduce nature per se, but rather the abstraction of an interplay between random and not random.

Refactor 2022 #6

I tend to easily get sidetracked. I put together the images in the previous and next few posts to test certain functionality. In this case various anti-alias algorithms. But then I get interested in the images for their own value.

The images could be a polished marble surface. I enjoy looking at the shapes and grain in stone and wood and similar surfaces. The patterns on a similar faux surface hold my interest only until I see where they repeat, then disappointment, as I can never not see the repetition again.

Refactor 2022 #5

So, what about these pictures?

I used simple things like lines to test my anti-alias code changes. When that was looking good I wanted a more complex example like a fractal. A fractal with a lot of details takes a while to generate, so I decided to look for something simpler and quicker. This is the result.

If you put the previous image side by side with this one you will see the difference that anti-aliasing makes. This one uses a simple average on four point on grid within a pixel. In sub-pixel coordinated, the sample points are (0.25, 0.25), (0.25,0.75), (0.75,0.25) and (0.75, 0.75).

Refactor 2022 #4

(Anti-Aliasing continued) In the line example it is possible to calculate the percentage of the pixel covered by the line. Then one can do anti-aliasing as described in the previous post. Squares and lines are simple things. The percentage calculation is not possible in general.

Generative art, such as fractals, but also anything more complex than simple geometric objects, also suffer from the equivalent of jagged lines. It is no longer a question of "in or out". That single pixel may contain many colors. Choosing just one of the colors generates noisy (sparkly) images. So in this case one samples several points within the pixel area and averages the results.

A very long time ago I set up an anti-alias method by sampling nine points on a 3x3 grid inside the pixel and then taking the simple average of the colors. That was the first thing that I tried, it worked well enough so I did not try anything else. I carried that code forward from program to program over the years.

There are other ways to the job. A 4x4 grid may give better results. A 2x2 grid would take less than half the time. Five points, four corners and the center may be good enough. Arguments could be made for a weighted average rather than a simple average. Also a good case can be made to use random points rather than points on a gird.

As part of this refactor journey I decided to add these options to my anti-alias code. I can then tweak the parameters and choose the best tradeoff between speed and noticeable quality improvement.

Refactor 2022 #3

All of the images in this blog, until recently, use a process called anti-aliasing. See https://en.wikipedia.org/wiki/Supersampling

If, for example, you want to draw a line on the computer screen, you could calculate which pixels are on the line and light them up. This almost works. We naturally think of a pixel as a zero-dimensional point, and the line as one dimensional. But a line needs some width, or it would be invisible, and a single pixel covers some small area of the display. A typical monitor has about 100 dpi, so a pixel is actually a 0.01 inch x 0.01 inch square. The pixels are very small, but if you build a diagonal line out of these square pixels, you will still see the corners of the individual pixels. The line looks jagged.

These edge pixels are not entirely on or entirely off the line. The line covers some intermediate portion of the pixel. A better way to draw these pixels is to consider the partial coverage. If the line covers 30% of the pixel, then give the pixel a blend of 30% the line color and 70% the background color.

Refactor 2022 #2

As with porting, when refactoring, the changes need testing. Sometimes the test cases generate interesting pictures. Sometimes they motivate the artistic side and the test case becomes a jumping off point for new creations. I like to explore the inspiration when it happens. But the program, being in the middle of an overhaul, is in a less stable state than usual.

Here the goal is to use the rugged terrain algorithm from early posts to add texture to a simple fractal. The previous picture looks like painting on a coarse canvas, or perhaps a needle point. Today's picture is the same design, but with the texture dialed back some.

Refactor 2022 #1

New Series, Refactor 2022.

Refactoring is for programmers who reject the advice "If it ain't broke, don't fix it".

Refactoring is actually a good thing. A working program may also be very fragile. A fragile program is difficult to modify without breaking it. Computer programs naturally tend to the state "working, but just barely". Refactoring cleans up the program, organizes it, makes it easier to understand and modify.

Refactor 2022 is a continuation or the Port 22 series. The porting task was finished some time ago, but as long as I had the hood open I decided to tinker with a few things. As usual one thing leads to another and two months later I feel like I am further away from my goal than when I started.

Port 22 #14

Same shapes as the previous one, with different coloring.

Port 22 #13

Port 22 #12

Back to rectangles intersecting rough terrain. A lava flow perhaps?

Port 22 #11

Another test case that evolved into a finished product.

Port 22 #10

Inspired by topographical maps.

While working on an algorithm to detect the intersection of two objects, some tests were needed. I put together test cases for various simple 2d and 3d objects such as lines, planes, spheres. For a more challenging test case, I created an bumpy terrain and tried to intersect it with a flat rectangle.

The test passed immediately, but I headed off on a tangent to create interesting, surreal landscapes. The results of that foray make up the next few posts.

Port 22 #9

Another one with imaginary lights reflecting off an invisible surface.

Port 22 #8

The new Visual Studio is working now. I decided that while I had the hood open, it would be a good time to tweak some other things. I had a long list of things that work, but could be made to work better.

Of course little things tend to become big things, and that great alternate design turns out to not be so great. I just accept that this is part of the process of learning and improving. I am not going to bore you with the details. It is mentioned only to provide an excuse for the recent scarcity of blog posts.

Along the way while making these program changes, I create test images. The main motivation is of course to test and explore new ideas. Some of those test images are interesting on their own. I classify these as "work in process" and save them to develop further in the future. For now, I need to keep focus on adding / improving program features.

This one gives an impression of colored lights in a dark room.

Port 22 #7

My posts have slowed down in the last three months. My program port to VS2022 has been complete for some time now. Previously I described two problems with the port. The first was solved by loading unnecessary dlls to get the compiler to work. The second, AssemblyLoadContext is a poor substitute for AppDomain, has two consequences; it wastes memory and prevents recovery when an add-on crashes or hangs. I grew up in an era where external storage access and memory were expensive. I need to keep reminding myself that this is not a problem in the 21st century. And if an add-on fails, I terminate the run and restart in debug mode. It does not happen often and when it does I usually need the debug tools to find fix the flaws anyway.

So I am content to live with the problems.

Port 22 #6

Another chaotic swirl.

The next problem with the software port was with AppDomains. An AppDomain is an isolated environment to run a program, or a part of a program. Every program runs in an AppDomain. The main program AppDomain is created when the program starts and taken down when the program exits. You never notice it or think about it. The tricky part is when you want more than one AppDomain.

A second AppDomain is useful with add-on code as described in the last couple of posts. If you load a program add-on into the main AppDomain, you cannot get rid of the add-on without terminating the main program. By loading the add-on into a separate AppDomain, you can discard the AppDomain, and the add-on code when you are done with it. I do this to recover the memory space used by the add-on, and to protect against crashes. The add-on code tends to be more experimental and less tested than the main program code.

My art program is the only program I have that uses more than one AppDomain. So I am deep into the port to VS 2022 when I learn that AppDomain support has been removed from .Net 6. Not changed, just flat out not supported.

The next best thing appears to be the AssemblyLoadContext class. You can load the add-on dll into a separate AssemblyLoadContext. When the add-on is done running the garbage collector eventually runs and frees the space. Well that is the theory at least.

There is a known bug with AssemblyLoadContext garbage collection when it runs WPF (Windows Presentation Foundation) code. WPF creates objects for windows and other display components. Some of these objects are never released, even after the window is closed. That prevents garbage collection. At the time I was discovering these problems the recommended work-around was the ever-so-helpful "just don't do that". In my case, my add-ons often come with custom editors which use WPF, so the recommended "solution" is not feasible. It may have been fixed by now, but I am not in a mood to fight that battle again.

I have given up on AppDomain and AssemblyLoadContext. Computers have so much memory these days, that worrying about add-on code memory use is a misplaced concerned. The memory used by the add-on program code is insignificant compared to the memory used for a single high resolution picture. As for program code defects, I just need to be more careful.

Port 22 #5

Another exotic formula. Looks like something mixing or flowing.

Continuing my VS2022 porting story. My first attempt to upgrade failed, a few months later I tried again, this time with more realistic expectations of the time commitment.

I had some success porting some smaller, and some not so small programs to the new framework. I did the first few slowly and carefully. Making sure I understood all of the differences. I compiled a detailed checklist, and the other programs ported smoothly. Eventually I started on the Art program, and had some initial success. The bulk of the program was up and running. Then I hit a wall. Two problems, both related to the dynamic, in-program compile and run add-on code feature.

The Roslyn compile needs a list of dlls used by the program code being compiled. The list contains both the dlls with that I wrote and dlls that contain the .Net 6 library features that I use. In the past it was easy to find the library dlls. If I guessed wrong, then the compiler error message would tell me which type (object) I missed, and the documentation told me the dll that has the missing type. The documentation is not reliable with .net 6. Adding the dll listed in the documentation does not find the missing type. Also, VS2022 introduces two types of dlls (with identical names), one for compilation and one for execution. Grabbing the wrong one causes failure, but only some of the time.

Eventually I made a list of all dlls included with .net 6 and fed that list to the compiler. It works. Since over 90% of them are not used, I know it slows down the compile step. That slowdown is in computer time, I do not notice it. In a whole lifetime, that less than a second delay each time I compile an add-on will not add up to the time already spent trying to debug the problem. This is a good example where trying to be efficient, pick out exactly the dlls I use, turns out to be very inefficient.

Port 22 #4

Here is a test of an escape time fractal with an exotic generating formula.

Now this discussion goes deep into the weeds of programming and development tools. Feel free to skip it if you are just here for the pictures.

Often a fractal program will have a way to add new code to implement formulas and other ideas that the base program does not provide. Today's picture is an example. Typically this is done in a custom scripting language.

Such a scripting language is not as efficient as a compiled program, and does not have all the features of a full general purpose programming language. I was too lazy to invent a new language, and then have to invest in debugging and optimizing it to do the things I want to do as fast as I want it to. Especially when I was already working in a perfectly good and familiar language, C#. So, in my program the scripting or add-on language is C#.

One of the core elements of VisualStudio is the Roslyn Compiler for C# and other languages. Roslyn is also a component of the .Net framework. You can use it to compile and execute new code within a program. I can write fractal or generative art hacks in C# code, and build and run optimized code without leaving the executing program.

You may ask why bother? Since I have the source code, I could just exit, modify the source code, compile and run. Several reasons, the first is probably the loss of "momentum" going through that stop, edit, compile, run cycle. There is also reproducibility, if I save the 'recipe' for an image, and then change the core program, the recipe may produce a different image, or fail entirely. Instead, the custom code changes are saved as part of the recipe. I can create a large number of variations, without ever changing the core program, and they have a high probability of working in the future.

Port 22 #3

Next thing to verify, small variations in the basic formula and zooming.

This is not a programming blog, and I should not bore you with programming minutiae. But I have nothing else to talk about right now.

Visual Studio is the development environment. C# is the programming language I use. .Net is a framework, or a set of libraries. Visual Studio 2022 comes with C# 10, and .net 6.0.

I installed VS2022 several months ago. These tools are for the most part supposed to be backwards compatible. I could not build any of my programs, which were built on .Net 4.8. The framework was properly installed (and re-installed several times) but VS2022 could not find it, and asked me to participate in an infinite loop of un-install and reinstall the framework.

In the past, Visual Studio always provided tools to easily move your programs from the older framework to the newer version. That is not provided this time, you need to do the port manually. As there are no significant benefits to running the old framework on the new IDE, and I did not have the time to invest in learning the new framework, and the product was still in 'preview' mode, I gave up.

← Previous 20 Showing 897-916 of 1412 Next 20 →