Canvas draw library: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
No edit summary
m (Heading level cleanup first level (one =)should not be used; fix double redirect)
(7 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{Note|Canvas draw is currently work in progress. You can test and improve it by downloading the [[Addons|CanvasDrawDev add-on]]. The canvas.draw library will hopefully be ready to use in FG version 2019.1}}
{{tip|The basic canvas.draw library was merged into FGDATA in 03/2020 so it available by default now. }}
{{Canvas Navigation}}
{{Canvas Navigation}}


= CanvasDrawDev add-on =
== Introduction ==
{{Note|As of 12/2018, the add-on is largely based on GPL'ed [[How to manipulate Canvas elements|plot2D code]] written originally by {{Usr|Rodolfo}} (rleibner on the forums) as part of the [[Spoken GCA]], [[PAR instrument]] and [[Oscilloscope addon|Oscilloscope]] add-ons.}}
 
The purpose of this add-on is to develop and grow a nasal library of canvas drawing
functions. This lib should eventually be moved to FGDATA/Nasal once it has
reached a sufficient quality.
 
== Motivation ==
There are basically two ways to populate a canvas, loading an SVG file which
There are basically two ways to populate a canvas, loading an SVG file which
has been created with some tool like inkscape, or by writing a nasal program
has been created with some tool like inkscape, or by writing a nasal program
Line 16: Line 9:
Both ways are valid and have their pros and cons.
Both ways are valid and have their pros and cons.


The canvas API is quite limited (at the time of writing), only a few basic  
The canvas API was quite limited, only a few basic commands were available, so canvas.draw library was created
commands are available. There should be more drawing commands to allow rapid
to provide more drawing commands.
development of canvas instruments for other aircraft.


== Goals ==
{{Note|Development of this library started in 12/2018 as a separate [[Addon|CanvasDrawDev add-on]] and is largely based on GPL'ed [[How to manipulate Canvas elements|plot2D code]] written originally by {{Usr|Rodolfo}}
(rleibner on the forums) as part of the [[Spoken GCA]], [[PAR instrument]] and [[Oscilloscope addon|Oscilloscope]] add-ons.}}
 
=== Goals ===
* Make the code flexible and powerful so people will like to use it
* Make the code flexible and powerful so people will like to use it
* Provide complex drawing commands needed for e.g. compass rose or speed tapes  
* Provide complex drawing commands needed for e.g. compass rose or speed tapes  
* Provide easy to use styles  
* Provide easy to use styles  
* Support API versioning to allow later extensions of the library


= canvas.draw API =
== canvas.draw API ==
{{Note|This API is subject to change without notice until it has been released with FG in FGDATA}}
=== draw module ===
{{note|All draw methods expect a canvas group element 'cgroup' as first parameter to draw on.}}


== draw module ==
==== draw.colors ====
All draw methods expect a canvas group element 'cgroup' as first parameter to draw on.  
 
=== draw.colors ===
A hash with some standard colors like white, black, red, green, blue, cyan, magenta, yellow, orange.  
A hash with some standard colors like white, black, red, green, blue, cyan, magenta, yellow, orange.  
Just in case you need them ;)
Just in case you need them ;)


=== circle() ===
==== circle() ====
circle(cgroup, radius, center_x = nil, center_y = nil)
circle(cgroup, radius, center_x = nil, center_y = nil)


Line 43: Line 35:
'''center_x''', '''center_y''' optional center of circle
'''center_x''', '''center_y''' optional center of circle


=== ellipse() ===
==== ellipse() ====
ellipse(cgroup, radius, center_x = nil, center_y = nil)
ellipse(cgroup, radius, center_x = nil, center_y = nil)


Line 50: Line 42:
'''center_x''', '''center_y''' optional center of ellipse
'''center_x''', '''center_y''' optional center of ellipse


=== arc() ===
==== arc() ====
[[File:Canvas-draw-scales03.png|thumb|circular marks]]
[[File:Canvas-draw-scales03.png|thumb|circular marks]]
arc(cgroup, radius, center, from_deg = nil, to_deg = nil)
arc(cgroup, radius, center, from_deg = nil, to_deg = nil)
Line 70: Line 62:
</syntaxhighlight>
</syntaxhighlight>


=== rectange() ===
==== rectange() ====
rectangle(cgroup, width, height, x = 0, y = 0, rounded = nil)  
rectangle(cgroup, width, height, x = 0, y = 0, rounded = nil)  


Line 79: Line 71:
'''rounded''' optional radius of corners in pixel
'''rounded''' optional radius of corners in pixel


=== square() ===
==== square() ====
square(cgroup, length, center_x = 0, center_y = 0, cfg = nil)
square(cgroup, length, center_x = 0, center_y = 0, cfg = nil)


shorthand for rectangle with width = height = length
shorthand for rectangle with width = height = length


=== deltoid() ===
==== deltoid() ====
[[File:Canvas-draw-shapes01.png|thumbnail|right]]
[[File:Canvas-draw-shapes01.png|thumbnail|right]]
deltoid (cgroup, dx, dy1, dy2, x = 0, y = 0)
deltoid (cgroup, dx, dy1, dy2, x = 0, y = 0)
Line 121: Line 113:
</syntaxhighlight>
</syntaxhighlight>


=== rhombus() / diamond() ===
==== rhombus() / diamond() ====
rhombus: func(cgroup, dx, dy, center_x = 0, center_y = 0)
rhombus: func(cgroup, dx, dy, center_x = 0, center_y = 0)


Line 133: Line 125:




=== grid() ===
==== grid() ====
[[File:Canvas-draw-grids01.png|thumbnail|right]]
[[File:Canvas-draw-grids01.png|thumbnail|right]]
Draw horizontal and verical lines. Two signatures are available
Draw horizontal and verical lines. Two signatures are available
Line 158: Line 150:
</syntaxhighlight>
</syntaxhighlight>


== Marks ==
=== Marks ===
Marks can be used to create scales / gauges.  
Marks can be used to create scales / gauges.  


=== marksLinear() ===
==== marksLinear() ====
[[File:Canvas-draw-scales01.png|thumb|linear marks (horizontal)|right]]
[[File:Canvas-draw-scales01.png|thumb|linear marks (horizontal)|right]]
[[File:Canvas-draw-scales02.png|thumb|linear marks (vertical)|right]]
[[File:Canvas-draw-scales02.png|thumb|linear marks (vertical)|right]]
Line 168: Line 160:
Draw marks for a linear scale on a canvas group, e.g. speed tape. Mark lines are draws perpendicular to baseline.
Draw marks for a linear scale on a canvas group, e.g. speed tape. Mark lines are draws perpendicular to baseline.


'''orientation''' of baseline, 0 = horizontal, 1 = vertical
'''orientation''' of baseline, "up", "down" (="vertical"), "left", "right" (="horizontal"), only first character is evaluated (u,d,v,l,r,h).


'''num_marks''' number of marks to draw
'''num_marks''' number of marks to draw
Line 205: Line 197:
</syntaxhighlight>
</syntaxhighlight>


=== marksCircular() ===
==== marksCircular() ====
[[File:Canvas-draw-scales03.png|thumb|circular marks|right]]
[[File:Canvas-draw-scales03.png|thumb|circular marks|right]]
marksCircular(cgroup, radius, interval, phi_start = 0, phi_stop = 360, style = nil)
marksCircular(cgroup, radius, interval, phi_start = 0, phi_stop = 360, style = nil)
Line 239: Line 231:
</syntaxhighlight>
</syntaxhighlight>


== draw.style ==
=== draw.style ===
Base class for styles.  
Base class for styles.  


== draw.marksStyle ==
=== draw.marksStyle ===
Parameter hash for drawing marks (see above).
Parameter hash for drawing marks (see above).


=== new() ===
==== new() ====
Create a new style instance.
Create a new style instance.
<syntaxhighlight lang="nasal">
<syntaxhighlight lang="nasal">
Line 251: Line 243:
</syntaxhighlight>
</syntaxhighlight>


=== setMarkLength() ===
==== setMarkLength() ====
Set length of marker in %interval or %radius
Set length of marker in %interval or %radius


=== setMarkOffset() ===
==== setMarkOffset() ====
Set alignment of markers.  
Set alignment of markers.  


Line 272: Line 264:
</syntaxhighlight>
</syntaxhighlight>


=== setMarkWidth() ===
==== setMarkWidth() ====
Set line width in pixel.
Set line width in pixel.


=== setSubdivisions() ===
==== setSubdivisions() ====
Set number of subdivisions (default = 0).
Set number of subdivisions (default = 0).


=== setSubdivisionLength() ===
==== setSubdivisionLength() ====
Set length of subdivision marker in %marker_length
Set length of subdivision marker in %marker_length
=== transform module ===
The following methods are available to manipulate existing canvas elements.
{{note|This chapter is a draft, see {{code|FGDATA/Nasal/canvas/draw/transform.nas}} for details }}
{{note|All transform methods expect a canvas element as first parameter.}}
==== move() ====
params: elem, dx, dy
==== rotate() ====
params: elem, deg, center
==== flipX() ====
params: elem, xaxis = 0
mirror element on x-axis
==== flipY() ====
params: elem, yaxis = 0
mirror element on y-axis
==== alignX() ====
Aligns the element, moving it horizontaly to ref.
params:
elem      element to be moved.
ref        reference may be an integer or another element.
alignment  as string: may be 'left-left', 'left-center', 'left-right', 'center-left', 'center-center', 'center-right', 'right-left', 'right-center', 'right-right'.
If ref is a single number, the 2nd word is ignored.
==== alignY() ====
Aligns the element, moving it vertically to ref.
params:
elem      element to be moved.
ref        reference may be an integer or another element.
alignment  as string: may be 'top-top', 'top-center', 'top-bottom', 'center-top', 'center-center', 'center-bottom', 'bottom-top', 'bottom-center', 'bottom-bottom'.
text elements also accept 'baseline' as reference.
If ref is a single number, the 2nd word is ignored.
 
 
==== rotate180() ====
params: elem, center = nil
center as [x,y] in pixels, otherwise: rotate in place
==== scaleX() ====
Stretch element horizontally
params:
elem        element to be stretched.
factor      the new-width/old-width ratio.
ref        the relative point to keep inplace. May be 'left', 'center' or 'right'.
==== scaleY() ====
strech element vertically
params:
elem        element to be stretched.
factor      the new-height/old-height ratio.
ref        the relative point to keep inplace. May be 'top', 'center' or 'bottom'.
==== resize() ====
params:
elem        element to be stretched.
factors    as vector [Xfactor, Yfactor]
ref        the relative point to keep inplace:
may be 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'.
== References ==
[[CompassRose]] is based on canvas draw.
{{Note|You can test, improve and play around with canvas.draw by downloading the [[Addon|CanvasDrawDev add-on]]. }}

Revision as of 10:10, 12 July 2020

Tip  The basic canvas.draw library was merged into FGDATA in 03/2020 so it available by default now.


Introduction

There are basically two ways to populate a canvas, loading an SVG file which has been created with some tool like inkscape, or by writing a nasal program that calls drawing functions of the canvas API.

Both ways are valid and have their pros and cons.

The canvas API was quite limited, only a few basic commands were available, so canvas.draw library was created to provide more drawing commands.

Note  Development of this library started in 12/2018 as a separate CanvasDrawDev add-on and is largely based on GPL'ed plot2D code written originally by Rodolfo

(rleibner on the forums) as part of the Spoken GCA, PAR instrument and Oscilloscope add-ons.

Goals

  • Make the code flexible and powerful so people will like to use it
  • Provide complex drawing commands needed for e.g. compass rose or speed tapes
  • Provide easy to use styles

canvas.draw API

draw module

Note  All draw methods expect a canvas group element 'cgroup' as first parameter to draw on.

draw.colors

A hash with some standard colors like white, black, red, green, blue, cyan, magenta, yellow, orange. Just in case you need them ;)

circle()

circle(cgroup, radius, center_x = nil, center_y = nil)

radius in pixel

center_x, center_y optional center of circle

ellipse()

ellipse(cgroup, radius, center_x = nil, center_y = nil)

radius_x, radius_y in pixel

center_x, center_y optional center of ellipse

arc()

circular marks

arc(cgroup, radius, center, from_deg = nil, to_deg = nil)

Draw an arc (part of a circle) with given radius around center. In the example image a red, green and blue arc are shown, nasal code below.

center center point of circle as vector [x,y]

from_deg, to_deg start and end of arc in degree of compass rose (0 = North, 90 = East, ...); defaults to 0, 360 (full circle)

    # [CX, CY] center point
    var start = -40;
    canvas.draw.arc(myGroup, radius, [CX, CY], start, start+190).setColor(COLORS["red"]);
    start += 90;
    canvas.draw.arc(myGroup, radius, [CX, CY], start, start+270).setColor(COLORS["green"]);
    start += 90;
    canvas.draw.arc(myGroup, radius, [CX, CY], start, start+190).setColor(COLORS["blue"]);

rectange()

rectangle(cgroup, width, height, x = 0, y = 0, rounded = nil)

width, height dimensions in pixels

x, y top-left corner of rectangle

rounded optional radius of corners in pixel

square()

square(cgroup, length, center_x = 0, center_y = 0, cfg = nil)

shorthand for rectangle with width = height = length

deltoid()

Canvas-draw-shapes01.png

deltoid (cgroup, dx, dy1, dy2, x = 0, y = 0)

Deltoid draws a kite (dy1 > 0 and dy2 > 0) or a arrow head (dy2 < 0).

dx width

dy1 height of "upper" triangle

dy2 height of "lower" triangle, < 0 draws an arrow head

x, y position of tip

    var dx = 40;
    #rhombus draws around a center point while deltoid uses x,y for tip point
    shapes["rhombus1"] = canvas.draw.rhombus(group, dx, 2*dx, 5 + dx/2, CY)
        .setColor(COLORS["cyan"]);

    # width = 40, height = 25 + 50;
    shapes["deltoid1"] = canvas.draw.deltoid(group, dx, 25, 50, 2*dx, CY)
        .setColor(COLORS["magenta"]);

    # y2 < 0 results in arrow head
    canvas.draw.deltoid(group, dx, 2*dx, -dx, 4*dx, CY)
        .setColor(COLORS["yellow"]);

    # y2 = 0 results in triangle
    canvas.draw.deltoid(group, dx, 2*dx, 0, 6*dx, CY)
        .setColor(COLORS["green"]);

    # y2 < 0 and |y2| > y1 results in arrowhead with tip above baseline
    canvas.draw.deltoid(group, dx, 1.5*dx, -2.5*dx, 8*dx, CY)
        .setColor(COLORS["red"]);

rhombus() / diamond()

rhombus: func(cgroup, dx, dy, center_x = 0, center_y = 0)

draws a diamond around [center_x, center_y] (first element from left in sample image above)

dx width

dy height


grid()

Canvas-draw-grids01.png

Draw horizontal and verical lines. Two signatures are available

1) (cgroup, [sizeX, sizeY], dx, dy, border = 1)

2) (cgroup, nx, ny, dx, dy, border = 1)

size [width, height] of grid in pixels.

nx, ny number of lines in x/y direction

dx, dy distance between lines in pixels.

border optional boolean, draw lines at sizeX and sizeY, true by default.

    var group = myRoot.createChild("group", "grid_scales");
    #-- grid test --
    canvas.draw.grid(group, [62, 55], 10, 10, 0).setTranslation(10, 100);
    canvas.draw.grid(group, [62, 55], 10, 10, 1).setTranslation(100, 100);
    canvas.draw.grid(group, 4, 4, 20, 10, 0).setTranslation(10, 200);
    canvas.draw.grid(group, 4, 4, 20, 10, 1).setTranslation(100, 200);

Marks

Marks can be used to create scales / gauges.

marksLinear()

linear marks (horizontal)
linear marks (vertical)

marksLinear(cgroup, orientation, num_marks, interval, style)

Draw marks for a linear scale on a canvas group, e.g. speed tape. Mark lines are draws perpendicular to baseline.

orientation of baseline, "up", "down" (="vertical"), "left", "right" (="horizontal"), only first character is evaluated (u,d,v,l,r,h).

num_marks number of marks to draw

interval distance between marks (pixel)

style marksStyle hash with more parameters

    #-- marks for linear scales --
    var group = myRoot.createChild("group", "marks");
    var style = canvas.draw.marksStyle.new()
        .setBaseline(1)
        .setMarkLength(0.8)     # 0.8 * interval
        .setSubdivisions(2);
    
    canvas.draw.marksLinear(group, "right", 5, 30, style).setTranslation(CX, 150)
        .setColor(COLORS["green"]);
    
    style.setSubdivisions(1);
    style.setMarkOffset(style.MARK_LEFT); # left/up
    canvas.draw.marksLinear(group, "v", 5, 20, style).setTranslation(50, CY)
        .setColor(COLORS["red"]);
    canvas.draw.marksLinear(group, "h", 4, 20, style).setTranslation(CX, 30)
        .setColor(COLORS["red"]);
    style.setMarkOffset(style.MARK_CENTER); #center
    canvas.draw.marksLinear(group, "down", 5, 20, style).setTranslation(100, CY)
        .setColor(COLORS["green"]);
    canvas.draw.marksLinear(group, "left", 4, 20, style).setTranslation(CX, 70)
        .setColor(COLORS["green"]);
    style.setMarkOffset(style.MARK_RIGHT); #right / down
    canvas.draw.marksLinear(group, "up", 5, 20, style).setTranslation(150, CY)
        .setColor(COLORS["blue"]);
    canvas.draw.marksLinear(group, "r", 4, 20, style).setTranslation(CX, 110)
        .setColor(COLORS["blue"]);

marksCircular()

circular marks

marksCircular(cgroup, radius, interval, phi_start = 0, phi_stop = 360, style = nil)

draw marks along an arc or circle of given radius

radius of baseline (circle)

interval distance of marks in degree

phi_start position of first mark in degree (default 0 = north)

phi_stop position of last mark in degree (default 360)

    #-- marks on arc / circle --
    style.setMarkLength(0.1);
    style.setMarkOffset(style.MARK_IN); # pointing to center
    style.setSubdivisions(2);
    style.setBaseline(1);
    var radius = 150;
    var interval = 30;
    canvas.draw.marksCircular(group, radius, interval, 30, 120, style)
        .setTranslation(CX,CY);

    style.setMarkOffset(style.MARK_OUT); # pointing outside
    style.setSubdivisions(1);
    radius = 90;
    interval = 10;
    canvas.draw.marksCircular(group, radius, interval, 0, 360, style)
        .setTranslation(CX,CY)
        .setColor(COLORS["cyan"]);

draw.style

Base class for styles.

draw.marksStyle

Parameter hash for drawing marks (see above).

new()

Create a new style instance.

var myMarkStyle = canvas.draw.marksStyle.new()

setMarkLength()

Set length of marker in %interval or %radius

setMarkOffset()

Set alignment of markers.

You can use marksStyle.MARK_LEFT marks left of vertical baseline

MARK_UP marks above horizontal baseline

MARK_CENTER marks centered on baseline

MARK_RIGHT marks right of vertical baseline

MARK_DOWN marks below horizontal baseline

var myMarkStyle = canvas.draw.marksStyle.new()
myMarkStyle.setMarkOffset(myMarkStyle.MARK_LEFT);

setMarkWidth()

Set line width in pixel.

setSubdivisions()

Set number of subdivisions (default = 0).

setSubdivisionLength()

Set length of subdivision marker in %marker_length

transform module

The following methods are available to manipulate existing canvas elements.

Note  This chapter is a draft, see FGDATA/Nasal/canvas/draw/transform.nas for details
Note  All transform methods expect a canvas element as first parameter.

move()

params: elem, dx, dy

rotate()

params: elem, deg, center

flipX()

params: elem, xaxis = 0

mirror element on x-axis

flipY()

params: elem, yaxis = 0

mirror element on y-axis

alignX()

Aligns the element, moving it horizontaly to ref.

params:

elem element to be moved.

ref reference may be an integer or another element.

alignment as string: may be 'left-left', 'left-center', 'left-right', 'center-left', 'center-center', 'center-right', 'right-left', 'right-center', 'right-right'. If ref is a single number, the 2nd word is ignored.


alignY()

Aligns the element, moving it vertically to ref.

params:

elem element to be moved.

ref reference may be an integer or another element.

alignment as string: may be 'top-top', 'top-center', 'top-bottom', 'center-top', 'center-center', 'center-bottom', 'bottom-top', 'bottom-center', 'bottom-bottom'. text elements also accept 'baseline' as reference. If ref is a single number, the 2nd word is ignored.


rotate180()

params: elem, center = nil

center as [x,y] in pixels, otherwise: rotate in place

scaleX()

Stretch element horizontally

params:

elem element to be stretched.

factor the new-width/old-width ratio.

ref the relative point to keep inplace. May be 'left', 'center' or 'right'.

scaleY()

strech element vertically

params:

elem element to be stretched.

factor the new-height/old-height ratio.

ref the relative point to keep inplace. May be 'top', 'center' or 'bottom'.

resize()

params:

elem element to be stretched.

factors as vector [Xfactor, Yfactor]

ref the relative point to keep inplace:

may be 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'.

References

CompassRose is based on canvas draw.

Note  You can test, improve and play around with canvas.draw by downloading the CanvasDrawDev add-on.