Advice App architecture for working with Canvas

Hi there,

I need an advice on how to structure a little app that I am trying to develop for myself.

The purpose of the app is to load a technical drawing (tif, jpeg) in a canvas and to draw vector based shapes (lines, circle…) on top of it with the ability to edit it afterward.

What about:
A Canvas for the drawing (Canvas1)
A second canvas (Canvas2) with transparency on top of it containing all the vector shapes
Keep each vector shape in a database table (each property like x, y width, height, color etc… being stored in a record)

Obviously when scrolling or resizing the Canvas1, the Canvas2 would have to be synchronised.

A REAL challenge for me :slight_smile:

Thanks for any advice

There’s no need for Canvas2. Just do all your drawing in a single canvas: start with the background picture, and then draw your vectors one by one in top of it, beginning with the one at the back and ending with the one in the front.

5 Likes

Under similar circumstances, I do not bother with a database.

I just keep the vector shapes (Object2D) in a Dictionary.

1 Like

Thanks Eric,

I (obviously :innocent:) have more questions:

  • Is it a good idea to try to store the different shapes in a database?
  • If yes, can I imagine to save my file in a format that would embed the DB and the initial Picture (like a package)?

Hi Robert,

Why?

Any advantages, caveats in each solutions?

How to save the file? I have no idea about file format. I thought I could do a package like the “.ods” which I think store multiple files like styles definition, data, images etc…

Sure, a database file is a fine way to do what you want. On the Mac, the “package” is called a “bundle” and is a pretty good option.

1 Like

What file are you trying to save? Are you trying to save the image that is being displayed in the Canvas.

I have never used a database for this purpose. It seems more elaborate. With a Dictionary, every thing is contained in the Project. If you want to save the Canvas image between sessions, then this becomes more complicated because the Dictionary will not retain its value between sessions. You can save the contents of a Dictionary in a JSON file perhaps and the repopulate the Dictionary by accessing this file at startup.

Maybe a Database would allow you to save between sessions. I personally have not needed this functionality.

Anyway, depending on your needs you can branch out into many directions.

Var dctObjectsOnCanvas As New Dictionary
Var lineRoad As CurveShape
lineRoad.Order = 0
lineRoad.X = 20
lineRoad.X2 = 100
lineRoad.Y = 30
lineRoad.Y2 = 30

Var lineTree As CurveShape
lineTree.Order = 0
lineTree.X = 40
lineTree.X2 = 40
lineTree.Y = 50
lineTree.Y2 = 200

dctObjectsOnCanvas.Value("theRoad") = lineTree

Then you have to make sure that in the Paint event of the Canvas, it accesses the Object2D in the Dictionary.


There is a “special” Object2D called a Group2D which can hold a collection of Object2D’s so. So instead of using a Dictionary, you could just pile your various objects into the Group2D and then make sure that the Paint event of the Canvas accesses that Group2D.

Var myObjects As Group2D
myObjects.AddObject(lineRoad)
myObjects.AddObject(lineTree)

You can access the individual Object2Ds in the Group2D using an index. But you have to keep track somehow which Item is in which index. The code becomes less easy to read than what a Dictionary allows with descriptive names like “theRoad” used to access a specific Object2D

objectWanted = dctObjectsOnCanvas.Value(“theRoad”)

rather than

objectWanted = myObjects.Item(1)

P.S. As I finish this comment, I noticed Eric Williams’s comment. He is a lot more experienced than I. I would believe what he said. I am still making this reply to offer alternatives.

3 Likes

I do this in one of my apps by zipping all of the files together then just saving it with an extension that I’ve added to the FileTypeGroups in my app. You can even add your own custom icon there.

Then when you double click on the file, it will be opened in your application, and you can unzip the contents.

I do the compression and decompression with a plugin, since there are more options there like unzipping directly into memory rather then needing a destination. Also, with the native zip functionality, it looks like you’ll have to create a folder first, add everything to that folder, then zip the folder.

You can do all of this in SpecialFolder.Temporary so that it’s hidden from the end user and makes it look like you’re doing it all in memory. Just move the resulting file to the chosen destination after.

1 Like

A bundle on the Mac is essentially what you’re describing without the compression. :grin: I doubt he’s likely to get to the point in this project where compression will make much difference.

Can you create a bundle that has a custom extension?

It seems like it just behaves like a folder in Finder if the extension isn’t .app or .bundle even if I use the extension that is recognized by my app.

That said, I’m adding the extension in Finder just to test, not sure if that makes a difference.

Waoow! Thank you Robert.

A lot for me to study :+1:

I apologize - I was confusing bundle with package. Package is a folder with a registered extension that identifies it as a package associated with your application, and it can have an arbitrary internal structure. Bundle is a standardized structure for executables - essentially a specific type of package.

2 Likes

That’s very kind of you to say, but I’ll come right back and say that your Dictionary-JSON is just as valid and possibly easier to implement, and it produces files that are mostly human-readable. A superior solution in several ways!

Typos
At end of Dictionary Code

dctObjectsOnCanvas.Value("theRoad") = lineRoad
dctObjectsOnCanvas.Value("theTree") = lineTree

Now, if at some point you want to change the color of the tree, you can access the object from the Dictionary and change its BorderColor and then have the Canvas do a Paint event.

Hi Robert,

I had a look into my DB idea. As I’ve been a big fan of Databases for many years I think it makes more sense for me to understand what I’m doing.

I think I’m going to use a In-memory database (SQLite) with a table to store the Image file and a table to store all the shapes. Therefore I’ll just have to save my project in a single DB file using backupDBFile.

I don’t know if it’s the optimum solution but I is much easier for me to grab the concept.

Thank you anyway for your time

Hi Robert,

I was going throught your code, but I have some questions:

  • As I wanted to have a dictionnary available for my Canvas I created a property (mDiction) as a Dictionnary. The aim being to store like you did the different elements to draw in my Canvas. However, when I try to test this approach with simplier datas like:
MyDICTION.Value("Name") = "John"
MyDICTION.Value("Surname") = "Doe"

My app throws me away :smile:

  • My second question is regarding adding 2Dobjects in my Canvas. If I tell my code to fire the paint event the whole canvas is redrawn, all previous elements are erased and just the new one is drawn. Do I have to parse throught ALL the dictionnary everytime I want to add, remove or refresh my Canvas?

TIA

You need to instantiate the Dictionary before you try to use it, assuming your exception is a NilObjectException. That means, somewhere, you need:

MyDICTION = new Dictionary

My second question is regarding adding 2Dobjects in my Canvas. If I tell my code to fire the paint event the whole canvas is redrawn, all previous elements are erased and just the new one is drawn. Do I have to parse throught ALL the dictionnary everytime I want to add, remove or refresh my Canvas?

Yes. I tend to buffer object images individually then redraw them only if they change, and wrap everything in classes rather than using a Dictionary for layer storage. This gives me an array of objects to draw, and will be speedier than looping over a dictionary and redrawing each layer on every redraw of the canvas.

1 Like

Here’s an example I quickly tossed together to show you the basics. This will still require a lot of learning and trial and error for you to build a complete system, but it’s a start.

This also includes the ability to export to JSON. Import is up to you, but it’s basically reversing the process.

drawobjects.zip (9.0 KB)

There are some advanced topics covered in this example, like class inheritance. The documentation is your friend.

4 Likes

Thank you very much Anthony, that is really kind of you :+1::+1::+1:

What does this mean?