elerea-examples 2.0.1 → 2.2.0
raw patch · 9 files changed
+128/−100 lines, 9 filesdep ~elerea
Dependency ranges changed: elerea
Files
- README +5/−4
- doc/Breakout.html +66/−51
- doc/Chase.html +46/−31
- elerea-examples.cabal +5/−8
- src/Bounce/Event.hs +1/−1
- src/Bounce/Main.hs +1/−1
- src/Bounce/Math.hs +1/−1
- src/Bounce/Util.hs +1/−1
- src/Bounce/Vector.hs +2/−2
README view
@@ -1,10 +1,11 @@-Example applications for Elerea. They are factored out into their own-package so as to avoid unnecessary dependencies in the library. Check-out the doc directory for the colourful literate sources.+Example applications for Elerea. They are factored out into their+own package so as to avoid unnecessary dependencies in the+library. Check out the @doc@ directory for the colourful literate+sources. The programs included are the following: -* bounce (current): an example for creating dynamic collections of+* bounce (legacy): an example for creating dynamic collections of signals with the experimental interface; left click to create balls, drag existing balls with left button to propel them and drag with right to select balls for deletion (upon release)
doc/Breakout.html view
@@ -1,8 +1,19 @@-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title></title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><meta name="generator" content="pandoc" /><link rel="stylesheet" href="hscolour.css" type="text/css" media="all" />-</head><body><h1 id="elerea-breakout-example">Elerea Breakout example</h1><p>Breakout is one of the simplest test applications that brings up some problems with non-trivial solutions. In particular, there is a circular dependency between the position and the velocity of the ball. The position is the integral of the velocity, while velocity can have instantaneous changes due to collisions, which is a function of the position of the ball and the current state of the field. The field is dynamic, and it also forms a mutual dependency loop with the ball position. On top of these, part of the field (at least if we regard the paddle as part of it) depends on user input, and its effects start propagating in the game state as soon as the ball reaches the level of the paddle.</p><p>Elerea lets us express all these circular dependencies in a natural way, by simply referring to the respective signals by their names. User input is also seen as a signal from inside the reactive part, while the imperative framework is given a sink fuction to update it as needed. Type safety is ensured all the way.</p><img src="Breakout.png" alt="Elerea Breakout in action" /><p>You can also have the program output the signal structure in <a href="http://www.graphviz.org/">Graphviz</a> dot format with the <code>--dump-dot</code> switch after the game is over. For instance, if Graphviz is installed, you can get an SVG rendition of the graph using the following command:</p><p><code>elerea-breakout --dump-dot | dot -Tsvg -o breakout.svg</code></p><p>Below follows the full source of the example.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE RecursiveDo #-}</span>+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">+<html xmlns="http://www.w3.org/1999/xhtml">+<head>+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />+ <meta name="generator" content="pandoc" />+ <title></title>+ <link rel="stylesheet" href="hscolour.css" type="text/css" />+</head>+<body>+<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>+<head>+<!-- Generated by HsColour, http://www.cs.york.ac.uk/fp/darcs/hscolour/ --><title>Haskell code</title> <link type='text/css' rel='stylesheet' href='hscolour.css' /></head>+<body><h1 id="elerea-breakout-example">Elerea Breakout example</h1><p>Breakout is one of the simplest test applications that brings up some problems with non-trivial solutions. In particular, there is a circular dependency between the position and the velocity of the ball. The position is the integral of the velocity, while velocity can have instantaneous changes due to collisions, which is a function of the position of the ball and the current state of the field. The field is dynamic, and it also forms a mutual dependency loop with the ball position. On top of these, part of the field (at least if we regard the paddle as part of it) depends on user input, and its effects start propagating in the game state as soon as the ball reaches the level of the paddle.</p><p>Elerea lets us express all these circular dependencies in a natural way, by simply referring to the respective signals by their names. User input is also seen as a signal from inside the reactive part, while the imperative framework is given a sink fuction to update it as needed. Type safety is ensured all the way.</p><img src="Breakout.png" alt="Elerea Breakout in action" /><p>You can also have the program output the signal structure in <a href="http://www.graphviz.org/">Graphviz</a> dot format with the <code>--dump-dot</code> switch after the game is over. For instance, if Graphviz is installed, you can get an SVG rendition of the graph using the following command:</p><p><code>elerea-breakout --dump-dot | dot -Tsvg -o breakout.svg</code></p><p>Below follows the full source of the example.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE RecursiveDo #-}</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>module</span> <span class='hs-conid'>Main</span> <span class='hs-keyword'>where</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Applicative</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Concurrent</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Monad</span>@@ -10,8 +21,8 @@ <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Data</span><span class='hs-varop'>.</span><span class='hs-conid'>List</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Data</span><span class='hs-varop'>.</span><span class='hs-conid'>Maybe</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Data</span><span class='hs-varop'>.</span><span class='hs-conid'>Traversable</span> <span class='hs-varid'>hiding</span> <span class='hs-layout'>(</span><span class='hs-varid'>mapM</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Graph</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span><span class='hs-varop'>.</span><span class='hs-conid'>Graph</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Graphics</span><span class='hs-varop'>.</span><span class='hs-conid'>UI</span><span class='hs-varop'>.</span><span class='hs-conid'>GLFW</span> <span class='hs-keyword'>as</span> <span class='hs-conid'>GLFW</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Graphics</span><span class='hs-varop'>.</span><span class='hs-conid'>Rendering</span><span class='hs-varop'>.</span><span class='hs-conid'>OpenGL</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>System</span><span class='hs-varop'>.</span><span class='hs-conid'>Environment</span>@@ -19,45 +30,45 @@ </pre> <pre><span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Utils</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Vector</span>-</pre><h2 id="global-constants">Global constants</h2><p>The dimensions of the ball, which behaves as a rectangle in collision detection, but is drawn as an ellipse.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>ballW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.04</span>-<span class='hs-varop'>></span> <span class='hs-varid'>ballH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.04</span>-</pre><p>The initial position and velocity of the ball.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>ballPos0</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-num'>0</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.4</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> <span class='hs-varid'>ballVel0</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.4</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0.35</span><span class='hs-layout'>)</span>-</pre><p>The dimensions and vertical position of the player.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>playerW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.2</span>-<span class='hs-varop'>></span> <span class='hs-varid'>playerH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.03</span>-<span class='hs-varop'>></span> <span class='hs-varid'>playerY</span> <span class='hs-keyglyph'>=</span> <span class='hs-comment'>-</span><span class='hs-varid'>fieldH</span><span class='hs-varop'>+</span><span class='hs-num'>0.01</span>-</pre><p>The dimensions of the field.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>fieldW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.8</span>-<span class='hs-varop'>></span> <span class='hs-varid'>fieldH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.5</span>-</pre><p>The dimensions of each brick.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>brickW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.05</span>-<span class='hs-varop'>></span> <span class='hs-varid'>brickH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.03</span>-</pre><p>The data structure describing the state of each brick. A brick can either be alive or dying. Dying bricks also keep track of their fadeout level.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>data</span> <span class='hs-conid'>BrickState</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>Live</span> <span class='hs-keyglyph'>|</span> <span class='hs-conid'>Dying</span> <span class='hs-varop'>!</span><span class='hs-conid'>Double</span> <span class='hs-keyword'>deriving</span> <span class='hs-layout'>(</span><span class='hs-conid'>Eq</span><span class='hs-layout'>,</span><span class='hs-conid'>Show</span><span class='hs-layout'>)</span>-</pre><p>The starting positions of the bricks.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>brickPos0</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>distributeBricks</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.7</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.1</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0.7</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0.4</span><span class='hs-layout'>)</span> <span class='hs-num'>18</span> <span class='hs-num'>10</span>+</pre><h2 id="global-constants">Global constants</h2><p>The dimensions of the ball, which behaves as a rectangle in collision detection, but is drawn as an ellipse.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>ballW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.04</span>+<span class='hs-varop'>></span> <span class='hs-definition'>ballH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.04</span>+</pre><p>The initial position and velocity of the ball.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>ballPos0</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-num'>0</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.4</span><span class='hs-layout'>)</span>+<span class='hs-varop'>></span> <span class='hs-definition'>ballVel0</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.4</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0.35</span><span class='hs-layout'>)</span>+</pre><p>The dimensions and vertical position of the player.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>playerW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.2</span>+<span class='hs-varop'>></span> <span class='hs-definition'>playerH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.03</span>+<span class='hs-varop'>></span> <span class='hs-definition'>playerY</span> <span class='hs-keyglyph'>=</span> <span class='hs-comment'>-</span><span class='hs-varid'>fieldH</span><span class='hs-varop'>+</span><span class='hs-num'>0.01</span>+</pre><p>The dimensions of the field.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>fieldW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.8</span>+<span class='hs-varop'>></span> <span class='hs-definition'>fieldH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.5</span>+</pre><p>The dimensions of each brick.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>brickW</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.05</span>+<span class='hs-varop'>></span> <span class='hs-definition'>brickH</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.03</span>+</pre><p>The data structure describing the state of each brick. A brick can either be alive or dying. Dying bricks also keep track of their fadeout level.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>data</span> <span class='hs-conid'>BrickState</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>Live</span> <span class='hs-keyglyph'>|</span> <span class='hs-conid'>Dying</span> <span class='hs-varop'>!</span><span class='hs-conid'>GLfloat</span> <span class='hs-keyword'>deriving</span> <span class='hs-layout'>(</span><span class='hs-conid'>Eq</span><span class='hs-layout'>,</span><span class='hs-conid'>Show</span><span class='hs-layout'>)</span>+</pre><p>The starting positions of the bricks.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>brickPos0</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>distributeBricks</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.7</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-num'>0.1</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0.7</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0.4</span><span class='hs-layout'>)</span> <span class='hs-num'>18</span> <span class='hs-num'>10</span> <span class='hs-varop'>></span> <span class='hs-keyword'>where</span> <span class='hs-varid'>distributeBricks</span> <span class='hs-varid'>xmin</span> <span class='hs-varid'>ymin</span> <span class='hs-varid'>xmax</span> <span class='hs-varid'>ymax</span> <span class='hs-varid'>xn</span> <span class='hs-varid'>yn</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyglyph'>[</span><span class='hs-layout'>(</span><span class='hs-varid'>xmin</span><span class='hs-varop'>+</span><span class='hs-varid'>xstep</span><span class='hs-varop'>*</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>ymin</span><span class='hs-varop'>+</span><span class='hs-varid'>ystep</span><span class='hs-varop'>*</span><span class='hs-varid'>y</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>|</span> <span class='hs-varop'>></span> <span class='hs-varid'>x</span> <span class='hs-keyglyph'>←</span> <span class='hs-keyglyph'>[</span><span class='hs-num'>0</span><span class='hs-keyglyph'>..</span><span class='hs-varid'>xn</span><span class='hs-comment'>-</span><span class='hs-num'>1</span><span class='hs-keyglyph'>]</span><span class='hs-layout'>,</span> <span class='hs-varid'>y</span> <span class='hs-keyglyph'>←</span> <span class='hs-keyglyph'>[</span><span class='hs-num'>0</span><span class='hs-keyglyph'>..</span><span class='hs-varid'>yn</span><span class='hs-comment'>-</span><span class='hs-num'>1</span><span class='hs-keyglyph'>]</span><span class='hs-keyglyph'>]</span> <span class='hs-varop'>></span> <span class='hs-keyword'>where</span> <span class='hs-varid'>xstep</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>xmax</span><span class='hs-comment'>-</span><span class='hs-varid'>xmin</span><span class='hs-comment'>-</span><span class='hs-varid'>xn</span><span class='hs-varop'>*</span><span class='hs-varid'>brickW</span><span class='hs-layout'>)</span><span class='hs-varop'>/</span><span class='hs-layout'>(</span><span class='hs-varid'>xn</span><span class='hs-comment'>-</span><span class='hs-num'>1</span><span class='hs-layout'>)</span><span class='hs-varop'>+</span><span class='hs-varid'>brickW</span> <span class='hs-varop'>></span> <span class='hs-varid'>ystep</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>ymax</span><span class='hs-comment'>-</span><span class='hs-varid'>ymin</span><span class='hs-comment'>-</span><span class='hs-varid'>yn</span><span class='hs-varop'>*</span><span class='hs-varid'>brickH</span><span class='hs-layout'>)</span><span class='hs-varop'>/</span><span class='hs-layout'>(</span><span class='hs-varid'>yn</span><span class='hs-comment'>-</span><span class='hs-num'>1</span><span class='hs-layout'>)</span><span class='hs-varop'>+</span><span class='hs-varid'>brickH</span>-</pre><p>The rate at which a brick fades out. The reciprocal of this value gives the fadeout time in seconds.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>brickFade</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.5</span>-</pre><h2 id="game-logic">Game logic</h2><p>The entry point performs some lightweight initialisation, and defines the two user-driven signals: window size and mouse position. The <code>external</code> function creates the signal and the corresponding sink at the same time.</p><p>When all is done <code>driveNetwork</code> is invoked. It is not a library function, but part of the tiny <code>Utils</code> module .</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>main</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The rate at which a brick fades out. The reciprocal of this value gives the fadeout time in seconds.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>brickFade</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>0.5</span>+</pre><h2 id="game-logic">Game logic</h2><p>The entry point performs some lightweight initialisation, and defines the two user-driven signals: window size and mouse position. The <code>external</code> function creates the signal and the corresponding sink at the same time.</p><p>When all is done <code>driveNetwork</code> is invoked. It is not a library function, but part of the tiny <code>Utils</code> module .</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>main</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-comment'>-- Creating a window without a depth buffer</span> <span class='hs-varop'>></span> <span class='hs-varid'>initialize</span> <span class='hs-varop'>></span> <span class='hs-varid'>openWindow</span> <span class='hs-layout'>(</span><span class='hs-conid'>Size</span> <span class='hs-num'>640</span> <span class='hs-num'>480</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>[</span><span class='hs-conid'>DisplayRGBBits</span> <span class='hs-num'>8</span> <span class='hs-num'>8</span> <span class='hs-num'>8</span><span class='hs-layout'>,</span> <span class='hs-conid'>DisplayAlphaBits</span> <span class='hs-num'>8</span><span class='hs-keyglyph'>]</span> <span class='hs-conid'>Window</span> <span class='hs-varop'>></span> <span class='hs-varid'>windowTitle</span> <span class='hs-varop'>$=</span> <span class='hs-str'>"Elerea Breakout"</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-comment'>-- External signals available for the game logic</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>windowSize</span><span class='hs-layout'>,</span><span class='hs-varid'>windowSizeSink</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>external</span> <span class='hs-varid'>vnull</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>mousePosition</span><span class='hs-layout'>,</span><span class='hs-varid'>mousePositionSink</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>external</span> <span class='hs-varid'>vnull</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-comment'>-- Wrapping up the init phase</span> <span class='hs-varop'>></span> <span class='hs-varid'>closed</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>newIORef</span> <span class='hs-conid'>False</span> <span class='hs-varop'>></span> <span class='hs-varid'>windowSizeCallback</span> <span class='hs-varop'>$=</span> <span class='hs-varid'>resizeGLScene</span> <span class='hs-varid'>windowSizeSink</span> <span class='hs-varop'>></span> <span class='hs-varid'>windowCloseCallback</span> <span class='hs-varop'>$=</span> <span class='hs-varid'>writeIORef</span> <span class='hs-varid'>closed</span> <span class='hs-conid'>True</span> <span class='hs-varop'>></span> <span class='hs-varid'>initGL</span> <span class='hs-num'>640</span> <span class='hs-num'>480</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-comment'>-- All we need to get going is an IO-valued signal and an IO</span> <span class='hs-varop'>></span> <span class='hs-comment'>-- function to update the external signals</span> <span class='hs-varop'>></span> <span class='hs-varid'>game</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>createSignal</span> <span class='hs-varop'>$</span> <span class='hs-varid'>breakout</span> <span class='hs-varid'>mousePosition</span> <span class='hs-varid'>windowSize</span> <span class='hs-varop'>></span> <span class='hs-varid'>driveNetwork</span> <span class='hs-varid'>game</span> <span class='hs-layout'>(</span><span class='hs-varid'>readInput</span> <span class='hs-varid'>mousePositionSink</span> <span class='hs-varid'>closed</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-comment'>-- The inevitable sad ending</span> <span class='hs-varop'>></span> <span class='hs-varid'>closeWindow</span> <span class='hs-varop'>></span>@@ -66,7 +77,7 @@ <span class='hs-varop'>></span> <span class='hs-varid'>when</span> <span class='hs-layout'>(</span><span class='hs-str'>"--dump-dot"</span> <span class='hs-varop'>`elem`</span> <span class='hs-varid'>args</span><span class='hs-layout'>)</span> <span class='hs-varop'>$</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>gameGraph</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>signalToDot</span> <span class='hs-varid'>game</span> <span class='hs-varop'>></span> <span class='hs-varid'>putStr</span> <span class='hs-varid'>gameGraph</span>-</pre><p>The <code>breakout</code> function creates a reactive signal that carries the rendering actions to be performed at each instant. The principal signals forming the game logic are the following:</p><ul><li><code>playerX</code>: the position of the player, a direct function of the mouse position;</li><li><code>ballPos</code>: the position of the ball given as an integral of its velocity, <code>ballVel</code>;</li><li><code>bricks</code>: the collection of live and dying bricks along with collision information.</li></ul><p>The position and velocity of the ball form a circular dependency through the bricks, as velocity is changed whenever a collision is detected, which is a function of the position.</p><p>The signal carrying the collection of the bricks is a higher-order signal, where each element of the list is a signal representing an individual brick. Bricks behave independently of each other: they are defined as separate transfer functions with the ball position as input signal. As soon as a brick is touched it enters the dying phase and fades out. Also, since other signals are mostly interested in the current state of the bricks, we have to define a flattened version, which carries the snapshots of all the brick signals. This is the <code>brickSamples</code> signal.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>breakout</span> <span class='hs-varid'>mousePos</span> <span class='hs-varid'>windowSize</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>mdo</span>+</pre><p>The <code>breakout</code> function creates a reactive signal that carries the rendering actions to be performed at each instant. The principal signals forming the game logic are the following:</p><ul><li><code>playerX</code>: the position of the player, a direct function of the mouse position;</li><li><code>ballPos</code>: the position of the ball given as an integral of its velocity, <code>ballVel</code>;</li><li><code>bricks</code>: the collection of live and dying bricks along with collision information.</li></ul><p>The position and velocity of the ball form a circular dependency through the bricks, as velocity is changed whenever a collision is detected, which is a function of the position.</p><p>The signal carrying the collection of the bricks is a higher-order signal, where each element of the list is a signal representing an individual brick. Bricks behave independently of each other: they are defined as separate transfer functions with the ball position as input signal. As soon as a brick is touched it enters the dying phase and fades out. Also, since other signals are mostly interested in the current state of the bricks, we have to define a flattened version, which carries the snapshots of all the brick signals. This is the <code>brickSamples</code> signal.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>breakout</span> <span class='hs-varid'>mousePos</span> <span class='hs-varid'>windowSize</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>mdo</span> </pre><p>User-driven player position:</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>playerX</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>adjustPlayerPos</span> <span class='hs-varop'><$></span> <span class='hs-varid'>mousePos</span> <span class='hs-varop'><*></span> <span class='hs-varid'>windowSize</span> <span class='hs-varop'>></span> <span class='hs-varid'>adjustPlayerPos</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>x</span> <span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>w</span> <span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>min</span> <span class='hs-layout'>(</span><span class='hs-varid'>fieldW</span><span class='hs-comment'>-</span><span class='hs-varid'>playerW</span><span class='hs-layout'>)</span> <span class='hs-varop'>$</span> <span class='hs-varid'>max</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-varid'>fieldW</span><span class='hs-layout'>)</span> <span class='hs-varop'>$</span> <span class='hs-num'>2</span><span class='hs-varop'>*</span><span class='hs-varid'>x</span><span class='hs-varop'>/</span><span class='hs-varid'>w</span><span class='hs-comment'>-</span><span class='hs-num'>1</span><span class='hs-comment'>-</span><span class='hs-varid'>playerW</span><span class='hs-varop'>/</span><span class='hs-num'>2</span> </pre><p>Ball state: position and velocity. We use a combination of <code>storeJust</code> and <code>toMaybe</code> to produce a latcher element that stores the value of a certain signal whenever a boolean control signal yields true.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>ballPos</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>integralVec</span> <span class='hs-varid'>ballPos0</span> <span class='hs-varid'>ballVel</span>@@ -74,7 +85,7 @@ <span class='hs-varop'>></span> <span class='hs-varid'>toMaybe</span> <span class='hs-varop'><$></span> <span class='hs-layout'>(</span><span class='hs-varid'>ballCollHorz</span> <span class='hs-varop'>||@</span> <span class='hs-varid'>ballCollVert</span> <span class='hs-varop'>||@</span> <span class='hs-varid'>ballCollPlayer</span><span class='hs-layout'>)</span> <span class='hs-varop'><*></span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>adjustVel</span> <span class='hs-varop'><$></span> <span class='hs-varid'>ballCollHorz</span> <span class='hs-varop'><*></span> <span class='hs-varid'>ballCollVert</span> <span class='hs-varop'><*></span> <span class='hs-varid'>ballCollPlayer</span> <span class='hs-varop'><*></span> <span class='hs-varop'>></span> <span class='hs-varid'>ballVel</span> <span class='hs-varop'><*></span> <span class='hs-varid'>ballNewVelX</span><span class='hs-layout'>)</span>-</pre><p>The <code>adjustVel</code> function calculates a candidate velocity for the next frame given collision information and the current velocity. Even though it would return the current speed if there are no collisions, we don’t evaluate it at all thanks to the laziness of applicative nodes. In the end, velocity is only recalculated when a collision is detected.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>adjustVel</span> <span class='hs-varid'>ch</span> <span class='hs-varid'>cv</span> <span class='hs-varid'>cp</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>bvx</span> <span class='hs-varid'>bvy</span><span class='hs-layout'>)</span> <span class='hs-varid'>bvx'</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span>+</pre><p>The <code>adjustVel</code> function calculates a candidate velocity for the next frame given collision information and the current velocity. Even though it would return the current speed if there are no collisions, we don’t evaluate it at all thanks to the laziness of applicative nodes. In the end, velocity is only recalculated when a collision is detected.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>adjustVel</span> <span class='hs-varid'>ch</span> <span class='hs-varid'>cv</span> <span class='hs-varid'>cp</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>bvx</span> <span class='hs-varid'>bvy</span><span class='hs-layout'>)</span> <span class='hs-varid'>bvx'</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span> <span class='hs-varop'>></span> <span class='hs-keyword'>where</span> <span class='hs-varid'>x</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-keyword'>if</span> <span class='hs-varid'>ch</span> <span class='hs-keyword'>then</span> <span class='hs-comment'>-</span><span class='hs-num'>1</span> <span class='hs-keyword'>else</span> <span class='hs-num'>1</span><span class='hs-layout'>)</span><span class='hs-varop'>*</span><span class='hs-layout'>(</span><span class='hs-keyword'>if</span> <span class='hs-varid'>cp</span> <span class='hs-keyword'>then</span> <span class='hs-varid'>bvx'</span><span class='hs-varop'>*</span><span class='hs-num'>4</span> <span class='hs-keyword'>else</span> <span class='hs-varid'>bvx</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>y</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>if</span> <span class='hs-varid'>cv</span> <span class='hs-varop'>||</span> <span class='hs-varid'>cp</span> <span class='hs-keyword'>then</span> <span class='hs-comment'>-</span><span class='hs-varid'>bvy</span> <span class='hs-keyword'>else</span> <span class='hs-varid'>bvy</span> <span class='hs-varop'>></span> <span class='hs-varid'>ballNewVelX</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>getX</span> <span class='hs-varop'><$></span> <span class='hs-varid'>ballPos</span><span class='hs-layout'>)</span><span class='hs-comment'>-</span><span class='hs-varid'>playerX</span><span class='hs-comment'>-</span><span class='hs-varid'>pure</span> <span class='hs-layout'>(</span><span class='hs-varid'>playerW</span><span class='hs-varop'>/</span><span class='hs-num'>2</span><span class='hs-layout'>)</span>@@ -88,11 +99,11 @@ <span class='hs-varop'>></span> <span class='hs-varid'>ballCollPlayer</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>check</span> <span class='hs-varop'><$></span> <span class='hs-varid'>ballPos</span> <span class='hs-varop'><*></span> <span class='hs-varid'>ballVel</span> <span class='hs-varop'><*></span> <span class='hs-varid'>playerX</span> <span class='hs-varop'>></span> <span class='hs-keyword'>where</span> <span class='hs-varid'>check</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>bx</span> <span class='hs-varid'>by</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-keyword'>_</span> <span class='hs-varid'>bvy</span><span class='hs-layout'>)</span> <span class='hs-varid'>px</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>bvy</span> <span class='hs-varop'><</span> <span class='hs-num'>0</span> <span class='hs-varop'>&&</span> <span class='hs-varop'>></span> <span class='hs-varid'>doRectsIntersect</span> <span class='hs-varid'>bx</span> <span class='hs-varid'>by</span> <span class='hs-varid'>ballW</span> <span class='hs-varid'>ballH</span> <span class='hs-varid'>px</span> <span class='hs-varid'>playerY</span> <span class='hs-varid'>playerW</span> <span class='hs-varid'>playerH</span>-</pre><p>Bricks are defined by the updater function <code>evolveBrick</code> as a transformer of the ball position. The transfer function takes care of fading and checking collision. Collision information is part of the state of the transfer function, even though it is strictly a function of the brick data and the ball position at the moment. However, since we need to check collisions in order to update the state of the brick, it’s simpler and more efficient to let the outer world see the results of these checks instead of having to recalculate them.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>brick</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-conid'>Live</span><span class='hs-layout'>,</span><span class='hs-conid'>False</span><span class='hs-layout'>,</span><span class='hs-conid'>False</span><span class='hs-layout'>)</span> <span class='hs-varid'>evolveBrick</span> <span class='hs-varid'>ballPos</span>+</pre><p>Bricks are defined by the updater function <code>evolveBrick</code> as a transformer of the ball position. The transfer function takes care of fading and checking collision. Collision information is part of the state of the transfer function, even though it is strictly a function of the brick data and the ball position at the moment. However, since we need to check collisions in order to update the state of the brick, it’s simpler and more efficient to let the outer world see the results of these checks instead of having to recalculate them.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>brick</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-conid'>Live</span><span class='hs-layout'>,</span><span class='hs-conid'>False</span><span class='hs-layout'>,</span><span class='hs-conid'>False</span><span class='hs-layout'>)</span> <span class='hs-varid'>evolveBrick</span> <span class='hs-varid'>ballPos</span> <span class='hs-varop'>></span> <span class='hs-varid'>getBrickData</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-varid'>s</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>getBrickHColl</span> <span class='hs-layout'>(</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-varid'>c</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>c</span> <span class='hs-varop'>></span> <span class='hs-varid'>getBrickVColl</span> <span class='hs-layout'>(</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-varid'>c</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>c</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>evolveBrick</span> <span class='hs-varid'>dt</span> <span class='hs-keyword'>_</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-conid'>Dying</span> <span class='hs-varid'>a</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-conid'>Dying</span> <span class='hs-layout'>(</span><span class='hs-varid'>a</span><span class='hs-comment'>-</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-varop'>*</span><span class='hs-varid'>brickFade</span><span class='hs-layout'>)</span><span class='hs-layout'>,</span><span class='hs-conid'>False</span><span class='hs-layout'>,</span><span class='hs-conid'>False</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>evolveBrick</span> <span class='hs-varid'>dt</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>bx</span> <span class='hs-varid'>by</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span><span class='hs-varid'>y</span><span class='hs-layout'>,</span><span class='hs-keyword'>if</span> <span class='hs-varid'>isKilled</span> <span class='hs-keyword'>then</span> <span class='hs-conid'>Dying</span> <span class='hs-num'>1</span> <span class='hs-keyword'>else</span> <span class='hs-conid'>Live</span><span class='hs-layout'>,</span><span class='hs-varid'>collHorz</span><span class='hs-layout'>,</span><span class='hs-varid'>collVert</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-keyword'>where</span> <span class='hs-varid'>isKilled</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>isHit</span> <span class='hs-varop'>||</span> <span class='hs-varid'>by</span> <span class='hs-varop'><</span> <span class='hs-comment'>-</span><span class='hs-varid'>fieldH</span><span class='hs-comment'>-</span><span class='hs-varid'>ballH</span>@@ -104,15 +115,15 @@ <span class='hs-varop'>></span> <span class='hs-varid'>yDist</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>abs</span> <span class='hs-layout'>(</span><span class='hs-layout'>(</span><span class='hs-varid'>y</span><span class='hs-varop'>+</span><span class='hs-varid'>brickH</span><span class='hs-varop'>/</span><span class='hs-num'>2</span><span class='hs-layout'>)</span><span class='hs-comment'>-</span><span class='hs-layout'>(</span><span class='hs-varid'>by</span><span class='hs-varop'>+</span><span class='hs-varid'>ballH</span><span class='hs-varop'>/</span><span class='hs-num'>2</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span> </pre><p>The <code>isBrickNeeded</code> function is used to decide whether a brick should be kept in the collection. As soon as it turns false, the brick in question is removed from the <code>bricks</code> signal.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>isBrickNeeded</span> <span class='hs-layout'>(</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-conid'>Dying</span> <span class='hs-varid'>a</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>a</span> <span class='hs-varop'>></span> <span class='hs-num'>0</span> <span class='hs-varop'>></span> <span class='hs-varid'>isBrickNeeded</span> <span class='hs-layout'>(</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-conid'>Live</span> <span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>,</span><span class='hs-keyword'>_</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>True</span>-</pre><p>The <code>brickSamples</code> signal contains a snapshot of every brick, and it’s obtained simply by traversing the collection (this is equivalent to rebuilding the structure with lifted constructors), then applying a sampler, which collapses the two signal layers into one. We take advantage of the fact that lists are instances of Traversable.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>brickSamples</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>sampler</span> <span class='hs-layout'>(</span><span class='hs-varid'>sequenceA</span> <span class='hs-varop'><$></span> <span class='hs-varid'>bricks</span><span class='hs-layout'>)</span>+</pre><p>The <code>brickSamples</code> signal contains a snapshot of every brick, and it’s obtained simply by traversing the collection (this is equivalent to rebuilding the structure with lifted constructors), then applying a sampler, which collapses the two signal layers into one. We take advantage of the fact that lists are instances of Traversable.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>brickSamples</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>sampler</span> <span class='hs-layout'>(</span><span class='hs-varid'>sequenceA</span> <span class='hs-varop'><$></span> <span class='hs-varid'>bricks</span><span class='hs-layout'>)</span> </pre><p>The <code>bricks</code> signal carries the dynamic list of bricks along with ball-brick collision information, all of which are updated in each frame. We start out with a number of live bricks in the positions given by the <code>brickPos0</code> list, and derive the list of the next frame from the current one by filtering out the bricks for which <code>isBrickNeeded</code> evaluates to false. These updates are made explicit by using <code>delay</code> to define the dynamic collection.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>bricks</span> <span class='hs-keyglyph'>←</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>bricksInit</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>mapM</span> <span class='hs-varid'>brick</span> <span class='hs-varid'>brickPos0</span> <span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>bricksNext</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>map</span> <span class='hs-varid'>snd</span> <span class='hs-varop'>.</span> <span class='hs-varid'>filter</span> <span class='hs-layout'>(</span><span class='hs-varid'>isBrickNeeded</span> <span class='hs-varop'>.</span> <span class='hs-varid'>fst</span><span class='hs-layout'>)</span> <span class='hs-varop'><$></span> <span class='hs-layout'>(</span><span class='hs-varid'>zip</span> <span class='hs-varop'><$></span> <span class='hs-varid'>brickSamples</span> <span class='hs-varop'><*></span> <span class='hs-varid'>bricks</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>delay</span> <span class='hs-varid'>bricksInit</span> <span class='hs-varid'>bricksNext</span>-</pre><p>And knowing all these signals we can finally assemble the signal of rendering actions, i.e. the animation:</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>return</span> <span class='hs-varop'>$</span> <span class='hs-varid'>renderLevel</span> <span class='hs-varop'><$></span> <span class='hs-varid'>playerX</span> <span class='hs-varop'><*></span> <span class='hs-varid'>ballPos</span> <span class='hs-varop'><*></span> <span class='hs-layout'>(</span><span class='hs-varid'>map</span> <span class='hs-varid'>getBrickData</span> <span class='hs-varop'><$></span> <span class='hs-varid'>brickSamples</span><span class='hs-layout'>)</span>-</pre><p>The <code>doRectsIntersect</code> function decides whether two rectangles defined by their top left corners and dimensions overlap.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>doRectsIntersect</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varid'>sx1</span> <span class='hs-varid'>sy1</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-varid'>sx2</span> <span class='hs-varid'>sy2</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>collIV</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>sx1</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>sx2</span> <span class='hs-varop'>&&</span> <span class='hs-varid'>collIV</span> <span class='hs-varid'>y1</span> <span class='hs-varid'>sy1</span> <span class='hs-varid'>y2</span> <span class='hs-varid'>sy2</span>+</pre><p>And knowing all these signals we can finally assemble the signal of rendering actions, i.e. the animation:</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>return</span> <span class='hs-varop'>$</span> <span class='hs-varid'>renderLevel</span> <span class='hs-varop'><$></span> <span class='hs-varid'>playerX</span> <span class='hs-varop'><*></span> <span class='hs-varid'>ballPos</span> <span class='hs-varop'><*></span> <span class='hs-layout'>(</span><span class='hs-varid'>map</span> <span class='hs-varid'>getBrickData</span> <span class='hs-varop'><$></span> <span class='hs-varid'>brickSamples</span><span class='hs-layout'>)</span>+</pre><p>The <code>doRectsIntersect</code> function decides whether two rectangles defined by their top left corners and dimensions overlap.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>doRectsIntersect</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varid'>sx1</span> <span class='hs-varid'>sy1</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-varid'>sx2</span> <span class='hs-varid'>sy2</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>collIV</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>sx1</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>sx2</span> <span class='hs-varop'>&&</span> <span class='hs-varid'>collIV</span> <span class='hs-varid'>y1</span> <span class='hs-varid'>sy1</span> <span class='hs-varid'>y2</span> <span class='hs-varid'>sy2</span> <span class='hs-varop'>></span> <span class='hs-keyword'>where</span> <span class='hs-varid'>collIV</span> <span class='hs-varid'>p1</span> <span class='hs-varid'>s1</span> <span class='hs-varid'>p2</span> <span class='hs-varid'>s2</span> <span class='hs-keyglyph'>=</span> <span class='hs-layout'>(</span><span class='hs-varid'>p1</span> <span class='hs-varop'><=</span> <span class='hs-varid'>p2</span> <span class='hs-varop'>&&</span> <span class='hs-varid'>p2</span> <span class='hs-varop'><=</span> <span class='hs-varid'>p1</span><span class='hs-varop'>+</span><span class='hs-varid'>s1</span><span class='hs-layout'>)</span> <span class='hs-varop'>||</span> <span class='hs-layout'>(</span><span class='hs-varid'>p2</span> <span class='hs-varop'><=</span> <span class='hs-varid'>p1</span> <span class='hs-varop'>&&</span> <span class='hs-varid'>p1</span> <span class='hs-varop'><=</span> <span class='hs-varid'>p2</span><span class='hs-varop'>+</span><span class='hs-varid'>s2</span><span class='hs-layout'>)</span>-</pre><p>The <code>renderLevel</code> function takes a snapshot of the game and turns it into an IO action that displays this snapshot on the screen. The <code>breakout</code> signal is the time-varying version of this IO action.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>renderLevel</span> <span class='hs-varid'>playerX</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>ballX</span> <span class='hs-varid'>ballY</span><span class='hs-layout'>)</span> <span class='hs-varid'>bricks</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>renderLevel</code> function takes a snapshot of the game and turns it into an IO action that displays this snapshot on the screen. The <code>breakout</code> signal is the time-varying version of this IO action.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>renderLevel</span> <span class='hs-varid'>playerX</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>ballX</span> <span class='hs-varid'>ballY</span><span class='hs-layout'>)</span> <span class='hs-varid'>bricks</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>drawRect</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span> <span class='hs-varid'>xs</span> <span class='hs-varid'>ys</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>loadIdentity</span> <span class='hs-varop'>></span> <span class='hs-varid'>renderPrimitive</span> <span class='hs-conid'>Quads</span> <span class='hs-varop'>$</span> <span class='hs-keyword'>do</span>@@ -128,7 +139,7 @@ <span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>a</span> <span class='hs-keyglyph'>=</span> <span class='hs-num'>2</span><span class='hs-varop'>*</span><span class='hs-varid'>pi</span><span class='hs-varop'>*</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>i</span><span class='hs-varop'>/</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>n</span> <span class='hs-varop'>></span> <span class='hs-varid'>vertex</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Vertex3</span> <span class='hs-layout'>(</span><span class='hs-varid'>xc</span><span class='hs-varop'>+</span><span class='hs-varid'>xs</span><span class='hs-varop'>/</span><span class='hs-num'>2</span><span class='hs-varop'>*</span><span class='hs-varid'>sin</span> <span class='hs-varid'>a</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>yc</span><span class='hs-varop'>+</span><span class='hs-varid'>ys</span><span class='hs-varop'>/</span><span class='hs-num'>2</span><span class='hs-varop'>*</span><span class='hs-varid'>cos</span> <span class='hs-varid'>a</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>vertex</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Vertex3</span> <span class='hs-varid'>xc</span> <span class='hs-varid'>yc</span> <span class='hs-layout'>(</span><span class='hs-num'>0</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>clear</span> <span class='hs-keyglyph'>[</span><span class='hs-conid'>ColorBuffer</span><span class='hs-keyglyph'>]</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>color</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>0.2</span> <span class='hs-num'>0.2</span> <span class='hs-num'>0.2</span> <span class='hs-layout'>(</span><span class='hs-num'>1</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span>@@ -142,13 +153,13 @@ <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>color</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>1</span> <span class='hs-num'>1</span> <span class='hs-num'>1</span> <span class='hs-layout'>(</span><span class='hs-num'>0.6</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>drawEllipse</span> <span class='hs-varid'>ballX</span> <span class='hs-varid'>ballY</span> <span class='hs-varid'>ballW</span> <span class='hs-varid'>ballH</span> <span class='hs-num'>20</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>color</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>0.3</span> <span class='hs-num'>0.4</span> <span class='hs-num'>0.8</span> <span class='hs-layout'>(</span><span class='hs-num'>0.5</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>drawRect</span> <span class='hs-varid'>playerX</span> <span class='hs-varid'>playerY</span> <span class='hs-varid'>playerW</span> <span class='hs-varid'>playerH</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>flush</span> <span class='hs-varop'>></span> <span class='hs-varid'>swapBuffers</span>-</pre><h2 id="backend">Backend</h2><p>The <code>readInput</code> function has two responsibilities: it provides input for the <code>mousePosition</code> peripheral signal through its associated sink, and also feeds the time difference between two states into the system, deciding when to exit altogether (by returning <code>Nothing</code> instead of the current <code>dt</code> value wrapped in <code>Just</code>).</p><p>The <code>threadDelay</code> call at the beginning is just a trick to give the scheduler a breath. It will cause a wait equal to a scheduler tick, which is 20ms by default. The program can run perfectly without it, but it eats up all the free CPU to produce an unnecessarily high frame rate.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>readInput</span> <span class='hs-varid'>mousePos</span> <span class='hs-varid'>closed</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><h2 id="backend">Backend</h2><p>The <code>readInput</code> function has two responsibilities: it provides input for the <code>mousePosition</code> peripheral signal through its associated sink, and also feeds the time difference between two states into the system, deciding when to exit altogether (by returning <code>Nothing</code> instead of the current <code>dt</code> value wrapped in <code>Just</code>).</p><p>The <code>threadDelay</code> call at the beginning is just a trick to give the scheduler a breath. It will cause a wait equal to a scheduler tick, which is 20ms by default. The program can run perfectly without it, but it eats up all the free CPU to produce an unnecessarily high frame rate.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>readInput</span> <span class='hs-varid'>mousePos</span> <span class='hs-varid'>closed</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>threadDelay</span> <span class='hs-num'>0</span> <span class='hs-varop'>></span> <span class='hs-varid'>t</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>get</span> <span class='hs-conid'>GLFW</span><span class='hs-varop'>.</span><span class='hs-varid'>time</span> <span class='hs-varop'>></span> <span class='hs-conid'>GLFW</span><span class='hs-varop'>.</span><span class='hs-varid'>time</span> <span class='hs-varop'>$=</span> <span class='hs-num'>0</span>@@ -157,44 +168,45 @@ <span class='hs-varop'>></span> <span class='hs-varid'>k</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>getKey</span> <span class='hs-conid'>ESC</span> <span class='hs-varop'>></span> <span class='hs-varid'>c</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>readIORef</span> <span class='hs-varid'>closed</span> <span class='hs-varop'>></span> <span class='hs-varid'>return</span> <span class='hs-layout'>(</span><span class='hs-keyword'>if</span> <span class='hs-varid'>c</span> <span class='hs-varop'>||</span> <span class='hs-varid'>k</span> <span class='hs-varop'>==</span> <span class='hs-conid'>Press</span> <span class='hs-keyword'>then</span> <span class='hs-conid'>Nothing</span> <span class='hs-keyword'>else</span> <span class='hs-conid'>Just</span> <span class='hs-varid'>t</span><span class='hs-layout'>)</span>-</pre><p>The <code>initGL</code> function sets up almost nothing, which means that most functionality is turned off. Only alpha blending is enabled to provide some minimalistic eye candy.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>initGL</span> <span class='hs-varid'>width</span> <span class='hs-varid'>height</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>initGL</code> function sets up almost nothing, which means that most functionality is turned off. Only alpha blending is enabled to provide some minimalistic eye candy.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>initGL</span> <span class='hs-varid'>width</span> <span class='hs-varid'>height</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>clearColor</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span> <span class='hs-num'>1</span> <span class='hs-varop'>></span> <span class='hs-varid'>blend</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Enabled</span> <span class='hs-varop'>></span> <span class='hs-varid'>blendFunc</span> <span class='hs-varop'>$=</span> <span class='hs-layout'>(</span><span class='hs-conid'>SrcAlpha</span><span class='hs-layout'>,</span><span class='hs-conid'>OneMinusSrcAlpha</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>cullFace</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Just</span> <span class='hs-conid'>Back</span>-</pre><p>The resize callback feeds the <code>windowSize</code> signal through its sink besides adjusting the projection matrix.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>resizeGLScene</span> <span class='hs-varid'>winSize</span> <span class='hs-varid'>size</span><span class='hs-keyglyph'>@</span><span class='hs-layout'>(</span><span class='hs-conid'>Size</span> <span class='hs-varid'>w</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The resize callback feeds the <code>windowSize</code> signal through its sink besides adjusting the projection matrix.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>resizeGLScene</span> <span class='hs-varid'>winSize</span> <span class='hs-varid'>size</span><span class='hs-keyglyph'>@</span><span class='hs-layout'>(</span><span class='hs-conid'>Size</span> <span class='hs-varid'>w</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>winSize</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>w</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>viewport</span> <span class='hs-varop'>$=</span> <span class='hs-layout'>(</span><span class='hs-conid'>Position</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span><span class='hs-layout'>,</span><span class='hs-varid'>size</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>matrixMode</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Projection</span> <span class='hs-varop'>></span> <span class='hs-varid'>loadIdentity</span> <span class='hs-varop'>></span> <span class='hs-varid'>scale</span> <span class='hs-num'>1</span> <span class='hs-layout'>(</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>w</span><span class='hs-varop'>/</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>1</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>matrixMode</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Modelview</span> <span class='hs-num'>0</span> </pre><h1 id="utils-module">Utils module</h1><p>This module contains some functions that might make it into the core library eventually.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>module</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Utils</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Applicative</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Monad</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Vector</span>-</pre><p>The <code>driveNetwork</code> function simply executes the supersteps while the <code>driver</code> function keeps returning valid delta time values.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>driveNetwork</span> <span class='hs-varid'>network</span> <span class='hs-varid'>driver</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>driveNetwork</code> function simply executes the supersteps while the <code>driver</code> function keeps returning valid delta time values.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>driveNetwork</span> <span class='hs-varid'>network</span> <span class='hs-varid'>driver</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>dt</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>driver</span> <span class='hs-varop'>></span> <span class='hs-keyword'>case</span> <span class='hs-varid'>dt</span> <span class='hs-keyword'>of</span> <span class='hs-varop'>></span> <span class='hs-conid'>Just</span> <span class='hs-varid'>dt</span> <span class='hs-keyglyph'>→</span> <span class='hs-keyword'>do</span> <span class='hs-varid'>join</span> <span class='hs-varop'>$</span> <span class='hs-varid'>superstep</span> <span class='hs-varid'>network</span> <span class='hs-varid'>dt</span> <span class='hs-varop'>></span> <span class='hs-varid'>driveNetwork</span> <span class='hs-varid'>network</span> <span class='hs-varid'>driver</span> <span class='hs-varop'>></span> <span class='hs-conid'>Nothing</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>return</span> <span class='hs-conid'>()</span>-</pre><p>A scalar integral function for <code>Fractional</code> instances.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>integral</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>+</span><span class='hs-varid'>v</span><span class='hs-varop'>*</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>-</pre><p>An integral function for two-dimensional vectors defined in the <code>Vector</code> module.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>integralVec</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>^+^</span><span class='hs-layout'>(</span><span class='hs-varid'>v</span><span class='hs-varop'>^*.</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>-</pre><h1 id="vector-module">Vector module</h1><p>This module contains a class for two-dimensional vectors, a strict datatype to instantiate it, and another instance for signals of the same type.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}</span>+</pre><p>A scalar integral function for <code>Fractional</code> instances.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>integral</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>+</span><span class='hs-varid'>v</span><span class='hs-varop'>*</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>+</pre><p>An integral function for two-dimensional vectors defined in the <code>Vector</code> module.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>integralVec</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>^+^</span><span class='hs-layout'>(</span><span class='hs-varid'>v</span><span class='hs-varop'>^*.</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>+</pre><h1 id="vector-module">Vector module</h1><p>This module contains a class for two-dimensional vectors, a strict datatype to instantiate it, and another instance for signals of the same type.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, TypeSynonymInstances #-}</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>module</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Vector</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Applicative</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Graphics</span><span class='hs-varop'>.</span><span class='hs-conid'>Rendering</span><span class='hs-varop'>.</span><span class='hs-conid'>OpenGL</span> <span class='hs-varop'>></span>-<span class='hs-varop'>></span> <span class='hs-keyword'>data</span> <span class='hs-conid'>Vec</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>{</span> <span class='hs-varid'>getX</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>Float</span><span class='hs-layout'>,</span> <span class='hs-varid'>getY</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>Float</span> <span class='hs-layout'>}</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>data</span> <span class='hs-conid'>Vec</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>{</span> <span class='hs-varid'>getX</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>GLfloat</span><span class='hs-layout'>,</span> <span class='hs-varid'>getY</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>GLfloat</span> <span class='hs-layout'>}</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>infixl</span> <span class='hs-num'>7</span> <span class='hs-varop'>^*.</span> <span class='hs-varop'>></span> <span class='hs-keyword'>infixl</span> <span class='hs-num'>7</span> <span class='hs-varop'>.*^</span>@@ -212,7 +224,7 @@ <span class='hs-varop'>></span> <span class='hs-varid'>dot</span> <span class='hs-keyglyph'>::</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>c</span> <span class='hs-varop'>></span> <span class='hs-varid'>cross</span> <span class='hs-keyglyph'>::</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>c</span> <span class='hs-varop'>></span>-<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-conid'>Vec</span> <span class='hs-conid'>Float</span> <span class='hs-keyword'>where</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-conid'>Vec</span> <span class='hs-conid'>GLfloat</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>^+^</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>x1</span><span class='hs-varop'>+</span><span class='hs-varid'>x2</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>y1</span><span class='hs-varop'>+</span><span class='hs-varid'>y2</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>^-^</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>x1</span><span class='hs-comment'>-</span><span class='hs-varid'>x2</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>y1</span><span class='hs-comment'>-</span><span class='hs-varid'>y2</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span> <span class='hs-varop'>^*.</span> <span class='hs-varid'>t</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-varop'>*</span><span class='hs-varid'>t</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>y</span><span class='hs-varop'>*</span><span class='hs-varid'>t</span><span class='hs-layout'>)</span>@@ -221,7 +233,7 @@ <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>`dot`</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>x1</span><span class='hs-varop'>*</span><span class='hs-varid'>y1</span><span class='hs-varop'>+</span><span class='hs-varid'>x2</span><span class='hs-varop'>*</span><span class='hs-varid'>y2</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>`cross`</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>x1</span><span class='hs-varop'>*</span><span class='hs-varid'>y2</span><span class='hs-comment'>-</span><span class='hs-varid'>x2</span><span class='hs-varop'>*</span><span class='hs-varid'>y1</span> <span class='hs-varop'>></span>-<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>Vec</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>Float</span><span class='hs-layout'>)</span> <span class='hs-keyword'>where</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>Vec</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varop'>^+^</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-layout'>(</span><span class='hs-varop'>^+^</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varop'>^-^</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-layout'>(</span><span class='hs-varop'>^-^</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varop'>^*.</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-layout'>(</span><span class='hs-varop'>^*.</span><span class='hs-layout'>)</span>@@ -229,5 +241,8 @@ <span class='hs-varop'>></span> <span class='hs-varid'>vnull</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>pure</span> <span class='hs-varid'>vnull</span> <span class='hs-varop'>></span> <span class='hs-varid'>dot</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-varid'>dot</span> <span class='hs-varop'>></span> <span class='hs-varid'>cross</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-varid'>cross</span>-</pre>-</body></html>+</pre></body>+</html>++</body>+</html>
doc/Chase.html view
@@ -1,30 +1,41 @@-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title></title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><meta name="generator" content="pandoc" /><link rel="stylesheet" href="hscolour.css" type="text/css" media="all" />-</head><body><h1 id="elerea-chase-example">Elerea Chase example</h1><p>This is a minimal example to show how to define signals that can be mutually recursive and can optionally depend on user input too. The grey square accelerates towards the red square at a rate proportional to their relative position, and it can be given a momentary impulse with the left mouse button.</p><p>For a slightly more complex example check out <code>Breakout.lhs</code>.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE RecursiveDo #-}</span>+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">+<html xmlns="http://www.w3.org/1999/xhtml">+<head>+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />+ <meta name="generator" content="pandoc" />+ <title></title>+ <link rel="stylesheet" href="hscolour.css" type="text/css" />+</head>+<body>+<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>+<head>+<!-- Generated by HsColour, http://www.cs.york.ac.uk/fp/darcs/hscolour/ --><title>Haskell code</title> <link type='text/css' rel='stylesheet' href='hscolour.css' /></head>+<body><h1 id="elerea-chase-example">Elerea Chase example</h1><p>This is a minimal example to show how to define signals that can be mutually recursive and can optionally depend on user input too. The grey square accelerates towards the red square at a rate proportional to their relative position, and it can be given a momentary impulse with the left mouse button.</p><p>For a slightly more complex example check out <code>Breakout.lhs</code>.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE RecursiveDo #-}</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>module</span> <span class='hs-conid'>Main</span> <span class='hs-keyword'>where</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Applicative</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Data</span><span class='hs-varop'>.</span><span class='hs-conid'>IORef</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Graphics</span><span class='hs-varop'>.</span><span class='hs-conid'>UI</span><span class='hs-varop'>.</span><span class='hs-conid'>GLFW</span> <span class='hs-keyword'>as</span> <span class='hs-conid'>GLFW</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Graphics</span><span class='hs-varop'>.</span><span class='hs-conid'>Rendering</span><span class='hs-varop'>.</span><span class='hs-conid'>OpenGL</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Utils</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Vector</span>-</pre><p>The <code>main</code> function contains the whole reactive logic. Note that <code>driveNetwork</code> is just a wrapper around the <code>superstep</code> function of the core library, and you can see its source below in the <code>Utils</code> module.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>main</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>main</code> function contains the whole reactive logic. Note that <code>driveNetwork</code> is just a wrapper around the <code>superstep</code> function of the core library, and you can see its source below in the <code>Utils</code> module.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>main</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>initialize</span> <span class='hs-varop'>></span> <span class='hs-varid'>openWindow</span> <span class='hs-layout'>(</span><span class='hs-conid'>Size</span> <span class='hs-num'>640</span> <span class='hs-num'>480</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>[</span><span class='hs-conid'>DisplayRGBBits</span> <span class='hs-num'>8</span> <span class='hs-num'>8</span> <span class='hs-num'>8</span><span class='hs-layout'>,</span> <span class='hs-conid'>DisplayAlphaBits</span> <span class='hs-num'>8</span><span class='hs-layout'>,</span> <span class='hs-conid'>DisplayDepthBits</span> <span class='hs-num'>24</span><span class='hs-keyglyph'>]</span> <span class='hs-conid'>Window</span> <span class='hs-varop'>></span> <span class='hs-varid'>windowTitle</span> <span class='hs-varop'>$=</span> <span class='hs-str'>"Elerea Chase"</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>windowSize</span><span class='hs-layout'>,</span><span class='hs-varid'>windowSizeSink</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>external</span> <span class='hs-varid'>vnull</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>mousePosition</span><span class='hs-layout'>,</span><span class='hs-varid'>mousePositionSink</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>external</span> <span class='hs-varid'>vnull</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>mousePress</span><span class='hs-layout'>,</span><span class='hs-varid'>mousePressSink</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>external</span> <span class='hs-conid'>False</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>closed</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>newIORef</span> <span class='hs-conid'>False</span> <span class='hs-varop'>></span> <span class='hs-varid'>windowSizeCallback</span> <span class='hs-varop'>$=</span> <span class='hs-varid'>resizeGLScene</span> <span class='hs-varid'>windowSizeSink</span> <span class='hs-varop'>></span> <span class='hs-varid'>windowCloseCallback</span> <span class='hs-varop'>$=</span> <span class='hs-varid'>writeIORef</span> <span class='hs-varid'>closed</span> <span class='hs-conid'>True</span> <span class='hs-varop'>></span> <span class='hs-varid'>initGL</span> <span class='hs-num'>640</span> <span class='hs-num'>480</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>greyPos</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>createSignal</span> <span class='hs-varop'>$</span> <span class='hs-keyword'>mdo</span> <span class='hs-varop'>></span> <span class='hs-varid'>mouseClick</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>edge</span> <span class='hs-varid'>mousePress</span> <span class='hs-varop'>></span>@@ -38,9 +49,9 @@ <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>driveNetwork</span> <span class='hs-layout'>(</span><span class='hs-varid'>render</span> <span class='hs-varop'><$></span> <span class='hs-varid'>windowSize</span> <span class='hs-varop'><*></span> <span class='hs-varid'>mousePosition</span> <span class='hs-varop'><*></span> <span class='hs-varid'>greyPos</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varid'>readInput</span> <span class='hs-varid'>mousePositionSink</span> <span class='hs-varid'>mousePressSink</span> <span class='hs-varid'>closed</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>closeWindow</span>-</pre><p>The <code>render</code> function takes a snapshot of the system (window size and the positions of the squares) and turns it into OpenGL calls. The signal executed by the <code>driveNetwork</code> function is the time-varying version of the IO action returned here.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>render</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>w</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>cx</span> <span class='hs-varid'>cy</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>ox</span> <span class='hs-varid'>oy</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>render</code> function takes a snapshot of the system (window size and the positions of the squares) and turns it into OpenGL calls. The signal executed by the <code>driveNetwork</code> function is the time-varying version of the IO action returned here.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>render</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>w</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>cx</span> <span class='hs-varid'>cy</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-varid'>ox</span> <span class='hs-varid'>oy</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-keyword'>let</span> <span class='hs-varid'>drawSquare</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>loadIdentity</span> <span class='hs-varop'>></span> <span class='hs-varid'>translate</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Vector3</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-varop'>/</span><span class='hs-varid'>w</span><span class='hs-varop'>*</span><span class='hs-num'>2</span><span class='hs-comment'>-</span><span class='hs-num'>1</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>h</span><span class='hs-varop'>/</span><span class='hs-varid'>w</span><span class='hs-comment'>-</span><span class='hs-varid'>y</span><span class='hs-varop'>/</span><span class='hs-varid'>w</span><span class='hs-varop'>*</span><span class='hs-num'>2</span><span class='hs-layout'>)</span> <span class='hs-num'>0</span>@@ -49,17 +60,17 @@ <span class='hs-varop'>></span> <span class='hs-varid'>vertex</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Vertex3</span> <span class='hs-layout'>(</span> <span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>vertex</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Vertex3</span> <span class='hs-layout'>(</span> <span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span> <span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>vertex</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Vertex3</span> <span class='hs-layout'>(</span><span class='hs-comment'>-</span><span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span> <span class='hs-varid'>s</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>0</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>clear</span> <span class='hs-keyglyph'>[</span><span class='hs-conid'>ColorBuffer</span><span class='hs-keyglyph'>]</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>color</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>1</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span> <span class='hs-layout'>(</span><span class='hs-num'>0.5</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>drawSquare</span> <span class='hs-varid'>cx</span> <span class='hs-varid'>cy</span> <span class='hs-num'>0.05</span> <span class='hs-varop'>></span> <span class='hs-varid'>color</span> <span class='hs-varop'>$</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>1</span> <span class='hs-num'>1</span> <span class='hs-num'>1</span> <span class='hs-layout'>(</span><span class='hs-num'>0.6</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-varid'>drawSquare</span> <span class='hs-varid'>ox</span> <span class='hs-varid'>oy</span> <span class='hs-num'>0.03</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>flush</span> <span class='hs-varop'>></span> <span class='hs-varid'>swapBuffers</span>-</pre><p>The <code>readInput</code> function provides the driver layer. It feeds the peripheral-bound signals and also decides when to stop execution by returning <code>Nothing</code> instead of the time elapsed since its last call.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>readInput</span> <span class='hs-varid'>mousePos</span> <span class='hs-varid'>mouseBut</span> <span class='hs-varid'>closed</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>readInput</code> function provides the driver layer. It feeds the peripheral-bound signals and also decides when to stop execution by returning <code>Nothing</code> instead of the time elapsed since its last call.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>readInput</span> <span class='hs-varid'>mousePos</span> <span class='hs-varid'>mouseBut</span> <span class='hs-varid'>closed</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>t</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>get</span> <span class='hs-conid'>GLFW</span><span class='hs-varop'>.</span><span class='hs-varid'>time</span> <span class='hs-varop'>></span> <span class='hs-conid'>GLFW</span><span class='hs-varop'>.</span><span class='hs-varid'>time</span> <span class='hs-varop'>$=</span> <span class='hs-num'>0</span> <span class='hs-varop'>></span> <span class='hs-conid'>Position</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>get</span> <span class='hs-conid'>GLFW</span><span class='hs-varop'>.</span><span class='hs-varid'>mousePos</span>@@ -69,43 +80,44 @@ <span class='hs-varop'>></span> <span class='hs-varid'>k</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>getKey</span> <span class='hs-conid'>ESC</span> <span class='hs-varop'>></span> <span class='hs-varid'>c</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>readIORef</span> <span class='hs-varid'>closed</span> <span class='hs-varop'>></span> <span class='hs-varid'>return</span> <span class='hs-layout'>(</span><span class='hs-keyword'>if</span> <span class='hs-varid'>c</span> <span class='hs-varop'>||</span> <span class='hs-varid'>k</span> <span class='hs-varop'>==</span> <span class='hs-conid'>Press</span> <span class='hs-keyword'>then</span> <span class='hs-conid'>Nothing</span> <span class='hs-keyword'>else</span> <span class='hs-conid'>Just</span> <span class='hs-varid'>t</span><span class='hs-layout'>)</span>-</pre><p>OpenGL is initialised with practically everything turned off. Only alpha blending is needed to be able to use translucent colours.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>initGL</span> <span class='hs-varid'>width</span> <span class='hs-varid'>height</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>OpenGL is initialised with practically everything turned off. Only alpha blending is needed to be able to use translucent colours.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>initGL</span> <span class='hs-varid'>width</span> <span class='hs-varid'>height</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>clearColor</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Color4</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span> <span class='hs-num'>1</span> <span class='hs-varop'>></span> <span class='hs-varid'>blend</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Enabled</span> <span class='hs-varop'>></span> <span class='hs-varid'>blendFunc</span> <span class='hs-varop'>$=</span> <span class='hs-layout'>(</span><span class='hs-conid'>SrcAlpha</span><span class='hs-layout'>,</span><span class='hs-conid'>OneMinusSrcAlpha</span><span class='hs-layout'>)</span>-</pre><p>The window size callback takes care of the <code>windowSize</code> signal and the projection matrix.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>resizeGLScene</span> <span class='hs-varid'>winSize</span> <span class='hs-varid'>size</span><span class='hs-keyglyph'>@</span><span class='hs-layout'>(</span><span class='hs-conid'>Size</span> <span class='hs-varid'>w</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The window size callback takes care of the <code>windowSize</code> signal and the projection matrix.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>resizeGLScene</span> <span class='hs-varid'>winSize</span> <span class='hs-varid'>size</span><span class='hs-keyglyph'>@</span><span class='hs-layout'>(</span><span class='hs-conid'>Size</span> <span class='hs-varid'>w</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>winSize</span> <span class='hs-layout'>(</span><span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>w</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>viewport</span> <span class='hs-varop'>$=</span> <span class='hs-layout'>(</span><span class='hs-conid'>Position</span> <span class='hs-num'>0</span> <span class='hs-num'>0</span><span class='hs-layout'>,</span><span class='hs-varid'>size</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>matrixMode</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Projection</span> <span class='hs-varop'>></span> <span class='hs-varid'>loadIdentity</span> <span class='hs-varop'>></span> <span class='hs-varid'>scale</span> <span class='hs-num'>1</span> <span class='hs-layout'>(</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>w</span><span class='hs-varop'>/</span><span class='hs-varid'>fromIntegral</span> <span class='hs-varid'>h</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-num'>1</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span>-<span class='hs-varop'>></span> +<span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-varid'>matrixMode</span> <span class='hs-varop'>$=</span> <span class='hs-conid'>Modelview</span> <span class='hs-num'>0</span> </pre><h1 id="utils-module">Utils module</h1><p>This module contains some functions that might make it into the core library eventually.</p><pre><span class='hs-varop'>></span> <span class='hs-keyword'>module</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Utils</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Applicative</span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Monad</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Vector</span>-</pre><p>The <code>driveNetwork</code> function simply executes the supersteps while the <code>driver</code> function keeps returning valid delta time values.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>driveNetwork</span> <span class='hs-varid'>network</span> <span class='hs-varid'>driver</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span>+</pre><p>The <code>driveNetwork</code> function simply executes the supersteps while the <code>driver</code> function keeps returning valid delta time values.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>driveNetwork</span> <span class='hs-varid'>network</span> <span class='hs-varid'>driver</span> <span class='hs-keyglyph'>=</span> <span class='hs-keyword'>do</span> <span class='hs-varop'>></span> <span class='hs-varid'>dt</span> <span class='hs-keyglyph'>←</span> <span class='hs-varid'>driver</span> <span class='hs-varop'>></span> <span class='hs-keyword'>case</span> <span class='hs-varid'>dt</span> <span class='hs-keyword'>of</span> <span class='hs-varop'>></span> <span class='hs-conid'>Just</span> <span class='hs-varid'>dt</span> <span class='hs-keyglyph'>→</span> <span class='hs-keyword'>do</span> <span class='hs-varid'>join</span> <span class='hs-varop'>$</span> <span class='hs-varid'>superstep</span> <span class='hs-varid'>network</span> <span class='hs-varid'>dt</span> <span class='hs-varop'>></span> <span class='hs-varid'>driveNetwork</span> <span class='hs-varid'>network</span> <span class='hs-varid'>driver</span> <span class='hs-varop'>></span> <span class='hs-conid'>Nothing</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>return</span> <span class='hs-conid'>()</span>-</pre><p>A scalar integral function for <code>Fractional</code> instances.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>integral</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>+</span><span class='hs-varid'>v</span><span class='hs-varop'>*</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>-</pre><p>An integral function for two-dimensional vectors defined in the <code>Vector</code> module.</p><pre><span class='hs-varop'>></span> <span class='hs-varid'>integralVec</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>^+^</span><span class='hs-layout'>(</span><span class='hs-varid'>v</span><span class='hs-varop'>^*.</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>-</pre><h1 id="vector-module">Vector module</h1><p>This module contains a class for two-dimensional vectors, a strict datatype to instantiate it, and another instance for signals of the same type.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}</span>+</pre><p>A scalar integral function for <code>Fractional</code> instances.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>integral</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>+</span><span class='hs-varid'>v</span><span class='hs-varop'>*</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>+</pre><p>An integral function for two-dimensional vectors defined in the <code>Vector</code> module.</p><pre><span class='hs-varop'>></span> <span class='hs-definition'>integralVec</span> <span class='hs-varid'>v0</span> <span class='hs-varid'>s</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>transfer</span> <span class='hs-varid'>v0</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>dt</span> <span class='hs-varid'>v</span> <span class='hs-varid'>v0</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v0</span><span class='hs-varop'>^+^</span><span class='hs-layout'>(</span><span class='hs-varid'>v</span><span class='hs-varop'>^*.</span><span class='hs-varid'>realToFrac</span> <span class='hs-varid'>dt</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span> <span class='hs-varid'>s</span>+</pre><h1 id="vector-module">Vector module</h1><p>This module contains a class for two-dimensional vectors, a strict datatype to instantiate it, and another instance for signals of the same type.</p><pre><span class='hs-varop'>></span> <span class='hs-comment'>{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, TypeSynonymInstances #-}</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>module</span> <span class='hs-conid'>Common</span><span class='hs-varop'>.</span><span class='hs-conid'>Vector</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Control</span><span class='hs-varop'>.</span><span class='hs-conid'>Applicative</span>-<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>FRP</span><span class='hs-varop'>.</span><span class='hs-conid'>Elerea</span><span class='hs-varop'>.</span><span class='hs-conid'>Legacy</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>import</span> <span class='hs-conid'>Graphics</span><span class='hs-varop'>.</span><span class='hs-conid'>Rendering</span><span class='hs-varop'>.</span><span class='hs-conid'>OpenGL</span> <span class='hs-varop'>></span>-<span class='hs-varop'>></span> <span class='hs-keyword'>data</span> <span class='hs-conid'>Vec</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>{</span> <span class='hs-varid'>getX</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>Float</span><span class='hs-layout'>,</span> <span class='hs-varid'>getY</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>Float</span> <span class='hs-layout'>}</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>data</span> <span class='hs-conid'>Vec</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>{</span> <span class='hs-varid'>getX</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>GLfloat</span><span class='hs-layout'>,</span> <span class='hs-varid'>getY</span> <span class='hs-keyglyph'>::</span> <span class='hs-varop'>!</span><span class='hs-conid'>GLfloat</span> <span class='hs-layout'>}</span> <span class='hs-varop'>></span> <span class='hs-varop'>></span> <span class='hs-keyword'>infixl</span> <span class='hs-num'>7</span> <span class='hs-varop'>^*.</span> <span class='hs-varop'>></span> <span class='hs-keyword'>infixl</span> <span class='hs-num'>7</span> <span class='hs-varop'>.*^</span>@@ -123,7 +135,7 @@ <span class='hs-varop'>></span> <span class='hs-varid'>dot</span> <span class='hs-keyglyph'>::</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>c</span> <span class='hs-varop'>></span> <span class='hs-varid'>cross</span> <span class='hs-keyglyph'>::</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>v</span> <span class='hs-keyglyph'>→</span> <span class='hs-varid'>c</span> <span class='hs-varop'>></span>-<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-conid'>Vec</span> <span class='hs-conid'>Float</span> <span class='hs-keyword'>where</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-conid'>Vec</span> <span class='hs-conid'>GLfloat</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>^+^</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>x1</span><span class='hs-varop'>+</span><span class='hs-varid'>x2</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>y1</span><span class='hs-varop'>+</span><span class='hs-varid'>y2</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>^-^</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>x1</span><span class='hs-comment'>-</span><span class='hs-varid'>x2</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>y1</span><span class='hs-comment'>-</span><span class='hs-varid'>y2</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x</span> <span class='hs-varid'>y</span> <span class='hs-varop'>^*.</span> <span class='hs-varid'>t</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>V</span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-varop'>*</span><span class='hs-varid'>t</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-varid'>y</span><span class='hs-varop'>*</span><span class='hs-varid'>t</span><span class='hs-layout'>)</span>@@ -132,7 +144,7 @@ <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>`dot`</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>x1</span><span class='hs-varop'>*</span><span class='hs-varid'>y1</span><span class='hs-varop'>+</span><span class='hs-varid'>x2</span><span class='hs-varop'>*</span><span class='hs-varid'>y2</span> <span class='hs-varop'>></span> <span class='hs-conid'>V</span> <span class='hs-varid'>x1</span> <span class='hs-varid'>y1</span> <span class='hs-varop'>`cross`</span> <span class='hs-conid'>V</span> <span class='hs-varid'>x2</span> <span class='hs-varid'>y2</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>x1</span><span class='hs-varop'>*</span><span class='hs-varid'>y2</span><span class='hs-comment'>-</span><span class='hs-varid'>x2</span><span class='hs-varop'>*</span><span class='hs-varid'>y1</span> <span class='hs-varop'>></span>-<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>Vec</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>Float</span><span class='hs-layout'>)</span> <span class='hs-keyword'>where</span>+<span class='hs-varop'>></span> <span class='hs-keyword'>instance</span> <span class='hs-conid'>Vector2D</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>Vec</span><span class='hs-layout'>)</span> <span class='hs-layout'>(</span><span class='hs-conid'>Signal</span> <span class='hs-conid'>GLfloat</span><span class='hs-layout'>)</span> <span class='hs-keyword'>where</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varop'>^+^</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-layout'>(</span><span class='hs-varop'>^+^</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varop'>^-^</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-layout'>(</span><span class='hs-varop'>^-^</span><span class='hs-layout'>)</span> <span class='hs-varop'>></span> <span class='hs-layout'>(</span><span class='hs-varop'>^*.</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-layout'>(</span><span class='hs-varop'>^*.</span><span class='hs-layout'>)</span>@@ -140,5 +152,8 @@ <span class='hs-varop'>></span> <span class='hs-varid'>vnull</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>pure</span> <span class='hs-varid'>vnull</span> <span class='hs-varop'>></span> <span class='hs-varid'>dot</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-varid'>dot</span> <span class='hs-varop'>></span> <span class='hs-varid'>cross</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>liftA2</span> <span class='hs-varid'>cross</span>-</pre>-</body></html>+</pre></body>+</html>++</body>+</html>
elerea-examples.cabal view
@@ -1,5 +1,5 @@ Name: elerea-examples-Version: 2.0.1+Version: 2.2.0 Cabal-Version: >= 1.2 Synopsis: Example applications for Elerea Category: reactivity, FRP@@ -28,7 +28,7 @@ Author: Patai Gergely Maintainer: Patai Gergely (patai@iit.bme.hu)-Copyright: (c) 2009, Patai Gergely+Copyright: (c) 2009-2011, Patai Gergely License: BSD3 License-File: LICENSE Stability: experimental@@ -51,22 +51,19 @@ README Executable elerea-bounce- Executable: elerea-bounce HS-Source-Dirs: src/Bounce Main-IS: Main.hs- Build-Depends: base >= 3 && < 5, elerea >= 2.0.0, OpenGL, GLFW >= 0.4+ Build-Depends: base >= 3 && < 5, elerea >= 2.2.0, OpenGL, GLFW >= 0.4 GHC-Options: -O2 -fno-warn-deprecated-flags Executable elerea-breakout- Executable: elerea-breakout HS-Source-Dirs: src Main-IS: Breakout.lhs- Build-Depends: base >= 3 && < 5, elerea >= 2.0.0, OpenGL, GLFW >= 0.4+ Build-Depends: base >= 3 && < 5, elerea >= 2.2.0, OpenGL, GLFW >= 0.4 GHC-Options: -O2 -fno-warn-deprecated-flags Executable elerea-chase- Executable: elerea-chase HS-Source-Dirs: src Main-IS: Chase.lhs- Build-Depends: base >= 3 && < 5, elerea >= 2.0.0, OpenGL, GLFW >= 0.4+ Build-Depends: base >= 3 && < 5, elerea >= 2.2.0, OpenGL, GLFW >= 0.4 GHC-Options: -O2 -fno-warn-deprecated-flags
src/Bounce/Event.hs view
@@ -6,7 +6,7 @@ import Control.Monad import Control.Monad.Fix import Data.Maybe-import FRP.Elerea.Delayed+import FRP.Elerea.Legacy.Delayed import Util
src/Bounce/Main.hs view
@@ -7,7 +7,7 @@ import Data.IORef import Data.List import Data.Maybe-import FRP.Elerea.Delayed+import FRP.Elerea.Legacy.Delayed import Graphics.UI.GLFW as GLFW import Graphics.Rendering.OpenGL
src/Bounce/Math.hs view
@@ -4,7 +4,7 @@ import Control.Applicative import Data.List-import FRP.Elerea.Delayed+import FRP.Elerea.Legacy.Delayed import Vector import Util
src/Bounce/Util.hs view
@@ -1,7 +1,7 @@ module Util where import Control.Applicative-import FRP.Elerea.Delayed+import FRP.Elerea.Legacy.Delayed -- Lifted conditional. ifS c s1 s2 = c >>= \b -> if b then s1 else s2
src/Bounce/Vector.hs view
@@ -1,9 +1,9 @@-{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, TypeSynonymInstances #-}+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-} module Vector where import Control.Applicative-import FRP.Elerea.Delayed+import FRP.Elerea.Legacy.Delayed import Graphics.Rendering.OpenGL data Vec = V { getX :: {-# UNPACK #-} !GLfloat, getY :: {-# UNPACK #-} !GLfloat }