DRAW - Shape sub-dialect
The shape sub-dialect allows you to create shapes (drawings) as blocks.
Some aspects of it remind me of "turtle-graphics". You can move your pen without drawing and coordinates can be absolute (relative to the face) or relative (relative to last position).
Shape sub-dialect also "closes" the shapes for you, allowing you to use fill-pen to add colors or patterns.
You may use fill-pen , pen , line-width , line-join and line-cap as commands in the shape block, but only the last command will be used for the entire shape.
The shape sub-dialect is based on SVG graphics. I found the following links to be helpful in understanding some of the concepts:
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
http://www.w3.org/TR/SVG11/paths.html
⊕ line
The most basic example:
Red [needs: view]
myshape: [line 10x10 70x70]
view compose/deep/only [
base draw [
shape (myshape)
]
]
Notice the compose/deep/only and the parentheses around the shape name. As far as I know, you must use those when working with shapes.
Automatic closing
In the example below, only two lines were actually drawn. I added fill-pen to illustrate it better:
Red [needs: view]
myshape: [
line 10x70 40x10 70x70 ;two lines only
]
view compose/deep/only [base draw [ fill-pen blue shape (myshape)]]
⊕ move
The most basic example:
Red [needs: view]
myshape: [
line 10x10 70x70 ;line from 10x10 to 70x70
move 10x70 ;moves the pen without drawing to 10x70
line 10x10 ;draws a line from current pen position (10x70) to 10x10
]
view compose/deep/only [base draw [shape (myshape)]]
relative positions
Coordinates become relative if you add an apostrophe (') before the command:
Red [needs: view]
myshape: [
line 10x40 40x40 ;horizontal line to the middle
'move 0x-10 ;new current position RELATIVE to old (up from the middle)
'line 20x0 ;draws a little horizontal line RELATIVE TO current position
]
view compose/deep/only [base draw [shape (myshape)]]
⊕ hline and ⊕ vline
Draws a horizontal or a vertical line from current pen position.
Red [needs: view]
myshape: [
move 10x10 ; puts pen at 10x10
hline 30 ;horizontal line to coordinate X =30
vline 30 ;vertical line to coordinate Y = 30
'hline 30 ;horizontal line 30 pixels long (longer than hline above)
'vline 30 ;vertical line 30 pixels long
'hline -20 ; just to show the use of RELATIVE negative lenghts
; shape dialect will close the shape now
]
view compose/deep/only [base draw [shape (myshape)]]
⊕ arc
From Red's official documentation (with eventual minor changes):
Syntax
arc <end> <radius-x> <radius-y> <angle> sweep large (absolute)
'arc <end> <radius-x> <radius-y> <angle> sweep large (relative)
<end> : arc's end point (pair!).
<radius-x> : radius of the circle along x axis (integer! float!).
<radius-y> : radius of the circle along y axis (integer! float!).
<angle> : angle between the starting and ending points of the arc in degrees (integer! float!).
sweep : (optional) draw the arc in the positive angle direction.
large : (optional) produces an inflated arc (goes with 'sweep option).
Description
Draws the arc of a circle between the current pen position and the end point, using radius values. The arc is defined by one angle value.
Here is an explanation about how arc works. Since you define your line (two points) and your ellipse (x-radius, y-radius and angle), there are only two positions for the ellipse that make your line a chord to it. The options sweep, large and sweep large define which arc of these ellipses will show in your drawing. Notice that in the illustration below, the angle of the ellipse is zero.
In the arc definition you only inform the arc's end position. That is because the start position is the current pen position. So, if arc is your first command in a shape, you must first move to the position you want to start at.
Red [needs: view]
myshape_1: [
move 35x50
arc 55x70 15 30 0
]
myshape_2: [
move 35x50
arc 55x70 15 30 0 large sweep
]
myshape_3: [
move 35x50
arc 55x70 15 30 0 sweep
]
myshape_4: [
move 35x50
arc 55x70 15 30 0 large
]
view compose/deep/only [
base 100x120 draw [fill-pen blue shape (myshape_1)]
base 100x120 draw [fill-pen blue shape (myshape_2)]
base 100x120 draw [fill-pen blue shape (myshape_3)]
base 100x120 draw [fill-pen blue shape (myshape_4)]
]
With an angle:
Red [needs: view]
myshape_1: [
move 35x50
arc 55x70 15 30 30
]
myshape_2: [
move 35x50
arc 55x70 15 30 30 large sweep
]
view compose/deep/only [
base 100x120 draw [fill-pen blue shape (myshape_1)]
base 100x120 draw [fill-pen blue shape (myshape_2)]
]
A circle:
Red [needs: view]
myshape_1: [
move 56x40
arc 56x41 16 16 0 large
]
view compose/deep/only [base draw [fill-pen blue shape (myshape_1)]]
⊕ qcurve
From Red's official documentation (with eventual minor changes):
Syntax
qcurve <point> <point> ... (absolute)
'qcurve <point> <point> ... (relative)
<point> : coordinates of a point (pair!).
Description
Draws a quadratic Bezier curve from a sequence of points, starting from the current pen position. At least 2 points are required to produce a curve (the first point is the implicit starting point).
Draw a quadratic Bezier curve from a sequence of 3 points. The following script draws two qcurves using <start-point> <control-point > <end-point/start-point> <control-point > <end-point>. Allows absolute or relative (for relative, use 'qcurve) coordinates.
Red [needs: view]
myshape: [
move 5x40
qcurve 20x20 40x76 60x20 76x40
]
view compose/deep/only [
base draw [
pen blue
circle 5x40 2 ;shows start point 1
circle 40x76 2 ;shows endpoint 1/start point 2
circle 76x40 2 ;shows endpoint 2
pen green
circle 20x20 2 ;shows control point 1
circle 60x20 2 ;shows control point 2
pen yellow
shape (myshape)
]
]
I added the approximate location of the fixed-points (blue) and the control-points (green) in the image bellow. They are not generated by the program, I edited the image.
⊕ curve
From Red's official documentation (with eventual minor changes):
Syntax
curve <point> <point> <point> ... (absolute)
'curve <point> <point> <point> ... (relative)
<point> : coordinates of a point (pair!).
Description
Draws a cubic Bezier curve from a sequence of points, starting from the current pen position. At least 3 points are required to produce a curve (the first point is the implicit starting point).
Draws a cubic Bezier curve using <start-point (current pen position)> <control-point1 (argument)> <control-point2 (argument)> <end-point (argument)> . Allows absolute or relative (for relative, use 'curve) coordinates.
Red [needs: view]
myshape_1: [
move 10x70 ; start-point
curve 30x20 50x20 70x70 ; control-point control-point end-point
]
view compose/deep/only [base draw [ pen yellow shape (myshape_1)]]
I added the approximate location of the fixed-points (blue) and the control-points (green) in the images bellow. They are not generated by the program, I edited them.
You may add more points to the curve command, they will create a new independent curve:
Red [needs: view]
myshape_1: [
move 10x70 ; start-point
curve 30x20 ;first control point
50x20 ;second control point
70x70 ;end-point first curve/ new start-point second curve
90x40 ;first control point second curve
110x100 ;second control point second curve
130x70 ;end-point second curve
]
view compose/deep/only [base 150x100 draw [ pen yellow shape(myshape_1)]]
⊕ qcurv
Syntax
qcurv <point> (absolute)
'qcurv <point> (relative)
<point> : coordinates of the ending point (pair!).
qcurv draws a smooth quadratic Bezier from the current pen position to the specified point.
You don't have to provide the control-point between start-point and end-point, qcurv creates this control-points as a reflection of the last control point given in the shape block, so, you must have a command that uses a control-point before using qcurv.
Red [needs: view]
myshape_1: [
move 30x60 ;start-point of qcurve
qcurve 50x30 70x60 ;control-point end-point
qcurv 110x60 ; end-point of qcurv
]
view compose/deep/only [
base 300x240 draw [
scale 2 2 ; just to make it bigger
pen yellow
shape (myshape_1)
]
]
As of april 2018, qcurv only works with one endpoint as argument.
⊕ curv
Draws a smooth cubic Bezier curve from a sequence of points.
Just like qcurv, curv creates control-points reflected relative to the last control-point in the shape block. But since cubic Beziers require 2 control-points, you must provide the second for each segment. This second control-point will be reflected as the first control-point of the next segment.
From Red's official documentation (with eventual minor changes):
Syntax
curv <point> <point> ... (absolute)
'curv <point> <point> ... (relative)
<point> : coordinates of a point (pair!).
Description
Draws a smooth cubic Bezier curve from a sequence of points, starting from the current pen position. At least 2 points are required to produce a curve (the first point is the implicit starting point).
"The first control point is assumed to be the reflection of the second control point on the previous command relative to the current point. (If there is no previous curve command, the first control point is the current point.)"
So, curv draws a cubic Bezier using <current pen position start-point ><automatically created reflected control-point1><control-point2> <end-point>.
So, the arguments you actually pass to curv are only: <control-point2> <end-point>[...]
Red [needs: view]
myshape_1: [
move 30x60 ;start-point of qcurve
qcurve 50x30 70x60 ;control-point end-point
curv 100x40 110x60 ; curv's second control-point and end-point
]
view compose/deep/only [
base 300x240 draw [
scale 2 2 ; just to make it bigger
pen yellow
shape (myshape_1)
]
]
curv may use many consecutive control-points and points:
Red [needs: view]
;second control-point point
myshape_1: [
move 10x40
qcurve 30x10 50x40
curv 70x10 90x40 110x10 130x40 150x10 170x40
move 10x40
]
view compose/deep/only [base 200x80 draw [ pen yellow shape (myshape_1)]
]