Harmony.codea - a procedural drawing tool

Hi @Ric_Esrey: nice idea, I’ll take a look. I’m sorry that you’re having difficulties installing the code - sometimes I forget to update the library. How are you getting a copy of my library? The most reliable method is to get it from the bzr repository, but I know that that’s not the easiest way to get it onto the iPad. I probably haven’t updated the “single file” to the latest one so if you used that, that’ll be what’s going wrong.

@shrike Yes, I saw that on vec2! Might have to rethink that bit.

True beziers proved to be too complicated to draw - creating the meshes took too much time (though I probably wasn’t optimising them enough). The chop-into-three seems to be enough to make it look nice without costing too much time. So I don’t know if it would be possible to make a brush that used beziers in a “proper” way. But maybe worth thinking about how to make new brushes that use this cut-down approach.

I’m thinking of adding text boxes next. Spent the morning on a bus so got most of a FontPicker working.

I presume you’ve seen my first post about my UI class. I was planning on going through the modules next. Anything particular you’re interested in?

Hi @Andrew_Stacey, I used the links you provided in your 16 August comment. I browsed to each .lua file, opened each one, and copy/pasted the code into a .txt document, which I transferred to Codea using iExplorer.
In your comment, you said they were .bzr files. Yet, when I attempted to download the Bazaar source code management software from www.bazaar.canonical.com, my virus software protested. Therefore, I opted to install it the hard way. I’ll go ahead and install the bazaar software and see if I works for me. Thanks.

Hmm, the individual files should be up to date as when I update the bzr repository then I also update the actual files. I’ve just checked and they are up to date. Did you download them a while ago?

I downloaded both Harmony files and the Library yesterday. If no one else is having a problem, it must be something I’m doing wrong. I’ll try again.

More likely no-one else has tried! Maybe I forgot to update the Harmony project files.

I should start putting revision information in so I know which version projects are compiled against.

Same results as before. It works fine if you use the old version of Brush.lua. The new Brush class has the line, “if l.start then local bza, bzb, th = QuickHobby (l.start, l.finish, x, l.th)” This is what causes the trouble. The QuickHobby() is the problem.

By the way, I’m interested in understanding how the Disc brush works. Currently, the discs get bigger as you draw faster. Is there a way to make the discs smaller with increased velocity? I’ve always wanted to make an airbrush simulator (only as a hobby–not for profit). A real airbrush makes fatter lines if you paint slowly and thinner lines as you speed up.

I see what happened. I forgot to add a file to the versionning system. Here’s the quickhobby routine:

function QuickHobby(a,b,c,tha)
    local da = a:dist(b)
    local db = b:dist(c)
    local wa = vec2(1,0):angleBetween(b-a)
    local wb = vec2(1,0):angleBetween(c-b)
    local psi = wb - wa
    if psi > math.pi then
        psi = psi - 2*math.pi
    end
    if psi <= -math.pi then
        psi = psi + 2*math.pi
    end
    local thb,phb,phc
    if tha then
        thb = -(2*psi + tha) * db / (2*db + da)
        phb = - psi - thb
        phc = thb
    else
        thb = - psi * db / (da + db)
        tha = - psi - thb
        phb = tha
        phc = thb
    end
    local alpha = math.sqrt(2) * (math.sin(tha) - 1/16 * math.sin(phb)) * (math.sin(phb) - 1/16 * math.sin(tha)) * (math.cos(tha) - math.cos(phb))
    local rho = (2 + alpha)/(1 + (1 - (3 - math.sqrt(5))/2) * math.cos(tha) + (3 - math.sqrt(5))/2 * math.cos(phb))
    local sigma = (2 - alpha)/(1 + (1 - (3 - math.sqrt(5))/2) * math.cos(phb) + (3 - math.sqrt(5))/2 * math.cos(tha))
    local ctrlaa = a + da * rho * vec2(math.cos(tha + wa), math.sin(tha + wa))/3
    local ctrlab = b - da * sigma * vec2(math.cos(wa - phb), math.sin(wa - phb))/3
    alpha = math.sqrt(2) * (math.sin(thb) - 1/16 * math.sin(phc)) * (math.sin(phc) - 1/16 * math.sin(thb)) * (math.cos(thb) - math.cos(phc))
    rho = (2 + alpha)/(1 + (1 - (3 - math.sqrt(5))/2) * math.cos(thb) + (3 - math.sqrt(5))/2 * math.cos(phc))
    sigma = (2 - alpha)/(1 + (1 - (3 - math.sqrt(5))/2) * math.cos(phc) + (3 - math.sqrt(5))/2 * math.cos(thb))
    local ctrlba = b + db * rho * vec2(math.cos(thb + wb), math.sin(thb + wb))/3
    local ctrlbb = b - db * sigma * vec2(math.cos(wb - phc), math.sin(wb - phc))/3
    return {a,ctrlaa,ctrlab,b},{b,ctrlba,ctrlbb,c},thb
end

You might also need to add the “Path” library to the import list.

I like the idea of a brush that lays down a fixed amount of ink in a given time interval. You’d probably need to add more points to get an even distribution.

I’ve updated the files and made sure that new files were added. So the directories should be in sync with my iPad now.

(The Path.lua file in the Harmony directory is only there temporarily - it’s annoying to have to keep swapping projects so I copied it from the Library while working on it. Once I’m happy with it, I’ll put it back on the shelf.)

Also had a first go at an airbrush. The radius is roughly inversely proportional to the distance between touches, and it smears out to look airbrush-y. Not convinced that it’s the best possible, but maybe a start?

@Andrew_Stacey… thanks, the modules in Library and Harmony appear to be working. I’m currently using your colour module to study a better way to blend colors.

Fairly major update (which required some restructuring in my library).

New features:

  • Text nodes: can create a text box and type text into it. Uses an internal keyboard (needs a bit of work - in particular, glyphs for the “auxiliary” keys)
  • Pictures: can add pictures from the Picture library, these can then be scaled, rotated, and translated using a two-touch system

These required a bit of restructuring of the project as well, splitting of a “Drawable” object to be a joint parent of the brushes and these new objects so that the Canvas didn’t have to know too much about them.

Hey @Andrew_Stacey. I’m interested in seeing your updates. Are you still working issues? The reason I ask is because I downloaded the library and noticed the FontPicker, ListSelector, and TextNode have nothing in them at this time.

@Ric_Esrey Ooops! The file permissions were wrong. My apologies.

(Maybe I’m not choosing the best method for sharing code! I seem to have to do more manually than I remember each time.)

No apology necessary. This forum and your library/drawing code is why I bought Codea. So anyway, all of the files are complete now. Yet, I’m getting an error when I run it. The text message says, “error calling global Font, a nil value.” The error points to the Keyboard Tab around line number 47. It has this code:

Keyboard.fullqwerty = {
    {"q","w","e","r","t","y","u","i","o","p",{"DEL",BACKSPACE}},
    {{nil,nil,nil,nil,.5},"a","s","d","f","g","h","j","k","l",
        {Sentence(Font({
        name = "Zapfino", size="100"
        }),"CR"),"\
",nil,nil,1.5}},
    {{"¥",Keyboard.advanceLock,"£",Keyboard.advanceLock},
        "z","x","c","v","b","n","m",{",",nil,"!"},{".",nil,"?"}},
    {"","","",{" "," ",nil,nil,6}}
}

This sounds like Keyboard class is being processed before the Font class. That shouldn’t happen, particularly as the Sentence class is in the same file as the Font class so I would have expected that to fail first.

How are you importing the library and the project? Are they two separate projects or have you combined them?

Whoops… The error was mine. I thought I had added all of the necessary tabs to the library before I imported the updated files. Yet, I missed the fact there is a Shape class and a Shapes class. So, even though I imported the files into Codea, they don’t show up in the project until I manually create All of the classes in the editor. Then, the updated files overwrite the placeholders when I import them using iExplorer. Anyway, I’ll be more careful when I import from now on. Thanks for the update.

So is it working now? Any suggestions for improvements?

Yes, it works now. I’m in the process of playing (I mean learning) with it. By the way, your Font Picker Spinner is…. Awesome. Definitely has coolness factor.
Well, if you’re asking me—a beginner programmer—what I would like to see you do next, I might as well shoot for the moon. In my humble opinion, you have demonstrated that a good painting program can be made—with Codea. If I had your ability, I would do the following:
Phase 1—Improve the airbrush tool. The current airbrush makes what I consider a sputtering, clogged airbrushed line. I would want this tool to provide a continuous line with the same falloff and variable thickness features. For instance, when I use Autodesk Sketchbook Pro and Savage Interactive Procreate (trademarks are the property of the respective companies, blah, blah, blah), it appears as if they record the path I draw and then stamp a bitmap at equal intervals along that path. You can see it because both apps let you adjust the spacing of the stamp along the line. When the stamps are spaced close together, you get a continuous line. I would try to draw the airbrush line the same way.
Yet, I would also want to keep information on each recorded spot along the path. That information would include instantaneous velocity, line thickness, falloff, transparency, color, and a clipping value (in amount and radians). The reason to record this point data is to move toward a resolution-independent drawing program. The reality is that pro artists will not paint on a device that will only produce 1024x768 pictures. Double the resolution, even triple is not good enough for commercial work. The Harmony code is a leap ahead because it is procedural (bitmaps are not used). Theoretically, I could use @Herwig’s Zoom Library to zoom into my picture. Then, we can reprocess the thickness, falloff, transparency, etc. information, which would allow us to zoom without ever seeing pixilation. Furthermore, we could tell the program’s output procedure to produce a final jpeg image at whatever resolution we want. We wouldn’t have to specify the canvas size ahead of time. I believe Autodesk is experimenting with this type of drawing program in their newly released Sketchbook Ink app.
I actually have five phases, but I’ll see how this one flies first. I don’t want to abuse the forum.

Interesting ideas. The main work in this would be serialising the data, and deciding which lines in the current implementation are lines and which are approximations to curves. For example, the QuickHobby stuff sort-of changes a straight line to a bezier curve, but then we throw away the bezier curve and convert it back to three straight lines. Clearly, we should keep the bezier information rather than the lines.

Playing with it last night, particularly with the pictures, I thought a nice use would be to produce a photo montage. But I had a similar thought to yours: you wouldn’t want to print the result at 1024x768. So my idea (which has been rumbling at the back of my mind for a bit, to be honest) is to create an SVG version of the picture as we go along which can then (somehow) be saved. This would tie in with your ideas since SVG is … scalable. To make your dream come true, we’d also need to be able to convert the SVG back to a picture.

(For a smaller project, I’m going to make the text boxes rotatable; the keyboard also needs a bit of work.)

The AirBrush was only a first approximation! So what it should do is produce a line where the amount of “ink” deposited is constant over time and the blur is huge (relatively speaking). The tricky bit with that will be that adjoining pieces of the line will be at different thicknesses so the transition will have to be made smoothly. Stamps are a nice idea, too. For that, one would have to “walk the line” leaving a stamp at fixed distances. Eminently possible, just need a repository of stamps! (Spritely to the rescue)

You know… @Eric added a Linq for Lua class, which could provide a collection class for serialising the data. As for keeping the Bezier information, that would be perfect. Eventually, you could make a mode where the artist could actually adjust his drawn lines by moving the control points around.

I understand the airbrush tool was your first draft. I’m not complaining about it. In fact it helped me understand how the brushes function. I’d rather you shared your raw examples then to withhold them until they are more polished. And, it does make a nice airbrush effect. It looks like a sputtering airbrush.

Another idea for producing the airbrushed line might be to have the thickness altered by a slider instead of by the drawing speed of the artist. Granted, with a real airbrush, the line does get thinner with speed, but not by much. The primary thickness controller (in a double action airbrush anyway) is the trigger. You push down for air and then back for paint. The farther you pull back, the thicker the line. I was considering using your Slider Class to make an airbrush ‘trigger.’ As you pulled a line with your right hand, you would interactively change the line thickness on the slider with your left hand.

As for using a repository of bitmaps to stamp along a line, that’s the part I’d like to see improved upon (if it’s possible). Procreate and Sketchbook use that method, which works fine. But, when you zoom into the picture, you see big, square pixels because they use bitmap stamps. Is there a way to make a calculated stamp? Or if the math is too time-consuming to do on the fly, could we make a dynamic bitmap? It would be a bitmap that is mathematically created only after the artist zooms into the picture and which is appropriate to that zoom level. I’d like to work in a resolution independent drawing environment if there is such a thing on an iOS device.

Anyway, I’ll continue to study your coding examples with the hope that one day I can produce something as good. I can’t wait to see how you do the rotatable text boxes.

Eventually, you could make a mode where the artist could actually adjust his drawn lines by moving the control points around.

I already have that! The difficulty is that rendering a bezier at the moment is quite costly - you have to triangulate it at a fine enough mesh that it appears smooth. It’s okay if the path is quite long, but with lots of short paths then it gets messy and slow.

With regard to the stamps, I believe that the GIMP has vector stamps now so all that really needs doing is replacing bitmap stamps by vector ones, and then figuring out a rasterisation routine.