;; @module canvas.lsp
;; @description Routines for creating HTML canvas tag graphics
;; @version 1.0 initial release
;; @version 1.1 doc changes only
;; @version 1.21 doc changes only
;; @version 1.3 compatibility pre and post 10.2.0 for new extend
;; @version 1.32 doc formatting, spelling
;; @version 1.33 took out license
;; @version 1.4 cv:petal was broken (JavaScript change?)
;; @version 1.44 doc corrections
;; @version 1.45 cv:render works on Emscripten newLISP
;; @version 1.52 changed Win32 to Windows and spelling
;; @version 1.6 sign error in shape caused incompatibility with postscript.lsp
;; @version 1.61 eliminated canvas-15.tgz link
;; @version 1.7 fixed ellipse, petal, pie, polygon, shape fill first the stroke
;; @version 1.71 fixed documentation for cv:clip
;; @author Lutz Mueller, March 2009, June 2012, January 2014, 2015
;; <h2>Turtle graphics for the HTML-5 canvas tag</h2>
;; This module generates HTML pages suited for browsers which recognize
;; the HTML-5 canvas tag. 

;; <h3>Example program</h3>
;; The following is a simple example how to create an HTML page
;; with one or more embedded graphics. 
;;
;; @example
;; (module "canvas.lsp") ; does a load from standard location
;;
;; (cv:html "&lt;h2&gt;First canvas&lt;/h2&gt;") ; optional mixed in HTML
;; (cv:canvas "FirstCanvas" 300 100) ; required canvas creation
;; (cv:fill-color 1.0 0.0 0.0)
;; (cv:rectangle 100 80 true) ; one or more drawing statements
;; (cv:html "&lt;h2&gt;Second canvas&lt;/h2&gt;")
;;
;; (cv:canvas "SecondCanvas" 300 100)
;; (cv:fill-color 0.0 1.0 0.0)
;; (cv:rectangle 200 80 true)
;; (cv:render "page.html") ; required rendering of the page
;; (exit)

;; All canvas functions are prefixed with 'cv' and emit the JavaScript
;; code necessary to the &lt;script&gt;&lt/script&gt; section in the
;; header section of the HTML page. Only the function 'html' is not
;; prefixed and outputs strings directoy to the &lt;body&gt;&lt/body&gt;
;; tagged section of the HTML page.
;;
;; @example
;; (html "&lt;p&gt;This is a parapgraph of text.&lt;/p&gt;")

;; Sometimes it is necessary to output JavaScript explicity to the
;; script section of the page, e.g. to use canvas features not implemented
;; in this module. This can be done via the 'cv' default functor:
;;
;; @example
;; (cv "document.write('this is a test');")

;; Most functions in the canvas 'cv' module work the same as in the older
;; postscript 'ps' modul, and some functions in the postscript module
;; have been changed to achieve more compatibility between both modules. 
;; The following example shows how name-space prefix switching can be used to
;; to run a <tt>postscript.lsp</tt> based program with <tt>canvas</tt>:
;;
;; @example
;; (module "canvas.lsp")
;; 
;; (html "<center>")
;; (cv:canvas "TheCanvas" 200 200)
;; ; reuse postrcipt.lsp code
;; (set 'ps cv) ; switch prefix
;;
;; (ps:goto 120 132)
;; (ps:line-color  1 0 0.6)
;; (ps:line-width 3.0)
;; (ps:angle 15)
;; (dotimes (i 12)
;;     (cv:turn 30)
;;     (cv:bezier -20 75 40 75 20 0))
;; ; end postscript snippet
;;
;; (html "</center>")
;; (cv:render "rose.html")
;; (exit)

;; A package with more demo files can be downloaded from 
;; @link http://www.newlisp.org/canvas/canvas-15.tgz www.newlisp.org/canvas/canvas-15.tgz .
;; The demo files assume the Safari (4.0 beta) browser on Mac OS X and Windows or
;; The Firefox browser (3.1. beta 3) on Linux and start the browser automatically.
;; Change the last 'cv:render' line in each of the demo file to manually load
;; the generated page-file spciefied in the 'cv:render' statement.
 
;; <h2>Using JavaScript directly</h2>
;; Complex graphics with many looping statements can generate huge HTML files which
;; may be slow to load over an internet connection. The newLISP program itself
;; may be small, but the JavaScript it generates may fill hundreds of kilobytes
;; because of repeated output of JavaScript statements. The <tt>cv</tt> function 
;; can be used to to emit JavaScript directly. For most <tt>cv:xxx</tt> (but not all) 
;; -functions a similar named JavaScript function exists in <tt>canvas.lsp</tt>.
;; The following example generates the same graphic twice, first as a looping
;; newLISP script, then as an explicitly written JavaScript script:
;;
;; @example
;; (module "canvas.lsp")
;; 
;; (html "<h3>Indirect draw</h3>")
;; 
;; (cv:canvas "CanvasOne" 400 200)
;; (cv:line-color 0 0.5 0.5)
;; 
;; (cv:angle -90)
;; (dotimes (i 180)
;;     (cv:goto 200 0)
;;     (cv:draw 300)
;;     (cv:turn 1))
;; 
;; (html "<h3>Direct draw</h3>")
;; 
;; (cv:canvas "CanvasTwo" 400 200)
;; (cv:line-color 0 0.5 0.5)
;; 
;; (cv [text]
;; Angle(-90);
;; for(var i = 0; i < 180; i++) {
;;     Goto(200, 0);
;;     Draw(300);
;;     Turn(1);
;;     }
;; [/text])
;;
;; (cv:render) ; render page automatically in browser on OS X (Safari 4.0)
;; ; as an alternative specify the HTML filename
;; (cv:render "direct.html") ; renders to file specified
;; (exit)

;; <h2>Differences to the postscript module</h2>
;; Differences between the <tt>canvas.lsp</tt> and <tt>postscript.lsp</tt> modules
;; are mainly in the treatment of text colors and fonts.
;; <ul>
;; <li>The text color for 'cv:text' is set by 'cv:fill-color' with 'canvas.lsp', but
;; with 'ps:line-color' when using 'postscript.lsp' and 'ps:text', except for outlined
;; text, which is drawn with 'line-color' in both modules.</li>
;; <li>The 'cv:font' parameters are different from the 'ps:font'
;; parameters. See the function reference for details.</li>
;; <li>Canvas graphics need the 'cv:canvas' statement at the beginning
;; to specify the canvas size. In Postscript the drawing area depends
;; on the printer driver settings. To port 'postscript.lsp' graphics
;; to 'canvas.lsp' use a width of 612 and height of 792 in the 'cv:canvas'
;; statement. The numbers correspond to a 8 1/2 by 11 inch US paper page
;; with translation of points to pixels.</li>
;; <li>Cipping with outlined text is not available yet in 'canvas.lsp'.
;; Current implementations are browser-specific and not part of the HTML-5
;; canvas tag specification.</li>
;; <li>Text drawn with 'cv:textoutline' or 'cv:textarcoutline' cannot be filled
;; with color as possible with 'ps:textoutline' and 'ps:textarcoutline' in the postscript
;; module.
;; <li>'canvas.lsp' has a 'cv:header' function to specify tags in the 
;; HTML header section.</li>
;; </ul>
 

;; <h2>Summary of functions</h2>
;; Return values from functions are not used when programming with canvas functions
;; and are not mentioned in the documentation.
;; <h3>Turtle coordinate positioning and turning</h3>
;; <pre>
;; cv:goto  - move turtle to position x, y
;; cv:move  - move turtle a distance s forward from the current position
;; cv:turn  - turn the turtle degress dg left (negative) or right (positive)
;; cv:angle - set the turtle orientation to dg degrees
;; </pre>
;; <h3>Line drawing</h3>
;; <pre>
;; cv:draw   - draw distance s forward from current position
;; cv:drawto - draw to the absolute position x,y from the current position
;; cv:line   - draw a multipart line consisting of line and bezier curve segments
;; cv:bezier - draw a Bezier curve 
;; </pre>
;; <h3>Closed shapes, filling and clipping</h3>
;; <pre>
;; cv:rectangle - draw a rectangle
;; cv:polygon   - draw a polygon with radius rad and n number of sides
;; cv:circle    - draw a circle
;; cv:ellipse   - draw an open or closed ellipse with x-rad and y-rad radius
;; cv:pie       - draw a pie piece with radius rad and width
;; cv:petal     - draw a petal shape from Bezier curves
;; cv:shape     - draw a shape defined by a list of line and bezier segments
;; cv:clip      - define a clipping path using line and Bezier segments
;; </pre>
;; <h3>Text output and clipping</h3>
;; <pre>
;; cv:text           - draw a solid text string
;; cv:textoutline    - draw text in outline shape
;; cv:textarc        - draw text around an arc
;; cv:textarcoutline - draw text in outline shape around an arc
;; </pre>
;; <h3>Global settings</h3>
;; <pre>
;; cv:translate  - move coordinate origin
;; cv:scale      - scale output
;; cv:rotate     - rotate output
;; cv:gsave      - save current graphics state (translation, scale, rotation)
;; cv:grestore   - restore current graphics state
;; cv:font       - set font specifications
;; cv:line-witdh - set line width in pixels
;; cv:line-cap   - set line termination shape
;; cv:line-join  - set line join mode
;; cv:line-color - set line color
;; cv:fill-color - set fill color
;; </pre>
;; <h3>Rendering and output</h3>
;; <pre>
;; cv:render     - render HTML output to CGI or a file
;; </pre>


;; @syntax (cv:angle <num-dg>) 
;; @param <num-dg> Angle degrees from 0 to 360.
;; <br>
;; Set the turtle angle to <num-dg> degrees.
;; Upwards is 0, right 90, downwards 180 and left 270 degrees.
;; The turtle position is saved on the graphics state stack when using 
;; '(cv:gsave)'.

;; @syntax (cv:bezier <num-x1> <num-y1> <num-x2> <num-y2> <num-x3> <num-y3>) 
;; @param  <num-x1,num-y1> Bezier coordinates of <p1> relative to <p0> = 0,0
;; @param  <num-x2,num-y2> Bezier coordinates of <p2> relative to <p0> = 0,0
;; @param  <num-x3,num-y3> Bezier coordinates of <p3> relative to <p0> = 0,0
;; <br>
;; Draw a Bezier curve.
;; The Bezier curve starts at point <p0> which is the current
;; turtle position and stops at point <p3> which is at offset
;; <num-x3> and <num-y3> relative to starting point. The turtle orientation
;; after the drawing the Bezier curve is perpendicular
;; to the Bezier curve baseline <p0> to <p3> and the position is <p3>.

;; @syntax (cv:circle <num-rad> [<bool-fill>])
;; @param <num-rad> Radius of the circle.
;; @param <bool-fill> Optional fill flag.
;; <br>
;; Draw a circle with radius <num-rad>. The optional <num-fill> flag 
;; with either 'true' or 'nil' (default) indicates if the circle
;; is filled with the fill color specified by 'cv:fill-color'.
;; The circle is drawn around the current turtle position.
;; The turtle position or orientation is not changed.

;; @syntax (cv:clip <list-of-lists>) 
;; @param <list-of-lists> A list of turtle movements and/or Bezier curves.
;; <br>
;; Define a clipping path using turtle movements (<degree> <distance>) and
;; Bezier curves (<x1> <y1> <x2> <y2> <x3> <y3>) starting from the 
;; last turtle coordinates <x0>, <y0> and finishing at <x3>, <y3>. 
;; All Bezier coordinates are relative to the previous turtle position and
;; orientation.
;;
;; Before redefining the clipping area '(cv:gsave)' should
;; be used to save the old graphics state parameters, after
;; clipping and drawing in the clipped area the graphics
;; state should be restored using '(cv:grestore)'.
;; The turtle position or orientation is not changed.

;; @syntax (cv:draw <num-s>) 
;; @param <num-s> Distance to draw.
;; <br>
;; Draw going forward distance <num-s>. Moves the turtle forward by 
;; the amount of pixels specified in <num-s> and draws with the current 
;; line color set by 'cv:line-color'.
;;

;; @syntax (cv:drawto <x> <y>) 
;; @param <x> The x coordinate to draw to.
;; @param <y> The y coordinate to draw to.
;; <br>
;; Draw a line to point <x>, <y>. Moves the turtle to point 
;; <x>, <y> like '(cv:goto x y)', but also draws a line from 
;; the old to the new position. The turtle position is changed to the
;; new point <x>, <y> and the orientation is changed to the orientaion of 
;; the line drawn.

;; @syntax (cv:ellipse <num-x-rad> <num-y-rad> <num-start> <num-end> [<bool-fill>]) 
;; @param <num-x-rad> The x axis radius.
;; @param <num-y-rad> The y axis radius.
;; @param <num-start> The start angle in 0 to 360 degrees.
;; @param <end-num> The end angle in 0 to 360 degrees.
;; <br>
;; Draw an ellipse with optional <bool-fill> either 'true' or 'nil' (default).
;; The ellipse is drawn around the current turtle position
;; with the Y axis oriented like the turtle.
;; For <num-start>, <num-end> set to 0, 360 an ellipse is drawn.
;; For a partial radius the opening is closed by a line
;; resulting in segment shape, i.e. -90, 90  would result
;; in a half circle from the left to the right of the turtle.
;; When <num-x-rad> and <num-y-rad> are of equal size a full circle
;; can be drawn. The turtle position or orientation is not changed.

;; @syntax (cv:fill-color <float-R> <float-G> <float-B>) 
;; @param <float-R> The red color value.
;; @param <float-G> The green color value.
;; @param <float-B> The blue color value.
;;
;; Set color for shape filling.
;; Color values assume the following value:
;;
;; <pre>
;;    R - value for red 0.0 to 1.0
;;    B - value for green 0.0 to 1.0
;;    G - value for blue 0.0 to 1.0
;; </pre>
;;
;; @syntax (cv:fill-color <str-hex>)
;; @param <str-hex> A hex string specifying the line color.
;; <br>
;; In an alternative syntax color values can be specified in a
;; hex string:
;;
;; <str-hex> is a hex string constant '"000000"' to '"FFFFFF"'
;; Colors are specified as usual in HTML coding.
;; Each two hex digits define a color: 'rrggbb'.  

;; @syntax (cv:font <str-name>) 
;; @param <str-name> The font name.
;; <br>
;; The current font is set for all subsequent text operations.
;; Depending on the browser and OS installed, different fonts are available.
;;
;; @example
;; (cv:font "normal 14px sans-serif") ; Helvetica
;; (cv:font "bold 20px serif") ; Times
;; (cv:font "italic 32px sans-serif") ; Cursive

;; @syntax (cv:goto <num-x> <num-y>)
;; @param <num-x> The new x coordinate.
;; @param <num-y> The new y coordinate.
;; <br>
;; Moves to position <num-x>, <num-y>. 
;; The turtle position can be
;; saved on the graphics state stack when using '(cv:gsave)'.

;; @syntax (cv:grestore)
;; Restores the graphics state from the stack.

;; @syntax (cv:gsave)
;; Saves the current graphics state. The function pushes the 
;; current graphics state on a special stack, from where it
;; can be resored using '(cv:grestore)'. States saved are:
;; The turtle position X, Y and  orientation, the transformation
;; scaling and rotation factors, the line cap and join value and
;; the colors set.

;; @syntax (cv:line <list-of-lists>) 
;; @param <list-of-lists> A list of turtle movements or Bezier curves.
;; <br>
;; Draw a multipart line.  <lists> are turtle movements (<num-dg> <num-s>),
;; or Bezier curves (<x1> <y1> <x2> <y2> <x3> <y3>) starting
;; from the last turtle coordinates <x0>, <y0> and
;; finishing at <x3>, <y3>. All Bezier coordinates are
;; relative to the previous turtle position and
;; orientation.
;;
;; The turtle position and orientation are changed after
;; drawing the line.

;; @syntax (cv:line-cap <num-mode | str-mode>) 
;; @param <mode> The line termination shape mode as a string or number
;; <br>
;; Sets the line termination shape as either a number or string:
;; <pre>
;;    0 or "butt"
;;    1 or "round"
;;    2 or "square"
;; </pre>

;; @syntax (cv:line-color <float-R> <float-G> <float-B>) 
;; @param <float-R> The red color value.
;; @param <float-G> The green color value.
;; @param <float-B> The blue color value.
;; <br>
;; Set color for line drawing.
;; Color values assume the following value:
;; <pre>
;;    R - value for red 0.0 to 1.0
;;    G - value for green 0.0 to 1.0
;;    B - value for blue 0.0 to 1.0
;; </pre>
;;
;; @syntax (cv:line-color <str-hex>)
;; @param <str-hex> A hex string specifying the line color.
;; <br>
;; In an alternative syntax color values can be specified in a
;; hex string:
;;
;; <str-hex> is a hex string constant '"000000"' to '"FFFFFF"'
;; Colors are specified as usual in HTML coding.
;; Each to two hex digits define a color: 'rrggbb'.  

;; @syntax (cv:line-join <num-mode> | <str-mode>)
;; @param <mode> The line join mode.
;; <br>
;; Sets the line join mode as either a number or string:
;; <pre>
;;    0 or "miter"
;;    1 or "round"
;;    2 or "bevel"
;; </pre>

;; @syntax (cv:line-width <int-pixels>)
;; @param <int-pixels> The line width in pixels.
;; <br>
;; Sets the line width in pixels for line drawing and the
;; outlines drawn by shapes and text outlines.

;; @syntax (cv:move <num-s>) 
;; @param <num-s> The distance to move the pen.
;; <br>
;; Move turtle the forward distance <s> without drawing.

;; @syntax (cv:petal <num-width> <num-height> [<bool-fill>]) 
;; @param <num-width> The 'x1' coordinate of the  underlying Bezier curve <p0> to <p1> <p2> <p3>.
;; @param <num-height> The 'y1' coordinate of the  underlying Bezier curve <p0> to <p1> <p2> <p3>.
;; @param <bool-fill> An optional fill flag for color fill.
;; <br>
;; Draws a petal using a Bezier curve with optional <num-fill> either 'true' or 'nil' (default).
;; The <num-height> and <num-width> parameters are relative to to the current position.
;; The petal is drawn with the tip at the current turtle
;; position and oriented in the direction of the turtle.
;; The turtle position or orientation is not changed.

;; @syntax (cv:pie <num-rad> <num-width> [<bool-fill>])
;; @param <num-rad> The radius of the pie.
;; @param <num-width> The width of the pie slice as an angle.
;; @param <bool-fill> An optional fill flag for color fill, 'true' or 'nil' (default).
;; <br>
;; Draw a pie slice with optional <bool-fill> either 'true' or 'nil' (default).
;; The left edge of the pie is in turtle orientation.
;; The width angle spreads clockwise. The pie is drawn around the current
;; turtle position. The turtle position or orientation is not changed.

;; @syntax (cv:polygon <num-rad> <num-n> [<bool-fill>])
;; @param <num-rad> Radius.
;; @param <num-n> Number of sides.
;; @param <fill> Optional fill flag.
;; <br>
;; Draw a polygon with radius <num-rad> and <num-n> sides.
;; <num-fill> is 'true' or 'nil' (default) for optional color fill
;; The polygon is drawn around the current turtle position.
;; The turtle position or orientation is not changed.

;; @syntax (cv:rectangle <num-width> <num-height> [<bool-fill>])
;; @param <num-width> The width of the rectangle.
;; @param <num-height> The height of the rectangle.
;; @param <bool-fill> An optional flag to draw a filled rectangle.
;; <br>
;; A rectangle is drawn at the current turtle position.
;; The width of the rectangle will be perpendicular to
;; the turtle orientation. If the turtle never turned or
;; the turtle angle never was set then the width of the
;; rectangle will lie horizontally.
;;
;; The position or orientation of the turtle will not change.

;; @syntax (cv:render [<HTML-file-name>])
;; @param  <HTML-file-name> Optionam HTML file-name to save to.
;; <br>
;; On Mac OX X system when using the function without a file-name,
;; the default HTML browser is opened automatically and a temporary
;; file <tt>/tmp/noname.html</tt> is shown. This is the best mode for
;; interactive development.
;;
;; On Windows 'cv:render' tries to open 'c:\Program Files\Safari\Safari.exe'.
;; The function 'cv:render' at the end of the source in 'canvas.lsp'
;; can be modified for a different browser.
;;
;; When a file-name is supplied, then 'cv:render' generates a HTML
;; file. When the file-name is specified as <tt>"cgi"</tt>, then
;; output is directed to standard out. This is useful for writing CGI
;; programs. The CGI program must take care to emit a content-type header 
;; first using:
;;
;; <pre>(print "Content-Type: text/html\r\n\r\n")</pre>

;; @syntax (cv:rotate <num-deg>) 
;; @param <num-deg> The degrees of rotation: -360 to 0 to 360.
;; <br>
;; Rotate the coordinate space. 
;; The coordinate space is rotated to the right for
;; positive angles and to the left for negative angles.
;; The current rotation angle is 0 (upwards) by default.
;; The rotation angle is part of the graphics state saved by
;; the 'cv:gsave' function and restored by 'cv:grestore'.

;; @syntax (cv:scale <num-x> <num-y>) 
;; @param <num-x> The new x scale factor.
;; @param <num-y> The new y scale factor.
;; <br>
;; Scale the coordinate space.
;; Scaling factors are 1.0 by default and compress for
;; factors less 1.0 or expand for factors bigger than 1.0.
;; With a scaling factor for x = 2.0 each point position
;; specified would cover the double of horizontal distance
;; on the page. Previou scaling factors can be saved on the graphics
;; state stack using the function 'cv:gsave' and restored using 'cv:grestore'.

;; @syntax (cv:shape <list-of-num-lists> [<bool-fill>])
;; @param <list-of-num-lists> A list of turtle movements and/or Bezier curves.
;; @param <bool-fill> An optional fill flag for color fill.
;; <br>
;; Draws a shape with optional <bool-fill> or eiher 'true' or 'nil' (default).
;; <num-lists> is either a turtle movement  (<degree> <distance>) or a Bezier curve 
;; (<x1> <y1> <x2> <y2> <x3> <y3>) starting from the last turtle coordinates 
;; <x0>, <y0> and finishing at <x3>, <y3>. All Bezier coordinates
;; are relative to the previous turtle position and orientation
;; The turtle position or orientation is not changed.

;; @syntax (cv:text <str-text>)
;; @param <str-text> The text to draw.
;; <br>
;; Draws text. Before drawing, a font can be specified, the default font after loading 
;; the 'canvas.lsp' modules is the default font of the canvas tag. The text color
;; is the current 'cv:fill-color'.
;;
;; The turtle position is changed to the baseline after the last character.
;; The turtle orientation stays the same.

;; @syntax (cv:textarc <str-text> <num-rad>)
;; @param <str-text> The text to draw. 
;; @param <num-rad> The radius of an imaginary circle path for <str-text>.
;; <br>
;; Draw text around a circle.
;; The text is drawn out side of an imaginary circle starting at 
;; turtle position and orientation and drawing at the current tangent.
;; For a positive radius text goes outside
;; the circle and clockwise. For a negative radius text goes inside the
;; circle and counter lock wise. The turtle position and orientation
;; move along the radius.

;; @syntax (cv:textarcoutline <str-text> <num-rad>) 
;; @param <str-text> The text to draw.
;; @param <num-rad> The radius of imaginary circle path for text.
;; <br>
;; Draw text around a circle.
;; Same as 'cv:textarc' but the text is drawn as ane outline.
;; The color of the text outline is the current 'cv:line-color'.
;; The turtle position and orientation move along the radius.

;; @syntax (cv:textoutline <str-text>)
;; @param <str-text> The text to draw. 
;; <br>
;; Draw a text outline
;; Before drawing a font can be specified
;; the default font after loading 'canvas.lsp' is the font
;; of the HTML canvas tag.
;;
;; The turtle position is changed to the baseline after the last character.
;; The turtle orientation stays the same.

;; @syntax (cv:translate <num-dx> <num-dy>) 
;; @syntax (cv:translate) 
;; @param <num-dx> Move the current x-origin  by 'dx'.
;; @param <num-y> Move the current y-origin by 'dy'.
;; <br>
;; Move the coordinate origin.
;; By default the origin 0,0 is in the bottom left corner
;; of the page. The <num-dx> and <num-dy> values extend to the right and top. 
;; When no <num-x>, <num-y> values are specified, the coordinate origin
;; is moved to the current position of the turtle. Previous translation
;; offsets can be saved on the graphics state stack using the
;; function 'cv:gsave' and restored using 'cv:grestore'.

;; @syntax (cv:turn <num-dg>)
;; @param <num-dg>  The degrees to turn: -360 to 0 to 360.
;; <br>
;; Turn the turtle pen by <num-dg> degrees. The degrees are specified in angles
;; from -380 to 0 to 360. For turning clockwise specifiy positive values.
;; Negative degrees turn the turtle pen counter clockwise. The turtle
;; position is aved on the graphics state stack when using '(cv:gsave)'.

(when (< (sys-info -2) 10110)
	(constant (global 'extend) write-buffer))

(define (html:html str) (extend cv:body-html str))

(context 'cv)

; global values and constants

(set 'pi (mul 2 (acos 0)))
(set 'pi/2 (acos 0))

(set 'line-feed (if (> (& 0xF (sys-info -1)) 5) "\r\n" "\n"))
(set 'header-tags "") ; header tags from cv:header go here
(set 'canvas-script "") ; graphics statements go here
(set 'body-html "") ; body html written with cv:html goes here

(set 'canvas-width 1000) ; current canvas
(set 'canvas-height 1000) ; current canvas

(set 'turtle-x 0)
(set 'turtle-y canvas-height)
(set 'turtle-orient (add pi 0.0000001))

(set 'line-color "#000000") ; for strokeStyle()
(set 'line-width 1) ; 
(set 'fill-color '(0xff 0 0 0)) ; for fillStyle()

(set 'template-header [text]<!-- newLISP generated page -->
<html>
<head>
<!-- header -->
%s[/text])

(set 'script-header [text]<script type="text/javascript">

var canvasWidth, canvasHeight;
var xpos, ypos, orient;
var turtleStack = new Array();
var xsave, ysave, orientsave;
var ctx;

function Ctx(c) { ctx = c; }

function Gsave() { 
	PushTurtle(); ctx.save(); 
	}

function Grestore() { 
	ctx.restore(); PopTurtle(); 
	}

function PushTurtle() {
	turtleStack.push(canvasHeight, xpos, ypos, orient);
	}

function PopTurtle() {
	orient = turtleStack.pop();
	ypos = turtleStack.pop();
	xpos = turtleStack.pop();
	canvasHeight = turtleStack.pop();
	}

function Scale(x, y) {
	ctx.scale(x, y);
	canvasHeight = canvasHeight / y;
	}
	
function Turn(dg) {
	orient = orient - Math.PI * dg / 180;
	}

function Goto(x, y) { xpos = x; ypos = canvasHeight - y; }

function Angle(dg) {
	orient = Math.PI - Math.PI * dg / 180;
	}

function Move(s) {
	xpos = xpos + s *  Math.sin(orient);
	ypos = ypos + s *  Math.cos(orient);
	}

function Draw(s) {
	ctx.beginPath();
	ctx.moveTo(xpos, ypos);
	xpos = xpos + s *  Math.sin(orient);
	ypos = ypos + s *  Math.cos(orient);
	ctx.lineTo(xpos, ypos);
	ctx.stroke();
	}

function Drawto(x, y) {
	ctx.beginPath(); ctx.moveTo(xpos, ypos);
	y = canvasHeight - y;
	ctx.lineTo(x, y); ctx.stroke();
	orient = Math.PI / 2 - Math.atan2(y - ypos, x - xpos); 
	xpos = x; ypos = y;
	}

function Line(lst) {
	Path(lst); ctx.stroke();
	}
	
function Shape(lst, flag) {
	turtleStack.push(xpos, ypos, orient);
	Path(lst);
	ctx.closePath(); 
	if(flag == true) { ctx.fill(); }
    ctx.stroke();
	orient = turtleStack.pop();
	ypos = turtleStack.pop();
	xpos = turtleStack.pop();
	}

function Clip(lst, flag) {
	turtleStack.push(xpos, ypos, orient);
	Path(lst); ctx.closePath();
	if(flag == true) ctx.stroke(); 
	ctx.clip();
	orient = turtleStack.pop();
	ypos = turtleStack.pop();
	xpos = turtleStack.pop();
	}

function Path(lst) {
	var x1, y1, x2, y2, x3, y3;
	var type, dg, dist, phi;
	
	ctx.beginPath(); ctx.moveTo(xpos, ypos);

	for (var i = 0; i < lst.length; ) {
		if(lst[i] == "B")
			{
			x1 = lst[i + 1]; y1 = -lst[i + 2]; 
			x2 = lst[i + 3]; y2 = -lst[i + 4]; 
			x3 = lst[i + 5]; y3 = -lst[i + 6]; 
			PathBezier(x1, y1, x2, y2, x3, y3);
			phi = Math.atan2(y3, x3);
			orient = orient - 0.5 * Math.PI - phi; // turn 90 right + phi
			if(phi == 0) Move(x3); else Move(y3 / Math.sin(phi)); 
			orient = orient + 0.5 * Math.PI; // turn 90 left
			i += 7;
			}
		else {
			if(lst[i] == "L") {
				dg = lst[i + 1];
				dist = lst[i + 2];
				orient = orient - Math.PI * dg / 180;
				xpos = xpos + dist *  Math.sin(orient);
				ypos = ypos + dist *  Math.cos(orient);
				ctx.lineTo(xpos, ypos);
				i += 3;
				}
			// else invalid format
			}
		}
	}
	
function Bezier(x1, y1, x2, y2, x3, y3) {
	var phi = Math.atan2(y3, x3);
	ctx.beginPath(); 
	PathBezier(x1, -y1, x2, -y2, x3, -y3); 
	orient += phi;
	orient = orient - Math.PI * 0.5; // turn 90
	if (phi == 0) { Move(x3); } else { Move(y3 / Math.sin(phi));}
	orient = orient + Math.PI * 0.5; // turn -90
	}
		
function PathBezier(x1, y1, x2, y2, x3, y3) {
	ctx.save(); 
	ctx.translate(xpos, ypos); ctx.rotate(Math.PI - orient);
	ctx.moveTo(0, 0); ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
	ctx.restore();
	}

function Polygon(rad, n, flag) {
	var orientinc = 2 * Math.PI / n;
	ctx.save();
	ctx.translate(xpos, ypos); ctx.rotate(-orient);
	ctx.beginPath(); ctx.moveTo(0, rad);
	for (var angle = 0; angle < 2 * Math.PI; angle += orientinc) {
		ctx.lineTo(Math.sin(angle) * rad, Math.cos(angle) * rad);
		}
	ctx.closePath(); 
	if (flag == true) ctx.fill();
    ctx.stroke();
	ctx.restore();
	}

function Pie(rad, height, flag) {
	var start = Math.PI / 2 - orient;
	var end = start + (Math.PI / 2) * (height / 90);
	ctx.beginPath(); ctx.moveTo(xpos, ypos);
	ctx.arc(xpos, ypos, rad, start, end, false); 
	ctx.closePath();
	if (flag == true) ctx.fill();
    ctx.stroke();
	}

function Circle(rad, flag) {
	ctx.beginPath(); ctx.moveTo(xpos + rad, ypos);
	ctx.arc(xpos, ypos, rad, 0, 6.39, 0);
	if (flag == true) ctx.fill();
	ctx.stroke();
	}

function Ellipse(xrad, yrad, start, end, flag) {
	ctx.save();
	ctx.translate(xpos, ypos);
	ctx.rotate(Math.PI - orient);
	ctx.beginPath();
	ctx.scale(1, yrad/xrad);
	start = Math.PI * start / 180 - Math.PI/2;
	end =  Math.PI * end / 180 - Math.PI/2;
	ctx.arc(0, 0, xrad, start, end, 0);
	ctx.closePath();
	if (flag == true) ctx.fill();
    ctx.stroke();
	ctx.restore();
	}   

function Text(str) {
	ctx.save(); ctx.translate(xpos, ypos); ctx.rotate(Math.PI / 2 - orient);
	ctx.fillText(str, 0, 0);
	var txt = ctx.measureText(str);	 
	ctx.restore();
	Move(txt.width);
	}

function TextOutline(str) {
	ctx.save(); ctx.translate(xpos, ypos); ctx.rotate(Math.PI / 2 - orient);
	ctx.strokeText(str, 0, 0);
	var txt = ctx.measureText(str);	
	ctx.restore();
	Move(txt.width);
	}

function TextArc(str, radius) {
	var ainc;
	for(var i = 0; i < str.length; i++) {
		ainc =  ctx.measureText(str[i]).width / (2 * radius);
		orient -= ainc; Text(str[i]); orient -= ainc;
		}
	}

function TextArcOutline(str, radius) {
	var ainc;
	for(var i = 0; i < str.length; i++) {
		ainc =  ctx.measureText(str[i]).width / (2 * radius);
		orient -= ainc; TextOutline(str[i]); orient -= ainc;
		}
	}

function drawAllCanvas() { try
	{
	// if(!ctx.fillText) document.write('function fillText() not suppported on this browser');
[/text])

(set 'script-template [text]
	var canvas=document.getElementById('%s');
	var ctx=canvas.getContext('2d'); Ctx(ctx);
	canvasWidth = %g; canvasHeight = %g;
	xpos = 0; ypos = canvasHeight;
	orient = Math.PI;
	turtleStack = new Array();
	ctx.lineWidth = 1;
	ctx.strokeStyle = 'rgb(0, 0, 0)';
	ctx.fillStyle = 'rgb(0, 0, 0)';

<!-- start generated JavaScript -->
[/text])

(define (cv:cv str) (write-line canvas-script str))

(set 'script-close [text]
<!-- end generated JavaScript -->

	} catch (er) {    }
} 
</script></head><body onload="drawAllCanvas();">
[/text])

; same definition as html:html
(define (cv:html str) (write-line body-html str))

(set 'canvas-template [text]<canvas id="%s" width="%d" height="%d"></canvas>[/text])

(set 'body-close "</body></html>\n")

; user functions

(define (cv:header tags )
	(set 'header-tags tags)
)

(define (cv:canvas canvas-name (width 300)  (height 200))
	(cv (format script-template canvas-name width height))
	(html (format canvas-template canvas-name width height))
)

(define (cv:goto x y)
	(cv (format "Goto(%g, %g);" x y))
)


(define (cv:move s)
	(cv (format "Move(%g);" s))
)


(define (cv:turn dg)
	(cv (format "Turn(%g);" dg))
)


(define (cv:angle dg)
	(cv (format "Angle(%g);" dg))
)


(define (cv:draw s)
	(cv (format "Draw(%g);" s))
)


(define (cv:drawto x y)
	(cv (format "Drawto(%g, %g);" x y))
)

	
(define (cv:line lst)
	(cv (format "Line(new Array(%s));" (list-path lst)))
)


(define (list-path lst)
	(let ( (buff "") (rec nil))
		(while (set 'rec (pop lst))
			(if (= (length rec) 6)
				(extend buff (string ",'B'," (join (map string rec) ","))))
			(if (= (length rec) 2)
				(extend buff (string ",'L'," (rec 0) "," (rec 1)))))
		(1 buff)
) )
	

(define (cv:bezier x1 y1 x2 y2 x3 y3)
	(cv (format "Bezier(%g, %g, %g, %g, %g, %g); ctx.stroke();"
			 x1 y1 x2 y2 x3 y3))
)


(define (cv:polygon rad n flag)
	(cv (format "Polygon(%g, %g, %s);"
		rad n (if flag "true" "false")))
)


(define (cv:circle rad flag)
	(cv (format "Circle(%g, %s);" rad (if flag "true" "false")))
)


(define (cv:ellipse xr yr start end mode)
	(cv (format "Ellipse(%g, %g, %g, %g, %s);"
			xr yr start end (if mode "true" "false")))	
)


(define (cv:rectangle width height flag)
	(shape (list (list 0 height) (list 90 width) (list 90 height) (list 90 width)) flag)
)


(define (cv:pie rad width flag)
	(cv (format "Pie(%g, %g, %s);" rad width (if flag "true" "false")))
)


(define (cv:petal width height flag)
    ; x3 (offset from x1) cannot be 0 or the Bezier does not get drawn
	(bezier (sub width) height width height 0.001 0 flag)
	(cv (format "Bezier(%g, %g, %g, %g, %g, %g);"
			 (sub width) height width height 0.001 0))
	(cv "ctx.closPath")
	(if flag (cv "ctx.fill();"))
    (cv "ctx.stroke();")
)


(define (cv:shape lst flag) 
	(cv (format "Shape(new Array(%s), %s);" 
			(list-path lst)
			(if flag "true" "false")))
)


(define (cv:clip lst flag) 
	(cv (format "Clip(new Array(%s), %s);" 
		(list-path lst)  (if flag "true" "false")))
)


(define (cv:text str)
	(cv (format {Text("%s");} str))
)

(define (textoutline str)
	(cv (format {TextOutline("%s");} str))
)


(define (cv:textarc str radius)
	(cv (format {TextArc("%s", %g);} str radius))
) 


(define (cv:textarcoutline str radius)
	(cv (format {TextArcOutline("%s", %g);} str radius))
) 


(define (cv:translate x y)
	(if (and x y)
		(cv (format "ctx.translate(%g, -%g);" x y))
		(cv "ctx.translate(xpos, - (canvasHeight - ypos));")
	)
)


(define (cv:scale x y)
	(cv (format "Scale(%g, %g);" x y))
)


(define (cv:rotate dg)
	(cv (format "ctx.rotate(%g);" (mul pi/2 (div dg 90))))
)


(define (cv:gsave) (cv "Gsave();"))


(define (cv:grestore) (cv "Grestore();"))


(define (cv:font spec) (cv (format {ctx.font = "%s";} spec)))


(define (cv:line-cap mode) 
	(if (number? mode) (set 'mode (nth mode '("but" "round" "square"))))
	(cv (format "ctx.lineCap = '%s';" mode)))


(define (cv:line-join mode)
	(if (number? mode) (set 'mode (nth mode '("miter" "round" "bevel")))) 
	(cv (format "ctx.lineJoin = '%s';" mode)))


(define (line-color red green blue alpha)
    (if (string? red)
        (let (color red)
          (set 'red (div (int (append "0x" (0 2 color)) 0 16) 255))
          (set 'green (div (int (append "0x" (2 2 color)) 0) 255))
          (set 'blue (div (int (append "0x" (4 2 color)) 0) 255))))

	(if alpha
   	  (cv (format "ctx.strokeStyle = 'rgba(%d, %d, %d, %g)';" 
			(mul red 255) (mul green 255) (mul blue 255) alpha))
   	  (cv (format "ctx.strokeStyle = 'rgb(%d, %d, %d)';" 
			(mul red 255) (mul green 255) (mul blue 255) )))
)


(define (cv:line-width width)
	(cv (format "ctx.lineWidth = %g;" width))
)

(define (fill-color red green blue alpha)
    (if (string? red)
        (let (color red)
          (set 'red (div (int (append "0x" (0 2 color)) 0 16) 255))
          (set 'green (div (int (append "0x" (2 2 color)) 0) 255))
          (set 'blue (div (int (append "0x" (4 2 color)) 0) 255))))

	(if alpha
   	  (cv (format "ctx.fillStyle = 'rgba(%d, %d, %d, %g)';" 
			(mul red 255) (mul green 255) (mul blue 255) alpha))
   	  (cv (format "ctx.fillStyle = 'rgb(%d, %d, %d)';" 
			(mul red 255) (mul green 255) (mul blue 255) )))
)

(define (cv:render mode)
	(let (page (append (format template-header header-tags)
				script-header
				canvas-script
				script-close
				body-html
				body-close)) 
		(cond 	
			( 	(nil? mode) 
                ; on Emscripten open tab 
                (if eval-string-js 
                    (display-html page true)
                    (show-in-browser)))

	 		( 	(= (upper-case mode) "CGI") 
				(println page))

			( true 
				(write-file mode page))
		)
	)
)

(define cv:save cv:render) ; compatibility with older postscript.lsp code
				
(define (show-in-browser)
(write-file "/tmp/noname.html" page)
  (cond
    ( (= ostype "OSX")
      (exec "open /tmp/noname.html"))
    ( (= ostype "Windows")
      (set 'prog (string "cmd /c \"" (env "PROGRAMFILES") 
;                 Use either Firefox or Safari 4.0
;                 "/Mozilla Firefox 3.1 Beta 2/firefox.exe\""))
                  "/Safari/Safari.exe\""))
;     (println "->" prog "<-")
      (exec (string prog " file://c:/tmp/noname.html")))
    ( true // all Linux and other unix
      (set 'files '(
            "/usr/bin/sensible-browser"
            "/usr/bin/x-www-browser"
            "/usr/bin/mozilla"
            "/usr/bin/firefox"
            "/usr/bin/konqueror"
            ))
      (set 'prog (find true (map file? files)))
      (if prog
        (exec (string (files prog) " file:///tmp/noname.html"))
        (println "Cannot find browser to display documentation" "warning")) )
  ) ; cond
) ; define

(context MAIN)
; eof


syntax highlighting with newLISP and newLISPdoc