shithub: libgraphics

Download patch

ref: 91d8cc8d9adfec7e9b5f415ebd7b7f740cde6e2d
author: rodri <rgl@antares-labs.eu>
date: Fri Apr 17 07:42:47 EDT 2020

standalone version release.

diff: cannot open b/doc//null: file does not exist: 'b/doc//null'
--- /dev/null
+++ b/camera.c
@@ -1,0 +1,77 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <geometry.h>
+#include <graphics.h>
+
+static int
+max(int a, int b)
+{
+	return a > b ? a : b;
+}
+
+static void
+verifycfg(Camera *c)
+{
+	assert(c->viewport != nil);
+	if(c->ptype == Ppersp)
+		assert(c->fov > 0 && c->fov < 360);
+	assert(c->clip.n > 0 && c->clip.n < c->clip.f);
+}
+
+void
+configcamera(Camera *c, Image *v, double fov, double n, double f, Projection p)
+{
+	c->viewport = v;
+	c->fov = fov;
+	c->clip.n = n;
+	c->clip.f = f;
+	c->ptype = p;
+	reloadcamera(c);
+}
+
+void
+placecamera(Camera *c, Point3 p, Point3 focus, Point3 up)
+{
+	c->p = p;
+	if(focus.w == 0)
+		c->bz = focus;
+	else
+		c->bz = normvec3(subpt3(c->p, focus));
+	c->bx = normvec3(crossvec3(up, c->bz));
+	c->by = crossvec3(c->bz, c->bx);
+}
+
+void
+aimcamera(Camera *c, Point3 focus)
+{
+	placecamera(c, c->p, focus, c->by);
+}
+
+void
+reloadcamera(Camera *c)
+{
+	double a;
+	double l, r, b, t;
+
+	verifycfg(c);
+	switch(c->ptype){
+	case Portho:
+		/*
+		r = Dx(c->viewport->r)/2;
+		t = Dy(c->viewport->r)/2;
+		l = -r;
+		b = -t;
+		*/
+		l = t = 0;
+		r = Dx(c->viewport->r);
+		b = Dy(c->viewport->r);
+		orthographic(c->proj, l, r, b, t, c->clip.n, c->clip.f);
+		break;
+	case Ppersp:
+		a = (double)Dx(c->viewport->r)/Dy(c->viewport->r);
+		perspective(c->proj, c->fov, a, c->clip.n, c->clip.f);
+		break;
+	default: sysfatal("unknown projection type");
+	}
+}
--- /dev/null
+++ b/doc/libgraphics.ms
@@ -1,0 +1,34 @@
+.TL
+libgraphics
+.AU
+Rodrigo G. López
+.sp
+rgl@antares-labs.eu
+.AI
+Antares Telecom Laboratories
+Albatera, Alicante
+.FS
+ACHTUNG! this is a
+.B "WORK IN PROGRESS"
+.FE
+.NH 1
+Data Structures
+.NH 2
+Camera
+.P1
+struct Camera {
+	RFrame3;		/* VCS */
+	Image *viewport;
+	double fov;		/* vertical FOV */
+	struct {
+		double n, f;	/* near and far clipping planes */
+	} clip;
+	Projection ptype;
+	Matrix3 proj;		/* VCS to viewport xform */
+};
+.P2
+.PP
+A camera is an image capturing entity, analog to the real world device
+we all know, that allows us to see the virtual 3-D world by projecting
+it into a viewport we can attach to a screen or window for real-time
+visualization or write out into a file.
--- /dev/null
+++ b/doc/libgraphics.pdf
@@ -1,0 +1,295 @@
+%PDF-1.2
+%�쏢
+5 0 obj
+<</Length 6 0 R/Filter /FlateDecode>>
+stream
+x�e��n�0��z
+�Ty�4K�/vi��� i=C�8n���[۹��S�FIIP�B��O$�!�u�eKB���#/D�8�����|.2��	d���$
A��6�fכ�}]����p�Y�#`2�%���}��`�a����cY�s�@��~�\�ߣ髁5f3��p�ɓOJ,����ς����ZX�Mכ����"s������|�IS�xM���I-Re݉s?P��D�2��������?�"Q� ���<�ѩi��r+�B�녃s,)����p޿z��j��Kp��q�C
+��8�ҧ��Đ�� �"�}�����(n�������[�?La%Q��q_�S��(Sz�+��i����������ߗ6&��X͋�z�rf����礞�endstream
+endobj
+6 0 obj
+386
+endobj
+4 0 obj
+<</Type/Page/MediaBox [0 0 612 792]
+/Rotate 0/Parent 3 0 R
+/Resources<</ProcSet[/PDF /Text]
+/Font 17 0 R
+>>
+/Contents 5 0 R
+>>
+endobj
+3 0 obj
+<< /Type /Pages /Kids [
+4 0 R
+] /Count 1
+>>
+endobj
+1 0 obj
+<</Type /Catalog /Pages 3 0 R
+>>
+endobj
+17 0 obj
+<</R15
+15 0 R/R8
+8 0 R/R10
+10 0 R/R12
+12 0 R/R14
+14 0 R/R16
+16 0 R>>
+endobj
+15 0 obj
+<</BaseFont/Times-Roman/Type/Font
+/Subtype/Type1>>
+endobj
+8 0 obj
+<</BaseFont/DIDNIM+LucidaSans-Demi/FontDescriptor 7 0 R/Type/Font
+/FirstChar 32/LastChar 117/Widths[
+319 0 0 0 0 0 0 0 0 0 0 0 0 0 247 0
+0 639 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 712 793 601 0 746 0 331 0 710 0 0 768 823
+613 0 690 571 0 0 0 904 0 0 0 0 0 0 0 0
+0 588 663 532 0 586 0 660 657 325 0 0 325 970 0 0
+663 0 454 566 405 657]
+/Encoding/WinAnsiEncoding/Subtype/Type1>>
+endobj
+10 0 obj
+<</BaseFont/ARKRES+LucidaSans-Italic/FontDescriptor 9 0 R/Type/Font
+/FirstChar 32/LastChar 243/Widths[
+316 0 0 0 0 0 0 0 0 0 0 0 0 633 316 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+858 0 0 0 0 0 0 702 0 0 0 0 545 0 0 0
+0 0 632 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 608 608 0 608 529 0 608 0 289 0 0 289 0 621 566
+608 0 478 489 383 621 0 0 0 0 536 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 566]
+/Encoding 22 0 R/Subtype/Type1>>
+endobj
+22 0 obj
+<</Type/Encoding/BaseEncoding/WinAnsiEncoding/Differences[
+45/minus]>>
+endobj
+12 0 obj
+<</BaseFont/XXIYSF+LucidaSansUnicode00/FontDescriptor 11 0 R/Type/Font
+/FirstChar 32/LastChar 116/Widths[
+316 316 0 0 0 0 0 0 0 0 0 0 316 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 690 0 692 0 0 0 723 735 0 0 0 533 0 739 0
+0 0 0 0 632 693 0 0 0 0 0 0 0 0 0 0
+0 552 630 512 0 557 0 0 620 289 0 0 289 934 620 614
+0 0 409 509 374]
+/Encoding 23 0 R/Subtype/Type1>>
+endobj
+23 0 obj
+<</Type/Encoding/BaseEncoding/WinAnsiEncoding/Differences[
+32/0020/0021
+44/002c
+65/0041
+67/0043
+71/0047/0048
+76/004c
+78/004e
+84/0054/0055
+97/0061/0062/0063
+101/0065
+104/0068/0069
+108/006c/006d/006e/006f
+114/0072/0073/0074]>>
+endobj
+14 0 obj
+<</BaseFont/SBRUOU+LucidaTypewriter/FontDescriptor 13 0 R/Type/Font
+/FirstChar 59/LastChar 125/Widths[ 723 0 0 0 0
+0 0 0 723 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 723 0 723 0 723 0 0 0 0 0 0 0 723 0 0
+0 0 723 723 723 723 0 0 0 0 0 723 0 723]
+/Encoding/WinAnsiEncoding/Subtype/Type1>>
+endobj
+16 0 obj
+<</BaseFont/Symbol/Type/Font
+/Subtype/Type1>>
+endobj
+7 0 obj
+<</Type/FontDescriptor/FontName/DIDNIM+LucidaSans-Demi/FontBBox[0 -205 895 771]/Flags 4
+/Ascent 771
+/CapHeight 771
+/Descent -205
+/ItalicAngle 0
+/StemV 134
+/MissingWidth 319
+/CharSet(/c/N/C/p/e/O/D/P/E/r/g/s/h/R/G/t/i/S/u/I/l/a/K/m/b/W/space/period/one)/FontFile3 18 0 R>>
+endobj
+18 0 obj
+<</Filter/LZWDecode
+/Subtype/Type1C/Length 2925>>stream
++T�u��g2���S ��"!V.b�2��|D+��!^]��Y�E��dFX�w-`���caI��+���{����{���E�c��:���N}��XE���>;� �@�{+�WIJ�Aq^FR�	�s�p^3C��¨�^D��a�ATpP�B+(���"]j�B`X�x7Fa�G�pS�v7B5@!G��|�-�C@�=���aT��<G�،�iI��Hk/nl�h3�+pR����lN��;%@@���a��n ����HD�U�!�@++�xA�+�	P3��E��\P	�D'�@��xE��%C��v�E�:���z�
+�Nm!pq���藞j@#F�L�Cĭ}�jW��=�8�c+!p�Ю�~#�����3����0{���Ǹ�T��}���!C�����E������Y<4�X�\+	($�H[��6H�
+�|9)�2�X�#8M�q'D���I+��Z���J�,"Ĩ��U��b.y�xJ.�'D"+�������o�rJ�nq�p7�.7���Y�|؊L��\��X
�Ja\
+�6'�`� �A
+!+@>@+��	���p�X <!"A"a&"+`"�Pa�K���Rf&��Ld���W�˸���S >����(b*�)���aRĮ>
��bH���+�*`�����@�	�����b)�`+m��+M�A�$+endstream
+endobj
+9 0 obj
+<</Type/FontDescriptor/FontName/ARKRES+LucidaSans-Italic/FontBBox[0 -205 889 771]/Flags 4
+/Ascent 771
+/CapHeight 771
+/Descent -205
+/ItalicAngle 0
+/StemV 133
+/MissingWidth 316
+/CharSet(/L/n/minus/z/o/d/at/p/e/r/g/oacute/s/R/G/t/i/u/acute/l/a/b/space/period)/FontFile3 19 0 R>>
+endobj
+19 0 obj
+<</Filter/LZWDecode
+/Subtype/Type1C/Length 2572>>stream
++��	+�&�
+~+E��@�[�f!�y�G�Do�����}�a�p-̣��O��yRs�>O%)LU�aZK�\�S��	+�	4D��Y�E�ɠq�(gHi����a�����&lR��1G��>R���+{�٬���t�d��d�91b��.a#�cAnDf�"+��b�N
+�B*0�ˆ_�zx` �}��+G�FaX}�u��H���d��(#����P��q�����\R��f&���BtK�qL%�eB�9���B�[`|/�P�!A��o�դ�r;�SIY,%���Ǹ_#+�\-��#���bL=`02Cp��,E�2����X
+�z'+C(Z�1<-�B�S
+�B*D��B\S+�%� ���J�X,EP��B�A��j� �M��0��z!�G�aDhua*:���Èp��E���i��4U�<�]�3	@���@a
!�*�`�%+
+,D ���D������;��0X���,{���#�� Cܡ���=�j]B�j�q;�X�´9
+@�*C0�I�}��r��+��$�h�������BdP�P<$�p��lHE0'K����{��n=���K�H	�d>��7��+�(6+!+a�A�!�^��A�@v`���*A(�*�,���J��+endstream
+endobj
+11 0 obj
+<</Type/FontDescriptor/FontName/XXIYSF+LucidaSansUnicode00/FontBBox[0 -157 843 771]/Flags 4
+/Ascent 771
+/CapHeight 771
+/Descent -157
+/ItalicAngle 0
+/StemV 126
+/MissingWidth 316
+/CharSet(/006e/0063/002c/006f/004e/0043/0065/0020/0021/0072/0073/0068/0047/0074/0069/0048/0054/0055/006c/0061/006d/0062/004c/0041)/FontFile3 20 0 R>>
+endobj
+20 0 obj
+<</Filter/LZWDecode
+/Subtype/Type1C/Length 2389>>stream
++�3I��S0��eSq��o2F4L�
�_��:E��/���.�	� 5hBOw�E����+4+DI+ +]��+��!�hҜ�hgY�P�4{�GX{�GX*�gX���*i�Ĺ+���J�a(i.	&��O�R�.	�i�x����^��AM�d�O��A���iE��X�E���"+ap�X���c��:#�4"��5
+c0�K���<B� >�@@d�C�~@�X{����tC�r3���M
���M+����H�"@��tG�.v�XS��2P��QJY�el�E�$0>c�@�b�}�:VL3�&��/硰+����C��D$XrE��	v*c��E��Y�T����Yf>i�f	`&��I��19L�@8�M���H�$��F�8��9�`��d!��q��%��@�j�g�Pg�e�$j���L���+�{����H��C����p�\T")X��J�A���,C���"���bfm��,"�r���BHQ� �8s��V��8DX[l"-�
+"D���1�X�B�E��)���l=��"�8	PF1C�X�f8�@�"°�0'1t/D���LG�A&%��+���l!�C��+@�+ah�t
+���>+���V�x���=�8|��va���*���}+!�9�� h�
�6;`f
+G�u	��T�Q|0+�X9a7DX�$T	%x����`���=ˆ���\V_�C�GQ%]K�L1����xǠ�}��乥���HKҷ�%�~�\���D m��$>@rRXP�����4�����^�Q.DX�"d1	@�#C����<��:���.���Qp����.+endstream
+endobj
+13 0 obj
+<</Type/FontDescriptor/FontName/SBRUOU+LucidaTypewriter/FontBBox[0 -157 720 771]/Flags 5
+/Ascent 771
+/CapHeight 771
+/Descent -157
+/ItalicAngle 0
+/StemV 108
+/AvgWidth 723
+/MaxWidth 723
+/MissingWidth 723
+/CharSet(/c/C/e/r/s/t/u/semicolon/braceleft/a/m/braceright)/FontFile3 21 0 R>>
+endobj
+21 0 obj
+<</Filter/LZWDecode
+/Subtype/Type1C/Length 1920>>stream
++�+DF��{���tA�(>H$�J��C�3�&i��ƙ�|O��qH*d� +�� 5�!"0��L�a��(	"h�6��>B�������~]$�ję4H���l�à�A����=�D �!a�����,���&{��1ZC�8>P�ĹS������|!����{��s�}�X*T��Y�<��r/B�Ba�%b��+
+�`�E���v{��!�*&�.M� �$G��2H��,��
+,CY6��La��?��Hf����+��N$�������J#y�>����&y�ke��	W[����
+O�'�uħ�,��:f�d���g�2>��I��}�`"�`���;�p�/���b�E��00E��LG��3�8`�CAX�",3���%�؟�tD	�2��!%����LJ`�㭛��(! ��"À�
Bd>��f!E0�,\u���X�T���,`�"�>��6�X�Ck6 �	Q0�C�5����k{��
���!Hd�qx;�vb�Q0@(�	cq�� �Q�F�F`�+�r&0�#hZ����b�,�a��@�	��i�@�"�x�eW+ ��bCT3� �"•2
|�!x<��ma=�A C;�e`��j9Q(�<
+���"�苮!�J�@�tB+�>+& �����4++endstream
+endobj
+2 0 obj
+<</Producer(AFPL Ghostscript 8.53)
+/CreationDate(D:20191105235851)
+/ModDate(D:20191105235851)>>endobj
+xref
+0 24
+0000000000 65535 f 
+0000000691 00000 n 
+0000014343 00000 n 
+0000000632 00000 n 
+0000000490 00000 n 
+0000000015 00000 n 
+0000000471 00000 n 
+0000002987 00000 n 
+0000000891 00000 n 
+0000006282 00000 n 
+0000001280 00000 n 
+0000009228 00000 n 
+0000001990 00000 n 
+0000012043 00000 n 
+0000002605 00000 n 
+0000000824 00000 n 
+0000002925 00000 n 
+0000000739 00000 n 
+0000003274 00000 n 
+0000006573 00000 n 
+0000009571 00000 n 
+0000012340 00000 n 
+0000001903 00000 n 
+0000002364 00000 n 
+trailer
+<< /Size 24 /Root 1 0 R /Info 2 0 R
+/ID [<83B4B497B4BE760E2302DD9469D51E12><83B4B497B4BE760E2302DD9469D51E12>]
+>>
+startxref
+14453
+%%EOF
--- /dev/null
+++ b/doc/libgraphics.ps
@@ -1,0 +1,583 @@
+%!PS-Adobe-2.0
+%%Version: 0.1
+%%Creator: troff, Plan 9 edition
+%%DocumentFonts: (atend)
+%%Pages: (atend)
+%%EndComments
+%
+% Version 3.3.2 prologue for troff files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/formsperpage 1 def
+/landscape false def
+/linewidth .3 def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/resolution 720 def
+/rotation 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/R  /Times-Roman def
+/I  /Times-Italic def
+/B  /Times-Bold def
+/BI /Times-BoldItalic def
+/H  /Helvetica def
+/HI /Helvetica-Oblique def
+/HB /Helvetica-Bold def
+/HX /Helvetica-BoldOblique def
+/CW /Courier def
+/CO /Courier def
+/CI /Courier-Oblique def
+/CB /Courier-Bold def
+/CX /Courier-BoldOblique def
+/PA /Palatino-Roman def
+/PI /Palatino-Italic def
+/PB /Palatino-Bold def
+/PX /Palatino-BoldItalic def
+/Hr /Helvetica-Narrow def
+/Hi /Helvetica-Narrow-Oblique def
+/Hb /Helvetica-Narrow-Bold def
+/Hx /Helvetica-Narrow-BoldOblique def
+/KR /Bookman-Light def
+/KI /Bookman-LightItalic def
+/KB /Bookman-Demi def
+/KX /Bookman-DemiItalic def
+/AR /AvantGarde-Book def
+/AI /AvantGarde-BookOblique def
+/AB /AvantGarde-Demi def
+/AX /AvantGarde-DemiOblique def
+/NR /NewCenturySchlbk-Roman def
+/NI /NewCenturySchlbk-Italic def
+/NB /NewCenturySchlbk-Bold def
+/NX /NewCenturySchlbk-BoldItalic def
+/ZD /ZapfDingbats def
+/ZI /ZapfChancery-MediumItalic def
+/S  /S def
+/S1 /S1 def
+/GR /Symbol def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/setup {
+	counttomark 2 idiv {def} repeat pop
+
+	landscape {/orientation 90 orientation add def} if
+	/scaling 72 resolution div def
+	linewidth setlinewidth
+	1 setlinecap
+
+	pagedimensions
+	xcenter ycenter translate
+	orientation rotation mul rotate
+	width 2 div neg height 2 div translate
+	xoffset inch yoffset inch neg translate
+	margin 2 div dup neg translate
+	magnification dup aspectratio mul scale
+	scaling scaling scale
+
+	addmetrics
+	0 0 moveto
+} def
+
+/pagedimensions {
+	useclippath userdict /gotpagebbox known not and {
+		/pagebbox [clippath pathbbox newpath] def
+		roundpage currentdict /roundpagebbox known and {roundpagebbox} if
+	} if
+	pagebbox aload pop
+	4 -1 roll exch 4 1 roll 4 copy
+	landscape {4 2 roll} if
+	sub /width exch def
+	sub /height exch def
+	add 2 div /xcenter exch def
+	add 2 div /ycenter exch def
+	userdict /gotpagebbox true put
+} def
+
+/addmetrics {
+	/Symbol /S null Sdefs cf
+	/Times-Roman /S1 StandardEncoding dup length array copy S1defs cf
+} def
+
+/pagesetup {
+	/page exch def
+	currentdict /pagedict known currentdict page known and {
+		page load pagedict exch get cvx exec
+	} if
+} def
+
+/decodingdefs [
+	{counttomark 2 idiv {y moveto show} repeat}
+	{neg /y exch def counttomark 2 idiv {y moveto show} repeat}
+	{neg moveto {2 index stringwidth pop sub exch div 0 32 4 -1 roll widthshow} repeat}
+	{neg moveto {spacewidth sub 0.0 32 4 -1 roll widthshow} repeat}
+	{counttomark 2 idiv {y moveto show} repeat}
+	{neg setfunnytext}
+] def
+
+/setdecoding {/t decodingdefs 3 -1 roll get bind def} bind def
+
+/w {neg moveto show} bind def
+/m {neg dup /y exch def moveto} bind def
+/done {/lastpage where {pop lastpage} if} def
+
+/f {
+	dup /font exch def findfont exch
+	dup /ptsize exch def scaling div dup /size exch def scalefont setfont
+	linewidth ptsize mul scaling 10 mul div setlinewidth
+	/spacewidth ( ) stringwidth pop def
+} bind def
+
+/changefont {
+	/fontheight exch def
+	/fontslant exch def
+	currentfont [
+		1 0
+		fontheight ptsize div fontslant sin mul fontslant cos div
+		fontheight ptsize div
+		0 0
+	] makefont setfont
+} bind def
+
+/sf {f} bind def
+
+/cf {
+	dup length 2 idiv
+	/entries exch def
+	/chtab exch def
+	/newencoding exch def
+	/newfont exch def
+
+	findfont dup length 1 add dict
+	/newdict exch def
+	{1 index /FID ne {newdict 3 1 roll put}{pop pop} ifelse} forall
+
+	newencoding type /arraytype eq {newdict /Encoding newencoding put} if
+
+	newdict /Metrics entries dict put
+	newdict /Metrics get
+	begin
+		chtab aload pop
+		1 1 entries {pop def} for
+		newfont newdict definefont pop
+	end
+} bind def
+
+%
+% A few arrays used to adjust reference points and character widths in some
+% of the printer resident fonts. If square roots are too high try changing
+% the lines describing /radical and /radicalex to,
+%
+%	/radical	[0 -75 550 0]
+%	/radicalex	[-50 -75 500 0]
+%
+% Move braceleftbt a bit - default PostScript character is off a bit.
+%
+
+/Sdefs [
+	/bracketlefttp		[201 500]
+	/bracketleftbt		[201 500]
+	/bracketrighttp		[-81 380]
+	/bracketrightbt		[-83 380]
+	/braceleftbt		[203 490]
+	/bracketrightex		[220 -125 500 0]
+	/radical		[0 0 550 0]
+	/radicalex		[-50 0 500 0]
+	/parenleftex		[-20 -170 0 0]
+	/integral		[100 -50 500 0]
+	/infinity		[10 -75 730 0]
+] def
+
+/S1defs [
+	/underscore		[0 80 500 0]
+	/endash			[7 90 650 0]
+] def
+%
+% Tries to round clipping path dimensions, as stored in array pagebbox, so they
+% match one of the known sizes in the papersizes array. Lower left coordinates
+% are always set to 0.
+%
+
+/roundpagebbox {
+    7 dict begin
+	/papersizes [8.5 inch 11 inch 14 inch 17 inch] def
+
+	/mappapersize {
+		/val exch def
+		/slop .5 inch def
+		/diff slop def
+		/j 0 def
+		0 1 papersizes length 1 sub {
+			/i exch def
+			papersizes i get val sub abs
+			dup diff le {/diff exch def /j i def} {pop} ifelse
+		} for
+		diff slop lt {papersizes j get} {val} ifelse
+	} def
+
+	pagebbox 0 0 put
+	pagebbox 1 0 put
+	pagebbox dup 2 get mappapersize 2 exch put
+	pagebbox dup 3 get mappapersize 3 exch put
+    end
+} bind def
+
+%%EndProlog
+%%BeginSetup
+mark
+%
+% Encoding vector and redefinition of findfont for the ISO Latin1 standard.
+% The 18 characters missing from ROM based fonts on older printers are noted
+% below.
+%
+
+/ISOLatin1Encoding [
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/space
+	/exclam
+	/quotedbl
+	/numbersign
+	/dollar
+	/percent
+	/ampersand
+	/quoteright
+	/parenleft
+	/parenright
+	/asterisk
+	/plus
+	/comma
+	/minus
+	/period
+	/slash
+	/zero
+	/one
+	/two
+	/three
+	/four
+	/five
+	/six
+	/seven
+	/eight
+	/nine
+	/colon
+	/semicolon
+	/less
+	/equal
+	/greater
+	/question
+	/at
+	/A
+	/B
+	/C
+	/D
+	/E
+	/F
+	/G
+	/H
+	/I
+	/J
+	/K
+	/L
+	/M
+	/N
+	/O
+	/P
+	/Q
+	/R
+	/S
+	/T
+	/U
+	/V
+	/W
+	/X
+	/Y
+	/Z
+	/bracketleft
+	/backslash
+	/bracketright
+	/asciicircum
+	/underscore
+	/quoteleft
+	/a
+	/b
+	/c
+	/d
+	/e
+	/f
+	/g
+	/h
+	/i
+	/j
+	/k
+	/l
+	/m
+	/n
+	/o
+	/p
+	/q
+	/r
+	/s
+	/t
+	/u
+	/v
+	/w
+	/x
+	/y
+	/z
+	/braceleft
+	/bar
+	/braceright
+	/asciitilde
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/dotlessi
+	/grave
+	/acute
+	/circumflex
+	/tilde
+	/macron
+	/breve
+	/dotaccent
+	/dieresis
+	/.notdef
+	/ring
+	/cedilla
+	/.notdef
+	/hungarumlaut
+	/ogonek
+	/caron
+	/space
+	/exclamdown
+	/cent
+	/sterling
+	/currency
+	/yen
+	/brokenbar		% missing
+	/section
+	/dieresis
+	/copyright
+	/ordfeminine
+	/guillemotleft
+	/logicalnot
+	/hyphen
+	/registered
+	/macron
+	/degree			% missing
+	/plusminus		% missing
+	/twosuperior		% missing
+	/threesuperior		% missing
+	/acute
+	/mu			% missing
+	/paragraph
+	/periodcentered
+	/cedilla
+	/onesuperior		% missing
+	/ordmasculine
+	/guillemotright
+	/onequarter		% missing
+	/onehalf		% missing
+	/threequarters		% missing
+	/questiondown
+	/Agrave
+	/Aacute
+	/Acircumflex
+	/Atilde
+	/Adieresis
+	/Aring
+	/AE
+	/Ccedilla
+	/Egrave
+	/Eacute
+	/Ecircumflex
+	/Edieresis
+	/Igrave
+	/Iacute
+	/Icircumflex
+	/Idieresis
+	/Eth			% missing
+	/Ntilde
+	/Ograve
+	/Oacute
+	/Ocircumflex
+	/Otilde
+	/Odieresis
+	/multiply		% missing
+	/Oslash
+	/Ugrave
+	/Uacute
+	/Ucircumflex
+	/Udieresis
+	/Yacute			% missing
+	/Thorn			% missing
+	/germandbls
+	/agrave
+	/aacute
+	/acircumflex
+	/atilde
+	/adieresis
+	/aring
+	/ae
+	/ccedilla
+	/egrave
+	/eacute
+	/ecircumflex
+	/edieresis
+	/igrave
+	/iacute
+	/icircumflex
+	/idieresis
+	/eth			% missing
+	/ntilde
+	/ograve
+	/oacute
+	/ocircumflex
+	/otilde
+	/odieresis
+	/divide			% missing
+	/oslash
+	/ugrave
+	/uacute
+	/ucircumflex
+	/udieresis
+	/yacute			% missing
+	/thorn			% missing
+	/ydieresis
+] def
+
+/NewFontDirectory FontDirectory maxlength dict def
+
+%
+% Apparently no guarantee findfont is defined in systemdict so the obvious
+%
+%	systemdict /findfont get exec
+%
+% can generate an error. So far the only exception is a VT600 (version 48.0).
+%
+
+userdict /@RealFindfont known not {
+	userdict begin
+		/@RealFindfont systemdict begin /findfont load end def
+	end
+} if
+
+/findfont {
+	dup NewFontDirectory exch known not {
+		dup
+		%dup systemdict /findfont get exec	% not always in systemdict
+		dup userdict /@RealFindfont get exec
+		dup /Encoding get StandardEncoding eq {
+			dup length dict begin
+				{1 index /FID ne {def}{pop pop} ifelse} forall
+				/Encoding ISOLatin1Encoding def
+				currentdict
+			end
+			/DummyFontName exch definefont
+		} if
+		NewFontDirectory 3 1 roll put
+	} if
+	NewFontDirectory exch get
+} bind def
+
+%%Patch from lp
+%%EndPatch from lp
+
+setup
+%%EndSetup
+%%Page: 1 1
+/saveobj save def
+mark
+1 pagesetup
+12 /LucidaSans-Demi f
+(libgraphics) 2533 1220 w
+10 /LucidaSans-Italic f
+(Rodrigo G. L\363pez) 2469 1480 w
+(rgl@antares-labs.eu) 2377 1760 w
+10 /LucidaSansUnicode00 f
+(Antares Telecom Laboratories) 2156 1960 w
+(Albatera, Alicante) 2451 2100 w
+10 /LucidaSans-Demi f
+(1.) 720 2700 w
+(Data Structures) 873 2700 w
+(1.1.) 720 2940 w
+(Camera) 962 2940 w
+9 /LucidaTypewriter f
+(struct) 920 3110 w
+(Camera) 1375 3110 w
+({) 1830 3110 w
+(};) 920 3330 w
+10 /LucidaSansUnicode00 f
+(A camera) 970 3546 w
+8 /S1 f
+(__________________) 720 6980 w
+8 /LucidaSansUnicode00 f
+(ACHTUNG!) 720 7080 w
+(this) 1163 7080 w
+(is) 1333 7080 w
+(a) 1423 7080 w
+8 /LucidaSans-Demi f
+(WORK) 1493 7080 w
+(IN) 1769 7080 w
+(PROGRESS) 1883 7080 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 1 1
+%%Trailer
+done
+%%DocumentFonts: S1 LucidaSansUnicode00 LucidaSans-Demi LucidaSans-Italic LucidaTypewriter
+%%Pages: 1
--- /dev/null
+++ b/doc/mkfile
@@ -1,0 +1,15 @@
+FONTS='.FP lucidasans'
+DOCNAME=libgraphics
+
+all:VQ: $DOCNAME.ps $DOCNAME.pdf
+
+clean:VQ:
+	rm -f $DOCNAME.ps $DOCNAME.pdf
+
+$DOCNAME.ps:V: $DOCNAME.ms
+	{echo $FONTS; cat $prereq}> _$prereq
+	eval `{doctype _$prereq} | lp -dstdout > $target && rm -f _$prereq
+
+$DOCNAME.pdf:V: $DOCNAME.ps
+	cat /sys/doc/docfonts $prereq > _$prereq
+	ps2pdf _$prereq $target && rm -f _$prereq
--- /dev/null
+++ b/graphics.h
@@ -1,0 +1,42 @@
+typedef enum {
+	Portho,		/* orthographic */
+	Ppersp		/* perspective */
+} Projection;
+
+typedef struct Vertex Vertex;
+typedef struct Camera Camera;
+
+struct Vertex {
+	Point3 p;	/* position */
+	Point3 n;	/* surface normal */
+};
+
+struct Camera {
+	RFrame3;		/* VCS */
+	Image *viewport;
+	double fov;		/* vertical FOV */
+	struct {
+		double n, f;
+	} clip;
+	Matrix3 proj;		/* VCS to NDC xform */
+	Projection ptype;
+};
+
+/* Camera */
+void configcamera(Camera*, Image*, double, double, double, Projection);
+void placecamera(Camera*, Point3, Point3, Point3);
+void aimcamera(Camera*, Point3);
+void reloadcamera(Camera*);
+
+/* rendering */
+#define FPS2MS(n)		(1000/(n))
+#define WORLD2VCS(cp, p)	(rframexform3((p), *(cp)))
+#define VCS2NDC(cp, p)		(xform3((p), (cp)->proj))
+#define WORLD2NDC(cp, p)	(VCS2NDC((cp), WORLD2VCS((cp), (p))))
+int isclipping(Point3);
+Point toviewport(Camera*, Point3);
+Point2 fromviewport(Camera*, Point);
+void perspective(Matrix3, double, double, double, double);
+void orthographic(Matrix3, double, double, double, double, double, double);
+void line3(Camera*, Point3, Point3, int, int, Image*);
+Point string3(Camera*, Point3, Image*, Font*, char*);
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,12 @@
+</$objtype/mkfile
+
+LIB=libgraphics.a$O
+OFILES=\
+	camera.$O\
+	render.$O\
+
+HFILES=graphics.h ../libgeometry/geometry.h
+
+CFLAGS=$CFLAGS -I. -I../libgeometry
+
+</sys/src/cmd/mklib
--- /dev/null
+++ b/readme
@@ -1,0 +1,6 @@
+libgraphics
+
+Libgraphics provides 3D computer graphics through draw(3).
+
+Still in early stages of research, subject to change, drastically, at
+any given time.
--- /dev/null
+++ b/render.c
@@ -1,0 +1,135 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include <graphics.h>
+
+//static Memimage*
+//imagetomemimage(Image *src)
+//{
+//	Memimage *dst;
+//	uchar *buf;
+//	uint buflen;
+//
+//	buflen = Dx(src->r)*Dy(src->r);
+//	buflen *= (chantodepth(src->chan)+7)/8;
+//	buf = malloc(buflen);
+//	if(buf == nil)
+//		sysfatal("malloc: %r");
+//	dst = allocmemimage(src->r, src->chan);
+//	if(dst == nil)
+//		sysfatal("allocmemimage: %r");
+//	if(src->repl){
+//		dst->flags |= Frepl;
+//		dst->clipr = Rect(-1e6, -1e6, 1e6, 1e6);
+//	}
+//	unloadimage(src, src->r, buf, buflen);
+//	loadmemimage(dst, src->r, buf, buflen);
+//	free(buf);
+//	return dst;
+//}
+
+static Point2
+flatten(Camera *c, Point3 p)
+{
+	Point2 p2;
+	Matrix S = {
+		Dx(c->viewport->r)/2, 0, 0,
+		0, Dy(c->viewport->r)/2, 0,
+		0, 0, 1,
+	}, T = {
+		1, 0, 1,
+		0, 1, 1,
+		0, 0, 1,
+	};
+
+	p2 = Pt2(p.x, p.y, p.w);
+	if(p2.w != 0)
+		p2 = divpt2(p2, p2.w);
+	mulm(S, T);
+	p2 = xform(p2, S);
+	return p2;
+}
+
+/* requires p to be in NDC */
+int
+isclipping(Point3 p)
+{
+	if(p.x > p.w || p.x < -p.w ||
+	   p.y > p.w || p.y < -p.w ||
+	   p.z > p.w || p.z < 0)
+		return 1;
+	return 0;
+}
+
+Point
+toviewport(Camera *c, Point3 p)
+{
+	Point2 p2;
+	RFrame rf = {
+		c->viewport->r.min.x, c->viewport->r.max.y, 1,
+		1, 0, 0,
+		0, -1, 0
+	};
+
+	p2 = invrframexform(flatten(c, p), rf);
+	return Pt(p2.x, p2.y);
+}
+
+Point2
+fromviewport(Camera *c, Point p)
+{
+	RFrame rf = {
+		c->viewport->r.min.x, c->viewport->r.max.y, 1,
+		1, 0, 0,
+		0, -1, 0
+	};
+
+	return rframexform(Pt2(p.x,p.y,1), rf);
+}
+
+void
+perspective(Matrix3 m, double fov, double a, double n, double f)
+{
+	double cotan;
+
+	cotan = 1/tan(fov/2*DEG);
+	identity3(m);
+	m[0][0] =  cotan/a;
+	m[1][1] =  cotan;
+	m[2][2] = -(f+n)/(f-n);
+	m[2][3] = -2*f*n/(f-n);
+	m[3][2] = -1;
+}
+
+void
+orthographic(Matrix3 m, double l, double r, double b, double t, double n, double f)
+{
+	identity3(m);
+	m[0][0] =  2/(r - l);
+	m[1][1] =  2/(t - b);
+	m[2][2] = -2/(f - n);
+	m[0][3] = -(r + l)/(r - l);
+	m[1][3] = -(t + b)/(t - b);
+	m[2][3] = -(f + n)/(f - n);
+}
+
+void
+line3(Camera *c, Point3 p0, Point3 p1, int end0, int end1, Image *src)
+{
+	p0 = WORLD2NDC(c, p0);
+	p1 = WORLD2NDC(c, p1);
+	if(isclipping(p0) || isclipping(p1))
+		return;
+	line(c->viewport, toviewport(c, p0), toviewport(c, p1), end0, end1, 0, src, ZP);
+}
+
+Point
+string3(Camera *c, Point3 p, Image *src, Font *f, char *s)
+{
+	p = WORLD2NDC(c, p);
+	if(isclipping(p))
+		return Pt(-1,-1);
+	return string(c->viewport, toviewport(c, p), src, ZP, f, s);
+}