Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
[[tags: egg]] == s9fes-char-graphics The ''Scheme 9 from Empty Space'' character graphics routines, and many extras. Features and API of the original are preserved, just extended. Extras: * canvas properties * canvas print w/ frame * canvas polyline * canvas i/o * canvas clear, flood, copy, paste, scroll * multiple virtual-canvases * compositing * canvas sub-region (rect) * canvas character random-access is O(1) * canvas record printers [[toc:]] === Char Canvas This is a set of routines for drawing characters and lines on a scaled, character-based (a.k.a. "ASCII Art") canvas. Assumes one character per column. Do not use wide characters. ==== Usage <enscript language=scheme> (import (s9fes char-canvas)) </enscript> ==== make-canvas <procedure>(make-canvas COLUMNS ROWS [WIDTH [HEIGHT]]) -> canvas</procedure> Creates a character canvas with a physical size of {{COLUMNS}} X {{ROWS}} characters. The virtual size of the canvas is {{WIDTH}} X {{HEIGHT}} "pixels". "Real coordinates" relate to the physical size of the canvas. "Virtual coordinates" are translated to real coordinates by scaling. Both types of coordinates are specified in X/Y notation. The origin 0/0 is at the lower left corner of the canvas. The new canvas will be filled with {{(current-plotter-bkgd-char)}} initially. ==== new-canvas <procedure>(new-canvas CANVAS WIDTH HEIGHT) -> canvas</procedure> Creates a {{canvas}}, sharing the {{char-canvas}} of {{CANVAS}}, but with a virtual size of {{WIDTH}} X {{HEIGHT}} "pixels". ==== canvas-duplicate <procedure>(canvas-duplicate CANVAS) -> canvas</procedure> Returns a deep-copy of the {{CANVAS}}. ==== canvas? ==== canvas-columns ==== canvas-rows ==== canvas-width ==== canvas-height <procedure>(canvas? OBJ) -> boolean</procedure> <procedure>(canvas-columns CANVAS) -> fixnum</procedure> <procedure>(canvas-rows CANVAS) -> fixnum</procedure> <procedure>(canvas-width CANVAS) -> integer</procedure> <procedure>(canvas-height CANVAS) -> integer</procedure> ==== canvas-real <procedure>(canvas-real CANVAS X Y) -> X Y</procedure> Returns the real (physical) coordinates for the supplied virtual {{X Y}}. ==== canvas-virtual <procedure>(canvas-virtual CANVAS X Y) -> X Y</procedure> Returns virtual coordinates for the supplied real (physical) {{X Y}}. '''Note''' that {{canvas-virtual}} <=> {{canvas-real}} is ''not'' guaranteed. So this routine is of little use. ==== canvas-dump <procedure>(canvas-dump CANVAS) -> (vector-of string)</procedure> Returns a {{(vector-of string)}} that contains the characters written to the canvas. The {{vector}} elements are the rows, the {{string}} ''code-points'' the columns. Original API, an alias of {{canvas->vector}} now. ==== canvas-print <procedure>(canvas-print CANVAS [FRAME])</procedure> Prints the {{CANVAS}}, one row per line, with an optional ''frame''. ; {{FRAME}} : {{(vector char char char char char char char char)}} ; top/bottom horizontal, top/bottom vertical, corner top left/right & bottom left/right chars; default {{#(#\- #\- #\| #\| #\+ #\+ #\+ #\+)}}. ; {{FRAME}} : {{#t}} ; use {{ASCII-FRAME-CHARS}}. ; {{ASCII-FRAME-CHARS}} : {{#(#\- #\- #\| #\| #\+ #\+ #\+ #\+)}} ; {{THIN-FRAME-CHARS}} : {{#(#\─ #\─ #\│ #\│ #\┌ #\┐ #\└ #\┘)}} ; {{THICK-FRAME-CHARS}} : {{#(#\━ #\━ #\┃ #\┃ #\┏ #\┓ #\┗ #\┛)}} ==== make-frame-chars <procedure>(make-frame-chars TOP BTM LFT RGT TOP-LFT TOP-RGT BTM-LFT BTM-RGT) -> frame-chars</procedure> ; {{TOP BTM LFT RGT}} ; {{char char char char}} ; straights ; {{TOP-LFT TOP-RGT BTM-LFT BTM-RGT}} : {{char char char char}} ; corners ==== frame-chars? <procedure>(frame-chars? OBJ) -> boolean</procedure> ==== string->canvas <procedure>(string->canvas STR) -> canvas</procedure> Returns a {{canvas}} from the line-feed-encoded {{STR}}. ; {{STR}} : {{string}} : string w/ embedded #\newline for each row. ==== with-output-to-canvas <procedure>(with-output-to-canvas THUNK) -> canvas</procedure> Returns a {{canvas}} from the read output of {{THUNK}}. Useful for the result capture of other character graphics output as a {{canvas}}. ; {{THUNK}} : {{(-> void)}} : writes to {{current-output-port}}. * Capture a plot: <enscript language=scheme> (define *data* '(0 1 2 3 4 5 6 7 8 9)) (define data-plot (lambda () (char-plot *data* 'data 7 35 #f))) (define data-plot-image (canvas->image (with-output-to-canvas data-plot))) </enscript> ==== canvas->vector <procedure>(canvas->vector CANVAS) -> (vector-of string)</procedure> Converts the {{CANVAS}} to a {{vector}}, row h-1 to row 0. ==== canvas->list <procedure>(canvas->list CANVAS) -> (list-of string)</procedure> Converts the {{CANVAS}} to a {{list}}, row h-1 to row 0. ==== canvas->string <procedure>(canvas->string CANVAS) -> string</procedure> Converts the {{CANVAS}} to a {{string}}, row h-1 to row 0, delimited by {{#\newline}}. ==== current-plotter-char <procedure>(current-plotter-char [CHAR]) -> char</procedure> ==== current-plotter-bkgd-char <procedure>(current-plotter-bkgd-char [CHAR]) -> char</procedure> Default {{char}} for clearing. ==== plot-config <procedure>(plot-config PLT-CHR ...) -> plotter-configuration</procedure> Makes a {{plotter-configuration}}. ; {{PLT-CHR}} : {{(or false char)}} ; plot character ==== canvas-plot <procedure>(canvas-plot CANVAS X Y [CHAR])</procedure> Draws the character {{CHAR}} at the virtual position {{X/Y}}. Draws a line from the virtual position X/Y to DX/DY using the character {{CHAR}}. All arguments must be integers. Lines originating or extending outside of the canvas will be clipped. ==== canvas-plot-string <procedure>(canvas-plot-string CANVAS X Y STRING)</procedure> Draws a {{STRING}} at virtual position {{X/Y}}. When {{STRING}} extends beyond the limits of the canvas, it will be clipped. ==== canvas-plot-line <procedure>(canvas-plot-line CANVAS X0 Y0 X1 Y1 [CHAR])</procedure> Plots a line from {{X0 Y0}} to {{X1 Y1}}, using {{CHAR}}. ; {{X0 Y0 X1 Y1}} : {{fixnum}} ; . ; {{CHAR}} : {{char}} ; drawing character, default {{(current-plotter-char)}}. ==== canvas-plot-lines <procedure>(canvas-plot-lines CANVAS LINES [CONFIG])</procedure> ; {{LINES}} : {{(list-of integer)}} ; list of line segment x y. ; {{CONFIG}} : {{(or false char plotter-configuration)}} ; drawing pattern, n elements, {{#f}} for skip or a {{char}} to draw; default is {{(current-plotter-char)}}. ==== canvas-draw <procedure>(canvas-draw CANVAS X Y [CHAR])</procedure> Draws character CHAR at position X/Y. It uses real coordinates. When the X or Y coordinate is outside of the canvas, CHAR will not be drawn. ==== canvas-draw-string <procedure>(canvas-draw-string CANVAS X Y STRING)</procedure> Draws a {{STRING}} at position X/Y. It uses real coordinates. When {{STRING}} extends beyond the limits of the canvas, it will be clipped. ==== canvas-draw-line <procedure>(canvas-draw-line CANVAS X0 Y0 X1 Y1 [CHAR])</procedure> Draws a line from {{X0 Y0}} to {{X1 Y1}}, using {{CHAR}}. ; {{X0 Y0 X1 Y1}} : {{fixnum}} ; . ; {{CHAR}} : {{char}} ; drawing character, default {{(current-plotter-char)}}. ==== canvas-draw-lines <procedure>(canvas-draw-lines CANVAS LINES [CONFIG])</procedure> ; {{LINES}} : {{(list-of fixnum)}} ; list of line segment x y. ; {{CONFIG}} : {{(or false char plotter-configuration)}} ; drawing pattern, n elements, {{#f}} for skip or a {{char}} to draw; default is {{(current-plotter-char)}}. ==== make-char-canvas ==== char-canvas? ==== char-canvas-columns ==== char-canvas-rows ==== char-canvas-cmap <procedure>(make-char-canvas COLS ROWS CHAR-OR-CMAP) -> char-canvas</procedure> <procedure>(char-canvas? OBJ) -> boolean</procedure> <procedure>(char-canvas-columns CHAR-CANVAS) -> fixnum</procedure> <procedure>(char-canvas-rows CHAR-CANVAS) -> fixnum</procedure> <procedure>(char-canvas-cmap CHAR-CANVAS) -> cmap</procedure> === Char Canvas Block ==== Usage <enscript language=scheme> (import (s9fes char-canvas block)) </enscript> ==== canvas-clear <procedure>(canvas-clear CANVAS [RGN])</procedure> Uses the {{(current-plotter-bkgd-char)}} to ''clear'' the {{CANVAS}} {{RGN}}. ; {{RGN}} : {{rect}} : canvas sub-area, default is whole canvas. ==== canvas-flood <procedure>(canvas-flood CANVAS FILL [RGN]))</procedure> Cover the {{CANVAS}} {{RGN}} with the {{FILL}} {{char}}. ; {{FILL}} : {{char}} : fill char, default {{(current-plotter-char)}}. ; {{RGN}} : {{rect}} : canvas sub-area, default is whole canvas. ==== canvas-scroll <procedure>(canvas-scroll CANVAS [DX [DY [RGN]]])</procedure> Scroll the {{CANVAS}} {{RGN}}. ; {{DX}} : {{integer}} : negative (left) or positive (right) ; {{DY}} : {{integer}} : negative (up) or positive (down) ; {{RGN}} : {{rect}} : canvas sub-area, default is whole canvas. ==== canvas-copy <procedure>(canvas-copy CANVAS [RGN [TRANS]]) -> canvas</procedure> Copy the {{CANVAS}} {{RGN}}, clipping the {{RGN}} to fit if necessary. ; {{RGN}} : {{rect}} : canvas sub-area, default is whole canvas. ; {{TRANS}} : {{(char char fixnum fixnum)}} ; transformation function (see {{canvas-paste}}) ==== canvas-paste <procedure>(canvas-paste CANVAS SOURCE-CANVAS [X [Y [TRANS]]])</procedure> Paste the {{SOURCE-CANVAS}} into {{CANVAS}} at lower-left {{X Y}}, clipping the {{SOURCE-CANVAS}} to fit if necessary. ; {{X}} : {{integer}} : x, default is {{0}} ; {{Y}} : {{integer}} : y, default is {{0}} ; {{TRANS}} : {{(char char fixnum fixnum -> char)}} ; result char is function of source char+index & target+index. The operation is performed, in the absence of a transformation function, as if {{(lambda (t s i j) s)}} was specified, i.e. overwrite. Example of transform as used by the {{image}} shape: <enscript language=scheme> (define ((compositor-alpha #!optional (c (current-plotter-bkgd-char))) tc sc i j) (if (char=? c sc) tc sc) ) </enscript> ==== canvas-clear-chars <procedure>(canvas-clear-chars CANVAS [RGN])</procedure> Uses the {{(current-plotter-bkgd-char)}} to ''clear'' the {{CANVAS}} {{RGN}}. ; {{RGN}} : {{char-rect}} : canvas sub-area, default is whole canvas. ==== canvas-flood-chars <procedure>(canvas-flood-chars CANVAS FILL [RGN]))</procedure> Fill the {{CANVAS}} {{RGN}} with the {{FILL}}. ; {{FILL}} : {{char}} : fill char, default {{(current-plotter-char)}}. ; {{RGN}} : {{char-rect}} : canvas sub-area, default is whole canvas. ==== canvas-scroll-chars <procedure>(canvas-scroll-chars CANVAS [DX [DY [RGN]]])</procedure> Scroll the {{CANVAS}} {{RGN}}. ; {{DX}} : {{fixnum}} : columns negative (left) or positive (right) ; {{DY}} : {{fixnum}} : rows negative (up) or positive (down) ; {{RGN}} : {{char-rect}} : canvas sub-area, default is whole canvas. ==== canvas-copy-chars <procedure>(canvas-copy-chars CANVAS [RGN [TRANS]]) -> canvas</procedure> Copy the {{CANVAS}} {{RGN}}. ; {{RGN}} : {{char-rect}} : canvas sub-area, default is whole canvas. ; {{TRANS}} : {{(char char fixnum fixnum)}} ; transformation function (see {{canvas-paste}}) ==== canvas-paste-chars <procedure>(canvas-paste-chars CANVAS SOURCE-CANVAS [X [Y [TRANS]]])</procedure> Paste the {{SOURCE-CANVAS}} into {{CANVAS}} at lower-left {{X Y}}. ; {{X}} : {{fixnum}} : x, default is {{0}} ; {{Y}} : {{fixnum}} : y, default is {{0}} ; {{TRANS}} : {{(char char fixnum fixnum)}} ; transformation function (see {{canvas-paste}}) The returned {{canvas}} is the {{TARGET-CANVAS}}. === Char Canvas Rectangle ==== Usage <enscript language=scheme> (import (s9fes char-canvas rect)) </enscript> Shared Arguments & Types: ; {{X Y}} : {{integer integer}} : position - x (column) & y (row) . ; {{WD HT}} : {{integer integer}} : size - width (columns) & height (rows). ==== rect ==== rect? ==== rect-x ==== rect-y ==== rect-wd ==== rect-ht <procedure>(rect X Y WD HT) -> rect</procedure> <procedure>(rect? OBJ) -> boolean</procedure> <procedure>(rect-x RECT) -> integer</procedure> <procedure>(rect-y RECT) -> integer</procedure> <procedure>(rect-wd RECT) -> integer</procedure> <procedure>(rect-ht RECT) -> integer</procedure> ==== rect-spot ==== rect-size <procedure>(rect-spot RECT) -> integer integer</procedure> <procedure>(rect-size RECT) -> integer integer</procedure> ==== rect-x-end <procedure>(rect-x-end RECT) -> integer</procedure> One beyond the canvas horizontal. ==== rect-y-end <procedure>(rect-y-end RECT) -> integer</procedure> One beyond the canvas vertical. ==== rect-x-min <procedure>(rect-x-min RECT) -> integer</procedure> Same as {{rect-x}}. For symmetry. ==== rect-y-min <procedure>(rect-y-min RECT) -> integer</procedure> Same as {{rect-y}}. For symmetry. ==== rect-x-max <procedure>(rect-x-max RECT) -> integer</procedure> Right-most horizontal coordinate. ==== rect-y-max <procedure>(rect-y-max RECT) -> integer</procedure> Top-most vertical coordinate. ==== rect-copy <procedure>(rect-copy RECT) -> rect</procedure> ==== rect-null <procedure>(rect-null) -> rect</procedure> Returns an empty rectangle. ==== rect-null? <procedure>(rect-null? OBJ) -> boolean</procedure> Is the rectangle empty? ({{0}} {{rect-wd}} or {{rect-ht}}) ==== rect-bias <procedure>(rect-bias RECT DX DY) -> rect</procedure> Shift the {{RECT}} by {{DX}} in the horizontal and {{DY}} in the vertical. Does not change the size. ; {{DX}} : {{integer}} : negative (left) or positive (right) ; {{DY}} : {{integer}} : negative (up) or positive (down) ==== rect-scale <procedure>(rect-scale RECT SX SY) -> rect</procedure> Grow the {{RECT}} by {{SX}} in the horizontal and {{SY}} in the vertical. Does not change the position. ; {{SX}} : {{integer}} : negative (thin) or positive (widen) ; {{SY}} : {{integer}} : negative (shorten) or positive (lengthen) ==== rect-inset <procedure>(rect-inset RECT DX DY) -> rect</procedure> Returns a {{rect}} ''inside'' the {{RECT}} by the supplied distance {{DX DY}}. ==== rect-outset <procedure>(rect-outset RECT DX DY) -> rect</procedure> Returns a {{rect}} ''outside'' the {{RECT}} by the supplied distance {{DX DY}}. ==== rect-reset <procedure>(rect-reset RECT [X0 [Y0]]) -> rect</procedure> Returns a {{rect}} with the supplied origin but size from {{RECT}}. ==== rect->list <procedure>(rect->list RECT) -> (list integer integer integer integer)</procedure> ==== list->rect <procedure>(list->rect LS) -> rect</procedure> ; {{LS}} : {{(list integer integer integer integer)}} ==== canvas-rect <procedure>(canvas-rect CANVAS) -> rect</procedure> Returns a {{rect}} from the {{CANVAS}} dimensions. ==== real-rect <procedure>(real-rect CANVAS RECT) -> rect</procedure> Returns the rectangle in the real (physical) coordinate system. ==== virtual-rect <procedure>(virtual-rect CANVAS RECT) -> rect</procedure> Returns the rectangle in the virtual coordinate system. ==== rect-area <procedure>(rect-area RECT) -> integer</procedure> Returns the size of the rectangle. ==== rect-smaller? <procedure>(rect-smaller? RECT1 RECT2) -> boolean</procedure> ==== rect-larger? <procedure>(rect-larger? RECT1 RECT2) -> boolean</procedure> ==== rect-relates <procedure>(rect-relates RECT1 RECT2) -> (list-of symbol)</procedure> Returns a, possibly empty, list of symbols describing the spatial relationship of {{RECT1}} & {{RECT2}}. ; {{left right}} : x ; {{above below}} : y ; {{thinner wider}} : width ; {{shorter taller}} : height ==== rect-union <procedure>(rect-union RECT1 RECT2) -> rect</procedure> Returns the encompassing rectangle of {{RECT1}} & {{RECT2}}. ==== rect-intersection <procedure>(rect-intersection RECT1 RECT2) -> rect</procedure> Returns the overlapping rectangle of {{RECT1}} & {{RECT2}}, which may be empty. ==== rect-overlaps? <procedure>(rect-overlaps? RECT1 RECT2) -> boolean</procedure> Does {{RECT1}} intersect {{RECT2}}? === Char Plot ==== Usage <enscript language=scheme> (import (s9fes char-plot)) </enscript> ==== char-plot <procedure>(char-plot LIST SYMBOL HEIGHT WDITH [COMPR?])</procedure> Creates a character canvas (see {{make-canvas}}), marks the data points in {{LIST}} with #\X and draws a line through the points with #\-. {{SYMBOL}} will be used to label the X axis (on which the data points will be distributed). {{HEIGHT}} and {{WDITH}} specify the physical dimensions of the char canvas. Its virtual dimensions will be computed in such a way that all data points can be displayed. When the {{COMPR?}} (compression) argument is set to {{#t}}, then the X axis will start at the magnitude of the least data point instead of zero, so that the entire width of the canvas is available for distributing the supplied data points. The default is {{#t}}. <enscript language=scheme> (char-plot '(0 1 2 3 4 5 6 7 8 9) 'foo 7 35 #f)</procedure> ;=> ----------- foo --> ----------------- | -X | | --X- | | --X--X- | | -X- | | -X---X- | | --X- | |X--X- | ----------- foo --> ----------------- </enscript> === Draw Tree ==== Usage <enscript language=scheme> (import (s9fes draw-tree)) </enscript> ==== draw-tree <procedure>(draw-tree ROOT [MAX-WIDTH])</procedure> Draws a character graphic representation of the tree {{LIST}} on {{(current-output-port)}}. ; ROOT : {{pair}} ; tree root node. ; MAX-WIDTH : {{fixnum}} ; maximum printed node width, in characters. default is {{7}}. <enscript language=scheme> (draw-tree '((a) (b . c) (d e))) ;=> [o|o]---[o|o]---[o|/] | | | [o|/] | [o|o]---[o|/] | | | | a | d e | [o|o]--- c | b </enscript> == Bugs & Limitations * None. A thing of beauty. == Notes * The ''plot/virtual'' API with the ''draw/real'' distinction follows the original pattern. However, except as implementation details, using the ''draw/real'' is unlikely to be worth the effort. For performance have ''pixels'' always be ''chars'': don't specify a virtual canvas size. == Requirements [[utf8]] [[srfi-1]] [[record-variants]] [[test]] [[test-utils]] == Author [[/users/kon-lovett|Kon Lovett]] == Repository This egg is hosted on the CHICKEN Subversion repository: [[https://anonymous@code.call-cc.org/svn/chicken-eggs/release/5/s9fes-char-graphics|https://anonymous@code.call-cc.org/svn/chicken-eggs/release/5/s9fes-char-graphics]] If you want to check out the source code repository of this egg and you are not familiar with Subversion, see [[/egg-svn-checkout|this page]]. == Version history ; 1.19.0 : Add {{rect-spot}} & {{rect-size}}. ; 1.18.0 : Add {{plot-config}}. ; 1.17.1 : Actually remove {{format}} dependency. ; 1.17.0 : Remove {{format}} dependency. ; 1.16.3 : Add {{frame-chars?}}. ; 1.16.2 : Add {{rect-inset}}, {{rect-outset}}, {{rect-reset}}. ; 1.16.1 : Add {{make-frame-chars}}. ; 1.16.0 : Move ''shapes'' to the {{s9fes-char-canvas-shapes)}} egg. ; 1.15.5 : Rename {{canvas->rect}} => {{canvas-rect}}. Move block operations to {{(s9fes char-canvas block)}}. ; 1.15.4 : Shape ''info'' is a vector, Add ''info'' helper syntax: {{^info}}, {{^info-real}}, {{^info-virtual}}, {{@info-class}}, {{@info-coords}}, {{@info}}. ; 1.15.3 : Fix {{shape-cardinality}} & {{shape-elements}}. ; 1.15.2 : Add {{registered-shape-methods}}, {{shape-cardinality}}, {{shape-elements}}, {{shape-layout}}, {{shape-show}}, {{shape-fold}} ; 1.15.1 : Shape configuration information includes kind & coordinate-system. Add {{(s9fes char-canvas shape shape)}} & {{shape-size}}. ; 1.15.0 : Remove {{(draw|plot)-(box|image|circle}}. {Rename ''icon' to ''image''.{{(s9fes char-canvas shape icon)}} => {{(s9fes char-canvas shape image)}}, {{shape-icon}} => {{shape-image}}. ; 1.14.6 : Rename ''oval'' to ''circle'': {{(s9fes char-canvas shape oval)}} => {{(s9fes char-canvas shape circle)}}, {{shape-oval}} => {{shape-circle}}. ; 1.14.5 : Rename {{shape-}} => {{real-shape-}} & {{virtual-shape-}} to {{shape-}}. ; 1.14.4 : Rename {{physical}} to {{real}}. Add {{virtual-rect}}. ; 1.14.3 : Add copy & paste clipping. ; 1.14.2 : Add {{canvas}} && {{char-canvas}} record printers. Better "too big" paste error. ; 1.14.1 : Add {{canvas->image}} & {{canvas-duplicate}}. ; 1.14.0 : Add "compositing" to {{canvas-copy}}, {{canvas-paste]}}{{canvas-copy-chars}}, {{canvas-paste-chars]}}, {{shape-image}}, & {{shape-image}}. ; 1.13.0 : Add {{(s9fes char-canvas shape image)}} {{module}}. ; 1.12.1 : Fix box shape plotter position. ; 1.12.0 : Rename to {{(s9fes char-canvas rect)}}. Add {{rect-null}}, {{rect-null?}}, {{rect-area}}, {{rect-overlaps?}}, {{rect-union}}, {{rect-intersection}}, {{rect-smaller?}}, {{rect-larger?}}, {{rect-relates}}. ; 1.11.0 : Add {{string->canvas}} & {{with-output-to-canvas}}. {{canvas-print}} takes optional frame (remove {{canvas-print/frame}}). Add {{(s9fes char-canvas shape box)}}. ; 1.10.0 : Add {{canvas-print/frame}}, {{new-canvas}}, {{canvas-flood}}, {{canvas-scroll}}, {{canvas-copy}}, {{canvas-paste}}, {{canvas-flood-chars}}, {{canvas-scroll-chars}}, {{canvas-copy-chars}}, {{canvas-paste-chars}}, {{canvas->list}}, {{canvas->vector}}. Add {{(s9fes rect)}} {{module}}. Add ''shape'' introspection. ; 1.9.1 : {{shape-oval}} & {{shape-oval}} proper shapes (optional center). ; 1.9.0 : Add {{shape-oval}} & {{shape-oval}}. ; 1.8.1 : Fix {{(s9fes char-canvas shape cross)}} naming, again. ; 1.8.0 : Fix {{(s9fes char-canvas shape cross)}} naming. ; 1.7.1 : Smaller. ; 1.7.0 : Fix {{shape-point-plotter}}, takes center, not top-left. Add {{cross-x-drawer}} & {{cross-+-drawer}}. ; 1.6.0 : Add {{canvas-draw-line}} & {{canvas-draw-lines}}. ; 1.5.0 : . ; 1.4.4 : Prevent division-by-zero in {{canvas-plot-line}}. ; 1.4.3 : . ; 1.4.2 : . ; 1.4.1 : . ; 1.4.0 : Add {{current-plotter-char}}, {{shape oval}} & {{shape cross}}. ; 1.3.2 : Fix octant stream. ; 1.3.1 : Fix {{canvas?}} type. ; 1.3.0 : Add {{generate-circle-octant}} & friends. ; 1.2.0 : Add {{canvas-plot-string}}. ; 1.1.0 : Add {{canvas-print}}, {{canvas-physical}}, {{canvas-virtual}}. ; 1.0.1 : . ; 1.0.0 : Initial release. == License Public Domain
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you multiply 0 by 7?