Working with Vector Graphics- Code

Got a LiveCode personal license? Are you a beginner, hobbyist or educator that's new to LiveCode? This forum is the place to go for help getting started. Welcome!

Moderators: Klaus, FourthWorld, heatherlaine, kevinmiller

Post Reply
Xero
Posts: 49
Joined: Sat Jun 23, 2018 2:22 pm

Working with Vector Graphics- Code

Post by Xero » Sun Jul 08, 2018 8:01 am

Hey Mods... not sure where to put this. If this is in the wrong place, you can tell me where to stick it. :P

Working with Vector Graphics

Livecode provides the user with the ability to draw a couple of different ways: Raster and Vector. For those of you who don't know, here's the difference:
Raster: when you change individual pixels to form an image. So the information of an image will be a series of pixel-by-pixel information that is fixed in place when done, as with any standard jpeg-like image.
Vector: A line drawn between two defined points. The information of this kind of image will be start (x,y) and finish (x,y) and a line drawn in between. These graphics can be manipulated by simply changing the start and finish points.
The benefits behind one or the other?
Rasters are fixed, simple and have an infinite number of ways they can be represented. They can be changed pixel-by-pixel using filters like photoshop would use.
Vectors are infintely scalable and changeable without loss. That means that no matter how you zoom or scale the image, the image will always be as clear as it started, without any jagged pixelisation. Vectors are also low in size due to the decreased amount of information. Rather than accounting for each pixel of the entire image or line, you just account for the start and finish of any individual line.

So...if you want to deal with vector graphics, LiveCode provides you with 2 ways.
1. Draw on the screen as you make the card, which can then stay as it is, or be scripted to change, or;
2. Script a drawing tool to allow the user to create graphics as they go, which can stay the same or be scripted to be changeable.

I tried to make a vector graphic section to a standalone app and found it very difficult to find everything I needed to make it work. Bits and pieces of the sccript were here and there, and what was scripted either didn't do what I wanted it to, or didn't fully work. So, I worked it up, and will present it all here in one place for the future newbies to get in one place, with explanations of what the script does, so you can learn as you go.

We will look at creating an app that allows the user to draw the following graphics: Line, Rectangle, Oval.
Each has their own intricacies and issues, and we will unravel the trip-up points as we go.

First up: LINE

One issue with the line command is the fact that LiveCode uses the term "line" to refer to lines of code. If you search for LINE in the dictionary, you tend o end up elsewhere, and if you use line (note no quotation marks) and not "line", you get bugs, because LiveCode is looking for something else.

To put together a line drawing app, start a new stack. Add a button. Add a rectangle. Drag and drop these from the tool pallette, and arrange them on screen so the button is at an edge, and the rectangle fills most of the card. Rename the button to be called Line.
You now have a button to make a line, and drawing area (rectangle). Please note that rather than using a rectangle, we could use the card itself. The script that would go on to the rectangle would then go into the card script.
What we need to do now is script the button to note that we will be drawing lines, and script the rectangle to create lines when we click on it.

Button script:

Code: Select all

global dType

on mousedown
   put "line" into dType
end mousedown
I know people hate globals, but they are useful when you are working with 2 objects working together...
Copy this script into the code for the button (click on the button, click on the code tool, and a box with numbered lines should appear)
What we just did:
Firstly, we defined a global variable, called "dType"- it could be called anything, but I have made it short for "drawing type" = dType. This will hold the type of drawing we are doing. If all we are doing is lines, you wouldn't need this, but as we will add rectangles and ovals later, this will come in handy.
Secondly, we stated that when the user clicks the mouse (mousedown command), the word "line" will go into the global variable dType. And we ended the mousedown command to say not to do anything else.

At this point, we have a little variable that is holding the word "line", to be used by the drawing area to define what we are drawing. In this case, lines.

Drawing area script:

Code: Select all

global dType
local tPointsList
on mousedown
   put the mouseloc into tPointsList
end mousedown

on mouseup
   put CR & the mouseloc after tPointsList
   create graphic "myNewLine"
   set the style of graphic "myNewLine" to dType
   set the lineSize of graphic "myNewLine" to 3 
   set the points of graphic "myNewLine" to tPointsList 
   set the antialiased of graphic "myNewLine" to true
end mouseup
The methodology for how the drawing will be done is important here. I have chosen a click and hold method, where the user clicks at a point, holds the mouse down, moves to another point, releases the mouse, and a line is drawn between the two. This is a common method. Another method would be a two-click system that would require a different code.
Copy the script into the code for the rectangle. (click on the rectangle, click on the code tool as per the button)
What we just did:
We called up the global variable dType, which will find that variable in the app.
We created the local variable tPointsList (temporary points list), which will house the list of points used to define our line. When the points are entered into it, it will look something like this:
x1, y1
x2,y2
Where x1 and y1 are the x and y co-ordinates of the start of the line, and x2 and y2 the end.
This variable is LOCAL and only used in this script so it can't be called up elsewhere. You don't actually need to create this variable, if you just say it later it will create itself.
We then said that when the mouse is clicked (in particular when the mouse goes down), we want to know the mouse location (mouseLoc), and we want to put that into our tPointsList variable so we can use it when we draw the line. That's all we want when the mouse goes down, so we closed out that command.
When the mouse is released (mouseup), we want the location of that too, then we want to draw the line.
HOWEVER... we need to use the code "CR & the mouseloc after" because we want to put this AFTER the pervious set of data (the mousedown mouse location), and we need to hit the return key first, to get it to a new line. So, we write put CR (hit the return key) & (and then) the mouseloc (mouse location) after the stuff that's already in tPointsList. That will give us the exact format of information as shown above with x1,y1 and x2, y2. It is this format only that is readable by the line graphic to write a line...
Now we have everything we need to define a line. we just need to draw it.
Then we create a line, and call it "myNewLine" so it ahs a name, and we can manipulate it via this name.
We set the type of vector graphic to be what it says in the global variable dType. In our case, "line". Livecode now knows we're drawing a line, and will look for the information required to draw a line.
We then define how wide that line is, in this case, 3 pixels wide.
We then say that the start point and end point will be the points we defined with our mousedown and mouseup commands, by calling up the local variable tPointsList. Livecode will look for 2 lines of 2 numbers, separated by commas, as we have set up.
Livecode will now draw the line.
Then we define the line as being antialiased (smooth at the sides with grey tones).
And we're done!
This script is simple and will just draw lines. No undoing, no erasing, no selecting or altering. We will add other functions later. You will also note when testing, that the line you drew will disappear when you draw another one. We'll fix this next...

Next up: RECTANGLES and OVALS

We will combine the two, as these have the same issues and can be addressed as we go.
Issues that arise with these graphics are: Rectangle is also used elsewhere in LiveCode, so the same applies here as to lines when it comes to using quotation marks. Use them to define a vector graphic, and not the outside of an object. Also, when defining, we can't define points, because a rectangle has a convention of points that create right angles. Defining each point makes a polygon, not a rectangle. Same goes for ovals, as we're defining the rectangle that goes around the oval. Consequently, we need to do a shuffle of the x's and y's to make sure they line up with the way we draw it, or else, we require the user to draw in one direction (i.e. top left to bottom right in our example). As this is not viable and frustrating to users, I have added a section to allow drawing in any direction.

The way I have decided to get the drawing done is as per the previous example for lines. Click, slide, release. Draw a rectangle between the click and release points.

Same as before, we need 2 new buttons, one for rectangles, one for ovals. Drag these next to the line button, and rename as Rectangle and Oval in Properties Inspector.

Script the buttons:

Code: Select all

Rectangle:
global dType

on mousedown
   put "rectangle" into dType
end mousedown
and Oval:

Code: Select all

global dType

on mousedown
   put "oval" into dType
end mousedown
Same as before, all we did differently now was to say different types for each button. When we click on the button, the words will be put into our global variable for use by the drawing area.

Open up the code for the drawing area (click on rectangle, click on code tool), and paste the following in to it:

Code: Select all

global dType
global GraphicNumber
local tPointsList

on mousedown
   put the mouseloc into tPointsList
end mousedown

on mouseup
   put CR & the mouseloc after tPointsList
if dType is "Line" then
   create graphic "myNewLine"
   set the style of graphic "myNewLine" to "line"
   set the lineSize of graphic "myNewLine" to 1 
   set the points of graphic "myNewLine" to tPointsList 
   set the antialiased of graphic "myNewLine" to true
   set the colors of graphic "myNewGrc" to red
   set the colorOverlay of graphic "myNewGrc" to red
else if dType is "Rectangle" then
   put item 1 of line 1 of tPointsList & comma & item 1 of line 2 of tPointsList into tXList
   put item 2 of line 1 of tPointsList & comma & item 2 of line 2 of tPointsList into tYList
   sort items of tXList ascending numeric
   sort items of tYList ascending numeric
   put empty into tPointsList
   put item 1 of tXList & comma & item 1 of tYList into tPointsList
   put cr & item 2 of tXList & comma & item 2 of tYList after tPointsList
   create graphic "myNewGrc" 
   set the style of graphic "myNewGrc" to dType
   set the lineSize of graphic "myNewGrc" to 3
   set the width of graphic "myNewGrc" to item 1 of line 2 of tPointslist - item 1 of line 1 of tPointsList
   set the height of graphic "myNewGrc" to item 2 of line 2 of tPointsList - item 2 of line 1 of tPointsList
   set the left of graphic "myNewGrc" to item 1 of line 1 of tPointsList
   set the top of graphic "myNewGrc" to item 2 of line 1 of tPointsList   
   set the antialiased of graphic "myNewGrc" to true
   set the colors of graphic "myNewGrc" to red
   set the colorOverlay of graphic "myNewGrc" to red
else if dType is "Oval" then
   put item 1 of line 1 of tPointsList & comma & item 1 of line 2 of tPointsList into tXList
   put item 2 of line 1 of tPointsList & comma & item 2 of line 2 of tPointsList into tYList
   sort items of tXList ascending numeric
   sort items of tYList ascending numeric
   put empty into tPointsList
   put item 1 of tXList & comma & item 1 of tYList into tPointsList
   put cr & item 2 of tXList & comma & item 2 of tYList after tPointsList
   create graphic "myNewGrc" 
   set the style of graphic "myNewGrc" to dType
   set the lineSize of graphic "myNewGrc" to 3
   set the width of graphic "myNewGrc" to item 1 of line 2 of tPointslist - item 1 of line 1 of tPointsList
   set the height of graphic "myNewGrc" to item 2 of line 2 of tPointsList - item 2 of line 1 of tPointsList
   set the left of graphic "myNewGrc" to item 1 of line 1 of tPointsList
   set the top of graphic "myNewGrc" to item 2 of line 1 of tPointsList   
   set the antialiased of graphic "myNewGrc" to true
   set the colors of graphic "myNewGrc" to red
   set the colorOverlay of graphic "myNewGrc" to red
end if
   set the name of last graphic to "Line"&GraphicNumber
   put GraphicNumber +1 into GraphicNumber
end mouseup
OK... now to explain!
Firstly, we have added a new global variable called GraphicNumber. This will be used to keep track of what graphic we are drawing. Essentially it will be used to rename graphics sequentially at the end of the script so tat they don't get erased every time we draw something.
The mousedown and start of the mouseup commands are exactly as previous. Just a way of getting the raw data of the location of the mouse at click and release.
Then, we create 3 section, one for line, one for rectangle, and one for oval. (I am sure there's a way of combining the rectangle and oval sections, but the code I used failed, so I went long-hand on it.) Each section is part of an if... then, else if... then command.
Firstly, we ask if the global variable dType is the word "Line", if it is, we continue in this section, if not, it will continue on to the next section. If it says line, it will execute the same script as before, except it will make the line red, using the last 2 lines of code. For some reason, when I simply put "set the colours ... to red", it put an overlay on it, and made that overlay green, meaning that the colours didn't show correctly. the last line rectifies this.
Secondly, if the global variable wasn't "Line", we ask it if it was "Rectangle". If so, it executes the remaining code in the section, if not, it passes on to the next section. If it wasn't "Oval" either, then it would end the code and do nothing. The rest of the rectangle code is the same as the oval code, so I shall explain only once.
At this point, we have a local variable that contains numbers that define the 2 mouse clicks. If we used them as-is, we would require the user to click top left to bottom right, as we define the graphic by the top and left, and width (right) and height (bottom). If the numbers don't line up and a negative number results, then a rectangle can't be drawn. So, the next few lines re-order the numbers to get them into a different order so that the first line of numbers in the tPointsList variable contain the lowest of the two numbers (x and y of either point) and the second line contains the highest. The first two lines of code separate the x value of the first and second click point into a variable called tXList and the y's into tYList. It does this by grabbing the first number (item 1) of the first line (x1,y1 in the example above) and the first number (item 1) of the second line (x2,y2), and writes them out with a comma (& comma &) in the variable. That variable would read (x1, x2). The same happens for the y value. We can now re-order the x's and y's separately. The next 2 lines do this and it should be self-evident how they do it. The tXList and tYList variables should read as something like 1,2 where the first number is smaller than the second. We then make sure the tPointsList variable is empty by "put empty into tPointsList" and then reinsert the smallest x and y value (item 1 of tXList and item 1 of tYList) into the tPoints List, and the largest on to the second line with the return (CR &) and item 2's. This way, we have a list that would read:
1,1
2,2
So, regardless of where the user clicks on screen, the numbers will mimic a top-left to bottom right click. i.e. 2,1 and 1,2 (Top Right to Bottom Left) will be re-ordered to be Top Left- Bottom Right 1,1 and 2,2. Confused? Well, don't consider the values as locations, consider them as top, left, width (x-shift) and height (y-shift). So it doesn't matter that the points are different, they are just different points on the SAME rectangle (points being 1,1 & 2,1 & 1,2 & 2,2- note that 1,1 and 2,2 are there are well as 2,1 and 1,2).
Next we need to make the rectangle. Remember I said you can't define a rectangle by points? We need to do 2 things now, and in a specific order. 1. Create the SIZE then 2. Create the location. Doing it in the other order will locate the rectangle, then make it bigger around the centre, throwing it off of the points we clicked to create it. To create the width, we take the largest of the 2 x locations and remove the smallest, as with the example above, it would be 2-1 = 1 unit wide, the actual width of the rectangle. Do the same with the y values to get the height. We have now created a rectangle that is 1 unit high, 1 unit wide. We then locate the left side using the x value of the first line, and the top using the y value of the first line. Set the antialiased and colour, and we're done drawing.
We then end out the "if" command that has drawn our graphics. We then need to rename the graphic so it doesn't get over-written, so we tell the app to name the last graphic we drew ("last graphic"), Line and whatever number is in the global variable GraphicNumber. If that variable was 0, then it would be called Line0. We then need to add a number to the variable GraphicNumber, making it 1, so that the next line will be called Line1, and so on. As they are now called different things, they won't keep getting over-written every time we create a new graphic.
Now is a good time to go into the Card Script (in the Object dropdown menus at the top) and write a little script that says this:

Code: Select all

global GraphicNumber
on cardopen
   put 0 into GraphicNumber
end cardopen
This will ensure that when the card opens, there is a number in the global variable GraphicNumber, and more numbers can be added to it.
And you should be done! Draw rectangles, lines and ovals.
I will add some script to change colours, line widths etc. soon. They are easy add-ons using global variables, and just changing things in the current script to be variable rather than fixed.
If anyone has any refinements to the scripts, I am more than happy to take them on board!
Last edited by Xero on Sun Jul 08, 2018 3:26 pm, edited 1 time in total.

bogs
Posts: 3912
Joined: Sat Feb 25, 2017 10:45 pm

Re: Working with Vector Graphics- Code

Post by bogs » Sun Jul 08, 2018 1:44 pm

Heya Xero,

Not for nothing, or to seem overly critical, but for long runs of code like you have there, please use the code boxes. Not only will they condense the post to something a bit more readable, but they alone allow formatting the code itself, as well as separating it from the text of your post, making it far more readable :D
Selection_003.png
Selection_003.png (7.33 KiB) Viewed 858 times
Image

capellan
Posts: 610
Joined: Wed Aug 15, 2007 11:09 pm
Contact:

Re: Working with Vector Graphics- Code

Post by capellan » Sun Jul 08, 2018 2:02 pm

Hi Xero,
If anyone has any refinements to the scripts, I am more than happy to take them on board!
Many developers in this platform, like Jim Hurley, Scott Rossi, Bernd Niggeman, Herman Hoch, Ian Mcphail, Richard Gaskin, Mark Waddingham and many other (including myself) have posted stacks and scripts for working with vector graphics. For example in this webpage, you could download a few of my stacks created many, many years ago: http://capellan2000.000space.com/

You are free to download and run my stacks (run these old stacks isolated, just in case any of these very old stacks crash more recent LiveCode versions), check scripts and include any of them in your own stacks.

My only requisite is the following: if you use any of my scripts in your own stacks, please include a link to download the unmodified original stack. You could put this unmodified original stack in your own server so the download link will be always available to your users.

If you ask me for the single best advice for working with vector graphics in LiveCode, then my best advice is the following:
Your real vector graphics are just a list of numbers, not the images that you draw on the screen.
The image that you draw on the screen is just a representation of a list of numbers.
After you realize that, everything becomes easier. Everything. :D

Enjoy this journey of discovering and learning with Livecode!

Al

capellan
Posts: 610
Joined: Wed Aug 15, 2007 11:09 pm
Contact:

Re: Working with Vector Graphics- Code

Post by capellan » Sun Jul 08, 2018 2:52 pm

Xero,

I made some small changes in your script to make it work
in my own setup. Check here:

Code: Select all

global dType
global GraphicNumber
local tPointsList

on mousedown
put the mouseloc into tPointsList
end mousedown

on mouseup
   put CR & the mouseloc after tPointsList
   
   if dType is "polygon" then
      lock screen
create graphic "myNewLine"
set the style of graphic "myNewLine" to "line"
set the lineSize of graphic "myNewLine" to 1
set the points of graphic "myNewLine" to tPointsList
set the antialiased of graphic "myNewLine" to true
set the colors of graphic "myNewLine" to red
set the colorOverlay of graphic "myNewLine" to red
unlock screen
end if

if dType is "Rectangle" then
put item 1 of line 1 of tPointsList & comma & item 1 of line 2 of tPointsList into tXList
put item 2 of line 1 of tPointsList & comma & item 2 of line 2 of tPointsList into tYList
sort items of tXList ascending numeric
sort items of tYList ascending numeric
put empty into tPointsList
put item 1 of tXList & comma & item 1 of tYList into tPointsList
put cr & item 2 of tXList & comma & item 2 of tYList after tPointsList
lock screen
create graphic "myNewGrc"
set the style of graphic "myNewGrc" to dType
set the lineSize of graphic "myNewGrc" to 3
set the width of graphic "myNewGrc" to item 1 of line 2 of tPointslist - item 1 of line 1 of tPointsList
set the height of graphic "myNewGrc" to item 2 of line 2 of tPointsList - item 2 of line 1 of tPointsList
set the left of graphic "myNewGrc" to item 1 of line 1 of tPointsList
set the top of graphic "myNewGrc" to item 2 of line 1 of tPointsList
set the antialiased of graphic "myNewGrc" to true
set the colors of graphic "myNewGrc" to red
set the colorOverlay of graphic "myNewGrc" to red
unlock screen
end if

if dType is "Oval" then
put item 1 of line 1 of tPointsList & comma & item 1 of line 2 of tPointsList into tXList
put item 2 of line 1 of tPointsList & comma & item 2 of line 2 of tPointsList into tYList
sort items of tXList ascending numeric
sort items of tYList ascending numeric
put empty into tPointsList
put item 1 of tXList & comma & item 1 of tYList into tPointsList
put cr & item 2 of tXList & comma & item 2 of tYList after tPointsList
lock screen
create graphic "myNewGrc"
set the style of graphic "myNewGrc" to dType
set the lineSize of graphic "myNewGrc" to 3
set the width of graphic "myNewGrc" to item 1 of line 2 of tPointslist - item 1 of line 1 of tPointsList
set the height of graphic "myNewGrc" to item 2 of line 2 of tPointsList - item 2 of line 1 of tPointsList
set the left of graphic "myNewGrc" to item 1 of line 1 of tPointsList
set the top of graphic "myNewGrc" to item 2 of line 1 of tPointsList
set the antialiased of graphic "myNewGrc" to true
set the colors of graphic "myNewGrc" to red
set the colorOverlay of graphic "myNewGrc" to red
unlock screen
end if

set the name of last graphic to "Line"&GraphicNumber
put GraphicNumber + 1 into GraphicNumber
end mouseup
Al

Xero
Posts: 49
Joined: Sat Jun 23, 2018 2:22 pm

Re: Working with Vector Graphics- Code

Post by Xero » Sun Jul 08, 2018 3:32 pm

Thanks for the tip Bogs. I'm new here, and learning. Adjusted post to include code.
Thanks Capellan. When I was writing this app, I couldn't find anything because it was so scattered, so I really wanted to consolidate it all into one place, open sourcing the code that I wrote (other people probably came to the same code... so maybe I just came to the same place as others). This way other people who are starting out can see what to code, and also what the bits and pieces and intricacies of each type is. It's little things like the "CR" code that I couldn't work out and had to ask. At least now it's here and maybe another learner could be less frustrated and learn quicker.
Thanks all!
XdM

bogs
Posts: 3912
Joined: Sat Feb 25, 2017 10:45 pm

Re: Working with Vector Graphics- Code

Post by bogs » Sun Jul 08, 2018 4:09 pm

Looks good!
Image

Xero
Posts: 49
Joined: Sat Jun 23, 2018 2:22 pm

Re: Working with Vector Graphics- Code

Post by Xero » Fri Aug 03, 2018 4:39 pm

Hey...
Seems the powers that be have taken a liking to this post and want me to present it at the LiveCode Global thingy in September.
I may be refining and testing some script to get this in one piece by then!!!
Wish me luck!
XdM

bogs
Posts: 3912
Joined: Sat Feb 25, 2017 10:45 pm

Re: Working with Vector Graphics- Code

Post by bogs » Fri Aug 03, 2018 8:14 pm

Good luck and congratulations :mrgreen:
Image

Xero
Posts: 49
Joined: Sat Jun 23, 2018 2:22 pm

Re: Working with Vector Graphics- Code

Post by Xero » Sat Aug 04, 2018 1:56 am

Oh...
And thank you all for helping me sort this out. I won't forget it!
XdM

Post Reply

Return to “Getting Started with LiveCode - Complete Beginners”