Quarto Game, Custom Shapes, and Canvas Shape DSL Tutorial
Tags: ruby, desktop, gui, gamedev
Published: 2022-01-06 15:22:10

At a Christmas party I attended a couple of weeks ago, I discovered a classic board game called [Quarto](https://en.wikipedia.org/wiki/Quarto_(board_game)). In fact, the host of the party who's worked for a major gaming company in the past asked me if I knew how to build it as a computer application. I discounted myself as a non-game-developer who only builds business applications, but then followed that by saying that if it is only a 2D game, it was simple to build. So, the challenge was on!!!

![Quarto Board Game](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/efeyysrlmyl6d428eleo.png)

Here is the [classic Quarto black board version](https://en.gigamic.com/game/quarto-classic) that the party attendees played with during Christmas:

![Classic Quarto](https://blogger.googleusercontent.com/img/a/AVvXsEjUhf10UE8LfVse4_DPwYCe-pJITuq7GDhWvbQAr_EHGCcqxUTqKZb651F7e2JI2THs7YCF5RCaz6bkVDQlTjGgKX2Ht2yQ7nz-qk1p9Ops0dq4KFrXNAKtBhKe9c-JpXVQRD2fns2q0fzbF_ayi4Om_VqzVmtN-NlvLEto9NlwXVVY5VOGaA=s16000)

I built Quarto as a [computer game](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto) in 4-5 days using [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)!

![Quarto Computer Game](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-quarto.png)

Here is a video demo of [Glimmer Quarto](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto):

<video controls autoplay>
  <source src="https://www.dropbox.com/s/y1luneuljgrgn9k/GlimmerQuarto.mp4?dl=1" type="video/mp4">
Your browser does not support the video tag.
</video>

The key features in [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) that helped me complete it very quickly are [Custom Shape support](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#custom-shapes) (e.g. building cylinder and cube Custom Shapes and reusing to model Quarto piece Custom Shape) and the effortless [Canvas Drag and Drop](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#hello-canvas-drag-and-drop) (e.g. designating one Custom Shape as a drag source and another as a drop target with on_drop listener). I also used affine [Transforms](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-transform-dsl) to tilt the board by 45 degrees from a standard grid.

The top-level Quarto code is included below, followed by the code of the Quarto piece, cylinder, and cube Custom Shapes, followed by a link to the rest of the code, and then finally a quick tutorial for the [Glimmer DSL for SWT Canvas Shape DSL](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-shape-dsl).

Quarto

```ruby
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto

# Top-level Quarto board GUI code that visually maps to real GUI
shell(:shell_trim, (:double_buffered unless OS.mac?)) {
  text 'Glimmer Quarto'
  minimum_size BOARD_DIAMETER + AREA_MARGIN + PIECES_AREA_WIDTH + SHELL_MARGIN*2 + (OS.linux? ? 52 : (OS.windows? ? 16 : 0)), BOARD_DIAMETER + 24 + SHELL_MARGIN*2 + (OS.linux? ? 96 : (OS.windows? ? 32 : 0))
  maximum_size BOARD_DIAMETER + AREA_MARGIN + PIECES_AREA_WIDTH + SHELL_MARGIN*2 + (OS.linux? ? 52 : (OS.windows? ? 16 : 0)), BOARD_DIAMETER + 24 + SHELL_MARGIN*2 + (OS.linux? ? 96 : (OS.windows? ? 32 : 0))
  background COLOR_WOOD

  quarto_menu_bar

  @board = board(game: @game, location_x: SHELL_MARGIN, location_y: SHELL_MARGIN)

  @available_pieces_area = available_pieces_area(game: @game, location_x: SHELL_MARGIN + BOARD_DIAMETER + AREA_MARGIN, location_y: SHELL_MARGIN)
  @selected_piece_area = selected_piece_area(game: @game, location_x: SHELL_MARGIN + BOARD_DIAMETER + AREA_MARGIN, location_y: SHELL_MARGIN + AVAILABLE_PIECES_AREA_HEIGHT + AREA_MARGIN)
}
```

Piece

```ruby
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto

require_relative 'cylinder'
require_relative 'cube'

class Quarto
  module View
    class Piece
      include Glimmer::UI::CustomShape
      
      SIZE_SHORT = 28
      SIZE_TALL = 48
      BASIC_SHAPE_WIDTH = 48
      BASIC_SHAPE_HEIGHT = 28
      LINE_THICKNESS = 2
      
      options :game, :model, :location_x, :location_y
      
      before_body do
        @background_color = model.light? ? COLOR_LIGHT_WOOD : COLOR_DARK_WOOD
        @size = model.short? ? SIZE_SHORT : SIZE_TALL
        @shape_location_x = 0
        @shape_location_y = model.short? ? 20 : 0
      end
      
      body {
        shape(location_x, location_y) {
          if model.is_a?(Model::Piece::Cylinder)
            cylinder(location_x: @shape_location_x, location_y: @shape_location_y, cylinder_height: @size, oval_width: BASIC_SHAPE_WIDTH, oval_height: BASIC_SHAPE_HEIGHT, pitted: model.pitted?, background_color: @background_color, line_thickness: LINE_THICKNESS)
          else
            cube(location_x: @shape_location_x, location_y: @shape_location_y, cube_height: @size, rectangle_width: BASIC_SHAPE_WIDTH, rectangle_height: BASIC_SHAPE_HEIGHT, pitted: model.pitted?, background_color: @background_color, line_thickness: LINE_THICKNESS)
          end
        }
      }
    end
  end
end
```

Cylinder

```ruby
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto

class Quarto
  module View
    class Cylinder
      include Glimmer::UI::CustomShape
      
      DEFAULT_SIZE = 28
      
      options :location_x, :location_y, :oval_width, :oval_height, :cylinder_height, :pitted, :background_color, :line_thickness
      alias pitted? pitted
      
      before_body do
        self.location_x ||= 0
        self.location_y ||= 0
        self.oval_width ||= oval_height || cylinder_height || DEFAULT_SIZE
        self.oval_height ||= oval_width || cylinder_height || DEFAULT_SIZE
        self.cylinder_height ||= oval_width || oval_height || DEFAULT_SIZE
        self.line_thickness ||= 1
      end
      
      body {
        shape(location_x, location_y) {
          oval(0, cylinder_height, oval_width, oval_height) {
            background background_color
            
            oval { # draws with foreground :black and has max size within parent by default
              line_width line_thickness
            }
          }
          rectangle(0, oval_height / 2.0, oval_width, cylinder_height) {
            background background_color
          }
          polyline(0, oval_height / 2.0 + cylinder_height, 0, oval_height / 2.0, oval_width, oval_height / 2.0, oval_width, oval_height / 2.0 + cylinder_height) {
            line_width line_thickness
          }
          oval(0, 0, oval_width, oval_height) {
            background background_color
            
            oval { # draws with foreground :black and has max size within parent by default
              line_width line_thickness
            }
          }
          if pitted?
            oval(oval_width / 4.0, oval_height / 4.0, oval_width / 2.0, oval_height / 2.0) {
              background :black
            }
          end
        }
      }
    end
  end
end
```

Cube

```ruby
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto

class Quarto
  module View
    class Cube
      include Glimmer::UI::CustomShape
      
      DEFAULT_SIZE = 28
      
      options :location_x, :location_y, :rectangle_width, :rectangle_height, :cube_height, :pitted, :background_color, :line_thickness
      alias pitted? pitted
      
      before_body do
        self.location_x ||= 0
        self.location_y ||= 0
        self.rectangle_width ||= rectangle_height || cube_height || DEFAULT_SIZE
        self.rectangle_height ||= rectangle_width || cube_height || DEFAULT_SIZE
        self.cube_height ||= rectangle_width || rectangle_height || DEFAULT_SIZE
        self.line_thickness ||= 1
      end
      
      body {
        shape(location_x, location_y) {
          polygon(0, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height, rectangle_width, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height + rectangle_height) {
            background background_color
          }
          polygon(0, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height, rectangle_width, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height + rectangle_height) {
            line_width line_thickness
          }
          rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
            background background_color
          }
          polyline(0, rectangle_height / 2.0 + cube_height, 0, rectangle_height / 2.0, rectangle_width, rectangle_height / 2.0, rectangle_width, rectangle_height / 2.0 + cube_height) {
            line_width line_thickness
          }
          polygon(0, rectangle_height / 2.0, rectangle_width / 2.0, 0, rectangle_width, rectangle_height / 2.0, rectangle_width / 2.0, rectangle_height) {
            background background_color
          }
          polygon(0, rectangle_height / 2.0, rectangle_width / 2.0, 0, rectangle_width, rectangle_height / 2.0, rectangle_width / 2.0, rectangle_height) {
            line_width line_thickness
          }
          line(rectangle_width / 2.0, cube_height + rectangle_height, rectangle_width / 2.0, rectangle_height) {
            line_width line_thickness
          }
          if pitted?
            oval(rectangle_width / 4.0, rectangle_height / 4.0, rectangle_width / 2.0, rectangle_height / 2.0) {
              background :black
            }
          end
        }
      }
    end
  end
end
```

Rest of the Quarto code:

Views:

https://github.com/AndyObtiva/glimmer-dsl-swt/tree/master/samples/elaborate/quarto/view

Models:

https://github.com/AndyObtiva/glimmer-dsl-swt/tree/master/samples/elaborate/quarto/model

Now, let us get into the [Canvas Shape DSL](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-shape-dsl) tutorial.

## Canvas Shape DSL Tutorial

Below are examples of using the [Canvas Shape DSL](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-shape-dsl) in [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt).

Example of `line` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    line(30, 30, 170, 170) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Line](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-line.png)

Example of `rectangle` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    rectangle(30, 50, 140, 100) {
      background :yellow
    }
    
    rectangle(30, 50, 140, 100) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Rectangle](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-rectangle.png)

Example of `rectangle` with round corners having 60 degree angles by default (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    rectangle(30, 50, 140, 100, round: true) {
      background :yellow
    }
    
    rectangle(30, 50, 140, 100, round: true) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Rectangle Round](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-rectangle-round.png)

Example of `rectangle` with round corners having different horizontal and vertical angles (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    rectangle(30, 50, 140, 100, 40, 80) {
      background :yellow
    }
    
    rectangle(30, 50, 140, 100, 40, 80) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Rectangle Round Angles](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-rectangle-round-angles.png)

Example of `oval` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    oval(30, 50, 140, 100) {
      background :yellow
    }
    
    oval(30, 50, 140, 100) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Oval](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-oval.png)

Example of `arc` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    arc(30, 30, 140, 140, 0, 270) {
      background :yellow
    }
    
    arc(30, 30, 140, 140, 0, 270) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Arc](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-arc.png)

Example of `polyline` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Polyline](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-polyline.png)

Example of `polygon` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    polygon(30, 90, 80, 20, 130, 40, 170, 90, 130, 140, 80, 170, 40, 160) {
      background :yellow
    }
    
    polygon(30, 90, 80, 20, 130, 40, 170, 90, 130, 140, 80, 170, 40, 160) {
      foreground :red
      line_width 3
    }
  }
}.open
```

![Canvas Shape DSL Polygon](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-polygon.png)

Example of `text` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    text(" This is \n rendered text ", 30, 50) {
      background :yellow
      foreground :red
      font height: 25, style: :italic
      
      rectangle { # automatically scales to match text extent
        foreground :red
        line_width 3
      }
    }
  }
}.open
```

![Canvas Shape DSL Text](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-text.png)

Example of `image` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 512, 542
  
  canvas {
    background :white
    
    image(File.expand_path('icons/scaffold_app.png', __dir__), 0, 5)
  }
}.open
```

![Canvas Shape DSL Image](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-image.png)

Example of `image` pre-built with a smaller height (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

@image_object = image(File.expand_path('icons/scaffold_app.png', __dir__), height: 200)

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 230
  
  canvas {
    background :white
    
    image(@image_object, 0, 5)
  }
}.open
```

![Canvas Shape DSL Image](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-image-shrunk.png)

Example of setting `background_pattern` attribute to a horizontal gradient (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    oval(30, 30, 140, 140) {
      background_pattern 0, 0, 200, 0, rgb(255, 255, 0), rgb(255, 0, 0)
    }
  }
}.open
```

![Canvas Shape DSL Oval Background Pattern Gradient](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-oval-background-pattern-gradient.png)

Example of setting `foreground_pattern` attribute to a vertical gradient (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    oval(30, 30, 140, 140) {
      foreground_pattern 0, 0, 0, 200, :blue, :green
      line_width 10
    }
  }
}.open
```

![Canvas Shape DSL Oval Foreground Pattern Gradient](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-oval-foreground-pattern-gradient.png)

Example of setting `line_style` attribute to `:dashdot` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    oval(30, 50, 140, 100) {
      background :yellow
    }
    
    oval(30, 50, 140, 100) {
      foreground :red
      line_width 3
      line_style :dashdot
    }
  }
}.open
```

![Canvas Shape DSL Oval](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-oval-line-style-dashdot.png)

Example of setting `line_width` attribute to `10`, `line_join` attribute to `:miter` (default) and `line_cap` attribute to `:flat` (default) (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
      foreground :red
      line_width 10
      line_join :miter
      line_cap :flat
    }
  }
}.open
```

![Canvas Shape DSL Polyline Line Join Miter Line Cap Flat](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-polyline-line-join-miter-line-cap-flat.png)

Example of setting `line_width` attribute to `10`, `line_join` attribute to `:round` and `line_cap` attribute to `:round` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
      foreground :red
      line_width 10
      line_join :round
      line_cap :round
    }
  }
}.open
```

![Canvas Shape DSL Polyline Line Join Round Line Cap Round](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-polyline-line-join-round-line-cap-round.png)

Example of setting `line_width` attribute to `10`, `line_join` attribute to `:bevel` and `line_cap` attribute to `:square` (you may copy/paste in [`girb`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GIRB.md)):

```ruby
require 'glimmer-dsl-swt'

include Glimmer

shell {
  text 'Canvas Shape DSL'
  minimum_size 200, 220
  
  canvas {
    background :white
    
    polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
      foreground :red
      line_width 10
      line_join :bevel
      line_cap :square
    }
  }
}.open
```

![Canvas Shape DSL Polyline Line Join Miter Line Cap Flat](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-canvas-shape-dsl-polyline-line-join-bevel-line-cap-square.png)

Happy [Glimmering](https://github.com/AndyObtiva/glimmer-dsl-swt)!