W1504 First Steps on a Path
Prerequisites[edit]
Research[edit]
- Read Vector Graphics. Pay particular attention to the "Operation" section
Prepare[edit]
Create a new Scenes shell project within your Experiences directory:
ty-cam@codermerlin:~$ cd ~/Experiences
ty-cam@codermerlin:~/Experiences$ git clone https://github.com/TheCoderMerlin/ScenesShellBasic W1504
Enter the Sources/ScenesShell directory of the new project:
ty-cam@codermerlin:~/Experiences$ cd W1504/Sources/ScenesShell/
Run the program.
ty-cam@codermerlin:~/Experiences/W1504/Sources/ScenesShell$ run |
Ensure that you are logged on to the wiki. Then, click on the Tools menu followed by right-clicking on IGIS and selecting the menu item Open in New Window or Open in New Tab.
You'll know you're successful if you see the title bar change to "Coder Merlin: IGIS". (The browser window will be blank because we haven't added any graphics yet.)
Experiment[edit]
Stop the running program.
Return to the console and press CONTROL-C |
Primitives[edit]
Paths enable us to execute a series of primitives (lines, curves, ellipses, arcs, etc.) into a single unit for later display. Let's start by rendering a path with a single arc.
Open the file Background.swift in emacs.
Add a new method (below init) as follows:
override func setup(canvasSize:Size, canvas:Canvas) {
let path = Path()
path.arc(center:Point(x:500, y:400), radius:100, startAngle:1.2*Double.pi, endAngle:1.8*Double.pi)
canvas.render(path)
}
Remember to save the file, then suspend emacs.
Run the program and view in a browser before continuing. |
- What do you observe?
- What is the purpose of the parameters
startAngle
andendAngle
? - In which direction do you think the arc is being drawn (clockwise or counterclockwise)?
- How can you test your hypothesis?
Note that there are always at least three steps to rendering a path:
- Create the Path object
- Build the Path by adding primitives. Often multiple primitives will be added. In many cases, as new primitives are added, the starting point of the new primitive connects with the ending point of the previous primitive.
- Render the Path on the canvas
Another Arc[edit]
Stop the running program.
Return to the console and press CONTROL-C |
Let's add a second arc. Modify setup as follows:
override func setup(canvasSize:Size, canvas:Canvas) {
let path = Path()
path.arc(center:Point(x:500, y:400), radius:100, startAngle:1.2*Double.pi, endAngle:1.8*Double.pi)
path.arc(center:Point(x:500, y:283), radius:100, startAngle:0.2*Double.pi, endAngle:0.8*Double.pi)
canvas.render(path)
}
Run the program and view in a browser before continuing. |
- What do you observe?
- Note that there is only a single canvas.render() statement. How does this single statement result in the rendering of two arcs?
Filled Paths[edit]
Stop the running program.
Return to the console and press CONTROL-C |
One of the many advantages of paths is that they can be both stroked and filled, just as rectangles. Modify setup as follows:
override func setup(canvasSize:Size, canvas:Canvas) {
let outerPath = Path(fillMode:.fillAndStroke)
outerPath.arc(center:Point(x:500, y:400), radius:100, startAngle:1.2*Double.pi, endAngle:1.8*Double.pi)
outerPath.arc(center:Point(x:500, y:283), radius:100, startAngle:0.2*Double.pi, endAngle:0.8*Double.pi)
let outerFillColor = FillStyle(color:Color(.seashell))
let outerLineWidth = LineWidth(width:2)
canvas.render(outerFillColor, outerLineWidth, outerPath)
}
Run the program and view in a browser before continuing. |
- What do you observe?
- Which line number determines whether or not this Path is filled?
Adding a Second Path[edit]
Stop the running program.
Return to the console and press CONTROL-C |
Let's add a new path. Modify setup as follows:
override func setup(canvasSize:Size, canvas:Canvas) {
let outerPath = Path(fillMode:.fillAndStroke)
outerPath.arc(center:Point(x:500, y:400), radius:100, startAngle:1.2*Double.pi, endAngle:1.8*Double.pi)
outerPath.arc(center:Point(x:500, y:283), radius:100, startAngle:0.2*Double.pi, endAngle:0.8*Double.pi)
let outerFillColor = FillStyle(color:Color(.seashell))
let outerLineWidth = LineWidth(width:2)
canvas.render(outerFillColor, outerLineWidth, outerPath)
let innerPath = Path(fillMode:.fillAndStroke)
innerPath.arc(center:Point(x:500, y:340), radius:15)
let innerFillColor = FillStyle(color:Color(.royalblue))
let innerLineWidth = LineWidth(width:1)
canvas.render(innerFillColor, innerLineWidth, innerPath)
}
Run the program and view in a browser before continuing. |
- What do you observe?
- The outerPath contains two arcs and each specifies a startAngle and endAngle. These angles aren't specified explicitly for the innerPath. What are the default angles in this case?
Ellipses[edit]
Stop the running program.
Return to the console and press CONTROL-C |
Let's add an ellipse. Note that we can combine different object types on the same canvas. Modify setup by adding the following text to the end of the method:
let pupil = Ellipse(center:Point(x:500, y:340), radiusX:4, radiusY:4, fillMode:.fill)
let pupilFillColor = FillStyle(color:Color(.black))
canvas.render(pupilFillColor, pupil)
Run the program and view in a browser before continuing. |
Lines[edit]
Stop the running program.
Return to the console and press CONTROL-C |
Let's add some lines. Note that in this case, because we are using the moveTo() primitive, the line segments are not connected to one another. Modify setup by adding the following text to the end of the method:
let lashes = Path()
lashes.moveTo(Point(x:415, y:325))
lashes.lineTo(Point(x:395, y:305))
lashes.moveTo(Point(x:440, y:310))
lashes.lineTo(Point(x:420, y:290))
let lashesStrokeColor = StrokeStyle(color:Color(.brown))
let lashesWidth = LineWidth(width:2)
canvas.render(lashesStrokeColor, lashesWidth, lashes)
Run the program and view in a browser before continuing. |
- What do you observe?
- Is a path necessarily constructed of a single, continuous line?