From f923c957a3b322ae3ee8e7a0b20df1580869bee7 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Sat, 10 Mar 2018 19:29:49 +0100 Subject: 2018-03-10 15:02:00 --- doc/context/documents/general/manuals/nodes.pdf | Bin 315323 -> 349282 bytes doc/context/documents/general/qrcs/setup-cs.pdf | Bin 844208 -> 844221 bytes doc/context/documents/general/qrcs/setup-de.pdf | Bin 844068 -> 844075 bytes doc/context/documents/general/qrcs/setup-en.pdf | Bin 848098 -> 848116 bytes doc/context/documents/general/qrcs/setup-fr.pdf | Bin 839769 -> 839780 bytes doc/context/documents/general/qrcs/setup-it.pdf | Bin 845612 -> 845621 bytes .../documents/general/qrcs/setup-mapping-cs.pdf | Bin 358993 -> 358994 bytes .../documents/general/qrcs/setup-mapping-de.pdf | Bin 438372 -> 438379 bytes .../documents/general/qrcs/setup-mapping-en.pdf | Bin 356098 -> 356101 bytes .../documents/general/qrcs/setup-mapping-fr.pdf | Bin 359081 -> 359078 bytes .../documents/general/qrcs/setup-mapping-it.pdf | Bin 357919 -> 357924 bytes .../documents/general/qrcs/setup-mapping-nl.pdf | Bin 357375 -> 357383 bytes .../documents/general/qrcs/setup-mapping-ro.pdf | Bin 624169 -> 624173 bytes doc/context/documents/general/qrcs/setup-nl.pdf | Bin 836733 -> 836725 bytes doc/context/documents/general/qrcs/setup-ro.pdf | Bin 839704 -> 839721 bytes .../sources/general/manuals/nodes/nodes.tex | 1151 +++++++++----- metapost/context/base/mpiv/mp-node.mpiv | 4 +- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 2 +- tex/context/base/mkii/mult-ro.mkii | 1 + tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/context.mkiv | 2 +- tex/context/base/mkiv/grph-epd.lua | 4 + tex/context/base/mkiv/grph-inc.lua | 9 + tex/context/base/mkiv/lpdf-epa.lua | 84 +- tex/context/base/mkiv/spac-ver.lua | 1592 ++++++++++---------- tex/context/base/mkiv/spac-ver.mkiv | 16 +- tex/context/base/mkiv/status-files.pdf | Bin 25855 -> 25856 bytes tex/context/base/mkiv/status-lua.pdf | Bin 253055 -> 252176 bytes tex/context/base/mkiv/util-sql-loggers.lua | 1 + tex/context/base/mkiv/util-sql-logins.lua | 136 +- tex/context/base/mkiv/util-sql-tickets.lua | 5 +- tex/context/interface/mkii/keys-ro.xml | 1 + tex/context/interface/mkiv/context-en.xml | 1 + tex/context/interface/mkiv/i-context.pdf | Bin 848098 -> 848116 bytes tex/context/interface/mkiv/i-readme.pdf | Bin 60776 -> 60774 bytes tex/context/interface/mkiv/i-vspace.xml | 3 +- tex/generic/context/luatex/luatex-fonts-merged.lua | 2 +- 38 files changed, 1741 insertions(+), 1277 deletions(-) diff --git a/doc/context/documents/general/manuals/nodes.pdf b/doc/context/documents/general/manuals/nodes.pdf index 343341b7a..8f0ec1b73 100644 Binary files a/doc/context/documents/general/manuals/nodes.pdf and b/doc/context/documents/general/manuals/nodes.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-cs.pdf b/doc/context/documents/general/qrcs/setup-cs.pdf index a16ac7c7f..de2869863 100644 Binary files a/doc/context/documents/general/qrcs/setup-cs.pdf and b/doc/context/documents/general/qrcs/setup-cs.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-de.pdf b/doc/context/documents/general/qrcs/setup-de.pdf index e73757bcb..9d9163c15 100644 Binary files a/doc/context/documents/general/qrcs/setup-de.pdf and b/doc/context/documents/general/qrcs/setup-de.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-en.pdf b/doc/context/documents/general/qrcs/setup-en.pdf index ac301f450..c53f0ae43 100644 Binary files a/doc/context/documents/general/qrcs/setup-en.pdf and b/doc/context/documents/general/qrcs/setup-en.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-fr.pdf b/doc/context/documents/general/qrcs/setup-fr.pdf index 49aab661d..f6ff554ee 100644 Binary files a/doc/context/documents/general/qrcs/setup-fr.pdf and b/doc/context/documents/general/qrcs/setup-fr.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-it.pdf b/doc/context/documents/general/qrcs/setup-it.pdf index 644d59f13..3c3616572 100644 Binary files a/doc/context/documents/general/qrcs/setup-it.pdf and b/doc/context/documents/general/qrcs/setup-it.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-cs.pdf b/doc/context/documents/general/qrcs/setup-mapping-cs.pdf index 846c88051..aa1ca8bfa 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-cs.pdf and b/doc/context/documents/general/qrcs/setup-mapping-cs.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-de.pdf b/doc/context/documents/general/qrcs/setup-mapping-de.pdf index c22f14ecb..63f97a235 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-de.pdf and b/doc/context/documents/general/qrcs/setup-mapping-de.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-en.pdf b/doc/context/documents/general/qrcs/setup-mapping-en.pdf index fd92bf8cb..a7cc85e52 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-en.pdf and b/doc/context/documents/general/qrcs/setup-mapping-en.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-fr.pdf b/doc/context/documents/general/qrcs/setup-mapping-fr.pdf index ae14bc0aa..42207c1f7 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-fr.pdf and b/doc/context/documents/general/qrcs/setup-mapping-fr.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-it.pdf b/doc/context/documents/general/qrcs/setup-mapping-it.pdf index ff7fb5ba1..3225e3b6f 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-it.pdf and b/doc/context/documents/general/qrcs/setup-mapping-it.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-nl.pdf b/doc/context/documents/general/qrcs/setup-mapping-nl.pdf index 1e275416d..385ded7ae 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-nl.pdf and b/doc/context/documents/general/qrcs/setup-mapping-nl.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-mapping-ro.pdf b/doc/context/documents/general/qrcs/setup-mapping-ro.pdf index 3db6a6009..5398ef863 100644 Binary files a/doc/context/documents/general/qrcs/setup-mapping-ro.pdf and b/doc/context/documents/general/qrcs/setup-mapping-ro.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-nl.pdf b/doc/context/documents/general/qrcs/setup-nl.pdf index b7cb3b39e..43d452f7e 100644 Binary files a/doc/context/documents/general/qrcs/setup-nl.pdf and b/doc/context/documents/general/qrcs/setup-nl.pdf differ diff --git a/doc/context/documents/general/qrcs/setup-ro.pdf b/doc/context/documents/general/qrcs/setup-ro.pdf index 5e0a18b0a..3a9bd0822 100644 Binary files a/doc/context/documents/general/qrcs/setup-ro.pdf and b/doc/context/documents/general/qrcs/setup-ro.pdf differ diff --git a/doc/context/sources/general/manuals/nodes/nodes.tex b/doc/context/sources/general/manuals/nodes/nodes.tex index 6875e043d..e9857f37e 100644 --- a/doc/context/sources/general/manuals/nodes/nodes.tex +++ b/doc/context/sources/general/manuals/nodes/nodes.tex @@ -20,7 +20,22 @@ % comment : This manual orginates in an article by Alan so anything wrong in here is Hans % fault as he converted it. % -% comment : The conver images are from the NASA website. +% comment : It is also being published as an article in TugBoat. +% +% comment : The cover images are from the NASA website. + + +% Alan : There is no need for % before \startfootnote .. the less such crap the better +% (as it might give disappearing content if one wraps). +% +% Alan : ḻṯ What is this? An editor glitch? +% +% Alan : $≠$ -> just use ≠ (also, soon the gyre fonts will have more symbols in text +% that match text. +% +% Alan : You can use \typ {x = 0} for something that breaks accross lines. + +%\enabletrackers[metapost.showlog] \definemeasure [layout:margin] [\paperheight/20] @@ -71,6 +86,13 @@ \setupalign [verytolerant,stretch] +% \definesymbol [1] [$\cdot$] + +% Dot rather than bullet ... Alan hates bullets! Hans hates too small dots. + +\setupitemize + [symbol=2] % dash rather than bullet, I hate bullets! + \setupnote [footnote] [next={ }, @@ -96,7 +118,7 @@ \kindofpagetextareas\plusone % partial page. HH: low level, no high level switch (yet) \definetextbackground - [fiction] + [aside] [location=paragraph, frame=off, leftoffset=1ex, @@ -106,6 +128,13 @@ background=color, backgroundcolor=lightgray] +\definedescription + [description] + [location=hanging, + width=broad, + before={\blank}, + after={\blank}] + \defineparagraphs [two] [n=2, @@ -113,7 +142,7 @@ background=color, backgroundcolor=gray] -\setupframed +\defineframed [node] [offset=1pt, foregroundstyle=\tfa] @@ -166,6 +195,16 @@ publisher = {American Physical Society}, XXurl = {http://link.aps.org/doi/10.1103/PhysRev.55.434} } + + @BOOK{Lawvere2009, + author = {Lawvere, F. William and Schanuel, Stephen H.}, + title = {Conceptual Mathematics} + subtitle = {A first introduction to categories}, + edition = {2\high{nd}}, + publisher = {Cambridge University Press}, + address = {Cambridge, UK}, + year = {2009} + } \stopbuffer \usebtxdefinitions [apa] @@ -173,27 +212,44 @@ \usebtxdataset [bib.buffer] +% Say, MP math arrows rather than font arrows: + +\useMPlibrary[mat] + +\definemathstackers + [mp] + [alternative=mp] + +\definemathextensible [mp] [leftarrow] ["2190] +\definemathextensible [mp] [rightarrow] ["2192] +\definemathextensible [mp] [leftrightarrow] ["2194] +\definemathextensible [mp] [longleftrightarrow] ["27F7] +\definemathextensible [mp] [rightoverleftarrow] ["21C4] + +\startMPinitializations + ahlength := EmWidth ; + ahangle := 30 ; + ahvariant := 1 ; % dimpled curved + ahdimple := 4/5 ; + + node_loopback_yscale := .7 ; +\stopMPinitializations + +% Only here do we use the special nodes:: instance + \defineframed [mynode] [node] [offset=1pt, foregroundcolor=white] -\startMPdefinitions - ahlength := 12pt ; - ahangle := 30 ; - ahvariant := 1 ; % dimpled -\stopMPdefinitions - -% Only here do we use the special nodes:: instance - \startreusableMPgraphic{nodes::krebs} % The Bethe cycle for energy production in stars (1939), following % Krebs (1946) save p ; path p[] ; - p1 := (for i=0 step 60 until 300: dir(90-i) .. endfor cycle) scaled 2.75cm ; + p1 := (for i=0 step 60 until 300: dir(90-i).. endfor cycle) scaled 2.75cm ; p0 := p1 scaled .5 ; p2 := p1 scaled 1.5 ; @@ -272,6 +328,8 @@ \startsetups document:start + % We each have our preferred cover... ;-) + \doifmodeelse {atpragma} { \startMPpage[imagename=nodes-sun-pia-03150] \includeMPgraphic{CoverPage} @@ -281,8 +339,15 @@ \includeMPgraphic{CoverPage} \stopMPpage } + \stopsetups +\startsetups document:stop + +\stopsetups + +% And now, the document! + \startdocument [title=Nodes, author=Alan Braslau, @@ -291,40 +356,86 @@ \startsubject [title=Introduction] -The graphical representation of textual diagrams is an extremely useful tool in -the communication of idea. These are composed of graphical objects and blocks of -text or the combination of both, i.e. a decorated label or text block, in some -spatial relation to one another. \footnote {The spatial relation may be a -representation of a temporal relationship.} A simple example is shown \in {in -figure} [fig:AB]. - -\startplacefigure [reference=fig:AB] - \startMPcode - u := 1cm ; - save nodepath ; path nodepath ; - nodepath := (left -- right) scaled u ; - - draw node(0,"\node{A}") ; - draw node(1,"\node{B}") ; - drawarrow fromto(0,0,1) ; - \stopMPcode -\stopplacefigure - -One often speaks of placing text with their accompanying decorations on \emph -{nodes}, points of intersection or branching or else points on a regular lattice. -The nodes of the above diagram are the two endpoints of a straight segment. +The graphical representation of textual diagrams is a very useful tool in the +communication of ideas. In category and topos theory, for example, many key +concepts, formulas, and theorems are expressed by means of \emph {commutative +diagrams}; these involve objects and arrows between them. Certain concepts +discovered by category theory, such as \emph {natural transformations}, are +becoming useful in areas outside of mathematics and natural science, e.g., in +philosophy. To make category and topos methods usable by both specialists and +non|-|specialists, commutative diagrams are an indispensable tool. +\startfootnote + For many examples of formal and informal commutative diagrams, see \cite + [authoryears] [Lawvere2009]. +\stopfootnote +The use of nodal diagrams is not limited to category theory, for they may +represent a flow diagram (of a process, for example), a chemical reaction +sequence or pathways, or that of phases and phase transitions, a hierarchical +structure (of anything), a timeline or sequence of events or dependencies, a tree +of descendance or ascendance, etc. + +The basic units of a node|-|based diagram include \emph {node objects}, each +attached to some point (= the \emph {node}) in some spatial relationship. Note +that to a single node might be associated a set of objects. Given a node, it also +stands in a spatial relation to some other node. The spatial relationship between +the set of nodes of a diagram need not be in a regular network, although they +quite often are. Note that the spatial relationship between nodes is graphical +and may represent, e.g., a temporal or logical relationship, or a transformation +of one object into another or into others (one interesting example might be that +representing cell division or, mitosis). + +Given a spatial relation between any two nodes, a node|-|based diagram often +includes some \emph {path segment} or segments (such as arrows or other curves) +between two given nodes that \emph {relates} them. Each path segment may be +augmented by some textual or graphical label. + +A very simple example of a node diagram is shown in \in{Figure} [fig:AB]. \startbuffer \startMPcode - save nodepath ; path nodepath ; - nodepath := (left -- right) scaled .75u ; + clearnodepath ; + nodepath = (left -- right) scaled .75cm ; draw node(0,"A") ; draw node(1,"B") ; drawarrow fromto(0,0,1) ; \stopMPcode \stopbuffer -The code producing \inlinebuffer\ \emph {could} be: +\startplacefigure [reference=fig:AB] + \getbuffer +\stopplacefigure + +\startplacefigure [reference=fig:ID, + location={right,+2*hang}] +\startMPcode + clearnodepath ; nodepath = origin ; + draw node(0,"$O$") ; + drawarrow fromto.urt (.75cm,0,0) ; + setbounds currentpicture to boundingbox currentpicture + enlarged (1.2cm,0) ; +\stopMPcode +\stopplacefigure + +More precisely, a \emph {node} is a point of intersection or branching of paths, +often a point on a regular lattice. (The nodes of the above diagram are the two +endpoints of a straight line segment.) Sometimes, however, a node might be a +single point as in an \emph {identity map} of category theory, referring to +itself: +\startfootnote + The standard arrowhead in \METAPOST\ is a simple triangle, whose length and + angle can be adjusted. \METAFUN\ provides further options, allowing this + arrowhead to be barbed or dimpled. In the present article, we use the + settings: \type {ahlength := EmWidth ; ahangle := 30 ; ahvariant := 1 ; + ahdimple := 4/5 ;} The loop|-|back arrow paths used here deviate from a + circular segment, becoming ellipsoidal, through the value \type + {node_loopback_yscale := .7 ;} These are all set, of course, between a \type + {\startMPinitializations} … \type {\stopMPinitializations} pair. +\stopfootnote + +In this article we discuss a new \METAPOST\ module designed for handling +node|-|based graphics as well as a derivative simple \CONTEXT\ interface. To +illustrate, the code producing \inlinebuffer\ {could} be, in \METAPOST\ and the +\CONTEXT\ interface respectively: \starttwo \METAPOST @@ -337,71 +448,89 @@ The code producing \inlinebuffer\ \emph {could} be: \stopTEX \two \CONTEXT - \startTEX - \startnodes - \node[0]{A} - \node[1]{B} - \fromto [0,1] [option=arrow] - \stopnodes - \stopTEX + \startbuffer + \startnodes [dx=1.5cm] + \placenode [0,0] {A} + \placenode [1,0] {B} + \connectnodes [0,1] + [alternative=arrow] + \stopnodes + \stopbuffer + \typebuffer [option=TEX] \stoptwo -drawing an arrow from A to B (or from node 0 to node 1). +drawing an arrow from A to B (or from node 0 to node 1): \getbuffer \startitemize[packed] \startitem - The \METAPOST\ code shown above has been simplified, as will be seen - later. + The \METAPOST\ code shown above has been slightly simplified, as will be + seen later. \stopitem \startitem - The \CONTEXT\ module has yet to be coded (and may not be useful). + The \CONTEXT\ interface as used here is limited and will be explained a + little later. \stopitem \stopitemize +For beginners or casual users of \CONTEXT\ |<|including those who might be +intimidated by \METAPOST\ syntax|>| the ability to construct simple diagrams by +means of standard \CONTEXT\ syntax is very helpful. For those who have tried the +\CONTEXT\ interface and/or want to draw more advanced diagrams, the \METAPOST\ +module is much more powerful and flexible. + \stopsubject \startsubject [title=\METAPOST] -\METAPOST\ is an inherently vectorial graphical language using \TEX\ to typeset -text; the \METAPOST\ language is integrated natively as MPlib in \CONTEXT. This -presents advantages over the use of an external graphical objects in providing a -great coherence of style along with great flexibility without bloat. \METAPOST\ -has further advantages over many other graphical subsystems, namely high -precision, high quality and the possibility to solve equations. This last point -is little used but should not be overlooked. - -It is quite natural in \METAPOST\ to locate these text nodes along a path or on -differing paths. This is a much more powerful concept than locating nodes at some -pair of coordinates, on a square or rectangular lattice, for example, as in a -table. These paths may be in three dimensions (or more); of course the printed -page will only be some projection onto two dimensions. Nor must the nodes be -located on the points defining a path: they may have as index any \emph {time} -along the path \type {p} ranging from the first defining point ($t = 0$) up to -the last point of a path ($t ≤ \mathtt{length(p)}$). \footnote {The time of a -cyclic path is taken modulo the length of the path.} - -For a given path \type {p}, nodes are defined (implicitly) as \type {picture} -elements: \typ {picture p.pic[] ;} This is a pseudo|-|array where the square -brackets indicates a set of numerical tokens, as in \type {p.pic[0]} but also -\type {p.pic0}. This number need not be an integer, and \type {p.pic[.5]} or -\type {p.pic.5} (not to be confused with \type {p.pic5}) are also valid. These -picture elements are taken to be located relative to the path \type {p}, with the -index \type {t} corresponding to a time along the path, as in \typ {draw p.pic[t] -shifted point t of p ;} (although one would not necessarily draw them in this -way). This convention allows the \quote {nodes} to be oriented and offset with -respect to the path in an arbitrary fashion. - -Note that a path can be defined and then nodes placed relative to this path, or -else the path may be simply declared yet remain undefined, to be determined -later, after the nodes are declared. Yet another possibility allows for the path -to be adjusted as needed, as a function of whatever nodes are to be occupied. -This will be illustrated through examples later. +\METAPOST\ is a vector|-|graphics language which calls upon \TeX\ to typeset text +(such as labels); in \CONTEXT, furthermore, \METAPOST\ is integrated natively +through the library MPlib as well as the macro package \METAFUN. The tight +integration of \CONTEXT\ and \METAPOST\ provides advantages over the use of +other, external graphics engines. These advantages include ease of maintaining +coherence of style, as well as extensive flexibility without bloat. \METAPOST\ +has further advantages over most other graphics engines, including a very high +degree of precision as well as the possibility to solve certain types of +algebraic equations. This last feature is rarely used but should not be +overlooked. + +It is quite natural in \METAPOST\ to locate our node objects along a path or on +differing paths. This is a much more powerful concept than merely locating a node +at some pair of coordinates, e.g., on a square or a rectangular lattice, for +example (as in a table). Furthermore, these paths may be in three dimensions (or +more); of course the printed page will only involve some projection onto two +dimensions. Nor are the nodes restricted to location on the points defining a +path: they may have, as index, any \emph {time} along a given path \type {p} +ranging from the first defining point ($t = 0$) up to the last point of that path +($t ≤ \mathtt {length(p)}$), the number of defining points of a path. +\startfootnote + Note that the time of a cyclic path is taken modulo the length of the path, + that is $t$ outside of the range $[\mathtt0,\mathtt{length(p)}]$ will return + the first or the last point of an open path, but will \quotation {wrap} for a + closed path. +\stopfootnote + +Given a path \type {p}, nodes are defined (implicitly) as \type {picture} +elements: \type {picture p.pic[] ;} This is a pseudo|-|array where the square +brackets indicates a set of numerical tokens, as in \type {p.pic[0]} or \type +{p.pic[i]} (for \type {i=0}), but also \type {p.pic0}. This number need not be an +integer, and \type {p.pic[.5]} or \type {p.pic.5} (not to be confused with \type +{p.pic5}) are also valid. These picture elements are taken to be located relative +to the path \type {p}, with the index \type {t} corresponding to a time along the +path, as in \type {draw p.pic[t] shifted point t of p;} (although it is not +necessary to draw them in this way). This convention allows the nodes to be +oriented and offset with respect to the path in an arbitrary manner. + +Note that a path can be defined, then nodes placed relative to this path. Or the +path may be declared but remain undefined, to be determined only after the nodes +are declared. In yet another possibility, the path may be adjusted as needed, as +a function of whatever nodes are to be occupied. This will be illustrated through +examples further down. \stopsubject \startsubject [title=A few simple examples] -\startplacefigure [location=left,reference=fig:square] +\startplacefigure [location=right,reference=fig:square] \startMPcode path p ; p := fullsquare scaled 3cm ; draw p ; @@ -412,17 +541,18 @@ This will be illustrated through examples later. endfor ; % this looks better in the figure placement: setbounds currentpicture to boundingbox currentpicture - rightenlarged 2mm ; + enlarged (.5cm,0) ; \stopMPcode \stopplacefigure -This might sound a bit arbitrary, so let's begin with the illustration of a -typical natural transformation of mathematics. A simple path and the points -defining this path are drawn in red here. Although it is trivial, this example -helps to introduce the \METAPOST\ syntax. (Of course, one should be able to use -this system without having to learn much \METAPOST.) +Let's begin with illustration of a typical commutative diagram from category +theory. Although it may appear trivial, this example helps to introduce +\METAPOST\ syntax. At the same time, a large part of the idea behind this module +is to facilitate use of this system without having to learn much \METAPOST. -\flushsidefloats +A path is drawn as well as the points defining the path. + +%\flushsidefloats \startTEX \startMPcode @@ -437,8 +567,8 @@ this system without having to learn much \METAPOST.) \startbuffer \startMPcode - save nodepath ; - path nodepath ; nodepath = p ; + clearnodepath ; + nodepath = p ; draw node(0,"\node{$G(X)$}") ; draw node(1,"\node{$G(Y)$}") ; draw node(2,"\node{$F(Y)$}") ; @@ -450,42 +580,51 @@ this system without having to learn much \METAPOST.) \stopMPcode \stopbuffer -\startplacefigure [location=left,reference=fig:natural] +\startplacefigure [location={right,+1*hang},reference=fig:natural, + title={Drawn using \METAPOST\ interface}] \getbuffer \stopplacefigure -Given the named path \type {nodepath}, we can define and draw nodes as well as -connections between them. +Given the named path \type {nodepath}, we can now define and draw nodes as well +as connections between them (see \in{Figure} [fig:natural]): -\flushsidefloats +%\flushsidefloats \typebuffer [option=TEX] -\startfiction -It is an important good practice with \METAPOST\ to reset or clear a variable -using the directive \type {save} as above for the suffix \type {nodepath}. The -macros used here rely on the creation of certain internal variables and may not -function correctly if the variable structure is not cleared. Indeed, any node may -contain a combination of picture elements, added successively, so it is very -important to \type {save} the variable, making its use local, rather than global. -This point is particularly true under \CONTEXT, where a single MPlib instance is -used and maintained over multiple runs. - -The \CONTEXT\ directives \type {\startMPcode} \unknown\ \type {\stopMPcode} -include grouping (\METAPOST\ \type {begingroup ;} \unknown\ \type {endgroup ;}) -and the use of \type {save} will make the suffix local to this code block. In the -examples above of \in{Figures} [fig:square] and \in [fig:natural], the path \type -{p} is not declared local (through the use of a \type {save}) therefore remains -available for other \METAPOST\ code blocks. We cannot do this with the default -suffix name \type {nodepath} without getting other unwanted consequences. -\stopfiction +\startaside +In working with \METAPOST, it is good practice to reset or clear a variable using +the directive \type {save} for the \emph {suffix} (or variable name) \type +{nodepath} contained in the directive \type {clearnodepath} (defined as +\quotation {\type {save nodepath ; path nodepath}}). The macros used here rely on +the creation of certain internal variables and may not function correctly if the +variable structure is not cleared. Indeed, any node may contain a combination of +picture elements, added successively, so it is very important to \type {save} the +variable, making its use local, rather than global. This point is particularly +true with \CONTEXT, where a single MPlib instance is used and maintained over +multiple runs. + +The \CONTEXT\ directives \type {\startMPcode} … \type {\stopMPcode} include +grouping (\METAPOST\ \type {begingroup ;}… \type {endgroup ;}) and the use of +\type {save} (in \type {clearnodepath}) will make the suffix \type {nodepath} +local to this code block. In the code for \in{Figures} [fig:square] and \in +[fig:natural], the path \type {p} itself is not declared local (through the use +of a \type {save}); it therefore remains available for other \METAPOST\ code +blocks. We cannot do this with the default suffix name \type {nodepath} without +undesired consequences. + +The directive \type {clearnodepath} used in the example above, much like the +\METAPOST\ command \type {clearxy} clearing the \type {(x,y)} pair also known as +\type {z}, uses \type {save} to clear the default suffix \type {nodepath}. +\stopaside Note that one should not confuse the \METAPOST\ function \type {node()} with the -\CONTEXT\ command \type {\node{}}, defined as: +\CONTEXT\ command \type {\node{}}, defined as follows: \starttwo \startTEX - \setupframed + \defineframed + [node] [frame=off, offset=1pt] \stopTEX @@ -498,51 +637,127 @@ Note that one should not confuse the \METAPOST\ function \type {node()} with the \stopTEX \stoptwo -placing the text within a \CONTEXT\ frame (with the frame border turned|-|off). -The \METAPOST\ function \type {node(i,"...")} sets and returns a picture element -associated with a point on path \type {nodepath} indexed by its first argument. -The second argument here is a string that gets typeset by \TEX. +\type {\node{}} places the text within a \CONTEXT\ frame (with the frame border +turned|-|off), whereas the \METAPOST\ function \type {node(i,"…")} sets and +returns a picture element associated with a point on path \type {nodepath} +indexed by its first argument. The second argument here is a string that gets +typeset by \TEX. (The use of \type {\node{}} adds an \type {offset}). -The \METAPOST\ function \type {fromto()} returns a path segment going from two -points of the path \type {nodepath}, by default. The first argument (\type {0} in +By default, the \METAPOST\ function \type {fromto()} returns a path segment going +between two points of the path \type {nodepath}. The first argument (\type {0} in the example above) can be used as a displacement to skew the path away from a -straight line. The last argument is a string to be typeset and placed midpoint of -the segment. The \emph {suffix} appended to the function name gives an offset -around this halfway point. This follows standard \METAPOST\ convention. +straight line (by an amount in units of the straight path length). The last +argument is a string to be typeset and placed midpoint of the segment. The +{suffix} appended to the function name gives an offset around this halfway point. +This follows standard \METAPOST\ conventions. + +It is important to draw or declare the nodes \emph {before} drawing the +connections, using \type {fromto()}, in order to be able to avoid overlapping +symbols, as one notices that the arrows drawn in the example above begin and end +on the border of the frame (or bounding box) surrounding the node text. This +would of course not be possible if the arrow were to be drawn before this text +was known. + +As will be seen further on, one can actually specify the use of any defined path, +without restriction to the built|-|in name \type {nodepath} that is used by +default. Furthermore, a function \type {fromtopaths()} can be used to draw +segments connecting any two paths which may be distinct. This too will be +illustrated further on. + +The \CONTEXT\ syntax for the current example looks like this: -\startfiction - The \CONTEXT\ syntax might be: - \startTEX - \startuseMPgraphic{node:square} - save nodepath ; path nodepath ; - nodepath := fullsquare scaled 6cm ; - \stopuseMPgraphic - - \setupfromto [option=arrow,position=auto] - - \startnodes [mp=node:square] - \node [0] {$G(X)$} - \node [1] {$G(Y)$} - \node [2] {$F(Y)$} - \node [3] {$F(X)$} - \fromto [0,1] [label={\tfxx $G(f)$}] - \fromto [3,2] [label={\tfxx $F(f)$}] - \fromto [2,1] [label={\tfxx $η_Y$}} - \fromto [3,0] [label={\tfxx $η_X$}] - \stopnodes - \stopTEX -\stopfiction +\startbuffer + \startnodes [dx=3cm,dy=3cm] + \placenode [0,0] {\node{$G(X)$}} + \placenode [1,0] {\node{$G(Y)$}} + \placenode [1,1] {\node{$F(Y)$}} + \placenode [0,1] {\node{$F(X)$}} + \connectnodes [0,1] [alternative=arrow, + label={\nodeSmall{$G(f)$}},position=bottom] + \connectnodes [3,2] [alternative=arrow, + label={\nodeSmall{$F(f)$}},position=top] + \connectnodes [2,1] [alternative=arrow, + label={\nodeSmall{$η_Y$}}, position=right] + \connectnodes [3,0] [alternative=arrow, + label={\nodeSmall{$η_X$}}, position=left] + \stopnodes +\stopbuffer + +\startplacefigure [location=right, + title={Drawn using \CONTEXT\ interface}] + \getbuffer +\stopplacefigure + +\typebuffer [option=TEX] + +\startplacefigure [reference=fig:indices, + location=right, + title={Coordinates and indices.\footnote{For variety, a rectangular oblique + lattice is drawn in \in{Figure} [fig:indices].} + }] +\stopfootnote + \startframed [frame=off,width=.33\textwidth,align=flushright] + \startnodes [dx=3cm,dy=2cm,rotation=60] + \placenode [0,0] {\node{(0,0)}} + \placenode [1,0] {\node{(1,0)}} + \placenode [1,1] {\node{(1,1)}} + \placenode [0,1] {\node{(0,1)}} + \connectnodes [0,1] [alternative=arrow, + label={\nodeSmall{$0\rightarrow1$}},position=bottom] + \connectnodes [3,2] [alternative=arrow, + label={\nodeSmall{$3\rightarrow2$}},position=top] + \connectnodes [2,1] [alternative=arrow, + label={\nodeSmall{$~2\rightarrow1$}}, position=right] + \connectnodes [3,0] [alternative=arrow, + label={\nodeSmall{$3\rightarrow0$}}, position=upperleft] + \stopnodes + \stopframed +\stopplacefigure + +This follows the more classic (and limited) approach of placing nodes on the +coordinates of a regular lattice, here defined as a 3~cm square network. +\footnote {The lattice can be square (\type {dx} $=$ \type {dy}), rectangular +(\type {dx} $≠$ \type {dy}), or oblique (through \type {rotation} $≠$ 90).} The +arguments are then $(x,y)$ coordinates of this lattice and the nodes are indexed +0, 1, 2, … in the order that they are drawn. These are used as reference indices +in the commands \type {\connectnodes} (rather than requiring two \emph {pairs} of +coordinates); see \in {Figure} [fig:indices]. This might seem a bit confusing at +first view, but it simplifies things in the end, really! + +An identity map, as shown in \in {Figure} [fig:ID], earlier, and, below, in \in +{Figure} [fig:Me] is achieved by connecting a node to itself: -Actually, as will be seen later, one can specify the use of any defined path, not -restricted to the built||in name \type {nodepath} that is used by default. -Furthermore, a function \type {fromtopaths()} can be used to draw segments -connecting any two paths which may be distinct. This, too, will be illustrated -later. +\startbuffer +\startnodes [dx=2cm,dy=1cm] + \placenode [0,0] {\node{Me}} + \placenode [1,-1] {\node{You}} + \connectnodes [0,0] [alternative=arrow, + offset=.75cm,position=topright, + label=myself] + \connectnodes [1,1] [alternative=arrow, + offset=.75cm,position=bottomright, + label=yourself] +\stopnodes +\stopbuffer + +\startplacefigure [reference=fig:Me,title=Identity maps, + location=right] + \getbuffer +\stopplacefigure + +\typebuffer [option=TEX] + +The scale (diameter) of the circular loop|-|back is set by the keyword \type +{offset=} (normally used to curve or bow|-|away a path connecting nodes from the +straight|-|line segment between them), and the \type {position=} keyword sets its +orientation. + +\page [yes] \startbuffer \startMPcode -save nodepath ; path nodepath ; -nodepath := fullsquare scaled 2cm ; +clearnodepath ; +nodepath = fullsquare scaled 2cm ; save A ; A = 3 ; draw node(A,"\node{A}") ; save B ; B = 2 ; draw node(B,"\node{B}") ; save C ; C = 0 ; draw node(C,"\node{C}") ; @@ -553,20 +768,29 @@ drawarrow fromto(0,A,D) crossingunder fromto(0,B,C) ; \stopMPcode \stopbuffer -\startplacefigure [location=left,reference=fig:crossingunder] - \getbuffer +\startplacefigure [reference=fig:crossingunder, + location={right,+3*hang}, + title=A$\rightarrow$D under B$\rightarrow$C] + \startframed [frame=off,width=5cm,align=middle] + \getbuffer + \stopframed \stopplacefigure -Consider now the following code illustrating the \METAFUN\ operator \type -{crossingunder} that draws a path with segments cut|-|out surrounding the -intersection(s) with a second path segment. This operator is of such general use -that it has been added to the \METAFUN\ base. - -\flushsidefloats +Let us now consider the following code which illustrates the \METAFUN\ operator +\type {crossingunder} +\startfootnote + The operator \type {crossingunder} is of such general use that it has been + added to the \METAFUN\ base. +\stopfootnote +(see \in {Figure}[fig:crossingunder]). The \type {nodepath} indices are put into +variables \type {A}, \type {B}, \type {C}, and \type {D}, thus simplifying the +code. \typebuffer [option=TEX] -\startplacefigure [reference=fig:sincos,location=left,title={\type{crossingunder}}] +\startplacefigure [reference=fig:sincos, + location=right, + title={\type{crossingunder}}] \startMPcode save u ; u := 2cm ; save p ; path p[] ; @@ -586,82 +810,78 @@ that it has been added to the \METAFUN\ base. Another illustration of the \type {crossingunder} operator in use is shown in \in {figure} [fig:sincos]. Because the diagrams are all defined and drawn in -\METAPOST, one can easily extend the simple mode drawing with any sort of -graphical decoration using all the power of the \METAPOST\ language. - -This brings up a delicate point that has inhibited the development of a \CONTEXT\ -module up to now: \METAPOST\ is such a powerful language that it is difficult to -design a set of commands having equivalent flexibility. Perhaps, as is done -presently with the Chemistry package, one need only allow for the injection of -arbitrary \METAPOST\ code when needed for such special cases. - -Should it be done like this? - -\startfiction -\startTEX -\startnodes [mp=node:square] - \node [3] {A} - \node [2] {B} - \node [0] {C} - \node [1] {D} - \fromto [2,0] - \fromto [3,1] [option={under[2,0]}] % ? -\stopnodes -\stopTEX -\stopfiction - -Such a simple example illustrates why we favor a pure \METAPOST\ interface and -why a \CONTEXT\ interface will not be developed, at least for now. +\METAPOST, one can easily use the power of \METAPOST\ to extend a simple node +drawing with any kind of graphical decoration. + +This brings up an important point that has limited the development of a +full|-|featured \CONTEXT\ module up to now. A pure \METAPOST\ interface affords +much more flexibility than can be conveniently reduced to a set of \TeX\ macros; +the \CONTEXT\ interface has been written to maintain only basic functionality. +\startfootnote + One can use \type {\nodeMPcode{}} to inject arbitrary \METAPOST\ code within + a \type {\startnode} … \type {\stopnode} pair, although in this example one + is probably better off using the straight \METAPOST\ interface. +\stopfootnote \stopsubject \startsubject [title=Cyclic diagrams] -In a slightly more complicated example, that of a catalytic process given in the -\cite[authoryears] [Krebs1946] representation, where the input is indicated -coming into the cycle from the center of a circle and the products of the cycle -are spun|-|off from the outside of the circle, we start by defining a circular -path where each point corresponds to a step in the cyclic process. Our example -will use six steps. +For a somewhat more complicated example, let us consider the representation of a +catalytic process such as that given by \cite [author] [Krebs1946]. \cite +[Krebs1946] The input is shown coming into the cycle from the center of a circle; +the products of the cycle are spun|-|off from the outside of the circle. We start +by defining a circular path where each point corresponds to a step in the cyclic +process. Our example will use six steps (see \in {Figure} [fig:circles]). -We will want to define a second circular path with the same number of points at -the interior of this first circle for the input and a third circular path at the -exterior for the output. Thus, +We also want to define a second circular path with the same number of points at +the interior of this first circle for the input, and a third circular path at the +exterior for the output. -\startTEX +The code is as follows: + +\page [yes] + +\startbuffer \startMPcode save p ; path p[] ; % define a fullcircle path with nodes at 60° (rather than 45°) - p1 := (for i=0 step 60 until 300: dir(90-i) .. endfor cycle) scaled 2.75cm ; + p1 := (for i=0 step 60 until 300: dir(90-i) .. endfor cycle) scaled 2.5cm ; p0 := p1 scaled .5 ; p2 := p1 scaled 1.5 ; -\stopMPcode -\stopTEX -\startplacefigure [location=left,title={The paths that we will use for anchoring for nodes.}] -\startMPcode - save p ; path p[] ; - p1 := (for i=0 step 60 until 300: dir(90-i) .. endfor cycle) scaled 2.75cm ; - p0 := p1 scaled .5 ; - p2 := p1 scaled 1.5 ; - for i=0 upto 2: - draw p[i] ; - for j=0 upto length p[i]: - draw point j of p[i] withpen currentpen scaled 5 withcolor red ; - endfor - label.bot("p" & decimal i, point 0 of p[i]) ; + for i=0 upto 2: + draw p[i] ; + label.bot("\bf p" & decimal i, point 0 of p[i]) ; + for j=1 upto length p[i]: + draw point j of p[i] withpen currentpen scaled 10 withcolor red ; + if i=1: + label.autoalign(angle point j of p[i]) (decimal j, point j of p[i]) + withcolor red ; + fi endfor + endfor \stopMPcode +\stopbuffer + +\typebuffer [option=TEX] + +\startplacefigure [reference=fig:circles, + location=right, + title={The paths that we will use for the anchoring of nodes.}] + \getbuffer \stopplacefigure +[\type {autoalign()} is a feature defined within \METAFUN.] + Nodes will then be drawn on each of these three circles and arrows will be used to connect these various nodes, either on the same path or else between paths. -The \METAPOST\ function \type {fromto()} is used to give a segment pointing -between nodes. It \emph {assumes} the path \type {nodepath}, and in fact calls -the function \type {fromtopaths} that explicitly takes path names as arguments, -i.e. \type {fromto (d, i, j, ...)} is equivalent to \type {fromtopaths (d, -nodepath, i, nodepath, j, ...)}. +The \METAPOST\ function \type {fromto()} is used to give a path segment that +points from one node to another. It \emph {assumes} the path \type {nodepath}, +and in fact calls the function \type {fromtopaths} that explicitly takes path +names as arguments. That is, \type {fromto (d, i, j, …)} is equivalent to \type +{fromtopaths (d, nodepath, i, nodepath, j, …)}. As stated above, this segment can be a straight line or else a path that can be bowed|-|away from this straight line by a transverse displacement given by the @@ -672,12 +892,42 @@ behavior is obtained by using any non|-|numeric value (such as \type {true}) in place of the first argument. Of course, this cannot work if the two nodes are not located on the same path. +The circular arc segments labeled \emph {\darkgreen a–f} are drawn on \in +{figure} [fig:Bethe] using the following: + +\startTEX +drawarrow fromtopaths.urt (true,p1,0,p1,1,"\nodeGreen{a}") ; +\stopTEX + +for example, where \type {\nodeGreen} is a frame that inherits from \type +{\node}, changing style and color: + +\startTEX +\defineframed + [nodeGreen] + [node] + [foregroundcolor=darkgreen, + foregroundstyle=italic] +\stopTEX + +The bowed|-|arrows feeding into the cyclic process and leading out to the +products, thus between different paths, from the path \type {p0} to the path +\type {p1} and from the path \type {p1} to the path \type {p2}, respectively, are +drawn using the deviations \type {+3/10} and \type {-1/10} (to and from +half|-|integer indices, thus mid|-|step, on path \type {p1}): + +\startTEX +drawarrow fromtopaths( 3/10,p0,0,p1,0.5) withcolor .6white ; +drawarrow fromtopaths(-1/10,p1,0.5,p2,1) withcolor .6white ; +\stopTEX + \startplacefigure [reference=fig:Bethe, title={The \cite[author] [Bethe1939a] cycle for the energy production in stars \cite[alternative=year,left=(,right=)] [Bethe1939a+Bethe1939b] in a - \cite[authoryears] [Krebs1946] representation of a catalytic process.}] + \cite[author] [Krebs1946] representation of a catalytic process + \cite[alternative=year,left=(,right=)] [Krebs1946].}] \startMPcode - + % differs slightly from \reuseMPgraphic{nodes::krebs} % The Bethe cycle for energy production in stars (1939), following Krebs (1946) @@ -730,62 +980,103 @@ located on the same path. \stopMPcode \stopplacefigure -The circular arc segments labeled \emph {\darkgreen a–f} are drawn on \in -{figure} [fig:Bethe] using +\startsubsubject [title={A lesson in \METAPOST}] -\startTEX -drawarrow fromtopaths.urt (true,p1,0,p1,1,"\nodeGreen{a}") ; -\stopTEX +An \quote {array} of paths is declared through \type {path p[] ;} it is not a +formal array, but rather a syntactic definition of a collection of path variables +\type {p0}, \type {p1}, … each of whose name is prefixed with the tag \quotation +{p} followed by any number, not necessarily an integer (e.g., \type {p3.14} is a +valid path name). The syntax allows enclosing this \quotation {index} within +square brackets, as in \type {p[0]} or, more typically, \type {p[i]}, where \type +{i} would be a numeric variable or the index of a loop. Note that the use of +brackets is required when using a negative index, as in \type {p[-1]} (for \type +{p-1} is interpreted as three tokens, representing a subtraction). Furthermore, +the variable \type {p} itself, would here be a numeric (by default), so \type +{p[p]} would be a valid syntactic construction! One could, additionally, declare +a set of variables \type {path p[][] ;} and so forth, defining also \type +{p[0][0]} (equivalently, \type {p0 0}) for example as a valid path, coexisting +with yet different from the path \type {p0}. + +\METAPOST\ also admits variable names reminiscent of a structure: \type {picture +p.pic[] ;} for example is used internally in the \type {node} macros, but this +becomes \type {picture p[]pic[] ;} when using a path \quote {array} syntax. These +variable names are associated with the suffix \type {p} and become all undefined +by \type {save p ;}. -for example, where \type {\nodeGreen} is a frame that inherits from \type -{\node}, changing style and color: +\stopsubsubject -\startTEX -\defineframed - [nodeGreen] - [node] - [foregroundcolor=darkgreen, - foregroundstyle=italic] -\stopTEX -\stopfootnote +\startsubsubject [title={Putting it together}] -The bowed|-|arrows feeding into the cyclic process and leading out to the -products, thus between different paths, from the path \type {p0} to the path -\type {p1} and from the path \type {p1} to the path \type {p2}, respectively, are -drawn using the deviations \type {+3/10} and \type {-1/10} (to and from -half|-|integer indices, thus mid|-|step, on path \type {p1}): +What follows is simple example of a natural transformation, discovered and +articulated in the course of a philosophical research project (by Idris Samawi +Hamid). \in {Figure} [fig:NT] represents what is called the Croce Topos [named +after the Italian philosopher Benedetto Croce (1866–1952)]: -\startTEX -drawarrow fromtopaths( 3/10,p0,0,p1,0.5) withcolor .6white ; -drawarrow fromtopaths(-1/10,p1,0.5,p2,1) withcolor .6white ; -\stopTEX +\startbuffer +\startMPcode + save nodepath ; + path nodepath ; + nodepath = ((0,0) -- (1,0) -- (3,0) -- + (3,1) -- (1,1) -- (0,1) -- + cycle) scaled 4cm ; + draw node(0,"\node{Practical}") ; + draw node(1,"\node{Economic}") ; + draw node(2,"\node{Moral}") ; + draw node(3,"\node{Conceptual}") ; + draw node(4,"\node{Aesthetic}") ; + draw node(5,"\node{Theoretical}") ; + + drawarrow fromto.rt (.1,5,0,"\node{$γ$}") ; + drawarrow fromto.lft(.1,0,5,"\node{$γ'$}") ; + drawarrow fromto.rt (.1,4,1,"\node{$Fγ$}") ; + drawarrow fromto.lft(.1,1,4,"\node{$Fγ'$}") ; + drawarrow fromto.rt (.1,3,2,"\node{$Gγ$}") ; + drawarrow fromto.lft(.1,2,3,"\node{$Gγ'$}") ; + + drawarrow fromto.top( 0,4,3,"\node{\it concretization$_1$}") ; + drawarrow fromto.bot(.1,3,4,"\node{\it abstraction$_1$}") + dashed evenly ; + drawarrow fromto.top( 0,1,2,"\node{\it concretization$_2$}") ; + drawarrow fromto.bot(.1,2,1,"\node{\it abstraction$_2$}") + dashed evenly ; +\stopMPcode +\stopbuffer -\startsubsubject [title={A lesson in \METAPOST}] +% Let's forget that and rather use \startnodes... -An \quote {array} of paths is declared through \typ {path p[] ;} it is not a -formal array, rather a syntactic definition of a collection of path variables -\type {p0}, \type {p1}, \unknown\ named beginning with the tag \quote {p} -followed by any number, not necessarily an integer (i.e. \type {p3.14} is a valid -path name). The syntax allows enclosing this \quote {index} within square -brackets, as in \type {p[0]} or, more typically, \type {p[i]}, where \type {i} -would be a numeric variable or the index of a loop. Note that the use of brackets -is required when using a negative index, as in \type {p[-1]} (for \type {p-1} is -interpreted as three tokens, representing a subtraction). Furthermore, the -variable \type {p} itself, would here be a numeric (by default), so \type {p[p]} -would be a valid syntactic construction! One could, additionally, declare a set -of variables \typ {path p[][] ;} and so forth, defining also \type {p[0][0]} -(equivalently, \type {p0 0}) for example as a valid path, coexisting with yet -different from the path \type {p0}. - -\METAPOST\ also admits variable names reminiscent of a structure: \typ {picture -p.pic[] ;} for example is used internally in the \type {node} macros, but this -becomes \typ {picture p[]pic[] ;} when using a path \quote {array} syntax. These -variable names are associated with the suffix \type {p} and become all undefined -by \typ {save p ;}. +\startbuffer +\startnodes [dx=4cm,dy=4cm,alternative=arrow] + \placenode [0,0] {\node{Practical}} + \placenode [1,0] {\node{Economic}} + \placenode [3,0] {\node{Moral}} + \placenode [3,1] {\node{Conceptual}} + \placenode [1,1] {\node{Aesthetic}} + \placenode [0,1] {\node{Theoretical}} + + \connectnodes [5,0] [offset=.1,position=right, label={\node{$γ$}}] + \connectnodes [0,5] [offset=.1,position=left, label={\node{$γ'$}}] + \connectnodes [4,1] [offset=.1,position=right, label={\node{$Fγ$}}] + \connectnodes [1,4] [offset=.1,position=left, label={\node{$Fγ'$}}] + \connectnodes [3,2] [offset=.1,position=right, label={\node{$Gγ$}}] + \connectnodes [2,3] [offset=.1,position=left, label={\node{$Gγ'$}}] + + \connectnodes [4,3] [position=top, label={\node{\it concretization$_1$}}] + \connectnodes [3,4] [postition=bottom,offset=.1, option=dashed, + label={\node{\it abstraction$_1$}}] + \connectnodes [1,2] [position=top, label={\node{\it concretization$_2$}}] + \connectnodes [2,1] [position=bottom,offset=.1, option=dashed, + label={\node{\it abstraction$_2$}}] +\stopnodes +\stopbuffer + +\startplacefigure [reference=fig:NT, + title={A representation of the Croce Topos}] + \getbuffer +\stopplacefigure -The \type {node} macros use the default name \type {nodepath} when no path is -explicitly specified. It is the user's responsibility to ensure the local or -global validity of this path if used. +Here we use the \CONTEXT\ interface to the node package: + +\typebuffer [option=TEX] \stopsubsubject @@ -793,10 +1084,11 @@ global validity of this path if used. \startsubject [title=Tree diagrams] -The tree diagram shown below is drawn using four paths, each one defining a row -or generation in the branching. The definition of the spacing of nodes was -crafted by hand and is somewhat arbitrary: 3.8, 1.7, and 1 for the first, second -and third generations. +The tree diagram shown in \in {Figure} [fig:DNA] is drawn using four paths, each +one defining a row or generation in the branching. The definition of the spacing +of nodes was crafted by hand and is somewhat arbitrary: 3.8, 1.7, and 1 for the +first, second and third generations. This might not be the best approach, but +this is how I was thinking when I first created this figure. \startplacefigure [location=force,reference=fig:DNA] \startMPcode @@ -843,25 +1135,28 @@ and third generations. \stopMPcode \stopplacefigure -One can do better by allowing \METAPOST\ to solve equations and to determine this -spacing automatically. This will be illustrated by a very simple example where -nodes are first placed on a declared but undefined path. +Ultimately, one can do better by allowing \METAPOST\ to solve the relevant +equations and to determine this spacing automatically. Because it is a somewhat +advanced procedure, this approach will be first illustrated through a very simple +example of a diagram where the nodes will be placed on a declared but undefined +path: \startTEX save p ; % path p ; \stopTEX -The \type {save p ;} assures that the path is undefined. This path will later get +The \type {save p ;} assures that the path is undefined. This path will later ḻṯ defined based on the contents of the nodes and a desired relative placement. In fact, it is not even necessary to declare that the suffix will be a path, as the path will be declared and automatically built once the positions of all the nodes -are determined, which is why the \type {path} declaration is commented|-|out -above. +are determined. To emphasize this point, the \type {path} declaration above is +commented|-|out. -The user is to be warned that solving equations in \METAPOST\ can be -non|-|trivial for those who are less mathematically inclined. One needs to -establish a coupled set of equations that is solvable: that is, fully but not -over|-|determined. +\startdescription {Warning:} +Solving equations in \METAPOST\ can be non|-|trivial for those who are less +mathematically inclined. One needs to establish a coupled set of equations that +is solvable: that is, fully but not over|-|determined. +\stopdescription A few helper functions have been defined: \type {makenode()} returns a suffix (variable name) corresponding to the node's position. The first such node can be @@ -869,7 +1164,7 @@ placed at any finite point, for example the drawing's origin. The following node can be placed in relation to this first node: % \startframed [frame=off,align=text,offset=overlay] % keep this together on one page. -\startTEX [style=small] +\startTEX save nodepath ; save first, second, third, fourth ; pair first, second, third, fourth ; @@ -892,15 +1187,18 @@ The helper function \type {betweennodes()} returns a vector pointing in a certai direction, here following the standard \METAPOST\ suffixes: \type {urt}, \type {lft}, and \type {bot}, that takes into account the bounding boxes of the contents of each node, plus an (optional) additional distance (here given in -units of the arrow||head length, \type {ahlength}). Using the keyword \type +units of the arrow|-|head length, \type {ahlength}). Using the keyword \type {whatever} tells \METAPOST\ to adjust this distance as necessary. The above set of equations is incomplete as written, so a fifth and final relation needs to be added; the fourth node is also to be located directly to the left of the very -first node: \footnote {Equivalently, we could declare that the first node located -to the right of the fourth node: \typ {first = fourth + betweennodes.rt -(nodepath, first.i, nodepath, fourth.i, 3ahlength) ;}} +first node: +\startfootnote + Equivalently, we could declare that the first node located to the right of + the fourth node: \type {first = fourth + betweennodes.rt (nodepath, first.i, + nodepath, fourth.i, 3ahlength) ;} +\stopfootnote -\startTEX [style=small] +\startTEX fourth = first + betweennodes.lft(nodepath,fourth.i,nodepath,first.i,3ahlength) ; \stopTEX @@ -908,24 +1206,12 @@ fourth = first Note that the helper function \type {makenode()} can be used as many times as needed; if given no content, only returning the node's position. Additional nodes can be added to this diagram along with appropriate relational equations, keeping -in mind that the equations must be solvable, of course. This last point is the -one challenge that most users might face. - -The function \type {node()} that was used previously and returning a picture -element to be drawn itself calls the function \type {makenode()}, used here. The -nodes have not been drawn as yet: +in mind that the equations must, of course, be solvable. This last issue is the +one challenge that most users might face. The function \type {node()} that was +used previously and returning a picture element to be drawn itself calls the +function \type {makenode()}, used here. The nodes have not yet been drawn: -\startTEX -for i = first.i, second.i, third.i, fourth.i : - draw node(i) ; - drawarrow fromto(0,i,i+1) ; -endfor -\stopTEX - -resulting in \in{figure} [fig:relative]. The path is now defined as one running -through the position of all of the defined nodes, and is cyclic. - -\startplacefigure [location=here,reference=fig:relative] +\startplacefigure [location=right,reference=fig:relative] \startMPcode save nodepath ; save first, second, third, fourth ; @@ -948,24 +1234,38 @@ through the position of all of the defined nodes, and is cyclic. \stopMPcode \stopplacefigure +\startTEX +for i = first.i, second.i, third.i, fourth.i : + draw node(i) ; + drawarrow fromto(0,i,i+1) ; +endfor +\stopTEX + +This results in \in {Figure} [fig:relative]. The path is now defined as one +running through the position of all of the defined nodes, and is cyclic. + Using this approach, that of defining but not drawing the nodes until a complete set of equations defining their relative positions has been constructed, imposes -several limitations: firstly, the nodes are expected to be numbered from $0$ up -to $n$, continuously, without any gaps for each defined path. This is just an -implicit convention of the path construction heuristic. Secondly, when finally +several limitations. First, the nodes are expected to be numbered from $0$ up to +$n$, continuously and without any gaps for each defined path. This is just an +implicit, heuristic convention of the path construction. Second, when finally defining all the nodes and their positions, the path needs to be constructed. A -function, \type {makenodepath(p) ;} accomplishing this gets implicitly called +function, \type {makenodepath(p) ;} accomplishes this; it gets implicitly called (once) upon the drawing of any \type {node()} or connecting \type {fromto}. Of -course, \type {makenodepath()} can always be explicitly called once the set of +course, \type {makenodepath()} can always be called explicitly once the set of equations determining the node positions is completely defined. \startparagraph [style=bold] -We again stress that the writing of a solvable yet not over|-|determined set of -equations can be a common source of error for many \METAPOST\ users. +We once again stress that the writing of a solvable yet not over|-|determined set +of equations can be a common source of error for many \METAPOST\ users. +\startfootnote + The generous use of descriptive variables as we try to illustrate in the + examples here can help tremendously in keeping track of multiple equations. +\stopfootnote \stopparagraph Another such example is the construction of a simple tree of descendance or -family tree. There are many ways to draw such a tree; in \in{figure} +family tree. There are many ways to draw such a tree; in \in {figure} [fig:descendance] we will show only three generations. \startplacefigure [location=here,reference=fig:descendance, @@ -1042,26 +1342,30 @@ family tree. There are many ways to draw such a tree; in \in{figure} We leave it as an exercise to the reader to come|-|up with the equations used to determine this tree (one can look at source of this document, if necessary). -The set of equations could be hidden from the user wishing to construct simple, -pre|-|defined types of diagrams. However, such cases would imply a loss of -generality and flexibility. Nevertheless, the \emph {node} module will probably -be extended in the future to provide a few simple models. One might be a -branching tree structure, yet even the above example as drawn does not fit into a -simple, general model. +The requisite set of equations could be hidden from the user wishing to construct +simple, pre|-|defined types of diagrams. However, such cases would involve a loss +of generality and flexibility. Nevertheless, the \ConTeXt-Nodes module \emph +{could} be extended in the future to provide a few simple models. One might be a +branching tree structure, although even the above example (as drawn) does not +easily fit into a simple, general model. + +\blank -A user on the mailing list asked if it is possible to make structure trees for -English sentences with categorical grammar, an example of which is shown in \in -{Figure} [fig:grammar]. +A user on the \CONTEXT\ mailing list asked if it is possible to make structure +trees for English sentences with categorical grammar, an example of which is +shown in \in {Figure} [fig:grammar]. \startbuffer \startMPcode - save p ; path p[] ; % we work first with unit paths. + save p ; path p[] ; save n ; n = 0 ; + % rather than parsing a string, we can use "suffixes": forsuffixes $=People,from,the,country,can,become,quite,lonely : - p [n] = makenode(p[n],0,"\node{\it" & (str $) & "}") = (n,0) ; + p[n] = makenode(p[n],0,"\node{\it" & (str $) & "}") + = (n,0) ; % we work first with unit paths. n := n + 1 ; endfor - save u ; u := TextWidth/n ; + save u ; u := MakeupWidth/n ; %(columns) TextWidth/n ; % build upward tree @@ -1135,50 +1439,72 @@ English sentences with categorical grammar, an example of which is shown in \in \stopbuffer \startplacefigure [reference=fig:grammar, - title=A categorical grammer structure tree] + title={A categorical grammer structure tree}] \getbuffer \stopplacefigure -I chose to define a series of parallel paths, one per word, with one path +Here, I chose to define a series of parallel paths, one per word, with one path terminating whenever it joins another path (or paths) at a common parent. -Labeling each branch of the tree structure requires, of course, a knowledge of -the tree structure. +Naturally, labeling each branch of the tree structure requires a knowledge of the +tree structure. The code is not short, but hopefully it is mostly clear. \typebuffer [option=TEX] +Note that diagrams such as those constructed here will each be significantly +different, making the writing of a general mechanism rather complex. For example, +one might need to construct a tree branching up rather than down, or to the right +(or left), or even following an arbitrary path, such as a random walk. These can +all be achieved individually in \METAPOST\ without too much difficulty. + \stopsubject \startsubject [title=A 3D projection] -% This special instance could become part of the base distribution... +Although \METAPOST\ is a 2D drawing language, it can be easily extended to work +in 3D. Several attempts have been made in the past ranging from simple to +complicated. Here, we will take a simple approach. + +The \METAPOST\ language includes a triplet variable type, used to handle \type +{rgb} colors (it also has a quadruplet type used for \type {cmyk} colors). We +will use this \type {triplet} type to hold 3D coordinates. There is a separate +\CONTEXT\ module, entitled \type {three}, which creates a new \METAPOST\ instance +(also named \type {three}), which loads a set of macros that can be used to +manipulate these triplet coordinates. \usemodule [three] -%\defineMPinstance [three] [format=metafun,extensions=yes,initializations=yes,method=double] +\startTEX +\usemodule [three] -%\startMPdefinitions{three} -%input mp-p-3d.mpiv ; -%\stopMPdefinitions +\startMPcode{three} + % code here +\stopMPcode +\stopTEX -\startMPcalculation{three} - ahlength := 12pt ; - ahangle := 30 ; - ahvariant := 1 ; % dimpled -\stopMPcalculation +For our purposes here, only one function is really necessary: \type +{projection()} that maps a 3D coordinate to a 2D projection on the page. This +will not be a perspective projection having a viewpoint and a focus point, but +rather a very simple oblique projection, useful for, e.g., pseudo|-|3D schematic +drawings. The \type {Z} coordinate is taken to be \type {up} and the \type {Y} +coordinate taken to be \type {right}, both in the page of the paper. The third +coordinate \type {X} is an oblique projection in a right|-|hand coordinate +system. -\startplacefigure [location=left] +\page [yes] +\startbuffer \startMPcode{three} - nodepath := - (projection Origin -- - projection (1,0,0) -- - projection (1,1,0) -- - projection (0,1,0) -- - projection (0,1,1) -- - projection (1,1,1) -- - projection (1,0,1) -- - projection (0,0,1) -- - cycle) scaled 5cm ; + save nodepath ; + path nodepath ; + nodepath = (projection Origin -- + projection (1,0,0) -- + projection (1,1,0) -- + projection (0,1,0) -- + projection (0,1,1) -- + projection (1,1,1) -- + projection (1,0,1) -- + projection (0,0,1) -- + cycle) scaled 5cm ; draw node(0,"\node{${\cal C}_{i\cal P}^{\mathrm{nt}}$}") ; draw node(1,"\node{${\cal C}_{i\cal G}^{\mathrm{nt}}$}") ; @@ -1197,29 +1523,36 @@ the tree structure. drawdoublearrows fromto(0,7,6) ; drawdoublearrows fromto(0,6,5) ; - drawdoublearrows fromto.lft(0,5,4,"\node{$τ_j$~}") ; - drawdoublearrows fromto.top(0,7,4,"\node{$σ$}") ; + drawdoublearrows fromto.ulft(0,5,4,"\node{$τ_j$~}") ; + drawdoublearrows fromto.top (0,7,4,"\node{$σ$}") ; drawdoublearrows fromto.lrt(0,0,7,"\node{$Ψ^{\mathrm{nt}}$}") - crossingunder fromto(0,6,5) ; + crossingunder fromto(0,6,5) ; drawdoublearrows fromto(0,1,6) ; drawdoublearrows fromto(0,2,5) ; drawdoublearrows fromto(0,3,4) ; \stopMPcode +\stopbuffer +\startplacefigure [location=right, + reference=fig:cube] + \getbuffer \stopplacefigure +Intended for schematic drawings, there is no automatic hidden|-|line removal nor +effects like shading, and line crossings need to be handled manually (using \type +{crossingunder} introduced previously). In \in{Figure} [fig:cube] we draw a +simple cubical commutative diagram, with a node at each corner. + +\typebuffer [option=TEX] + +Note the use of \type {drawdoublearrows}, a new \METAFUN\ command that is +introduced here. + \stopsubject \startsubject [title=Two final examples] -\defineframed - [smallnode] - [node] - [foregroundstyle=\tfxx, - background=color, - backgroundcolor=white] - \startbuffer[mp:tikz-cd] \startMPcode save nodepath ; save l ; l = 5ahlength ; @@ -1239,39 +1572,61 @@ the tree structure. for i = XxY.i, X.i, Z.i, Y.i, T.i: draw node(i) ; endfor - drawarrow fromto.top(0, XxY.i,X.i,"\smallnode{$p$}") ; - drawarrow fromto.rt (0, X.i,Z.i,"\smallnode{$f$}") ; - drawarrow fromto.top(0, Y.i,Z.i,"\smallnode{$g$}") ; - drawarrow fromto.rt (0, XxY.i,Y.i,"\smallnode{$q$}") ; - drawarrow fromto.top( .13,T.i,X.i,"\smallnode{$x$}") ; - drawarrow fromto.urt(-.13,T.i,Y.i,"\smallnode{$y$}") ; - drawarrow fromto (0, T.i,XxY.i,"\smallnode{$(x,y)$}") + drawarrow fromto.top(0, XxY.i,X.i, "\nodeSmall{$p$}") ; + drawarrow fromto.rt (0, X.i,Z.i, "\nodeSmall{$f$}") ; + drawarrow fromto.top(0, Y.i,Z.i, "\nodeSmall{$g$}") ; + drawarrow fromto.rt (0, XxY.i,Y.i, "\nodeSmall{$q$}") ; + drawarrow fromto.top( .13,T.i,X.i, "\nodeSmall{$x$}") ; + drawarrow fromto.urt(-.13,T.i,Y.i, "\nodeSmall{$y$}") ; + drawarrow fromto (0, T.i,XxY.i,"\nodeSmall{$(x,y)$}") dashed withdots scaled .5 withpen currentpen scaled 2 ; \stopMPcode \stopbuffer -\startplacefigure [location=left,reference=fig:tikz-cd] +% Rather, let's do it similarly to TikZ. + +\startbuffer[mp:tikz-cd] +\startnodes [dx=2.5cm,dy=2cm,alternative=arrow] + \placenode [0, 0] {\node{$X\times_Z Y$}} + \placenode [1, 0] {\node{$X$}} + \placenode [1,-1] {\node{$Z$}} + \placenode [0,-1] {\node{$Y$}} + \placenode [-1,1] {\node{$T$}} + + \connectnodes [0,1] [position=top, label={\nodeSmall{$p$}}] + \connectnodes [1,2] [position=right, label={\nodeSmall{$f$}}] + \connectnodes [0,3] [position=right, label={\nodeSmall{$q$}}] + \connectnodes [3,2] [position=top, label={\nodeSmall{$g$}}] + \connectnodes [4,0] [option=dotted,rulethickness=1pt, + label={\nodeSmall{$(x,y)$}}] + \connectnodes [4,1] [offset=+.13,position=top, + label={\nodeSmall{$x$}}] + \connectnodes [4,3] [offset=-.13,position=topright, + label={\nodeSmall{$y$}}] +\stopnodes +\stopbuffer + +\startplacefigure [reference=fig:tikz-cd, + location={right,+3*hang}] \getbuffer[mp:tikz-cd] \stopplacefigure -The following example, shown in \in{figure} [fig:tikz-cd] and drawn here using -the present \METAPOST\ \type {node} macros, is inspired from the TikZ CD -(commutative diagrams) package. \footnote {The TikZ-CD package uses a totally -different approach: the diagram is defined and laid|-|out as a table with -decorations (arrows) running between cells.} The nodes are again given relative -positions rather than being placed on a predefined path. The arrow labeled \quote -{$(x,y)$} is drawn \typ {dashed withdots} and illustrates how the line gets -broken, here \type {crossingunder} its centered label. - -\startparagraph [style=small] - \typebuffer[mp:tikz-cd] [option=TEX] -\stopparagraph +We end this manual with two examples of more advanced commutative diagrams. The +following example, shown in \in {Figure} [fig:tikz-cd], illustrates what in +category theory is called a \emph {pullback}. It is inspired from an example +given in the TikZ CD (commutative diagrams) package. -% \doifmode {screen} {\page [yes]} +The arrow labeled \quotation {$(x,y)$} is drawn \type {dashed withdots} and +illustrates how the line gets broken, implicitly \type {crossingunder} its +centered label. -\in {Figure} [fig:tikz-cd2] is another \quotation{real|-|life} example, also -inspired by TikZ-CD. +\typebuffer[mp:tikz-cd] [option=TEX] + +The previous diagram was drawn using the \CONTEXT\ interface. Our final example, +shown in \in {Figure} [fig:tikz-cd2], gives another \quotation {real|-|life} +example of a categorical pullback, also inspired by TikZ-CD, but this time drawn +through the \METAPOST\ interface and solving for positions. \startbuffer[mp:tikz-cd2] \startMPcode @@ -1279,7 +1634,8 @@ inspired by TikZ-CD. save A, B, C, D, E ; pair A, B, C, D, E ; A.i = 0 ; A = makenode(A.i,"\node{$\pi_1(U_1\cap U_2)$}") ; - B.i = 1 ; B = makenode(B.i,"\node{$\pi_1(U_1)\ast_{\pi_1(U_1\cap U_2)}\pi_1(U_2)$}") ; + B.i = 1 ; B = makenode(B.i, + "\node{$\pi_1(U_1)\ast_{\pi_1(U_1\cap U_2)}\pi_1(U_2)$}") ; C.i = 2 ; C = makenode(C.i,"\node{$\pi_1(X)$}") ; D.i = 3 ; D = makenode(D.i,"\node{$\pi_1(U_2)$}") ; E.i = 4 ; E = makenode(E.i,"\node{$\pi_1(U_1)$}") ; @@ -1308,33 +1664,34 @@ inspired by TikZ-CD. \getbuffer[mp:tikz-cd2] \stopplacefigure -\startparagraph [style=small] - \typebuffer[mp:tikz-cd2] [option=TEX] -\stopparagraph - -\placefloats +\typebuffer[mp:tikz-cd2] [option=TEX] \stopsubject \startsubject [title=Conclusions] -It was decided at the 2017 \CONTEXT\ Meeting in Maibach, Germany where this -package was presented that there was little use of developing a purely \CONTEXT\ -interface. Rather, the \METAPOST\ package should be sufficiently accessible. +There was initial consensus at the 2017 \CONTEXT\ Meeting in Maibach, Germany, +where a version of this package was presented, that there was little use of +developing a purely \CONTEXT\ interface. Rather, the \METAPOST\ package should be +sufficiently accessible. Since then, however, we decided that the development of +a derivative \CONTEXT\ interface implementing some basic functionality could +indeed be useful for many users, although it will necessarily remain somewhat +limited. Users are recommended to turn to the pure \METAPOST\ interface when more +sophisticated functionality is needed. \stopsubject \startsubject [title=Acknowledgements] This module was inspired by a request made by Idris Samawi Hamid to draw a -natural transformation diagram (\in{Figure} [fig:natural]). The \METAPOST\ -macros that were developed then benefited from improvements suggested by Hans -Hagen as well as inspiration provided by Taco Hoekwater. +natural transformation diagram (\in{Figure} [fig:natural]). The \METAPOST\ macros +that were developed then benefited from improvements suggested by Hans Hagen as +well as inspiration provided by Taco Hoekwater. The cover artwork one can recognize as coming under the hand of Hans Hagen that he produced when I mentioned that I wanted to do something along these lines. It fits very well into the style of manual covers that we distribute with \CONTEXT. -This manual therefore has been co||written by Hans and Alan. +This manual therefore has been co|-|written by Hans and Alan. \stopsubject diff --git a/metapost/context/base/mpiv/mp-node.mpiv b/metapost/context/base/mpiv/mp-node.mpiv index fe6b40c64..79391220b 100644 --- a/metapost/context/base/mpiv/mp-node.mpiv +++ b/metapost/context/base/mpiv/mp-node.mpiv @@ -135,6 +135,8 @@ vardef mfun_node@#(suffix p)(expr i)(text t) = fi enddef ; +newinternal node_loopback_yscale ; node_loopback_yscale := 1 ; + % returns a path vardef fromtopaths@#(expr d)(suffix p)(expr f)(suffix q)(text s) = @@ -159,7 +161,7 @@ vardef fromtopaths@#(expr d)(suffix p)(expr f)(suffix q)(text s) = save deviation ; deviation := if numeric d: d else: 0 fi ; r1 = if (point 0 of r0) = (point length r0 of r0) : - (fullcircle rotated 180 + (fullcircle yscaled node_loopback_yscale rotated 180 if mfun_laboff@# <> origin : rotated angle mfun_laboff@# shifted .5mfun_laboff@# diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 55741083b..51b6ba7e2 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2018.03.07 12:18} +\newcontextversion{2018.03.10 14:52} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index f2fce340e..4d00fa41e 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2018.03.07 12:18} +\edef\contextversion{2018.03.10 14:52} %D For those who want to use this: diff --git a/tex/context/base/mkii/mult-ro.mkii b/tex/context/base/mkii/mult-ro.mkii index de3a8c84a..46b61c882 100644 --- a/tex/context/base/mkii/mult-ro.mkii +++ b/tex/context/base/mkii/mult-ro.mkii @@ -1077,6 +1077,7 @@ \setinterfaceconstant{preview}{previzualizare} \setinterfaceconstant{previous}{precendent} \setinterfaceconstant{previousnumber}{numarprecedent} +\setinterfaceconstant{print}{print} \setinterfaceconstant{printable}{tiparibil} \setinterfaceconstant{process}{process} \setinterfaceconstant{profile}{profile} diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index ae8748f0f..79be46772 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2018.03.07 12:18} +\newcontextversion{2018.03.10 14:52} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 8cf5eff00..482a24192 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -42,7 +42,7 @@ %D has to match \type {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2018.03.07 12:18} +\edef\contextversion{2018.03.10 14:52} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/mkiv/grph-epd.lua b/tex/context/base/mkiv/grph-epd.lua index a189a8706..7855ce891 100644 --- a/tex/context/base/mkiv/grph-epd.lua +++ b/tex/context/base/mkiv/grph-epd.lua @@ -11,6 +11,10 @@ local settings_to_hash = utilities.parsers.settings_to_hash -- todo: page, name, file, url +-- I have some experimental code for including comments and fields but it's +-- unfinished and not included as it was just a proof of concept to get some idea +-- about what is needed and possible. But the placeholders are here already. + local codeinjections = backends.codeinjections local function mergegoodies(optionlist) diff --git a/tex/context/base/mkiv/grph-inc.lua b/tex/context/base/mkiv/grph-inc.lua index 71ee2f7ea..47eb7bbbb 100644 --- a/tex/context/base/mkiv/grph-inc.lua +++ b/tex/context/base/mkiv/grph-inc.lua @@ -1923,6 +1923,15 @@ function figures.getinfo(name,page) end end +function figures.getpdfinfo(name,page,metadata) + -- not that useful but as we have it for detailed inclusion we can as + -- we expose it + if type(name) ~= "table" then + name = { name = name, page = page, metadata = metadata } + end + return codeinjections.getinfo(name) +end + -- interfacing implement { diff --git a/tex/context/base/mkiv/lpdf-epa.lua b/tex/context/base/mkiv/lpdf-epa.lua index d17ae5065..89b2c6e0e 100644 --- a/tex/context/base/mkiv/lpdf-epa.lua +++ b/tex/context/base/mkiv/lpdf-epa.lua @@ -14,6 +14,7 @@ local formatters = string.formatters local abs = math.abs local expandname = file.expandname local allocate = utilities.storage.allocate +local isfile = lfs.isfile ----- lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns @@ -21,6 +22,8 @@ local trace_links = false trackers.register("figures.links", function(v) local trace_outlines = false trackers.register("figures.outliness", function(v) trace_outlines = v end) local report_link = logs.reporter("backend","link") +local report_comment = logs.reporter("backend","comment") +local report_field = logs.reporter("backend","field") local report_outline = logs.reporter("backend","outline") local epdf = epdf @@ -324,7 +327,7 @@ function codeinjections.getbookmarks(filename) local document = nil - if lfs.isfile(filename) then + if isfile(filename) then document = loadpdffile(filename) else report_outline("unknown file %a",filename) @@ -467,3 +470,82 @@ function codeinjections.mergebookmarks(specification) end end end + +-- placeholders: + +function codeinjections.mergecomments(specification) + report_comment("unfinished experimental code, not used yet") +end + +function codeinjections.mergefields(specification) + report_field("unfinished experimental code, not used yet") +end + +-- A bit more than a placeholder but in the same perspective as +-- inclusion of comments and fields: +-- +-- getinfo{ filename = "tt.pdf", metadata = true } +-- getinfo{ filename = "tt.pdf", page = 1, metadata = "xml" } +-- getinfo("tt.pdf") + +function codeinjections.getinfo(specification) + if type(specification) == "string" then + specification = { filename = specification } + end + local filename = specification.filename + if type(filename) == "string" and isfile(filename) then + local pdffile = loadpdffile(filename) + if pdffile then + local pagenumber = specification.page or 1 + local metadata = specification.metadata + local catalog = pdffile.Catalog + local info = pdffile.Info + local pages = pdffile.pages + local nofpages = pages.n + if metadata then + local m = catalog.Metadata + if m then + m = m() + if metadata == "xml" then + metadata = xml.convert(m) + else + metadata = m + end + else + metadata = nil + end + else + metadata = nil + end + if pagenumber > nofpages then + pagenumber = nofpages + end + local nobox = { 0, 0, 0, 0 } + local crop = nobox + local media = nobox + local page = pages[pagenumber] + if page then + crop = page.CropBox or nobox + media = page.MediaBox or crop or nobox + crop.n = nil -- nicer + media.n = nil -- nicer + end + local bbox = crop or media or nobox + return { + filename = filename, + pdfversion = tonumber(catalog.Version), + nofpages = nofpages, + title = info.Title, + creator = info.Creator, + producer = info.Producer, + creationdate = info.CreationDate, + modification = info.ModDate, + metadata = metadata, + width = bbox[4] - bbox[2], + height = bbox[3] - bbox[1], + cropbox = { crop[1], crop[2], crop[3], crop[4] }, -- we need access + mediabox = { media[1], media[2], media[3], media[4] } , -- we need access + } + end + end +end diff --git a/tex/context/base/mkiv/spac-ver.lua b/tex/context/base/mkiv/spac-ver.lua index 3d263dafd..288630a5d 100644 --- a/tex/context/base/mkiv/spac-ver.lua +++ b/tex/context/base/mkiv/spac-ver.lua @@ -195,6 +195,8 @@ local belowdisplayskip_code = skipcodes.belowdisplayskip local abovedisplayshortskip_code = skipcodes.abovedisplayshortskip local belowdisplayshortskip_code = skipcodes.belowdisplayshortskip +local properties = nodes.properties.data + local vspacing = builders.vspacing or { } builders.vspacing = vspacing @@ -699,45 +701,43 @@ local function snap_topskip(current,method) return w, 0 end -do - - local categories = allocate { - [0] = 'discard', - [1] = 'largest', - [2] = 'force' , - [3] = 'penalty', - [4] = 'add' , - [5] = 'disable', - [6] = 'nowhite', - [7] = 'goback', - [8] = 'together', -- not used (?) - [9] = 'overlay', - [10] = 'notopskip', - } +local categories = { + [0] = "discard", + [1] = "largest", + [2] = "force", + [3] = "penalty", + [4] = "add", + [5] = "disable", + [6] = "nowhite", + [7] = "goback", + [8] = "packed", + [9] = "overlay", + [10] = "enable", + [11] = "notopskip", +} - vspacing.categories = categories +categories = allocate(table.swapped(categories,categories)) +vspacing.categories = categories - function vspacing.tocategories(str) - local t = { } - for s in gmatch(str,"[^, ]") do -- use lpeg instead - local n = tonumber(s) - if n then - t[categories[n]] = true - else - t[b] = true - end - end - return t - end - - function vspacing.tocategory(str) -- can be optimized - if type(str) == "string" then - return set.tonumber(vspacing.tocategories(str)) +function vspacing.tocategories(str) + local t = { } + for s in gmatch(str,"[^, ]") do -- use lpeg instead + local n = tonumber(s) + if n then + t[categories[n]] = true else - return set.tonumber({ [categories[str]] = true }) + t[b] = true end end + return t +end +function vspacing.tocategory(str) -- can be optimized + if type(str) == "string" then + return set.tonumber(vspacing.tocategories(str)) + else + return set.tonumber({ [categories[str]] = true }) + end end vspacingdata.map = vspacingdata.map or { } -- allocate ? @@ -783,6 +783,7 @@ do -- todo: interface.variables and properties local ctx_flushblankhandling = context.flushblankhandling local ctx_addpredefinedblankskip = context.addpredefinedblankskip local ctx_addaskedblankskip = context.addaskedblankskip + local ctx_setblankpacked = context.setblankpacked local ctx_pushlogger = context.pushlogger local ctx_startblankhandling = context.startblankhandling @@ -791,6 +792,8 @@ do -- todo: interface.variables and properties local pattern = nil + local packed = categories.packed + local function handler(amount, keyword, detail) if not keyword then report_vspacing("unknown directive %a",s) @@ -804,7 +807,9 @@ do -- todo: interface.variables and properties ctx_flexibleblankskip() elseif keyword == k_category then local category = tonumber(detail) - if category then + if category == packed then + ctx_setblankpacked() + elseif category then ctx_setblankcategory(category) ctx_flushblankhandling() end @@ -961,14 +966,14 @@ function vspacing.snapbox(n,how) end else local h, d, ch, cd, lines, extra = snap_hlist("box",box,sv,ht,dp) -setprop(box,"snapper",{ - ht = h, - dp = d, - ch = ch, - cd = cd, - extra = extra, - current = current, -}) + setprop(box,"snapper",{ + ht = h, + dp = d, + ch = ch, + cd = cd, + extra = extra, + current = current, + }) setwhd(box,wd,ch,cd) if trace_vsnapping then report_snapper("box list snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s", @@ -993,702 +998,702 @@ end -- We can register and copy the rule instead. -local w, h, d = 0, 0, 0 ------ w, h, d = 100*65536, 65536, 65536 +do -local function forced_skip(head,current,width,where,trace) -- looks old ... we have other tricks now - if head == current then - if getsubtype(head) == baselineskip_code then - width = width - getwidth(head) + local w, h, d = 0, 0, 0 + ----- w, h, d = 100*65536, 65536, 65536 + + local function forced_skip(head,current,width,where,trace) -- looks old ... we have other tricks now + if head == current then + if getsubtype(head) == baselineskip_code then + width = width - getwidth(head) + end end + if width == 0 then + -- do nothing + elseif where == "after" then + head, current = insert_node_after(head,current,new_rule(w,h,d)) + head, current = insert_node_after(head,current,new_kern(width)) + head, current = insert_node_after(head,current,new_rule(w,h,d)) + else + local c = current + head, current = insert_node_before(head,current,new_rule(w,h,d)) + head, current = insert_node_before(head,current,new_kern(width)) + head, current = insert_node_before(head,current,new_rule(w,h,d)) + current = c + end + if trace then + report_vspacing("inserting forced skip of %p",width) + end + return head, current end - if width == 0 then - -- do nothing - elseif where == "after" then - head, current = insert_node_after(head,current,new_rule(w,h,d)) - head, current = insert_node_after(head,current,new_kern(width)) - head, current = insert_node_after(head,current,new_rule(w,h,d)) - else - local c = current - head, current = insert_node_before(head,current,new_rule(w,h,d)) - head, current = insert_node_before(head,current,new_kern(width)) - head, current = insert_node_before(head,current,new_rule(w,h,d)) - current = c - end - if trace then - report_vspacing("inserting forced skip of %p",width) - end - return head, current -end - --- penalty only works well when before skip -local discard = 0 -local largest = 1 -local force = 2 -local penalty = 3 -local add = 4 -local disable = 5 -local nowhite = 6 -local goback = 7 -local together = 8 -- not used (?) -local overlay = 9 -local enable = 10 + -- penalty only works well when before skip --- [whatsits][hlist][glue][glue][penalty] + local discard = categories.discard + local largest = categories.largest + local force = categories.force + local penalty = categories.penalty + local add = categories.add + local disable = categories.disable + local nowhite = categories.nowhite + local goback = categories.goback + local packed = categories.packed + local overlay = categories.overlay + local enable = categories.enable + local notopskip = categories.notopskip -local special_penalty_min = 32250 -local special_penalty_max = 35000 -local special_penalty_xxx = 0 + -- [whatsits][hlist][glue][glue][penalty] --- this is rather messy and complex: we want to make sure that successive --- header don't break but also make sure that we have at least a decent --- break when we have succesive ones (often when testing) + local special_penalty_min = 32250 + local special_penalty_max = 35000 + local special_penalty_xxx = 0 --- todo: mark headers as such so that we can recognize them + -- this is rather messy and complex: we want to make sure that successive + -- header don't break but also make sure that we have at least a decent + -- break when we have succesive ones (often when testing) -local specialmethods = { } -local specialmethod = 1 + -- todo: mark headers as such so that we can recognize them -local properties = nodes.properties.data + local specialmethods = { } + local specialmethod = 1 -specialmethods[1] = function(pagehead,pagetail,start,penalty) - -- - if not pagehead or penalty < special_penalty_min or penalty > special_penalty_max then - return - end - local current = pagetail - -- - -- nodes.showsimplelist(pagehead,0) - -- - if trace_specials then - report_specials("checking penalty %a",penalty) - end - while current do - local id = getid(current) - if id == penalty_code then - local p = properties[current] - if p then - local p = p.special_penalty - if not p then - if trace_specials then - report_specials(" regular penalty, continue") - end - elseif p == penalty then - if trace_specials then - report_specials(" context penalty %a, same level, overloading",p) - end - return special_penalty_xxx - elseif p > special_penalty_min and p < special_penalty_max then - if penalty < p then + specialmethods[1] = function(pagehead,pagetail,start,penalty) + -- + if not pagehead or penalty < special_penalty_min or penalty > special_penalty_max then + return + end + local current = pagetail + -- + -- nodes.showsimplelist(pagehead,0) + -- + if trace_specials then + report_specials("checking penalty %a",penalty) + end + while current do + local id = getid(current) + if id == penalty_code then + local p = properties[current] + if p then + local p = p.special_penalty + if not p then if trace_specials then - report_specials(" context penalty %a, lower level, overloading",p) + report_specials(" regular penalty, continue") end - return special_penalty_xxx - else + elseif p == penalty then if trace_specials then - report_specials(" context penalty %a, higher level, quitting",p) + report_specials(" context penalty %a, same level, overloading",p) end - return - end - elseif trace_specials then - report_specials(" context penalty %a, higher level, continue",p) - end - else - local p = getpenalty(current) - if p < 10000 then - -- assume some other mechanism kicks in so we seem to have content - if trace_specials then - report_specials(" regular penalty %a, quitting",p) + return special_penalty_xxx + elseif p > special_penalty_min and p < special_penalty_max then + if penalty < p then + if trace_specials then + report_specials(" context penalty %a, lower level, overloading",p) + end + return special_penalty_xxx + else + if trace_specials then + report_specials(" context penalty %a, higher level, quitting",p) + end + return + end + elseif trace_specials then + report_specials(" context penalty %a, higher level, continue",p) end - break else - if trace_specials then - report_specials(" regular penalty %a, continue",p) + local p = getpenalty(current) + if p < 10000 then + -- assume some other mechanism kicks in so we seem to have content + if trace_specials then + report_specials(" regular penalty %a, quitting",p) + end + break + else + if trace_specials then + report_specials(" regular penalty %a, continue",p) + end end end end + current = getprev(current) end - current = getprev(current) - end - -- none found, so no reson to be special - if trace_specials then - if pagetail then - report_specials(" context penalty, discarding, nothing special") - else - report_specials(" context penalty, discarding, nothing preceding") + -- none found, so no reson to be special + if trace_specials then + if pagetail then + report_specials(" context penalty, discarding, nothing special") + else + report_specials(" context penalty, discarding, nothing preceding") + end end + return special_penalty_xxx end - return special_penalty_xxx -end --- specialmethods[2] : always put something before and use that as to-be-changed --- --- we could inject a vadjust to force a recalculation .. a mess --- --- So, the next is far from robust and okay but for the moment this overlaying --- has to do. Always test this with the examples in spac-ver.mkvi! - -local function check_experimental_overlay(head,current) - local p = nil - local c = current - local n = nil - local function overlay(p,n,mvl) - local p_wd, p_ht, p_dp = getwhd(p) - local n_wd, n_ht, n_dp = getwhd(n) - local skips = 0 - -- - -- We deal with this at the tex end .. we don't see spacing .. enabling this code - -- is probably harmless but then we need to test it. - -- - -- we could calculate this before we call - -- - -- problem: prev list and next list can be unconnected - -- - local c = getnext(p) - local l = c - while c and c ~= n do - local id = getid(c) - if id == glue_code then - skips = skips + getwidth(c) - elseif id == kern_code then - skips = skips + getkern(c) + -- This will be replaced after 0.80+ when we have a more robust look-back and + -- can look at the bigger picture. + + -- todo: look back and when a special is there before a list is seen penalty keep ut + + -- we now look back a lot, way too often + + -- userskip + -- lineskip + -- baselineskip + -- parskip + -- abovedisplayskip + -- belowdisplayskip + -- abovedisplayshortskip + -- belowdisplayshortskip + -- topskip + -- splittopskip + + -- we could inject a vadjust to force a recalculation .. a mess + -- + -- So, the next is far from robust and okay but for the moment this overlaying + -- has to do. Always test this with the examples in spac-ver.mkvi! + + local function check_experimental_overlay(head,current) + local p = nil + local c = current + local n = nil + local function overlay(p,n,mvl) + local p_wd, p_ht, p_dp = getwhd(p) + local n_wd, n_ht, n_dp = getwhd(n) + local skips = 0 + -- + -- We deal with this at the tex end .. we don't see spacing .. enabling this code + -- is probably harmless but then we need to test it. + -- + -- we could calculate this before we call + -- + -- problem: prev list and next list can be unconnected + -- + local c = getnext(p) + local l = c + while c and c ~= n do + local id = getid(c) + if id == glue_code then + skips = skips + getwidth(c) + elseif id == kern_code then + skips = skips + getkern(c) + end + l = c + c = getnext(c) end - l = c - c = getnext(c) - end - local c = getprev(n) - while c and c ~= n and c ~= l do - local id = getid(c) - if id == glue_code then - skips = skips + getwidth(c) - elseif id == kern_code then - skips = skips + getkern(c) + local c = getprev(n) + while c and c ~= n and c ~= l do + local id = getid(c) + if id == glue_code then + skips = skips + getwidth(c) + elseif id == kern_code then + skips = skips + getkern(c) + end + c = getprev(c) end - c = getprev(c) - end - -- - local delta = n_ht + skips + p_dp - texsetdimen("global","d_spac_overlay",-delta) -- for tracing - -- we should adapt pagetotal ! (need a hook for that) .. now we have the wrong pagebreak - local k = new_kern(-delta) - head = insert_node_before(head,n,k) - if n_ht > p_ht then - local k = new_kern(n_ht-p_ht) - head = insert_node_before(head,p,k) - end - if trace_vspacing then - report_vspacing("overlaying, prev height: %p, prev depth: %p, next height: %p, skips: %p, move up: %p",p_ht,p_dp,n_ht,skips,delta) + -- + local delta = n_ht + skips + p_dp + texsetdimen("global","d_spac_overlay",-delta) -- for tracing + -- we should adapt pagetotal ! (need a hook for that) .. now we have the wrong pagebreak + local k = new_kern(-delta) + head = insert_node_before(head,n,k) + if n_ht > p_ht then + local k = new_kern(n_ht-p_ht) + head = insert_node_before(head,p,k) + end + if trace_vspacing then + report_vspacing("overlaying, prev height: %p, prev depth: %p, next height: %p, skips: %p, move up: %p",p_ht,p_dp,n_ht,skips,delta) + end + return remove_node(head,current,true) end - return remove_node(head,current,true) - end - -- goto next line - while c do - local id = getid(c) - if id == glue_code or id == penalty_code or id == kern_code then - -- skip (actually, remove) - c = getnext(c) - elseif id == hlist_code then - n = c - break - else - break - end - end - if n then - -- we have a next line, goto prev line - c = current + -- goto next line while c do local id = getid(c) - if id == glue_code or id == penalty_code then -- kern ? - c = getprev(c) + if id == glue_code or id == penalty_code or id == kern_code then + -- skip (actually, remove) + c = getnext(c) elseif id == hlist_code then - p = c + n = c break else break end end - if not p then - if a_snapmethod == a_snapvbox then - -- quit, we're not on the mvl - else - -- inefficient when we're at the end of a page - local c = tonut(texlists.page_head) - while c and c ~= n do - local id = getid(c) - if id == hlist_code then - p = c - end - c = getnext(c) + if n then + -- we have a next line, goto prev line + c = current + while c do + local id = getid(c) + if id == glue_code or id == penalty_code then -- kern ? + c = getprev(c) + elseif id == hlist_code then + p = c + break + else + break end - if p and p ~= n then - return overlay(p,n,true) + end + if not p then + if a_snapmethod == a_snapvbox then + -- quit, we're not on the mvl + else + -- inefficient when we're at the end of a page + local c = tonut(texlists.page_head) + while c and c ~= n do + local id = getid(c) + if id == hlist_code then + p = c + end + c = getnext(c) + end + if p and p ~= n then + return overlay(p,n,true) + end end + elseif p ~= n then + return overlay(p,n,false) end - elseif p ~= n then - return overlay(p,n,false) end + -- in fact, we could try again later ... so then no remove (a few tries) + return remove_node(head, current, true) end - -- in fact, we could try again later ... so then no remove (a few tries) - return remove_node(head, current, true) -end - --- This will be replaced after 0.80+ when we have a more robust look-back and --- can look at the bigger picture. - --- todo: look back and when a special is there before a list is seen penalty keep ut --- we now look back a lot, way too often - --- userskip --- lineskip --- baselineskip --- parskip --- abovedisplayskip --- belowdisplayskip --- abovedisplayshortskip --- belowdisplayshortskip --- topskip --- splittopskip - -local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail - if trace then - reset_tracing(head) - end - local current, oldhead = head, head - local glue_order, glue_data, force_glue = 0, nil, false - local penalty_order, penalty_data, natural_penalty, special_penalty = 0, nil, nil, nil - local parskip, ignore_parskip, ignore_following, ignore_whitespace, keep_together = nil, false, false, false, false - local lastsnap = nil - -- - -- todo: keep_together: between headers - -- - local pagehead = nil - local pagetail = nil - - local function getpagelist() - if not pagehead then - pagehead = texlists.page_head - if pagehead then - pagehead = tonut(pagehead) - pagetail = find_node_tail(pagehead) -- no texlists.page_tail yet-- no texlists.page_tail yet - end - end - end - -- - local function compensate(n) - local g = 0 - while n and getid(n) == glue_code do - g = g + getwidth(n) - n = getnext(n) + local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail + if trace then + reset_tracing(head) end - if n then - local p = getprop(n,"snapper") - if p then - local extra = p.extra - if extra and extra < 0 then -- hm, extra can be unset ... needs checking - local h = p.ch -- getheight(n) - -- maybe an extra check - -- if h - extra < g then - setheight(n,h-2*extra) - p.extra = 0 - if trace_vsnapping then - report_snapper("removed extra space at top: %p",extra) - end - -- end + local current, oldhead = head, head + local glue_order, glue_data, force_glue = 0, nil, false + local penalty_order, penalty_data, natural_penalty, special_penalty = 0, nil, nil, nil + local parskip, ignore_parskip, ignore_following, ignore_whitespace, keep_together = nil, false, false, false, false + local lastsnap = nil + -- + -- todo: keep_together: between headers + -- + local pagehead = nil + local pagetail = nil + + local function getpagelist() + if not pagehead then + pagehead = texlists.page_head + if pagehead then + pagehead = tonut(pagehead) + pagetail = find_node_tail(pagehead) -- no texlists.page_tail yet-- no texlists.page_tail yet end end - return n end - end - -- - local function removetopsnap() - getpagelist() - if pagehead then - local n = pagehead and compensate(pagehead) - if n and n ~= pagetail then - local p = getprop(pagetail,"snapper") + -- + local function compensate(n) + local g = 0 + while n and getid(n) == glue_code do + g = g + getwidth(n) + n = getnext(n) + end + if n then + local p = getprop(n,"snapper") if p then - local e = p.extra - if e and e < 0 then - local t = texget("pagetotal") - if t > 0 then - local g = texget("pagegoal") -- 1073741823 is signal - local d = g - t - if d < -e then - local penalty = new_penalty(1000000) - setlink(penalty,head) - head = penalty - report_snapper("force pagebreak due to extra space at bottom: %p",e) + local extra = p.extra + if extra and extra < 0 then -- hm, extra can be unset ... needs checking + local h = p.ch -- getheight(n) + -- maybe an extra check + -- if h - extra < g then + setheight(n,h-2*extra) + p.extra = 0 + if trace_vsnapping then + report_snapper("removed extra space at top: %p",extra) end - end + -- end end end + return n end - elseif head then - compensate(head) end - end - -- - local function getavailable() - getpagelist() - if pagehead then - local t = texget("pagetotal") - if t > 0 then - local g = texget("pagegoal") - return g - t + -- + local function removetopsnap() + getpagelist() + if pagehead then + local n = pagehead and compensate(pagehead) + if n and n ~= pagetail then + local p = getprop(pagetail,"snapper") + if p then + local e = p.extra + if e and e < 0 then + local t = texget("pagetotal") + if t > 0 then + local g = texget("pagegoal") -- 1073741823 is signal + local d = g - t + if d < -e then + local penalty = new_penalty(1000000) + setlink(penalty,head) + head = penalty + report_snapper("force pagebreak due to extra space at bottom: %p",e) + end + end + end + end + end + elseif head then + compensate(head) end end - return false - end - -- - local function flush(why) - if penalty_data then - local p = new_penalty(penalty_data) - if trace then - trace_done("flushed due to " .. why,p) - end - if penalty_data >= 10000 then -- or whatever threshold? - local prev = getprev(current) - if getid(prev) == glue_code then -- maybe go back more, or maybe even push back before any glue - -- tricky case: spacing/grid-007.tex: glue penalty glue - head = insert_node_before(head,prev,p) - else - head = insert_node_before(head,current,p) + -- + local function getavailable() + getpagelist() + if pagehead then + local t = texget("pagetotal") + if t > 0 then + local g = texget("pagegoal") + return g - t end - else - head = insert_node_before(head,current,p) - end - -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then - local props = properties[p] - if props then - props.special_penalty = special_penalty or penalty_data - else - properties[p] = { - special_penalty = special_penalty or penalty_data - } end - -- end + return false end - if glue_data then - if force_glue then + -- + local function flush(why) + if penalty_data then + local p = new_penalty(penalty_data) if trace then - trace_done("flushed due to forced " .. why,glue_data) + trace_done("flushed due to " .. why,p) end - head = forced_skip(head,current,getwidth(glue_data,width),"before",trace) - flush_node(glue_data) - else - local width, stretch, shrink = getglue(glue_data) - if width ~= 0 then - if trace then - trace_done("flushed due to non zero " .. why,glue_data) + if penalty_data >= 10000 then -- or whatever threshold? + local prev = getprev(current) + if getid(prev) == glue_code then -- maybe go back more, or maybe even push back before any glue + -- tricky case: spacing/grid-007.tex: glue penalty glue + head = insert_node_before(head,prev,p) + else + head = insert_node_before(head,current,p) end - head = insert_node_before(head,current,glue_data) - elseif stretch ~= 0 or shrink ~= 0 then + else + head = insert_node_before(head,current,p) + end + -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then + local props = properties[p] + if props then + props.special_penalty = special_penalty or penalty_data + else + properties[p] = { + special_penalty = special_penalty or penalty_data + } + end + -- end + end + if glue_data then + if force_glue then if trace then - trace_done("flushed due to stretch/shrink in" .. why,glue_data) + trace_done("flushed due to forced " .. why,glue_data) end - head = insert_node_before(head,current,glue_data) - else - -- report_vspacing("needs checking (%s): %p",skipcodes[getsubtype(glue_data)],w) + head = forced_skip(head,current,getwidth(glue_data,width),"before",trace) flush_node(glue_data) + else + local width, stretch, shrink = getglue(glue_data) + if width ~= 0 then + if trace then + trace_done("flushed due to non zero " .. why,glue_data) + end + head = insert_node_before(head,current,glue_data) + elseif stretch ~= 0 or shrink ~= 0 then + if trace then + trace_done("flushed due to stretch/shrink in" .. why,glue_data) + end + head = insert_node_before(head,current,glue_data) + else + -- report_vspacing("needs checking (%s): %p",skipcodes[getsubtype(glue_data)],w) + flush_node(glue_data) + end end end - end + if trace then + trace_node(current) + end + glue_order, glue_data, force_glue = 0, nil, false + penalty_order, penalty_data, natural_penalty = 0, nil, nil + parskip, ignore_parskip, ignore_following, ignore_whitespace = nil, false, false, false + end + -- + if trace_vsnapping then + report_snapper("global ht/dp = %p/%p, local ht/dp = %p/%p", + texgetdimen("globalbodyfontstrutheight"), + texgetdimen("globalbodyfontstrutdepth"), + texgetdimen("bodyfontstrutheight"), + texgetdimen("bodyfontstrutdepth") + ) + end if trace then - trace_node(current) + trace_info("start analyzing",where,what) end - glue_order, glue_data, force_glue = 0, nil, false - penalty_order, penalty_data, natural_penalty = 0, nil, nil - parskip, ignore_parskip, ignore_following, ignore_whitespace = nil, false, false, false - end - -- - if trace_vsnapping then - report_snapper("global ht/dp = %p/%p, local ht/dp = %p/%p", - texgetdimen("globalbodyfontstrutheight"), - texgetdimen("globalbodyfontstrutdepth"), - texgetdimen("bodyfontstrutheight"), - texgetdimen("bodyfontstrutdepth") - ) - end - if trace then - trace_info("start analyzing",where,what) - end - if snap and where == "page" then - removetopsnap() - end - while current do - local id = getid(current) - if id == hlist_code or id == vlist_code then - -- needs checking, why so many calls - if snap then - lastsnap = nil - local list = getlist(current) - local s = getattr(current,a_snapmethod) - if not s then - -- if trace_vsnapping then - -- report_snapper("mvl list not snapped") - -- end - elseif s == 0 then - if trace_vsnapping then - report_snapper("mvl %a not snapped, already done: %s",nodecodes[id],listtoutf(list)) - end - else - local sv = snapmethods[s] - if sv then - -- check if already snapped - local done = list and already_done(id,list,a_snapmethod) - if done then - -- assume that the box is already snapped - if trace_vsnapping then - local w, h, d = getwhd(current) - report_snapper("mvl list already snapped at (%p,%p): %s",h,d,listtoutf(list)) - end - else - local h, d, ch, cd, lines, extra = snap_hlist("mvl",current,sv,false,false) - lastsnap = { - ht = h, - dp = d, - ch = ch, - cd = cd, - extra = extra, - current = current, - } - setprop(current,"snapper",lastsnap) - if trace_vsnapping then - report_snapper("mvl %a snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s", - nodecodes[id],h,d,ch,cd,sv.name,sv.specification,where,lines,listtoutf(list)) + if snap and where == "page" then + removetopsnap() + end + while current do + local id = getid(current) + if id == hlist_code or id == vlist_code then + -- needs checking, why so many calls + if snap then + lastsnap = nil + local list = getlist(current) + local s = getattr(current,a_snapmethod) + if not s then + -- if trace_vsnapping then + -- report_snapper("mvl list not snapped") + -- end + elseif s == 0 then + if trace_vsnapping then + report_snapper("mvl %a not snapped, already done: %s",nodecodes[id],listtoutf(list)) + end + else + local sv = snapmethods[s] + if sv then + -- check if already snapped + local done = list and already_done(id,list,a_snapmethod) + if done then + -- assume that the box is already snapped + if trace_vsnapping then + local w, h, d = getwhd(current) + report_snapper("mvl list already snapped at (%p,%p): %s",h,d,listtoutf(list)) + end + else + local h, d, ch, cd, lines, extra = snap_hlist("mvl",current,sv,false,false) + lastsnap = { + ht = h, + dp = d, + ch = ch, + cd = cd, + extra = extra, + current = current, + } + setprop(current,"snapper",lastsnap) + if trace_vsnapping then + report_snapper("mvl %a snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s", + nodecodes[id],h,d,ch,cd,sv.name,sv.specification,where,lines,listtoutf(list)) + end end + elseif trace_vsnapping then + report_snapper("mvl %a not snapped due to unknown snap specification: %s",nodecodes[id],listtoutf(list)) end - elseif trace_vsnapping then - report_snapper("mvl %a not snapped due to unknown snap specification: %s",nodecodes[id],listtoutf(list)) + setattr(current,a_snapmethod,0) end - setattr(current,a_snapmethod,0) + else + -- end - else - -- - end - -- tex.prevdepth = 0 - flush("list") - current = getnext(current) - elseif id == penalty_code then - -- natural_penalty = getpenalty(current) - -- if trace then - -- trace_done("removed penalty",current) - -- end - -- head, current = remove_node(head, current, true) - current = getnext(current) - elseif id == kern_code then - if snap and trace_vsnapping and getkern(current) ~= 0 then - report_snapper("kern of %p kept",getkern(current)) - end - flush("kern") - current = getnext(current) - elseif id == glue_code then - local subtype = getsubtype(current) - if subtype == userskip_code then - local sc = getattr(current,a_skipcategory) -- has no default, no unset (yet) - local so = getattr(current,a_skiporder) or 1 -- has 1 default, no unset (yet) - local sp = getattr(current,a_skippenalty) -- has no default, no unset (yet) - if sp and sc == penalty then - if where == "page" then - getpagelist() - local p = specialmethods[specialmethod](pagehead,pagetail,current,sp) - if p then - -- todo: other tracer - -- - -- if trace then - -- trace_skip("previous special penalty %a is changed to %a using method %a",sp,p,specialmethod) - -- end - special_penalty = sp - sp = p + -- tex.prevdepth = 0 + flush("list") + current = getnext(current) + elseif id == penalty_code then + -- natural_penalty = getpenalty(current) + -- if trace then + -- trace_done("removed penalty",current) + -- end + -- head, current = remove_node(head, current, true) + current = getnext(current) + elseif id == kern_code then + if snap and trace_vsnapping and getkern(current) ~= 0 then + report_snapper("kern of %p kept",getkern(current)) + end + flush("kern") + current = getnext(current) + elseif id == glue_code then + local subtype = getsubtype(current) + if subtype == userskip_code then + local sc = getattr(current,a_skipcategory) -- has no default, no unset (yet) + local so = getattr(current,a_skiporder) or 1 -- has 1 default, no unset (yet) + local sp = getattr(current,a_skippenalty) -- has no default, no unset (yet) + if sp and sc == penalty then + if where == "page" then + getpagelist() + local p = specialmethods[specialmethod](pagehead,pagetail,current,sp) + if p then + -- todo: other tracer + -- + -- if trace then + -- trace_skip("previous special penalty %a is changed to %a using method %a",sp,p,specialmethod) + -- end + special_penalty = sp + sp = p + end end - end - if not penalty_data then - penalty_data = sp - elseif penalty_order < so then - penalty_order, penalty_data = so, sp - elseif penalty_order == so and sp > penalty_data then - penalty_data = sp - end - if trace then - trace_skip("penalty in skip",sc,so,sp,current) - end - head, current = remove_node(head, current, true) - elseif not sc then -- if not sc then - if glue_data then - if trace then - trace_done("flush",glue_data) + if not penalty_data then + penalty_data = sp + elseif penalty_order < so then + penalty_order, penalty_data = so, sp + elseif penalty_order == so and sp > penalty_data then + penalty_data = sp end - head = insert_node_before(head,current,glue_data) if trace then - trace_natural("natural",current) + trace_skip("penalty in skip",sc,so,sp,current) end - current = getnext(current) - else - -- not look back across head - -- todo: prev can be whatsit (latelua) - local previous = getprev(current) - if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then - local pwidth, pstretch, pshrink, pstretch_order, pshrink_order = getglue(previous) - local cwidth, cstretch, cshrink, cstretch_order, cshrink_order = getglue(current) - if pstretch_order == 0 and pshrink_order == 0 and cstretch_order == 0 and cshrink_order == 0 then - setglue(previous,pwidth + cwidth, pstretch + cstretch, pshrink + cshrink) - if trace then - trace_natural("removed",current) - end - head, current = remove_node(head, current, true) - if trace then - trace_natural("collapsed",previous) + head, current = remove_node(head, current, true) + elseif not sc then -- if not sc then + if glue_data then + if trace then + trace_done("flush",glue_data) + end + head = insert_node_before(head,current,glue_data) + if trace then + trace_natural("natural",current) + end + current = getnext(current) + else + -- not look back across head + -- todo: prev can be whatsit (latelua) + local previous = getprev(current) + if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then + local pwidth, pstretch, pshrink, pstretch_order, pshrink_order = getglue(previous) + local cwidth, cstretch, cshrink, cstretch_order, cshrink_order = getglue(current) + if pstretch_order == 0 and pshrink_order == 0 and cstretch_order == 0 and cshrink_order == 0 then + setglue(previous,pwidth + cwidth, pstretch + cstretch, pshrink + cshrink) + if trace then + trace_natural("removed",current) + end + head, current = remove_node(head, current, true) + if trace then + trace_natural("collapsed",previous) + end + else + if trace then + trace_natural("filler",current) + end + current = getnext(current) end else if trace then - trace_natural("filler",current) + trace_natural("natural (no prev)",current) end current = getnext(current) end - else + end + glue_order, glue_data = 0, nil + elseif sc == disable or sc == enable then + local next = getnext(current) + if next then + ignore_following = sc == disable if trace then - trace_natural("natural (no prev)",current) + trace_skip(sc == disable and "disable" or "enable",sc,so,sp,current) end - current = getnext(current) + head, current = remove_node(head, current, true) + else + current = next end - end - glue_order, glue_data = 0, nil - elseif sc == disable or sc == enable then - local next = getnext(current) - if next then - ignore_following = sc == disable + elseif sc == packed then if trace then - trace_skip(sc == disable and "disable" or "enable",sc,so,sp,current) + trace_skip("packed",sc,so,sp,current) end + -- can't happen ! head, current = remove_node(head, current, true) - else - current = next - end - elseif sc == together then - local next = getnext(current) - if next then - keep_together = true + elseif sc == nowhite then + local next = getnext(current) + if next then + ignore_whitespace = true + head, current = remove_node(head, current, true) + else + current = next + end + elseif sc == discard then if trace then - trace_skip("together",sc,so,sp,current) + trace_skip("discard",sc,so,sp,current) end head, current = remove_node(head, current, true) - else - current = next - end - elseif sc == nowhite then - local next = getnext(current) - if next then - ignore_whitespace = true - head, current = remove_node(head, current, true) - else - current = next - end - elseif sc == discard then - if trace then - trace_skip("discard",sc,so,sp,current) - end - head, current = remove_node(head, current, true) - elseif sc == overlay then - -- todo (overlay following line over previous - if trace then - trace_skip("overlay",sc,so,sp,current) - end - -- beware: head can actually be after the affected nodes as - -- we look back ... some day head will the real head - head, current = check_experimental_overlay(head,current,a_snapmethod) - elseif ignore_following then - if trace then - trace_skip("disabled",sc,so,sp,current) - end - head, current = remove_node(head, current, true) - elseif not glue_data then - if trace then - trace_skip("assign",sc,so,sp,current) - end - glue_order = so - head, current, glue_data = remove_node(head, current) - elseif glue_order < so then - if trace then - trace_skip("force",sc,so,sp,current) - end - glue_order = so - flush_node(glue_data) - head, current, glue_data = remove_node(head, current) - elseif glue_order == so then - -- is now exclusive, maybe support goback as combi, else why a set - if sc == largest then - local cw = getwidth(current) - local gw = getwidth(glue_data) - if cw > gw then - if trace then - trace_skip("largest",sc,so,sp,current) - end - flush_node(glue_data) - head, current, glue_data = remove_node(head,current) - else - if trace then - trace_skip("remove smallest",sc,so,sp,current) - end - head, current = remove_node(head, current, true) + elseif sc == overlay then + -- todo (overlay following line over previous + if trace then + trace_skip("overlay",sc,so,sp,current) end - elseif sc == goback then + -- beware: head can actually be after the affected nodes as + -- we look back ... some day head will the real head + head, current = check_experimental_overlay(head,current,a_snapmethod) + elseif ignore_following then if trace then - trace_skip("goback",sc,so,sp,current) + trace_skip("disabled",sc,so,sp,current) end - flush_node(glue_data) - head, current, glue_data = remove_node(head,current) - elseif sc == force then - -- last one counts, some day we can provide an accumulator and largest etc - -- but not now + head, current = remove_node(head, current, true) + elseif not glue_data then if trace then - trace_skip("force",sc,so,sp,current) + trace_skip("assign",sc,so,sp,current) end - flush_node(glue_data) + glue_order = so head, current, glue_data = remove_node(head, current) - elseif sc == penalty then + elseif glue_order < so then if trace then - trace_skip("penalty",sc,so,sp,current) + trace_skip("force",sc,so,sp,current) end + glue_order = so flush_node(glue_data) - glue_data = nil - head, current = remove_node(head, current, true) - elseif sc == add then - if trace then - trace_skip("add",sc,so,sp,current) + head, current, glue_data = remove_node(head, current) + elseif glue_order == so then + -- is now exclusive, maybe support goback as combi, else why a set + if sc == largest then + local cw = getwidth(current) + local gw = getwidth(glue_data) + if cw > gw then + if trace then + trace_skip("largest",sc,so,sp,current) + end + flush_node(glue_data) + head, current, glue_data = remove_node(head,current) + else + if trace then + trace_skip("remove smallest",sc,so,sp,current) + end + head, current = remove_node(head, current, true) + end + elseif sc == goback then + if trace then + trace_skip("goback",sc,so,sp,current) + end + flush_node(glue_data) + head, current, glue_data = remove_node(head,current) + elseif sc == force then + -- last one counts, some day we can provide an accumulator and largest etc + -- but not now + if trace then + trace_skip("force",sc,so,sp,current) + end + flush_node(glue_data) + head, current, glue_data = remove_node(head, current) + elseif sc == penalty then + if trace then + trace_skip("penalty",sc,so,sp,current) + end + flush_node(glue_data) + glue_data = nil + head, current = remove_node(head, current, true) + elseif sc == add then + if trace then + trace_skip("add",sc,so,sp,current) + end + local cwidth, cstretch, cshrink = getglue(current) + local gwidth, gstretch, gshrink = getglue(glue_data) + setglue(old,gwidth + cwidth, gstretch + cstretch, gshrink + cshrink) + -- toto: order + head, current = remove_node(head, current, true) + else + if trace then + trace_skip("unknown",sc,so,sp,current) + end + head, current = remove_node(head, current, true) end - local cwidth, cstretch, cshrink = getglue(current) - local gwidth, gstretch, gshrink = getglue(glue_data) - setglue(old,gwidth + cwidth, gstretch + cstretch, gshrink + cshrink) - -- toto: order - head, current = remove_node(head, current, true) else if trace then trace_skip("unknown",sc,so,sp,current) end head, current = remove_node(head, current, true) end - else - if trace then - trace_skip("unknown",sc,so,sp,current) + if sc == force then + force_glue = true end - head, current = remove_node(head, current, true) - end - if sc == force then - force_glue = true - end - elseif subtype == lineskip_code then - if snap then - local s = getattr(current,a_snapmethod) - if s and s ~= 0 then - setattr(current,a_snapmethod,0) - setwidth(current,0) - if trace_vsnapping then - report_snapper("lineskip set to zero") + elseif subtype == lineskip_code then + if snap then + local s = getattr(current,a_snapmethod) + if s and s ~= 0 then + setattr(current,a_snapmethod,0) + setwidth(current,0) + if trace_vsnapping then + report_snapper("lineskip set to zero") + end + else + if trace then + trace_skip("lineskip",sc,so,sp,current) + end + flush("lineskip") end else if trace then @@ -1696,21 +1701,21 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also end flush("lineskip") end - else - if trace then - trace_skip("lineskip",sc,so,sp,current) - end - flush("lineskip") - end - current = getnext(current) - elseif subtype == baselineskip_code then - if snap then - local s = getattr(current,a_snapmethod) - if s and s ~= 0 then - setattr(current,a_snapmethod,0) - setwidth(current,0) - if trace_vsnapping then - report_snapper("baselineskip set to zero") + current = getnext(current) + elseif subtype == baselineskip_code then + if snap then + local s = getattr(current,a_snapmethod) + if s and s ~= 0 then + setattr(current,a_snapmethod,0) + setwidth(current,0) + if trace_vsnapping then + report_snapper("baselineskip set to zero") + end + else + if trace then + trace_skip("baselineskip",sc,so,sp,current) + end + flush("baselineskip") end else if trace then @@ -1718,53 +1723,53 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also end flush("baselineskip") end - else - if trace then - trace_skip("baselineskip",sc,so,sp,current) - end - flush("baselineskip") - end - current = getnext(current) - elseif subtype == parskip_code then - -- parskip always comes later - if ignore_whitespace then - if trace then - trace_natural("ignored parskip",current) - end - head, current = remove_node(head, current, true) - elseif glue_data then - local w = getwidth(current) - if (w ~= 0) and (w > getwidth(glue_data)) then - glue_data = current + current = getnext(current) + elseif subtype == parskip_code then + -- parskip always comes later + if ignore_whitespace then if trace then - trace_natural("taking parskip",current) + trace_natural("ignored parskip",current) + end + head, current = remove_node(head, current, true) + elseif glue_data then + local w = getwidth(current) + if (w ~= 0) and (w > getwidth(glue_data)) then + glue_data = current + if trace then + trace_natural("taking parskip",current) + end + head, current = remove_node(head, current) + else + if trace then + trace_natural("removed parskip",current) + end + head, current = remove_node(head, current, true) end - head, current = remove_node(head, current) else if trace then - trace_natural("removed parskip",current) + trace_natural("honored parskip",current) end - head, current = remove_node(head, current, true) + head, current, glue_data = remove_node(head, current) end - else - if trace then - trace_natural("honored parskip",current) + elseif subtype == topskip_code or subtype == splittopskip_code then + local next = getnext(current) + if next and getattr(next,a_skipcategory) == notopskip then + nuts.setglue(current) -- zero end - head, current, glue_data = remove_node(head, current) - end - elseif subtype == topskip_code or subtype == splittopskip_code then - local next = getnext(current) - if next and getattr(next,a_skipcategory) == 10 then -- no top skip - nuts.setglue(current) -- zero - end - if snap then - local s = getattr(current,a_snapmethod) - if s and s ~= 0 then - setattr(current,a_snapmethod,0) - local sv = snapmethods[s] - local w, cw = snap_topskip(current,sv) - if trace_vsnapping then - report_snapper("topskip snapped from %p to %p for %a",w,cw,where) + if snap then + local s = getattr(current,a_snapmethod) + if s and s ~= 0 then + setattr(current,a_snapmethod,0) + local sv = snapmethods[s] + local w, cw = snap_topskip(current,sv) + if trace_vsnapping then + report_snapper("topskip snapped from %p to %p for %a",w,cw,where) + end + else + if trace then + trace_skip("topskip",sc,so,sp,current) + end + flush("topskip") end else if trace then @@ -1772,130 +1777,124 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also end flush("topskip") end - else + current = getnext(current) + elseif subtype == abovedisplayskip_code and remove_math_skips then + -- if trace then - trace_skip("topskip",sc,so,sp,current) + trace_skip("above display skip (normal)",sc,so,sp,current) end - flush("topskip") - end - current = getnext(current) - elseif subtype == abovedisplayskip_code and remove_math_skips then - -- - if trace then - trace_skip("above display skip (normal)",sc,so,sp,current) - end - flush("above display skip (normal)") - current = getnext(current) - -- - elseif subtype == belowdisplayskip_code and remove_math_skips then - -- - if trace then - trace_skip("below display skip (normal)",sc,so,sp,current) - end - flush("below display skip (normal)") - current = getnext(current) - -- - elseif subtype == abovedisplayshortskip_code and remove_math_skips then - -- - if trace then - trace_skip("above display skip (short)",sc,so,sp,current) - end - flush("above display skip (short)") - current = getnext(current) - -- - elseif subtype == belowdisplayshortskip_code and remove_math_skips then - -- - if trace then - trace_skip("below display skip (short)",sc,so,sp,current) - end - flush("below display skip (short)") - current = getnext(current) - -- - else -- other glue - if snap and trace_vsnapping then - local w = getwidth(current) - if w ~= 0 then - report_snapper("glue %p of type %a kept",w,skipcodes[subtype]) + flush("above display skip (normal)") + current = getnext(current) + -- + elseif subtype == belowdisplayskip_code and remove_math_skips then + -- + if trace then + trace_skip("below display skip (normal)",sc,so,sp,current) end + flush("below display skip (normal)") + current = getnext(current) + -- + elseif subtype == abovedisplayshortskip_code and remove_math_skips then + -- + if trace then + trace_skip("above display skip (short)",sc,so,sp,current) + end + flush("above display skip (short)") + current = getnext(current) + -- + elseif subtype == belowdisplayshortskip_code and remove_math_skips then + -- + if trace then + trace_skip("below display skip (short)",sc,so,sp,current) + end + flush("below display skip (short)") + current = getnext(current) + -- + else -- other glue + if snap and trace_vsnapping then + local w = getwidth(current) + if w ~= 0 then + report_snapper("glue %p of type %a kept",w,skipcodes[subtype]) + end + end + if trace then + trace_skip(formatters["glue of type %a"](subtype),sc,so,sp,current) + end + flush("some glue") + current = getnext(current) end - if trace then - trace_skip(formatters["glue of type %a"](subtype),sc,so,sp,current) - end - flush("some glue") + else + flush(formatters["node with id %a"](id)) current = getnext(current) end - else - flush(formatters["node with id %a"](id)) - current = getnext(current) end - end - if trace then - trace_info("stop analyzing",where,what) - end - -- if natural_penalty and (not penalty_data or natural_penalty > penalty_data) then - -- penalty_data = natural_penalty - -- end - if trace and (glue_data or penalty_data) then - trace_info("start flushing",where,what) - end - local tail - if penalty_data then - tail = find_node_tail(head) - local p = new_penalty(penalty_data) if trace then - trace_done("result",p) + trace_info("stop analyzing",where,what) end - setlink(tail,p) - -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then - local props = properties[p] - if props then - props.special_penalty = special_penalty or penalty_data - else - properties[p] = { - special_penalty = special_penalty or penalty_data - } - end + -- if natural_penalty and (not penalty_data or natural_penalty > penalty_data) then + -- penalty_data = natural_penalty -- end - end - if glue_data then - if not tail then tail = find_node_tail(head) end - if trace then - trace_done("result",glue_data) - end - if force_glue then - head, tail = forced_skip(head,tail,getwidth(glue_data),"after",trace) - flush_node(glue_data) - glue_data = nil - elseif tail then - setlink(tail,glue_data) - else - head = glue_data + if trace and (glue_data or penalty_data) then + trace_info("start flushing",where,what) end - texnest[texnest.ptr].prevdepth = 0 -- appending to the list bypasses tex's prevdepth handler - end - if trace then - if glue_data or penalty_data then - trace_info("stop flushing",where,what) + local tail + if penalty_data then + tail = find_node_tail(head) + local p = new_penalty(penalty_data) + if trace then + trace_done("result",p) + end + setlink(tail,p) + -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then + local props = properties[p] + if props then + props.special_penalty = special_penalty or penalty_data + else + properties[p] = { + special_penalty = special_penalty or penalty_data + } + end + -- end + end + if glue_data then + if not tail then tail = find_node_tail(head) end + if trace then + trace_done("result",glue_data) + end + if force_glue then + head, tail = forced_skip(head,tail,getwidth(glue_data),"after",trace) + flush_node(glue_data) + glue_data = nil + elseif tail then + setlink(tail,glue_data) + else + head = glue_data + end + texnest[texnest.ptr].prevdepth = 0 -- appending to the list bypasses tex's prevdepth handler end - show_tracing(head) - if oldhead ~= head then - trace_info("head has been changed from %a to %a",nodecodes[getid(oldhead)],nodecodes[getid(head)]) + if trace then + if glue_data or penalty_data then + trace_info("stop flushing",where,what) + end + show_tracing(head) + if oldhead ~= head then + trace_info("head has been changed from %a to %a",nodecodes[getid(oldhead)],nodecodes[getid(head)]) + end end + return head, true end - return head, true -end --- alignment after_output end box new_graf vmode_par hmode_par insert penalty before_display after_display --- \par -> vmode_par --- --- status.best_page_break --- tex.lists.best_page_break --- tex.lists.best_size (natural size to best_page_break) --- tex.lists.least_page_cost (badness of best_page_break) --- tex.lists.page_head --- tex.lists.contrib_head + -- alignment after_output end box new_graf vmode_par hmode_par insert penalty before_display after_display + -- \par -> vmode_par + -- + -- status.best_page_break + -- tex.lists.best_page_break + -- tex.lists.best_size (natural size to best_page_break) + -- tex.lists.least_page_cost (badness of best_page_break) + -- tex.lists.page_head + -- tex.lists.contrib_head -do + -- do local stackhead, stacktail, stackhack = nil, nil, false @@ -1933,13 +1932,14 @@ do elseif subtype == parskip_code then -- if where == new_graf then ... end if texgetcount("c_spac_vspacing_ignore_parskip") > 0 then - texsetcount("c_spac_vspacing_ignore_parskip",0) +-- texsetcount("c_spac_vspacing_ignore_parskip",0) setglue(n) -- maybe removenode end end end end + texsetcount("c_spac_vspacing_ignore_parskip",0) if flush then if stackhead then if trace_collect_vspacing then report("%s > appending %s nodes to stack (final): %s",where,newhead) end @@ -1973,10 +1973,6 @@ do return nil end -end - -do - local ignore = table.tohash { "split_keep", "split_off", diff --git a/tex/context/base/mkiv/spac-ver.mkiv b/tex/context/base/mkiv/spac-ver.mkiv index 98e46fa39..a1bc0559f 100644 --- a/tex/context/base/mkiv/spac-ver.mkiv +++ b/tex/context/base/mkiv/spac-ver.mkiv @@ -1956,6 +1956,7 @@ \newconditional\c_space_vspacing_done \newconditional\c_space_vspacing_fixed +\newconditional\c_space_ignore_parskip \appendtoks \s_spac_vspacing_temp\zeropoint @@ -1978,6 +1979,9 @@ \relax \to \everyafterblankhandling +\unexpanded\def\setblankpacked + {\settrue\c_space_ignore_parskip} + \unexpanded\def\setblankcategory#1% {\settrue\c_space_vspacing_done \attribute\skipcategoryattribute#1\relax} @@ -2016,6 +2020,7 @@ \def\dostartblankhandling {\begingroup \setfalse\c_space_vspacing_done + \setfalse\c_space_ignore_parskip \the\everybeforeblankhandling} \unexpanded\def\stopblankhandling @@ -2023,7 +2028,11 @@ \ifconditional\c_space_vspacing_done \vskip\s_spac_vspacing_temp \fi - \endgroup} + \ifconditional\c_space_ignore_parskip + \endgroup\ignoreparskip + \else + \endgroup + \fi} \unexpanded\def\flushblankhandling {\the\everyafterblankhandling @@ -2210,10 +2219,13 @@ \definevspacing[\v!disable] [category:5] \definevspacing[\v!nowhite] [category:6] \definevspacing[\v!back] [category:7] -% together [category:8] +\definevspacing[\v!packed] [category:8] % noparskip (kind of special) \definevspacing[\v!overlay] [category:9] \definevspacing[\v!enable] [category:10] +%definevspacing[\v!noparskip] [category:8] +%definevspacing[\v!notopskip] [category:11] + \definevspacing[\v!weak] [order:0] \definevspacing[\v!strong] [order:100] diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf index b5094c116..319680a90 100644 Binary files a/tex/context/base/mkiv/status-files.pdf and b/tex/context/base/mkiv/status-files.pdf differ diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf index 5ee29c774..767fa89ad 100644 Binary files a/tex/context/base/mkiv/status-lua.pdf and b/tex/context/base/mkiv/status-lua.pdf differ diff --git a/tex/context/base/mkiv/util-sql-loggers.lua b/tex/context/base/mkiv/util-sql-loggers.lua index 7e64a435a..7cf4f6175 100644 --- a/tex/context/base/mkiv/util-sql-loggers.lua +++ b/tex/context/base/mkiv/util-sql-loggers.lua @@ -78,6 +78,7 @@ local sqlite_template = [[ function loggers.createdb(presets,datatable) local db = checkeddb(presets,datatable) + db.execute { template = (db.usedmethod == "sqlite" or db.usedmethod == "sqlffi") and sqlite_template or template, variables = { diff --git a/tex/context/base/mkiv/util-sql-logins.lua b/tex/context/base/mkiv/util-sql-logins.lua index e4329ecc2..9717a8175 100644 --- a/tex/context/base/mkiv/util-sql-logins.lua +++ b/tex/context/base/mkiv/util-sql-logins.lua @@ -27,7 +27,13 @@ logins.cooldowntime = logins.cooldowntime or 10 * 60 logins.purgetime = logins.purgetime or 1 * 60 * 60 logins.autopurge = true -local template_create = [[ +local function checkeddb(presets,datatable) + return sql.usedatabase(presets,datatable or presets.datatable or "logins") +end + +logins.usedb = checkeddb + +local template = [[ CREATE TABLE `logins` ( @@ -46,6 +52,42 @@ CREATE TABLE COMMENT='state: 0=unset 1=known 2=unknown' ]] +function logins.createdb(presets,datatable) + + local db = checkeddb(presets,datatable) + + local data, keys = db.execute { + template = template, + variables = { + basename = db.basename, + }, + } + + report_logins("datatable %a created in %a",db.name,db.base) + + return db + +end + +local template =[[ + DROP TABLE IF EXISTS %basename% ; +]] + +function logins.deletedb(presets,datatable) + + local db = checkeddb(presets,datatable) + + local data, keys = db.execute { + template = template, + variables = { + basename = db.basename, + }, + } + + report_logins("datatable %a removed in %a",db.name,db.base) + +end + local states = { [0] = "unset", [1] = "known", @@ -105,58 +147,54 @@ local template_purge = [[ local cache = { } setmetatable(cache, { __mode = 'v' }) -local function usercreate(presets) - sqlexecute { - template = template_create, - presets = presets, - } -end +-- local function usercreate(presets) +-- sqlexecute { +-- template = template_create, +-- presets = presets, +-- } +-- end -local function userunknown(presets,name) +function logins.userunknown(db,name) local d = { name = name, state = 2, time = ostime(), n = 0, } - sqlexecute { + db.execute { template = template_update, - presets = presets, variables = d, } cache[name] = d report_logins("user %a is registered as unknown",name) end -local function userknown(presets,name) +function logins.userknown(db,name) local d = { name = name, state = 1, time = ostime(), n = 0, } - sqlexecute { + db.execute { template = template_update, - presets = presets, variables = d, } cache[name] = d report_logins("user %a is registered as known",name) end -local function userreset(presets,name) - sqlexecute { +function logins.userreset(db,name) + db.execute { template = template_delete, - presets = presets, } cache[name] = nil report_logins("user %a is reset",name) end -local function userpurge(presets,delay) - sqlexecute { +local function userpurge(db,delay) + db.execute { template = template_purge, - presets = presets, variables = { time = ostime() - (delay or logins.purgetime), } @@ -165,6 +203,8 @@ local function userpurge(presets,delay) report_logins("users are purged") end +logins.userpurge = userpurge + local function verdict(okay,...) if not trace_logins then -- no tracing @@ -178,11 +218,11 @@ end local lasttime = 0 -local function userpermitted(presets,name) +function logins.userpermitted(db,name) local currenttime = ostime() if logins.autopurge and (lasttime == 0 or (currenttime - lasttime > logins.purgetime)) then report_logins("automatic purge triggered") - userpurge(presets) + userpurge(db) lasttime = currenttime end local data = cache[name] @@ -190,9 +230,8 @@ local function userpermitted(presets,name) report_logins("user %a is cached",name) else report_logins("user %a is fetched",name) - data = sqlexecute { + data = db.execute { template = template_fetch, - presets = presets, converter = converter_fetch, variables = { name = name, @@ -206,9 +245,8 @@ local function userpermitted(presets,name) time = currenttime, n = 1, } - sqlexecute { + db.execute { template = template_insert, - presets = presets, variables = d, } cache[name] = d @@ -237,9 +275,8 @@ local function userpermitted(presets,name) time = currenttime, n = 1, } - sqlexecute { + db.execute { template = template_update, - presets = presets, variables = d, } cache[name] = d @@ -251,9 +288,8 @@ local function userpermitted(presets,name) time = currenttime, n = n + 1, } - sqlexecute { + db.execute { template = template_update, - presets = presets, variables = d, } cache[name] = d @@ -261,46 +297,4 @@ local function userpermitted(presets,name) end end -logins.create = usercreate -logins.known = userknown -logins.unknown = userunknown -logins.reset = userreset -logins.purge = userpurge -logins.permitted = userpermitted - return logins - --- -- - --- sql.setmethod("client") - --- local presets = { --- database = "test", --- username = "root", --- password = "something", --- } - --- logins.cooldowntime = 2*60 --- logins.maxnoflogins = 3 - --- sql.logins.purge(presets,0) - --- for i=1,6 do --- print("") --- sql.logins.permitted(presets,"hans") --- sql.logins.permitted(presets,"kees") --- sql.logins.permitted(presets,"ton") --- if i == 1 then --- -- sql.logins.unknown(presets,"hans") --- -- sql.logins.known(presets,"kees") --- end --- end - --- if loginpermitted(presets,username) then --- if validlogin(username,...) then --- -- sql.logins.known(presets,username) --- elseif unknownuser(username) then --- sql.logins.unknown(presets,username) --- end --- end - diff --git a/tex/context/base/mkiv/util-sql-tickets.lua b/tex/context/base/mkiv/util-sql-tickets.lua index f392c0b91..68e54a015 100644 --- a/tex/context/base/mkiv/util-sql-tickets.lua +++ b/tex/context/base/mkiv/util-sql-tickets.lua @@ -18,6 +18,8 @@ local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime local random = math.random local concat = table.concat +if not utilities.sql then require("util-sql") end + local sql = utilities.sql local tickets = { } sql.tickets = tickets @@ -27,7 +29,6 @@ local report = logs.reporter("sql","tickets") local serialize = sql.serialize local deserialize = sql.deserialize -local execute = sql.execute tickets.newtoken = sql.tokens.new @@ -87,7 +88,9 @@ local template =[[ ]] function tickets.createdb(presets,datatable) + local db = checkeddb(presets,datatable) + local data, keys = db.execute { template = template, variables = { diff --git a/tex/context/interface/mkii/keys-ro.xml b/tex/context/interface/mkii/keys-ro.xml index 89f2650f2..c0308086c 100644 --- a/tex/context/interface/mkii/keys-ro.xml +++ b/tex/context/interface/mkii/keys-ro.xml @@ -1083,6 +1083,7 @@ + diff --git a/tex/context/interface/mkiv/context-en.xml b/tex/context/interface/mkiv/context-en.xml index 83ef9fb62..bc8e96fbf 100644 --- a/tex/context/interface/mkiv/context-en.xml +++ b/tex/context/interface/mkiv/context-en.xml @@ -44488,6 +44488,7 @@ + diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf index ac301f450..c53f0ae43 100644 Binary files a/tex/context/interface/mkiv/i-context.pdf and b/tex/context/interface/mkiv/i-context.pdf differ diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf index d7c363731..39d5fb89d 100644 Binary files a/tex/context/interface/mkiv/i-readme.pdf and b/tex/context/interface/mkiv/i-readme.pdf differ diff --git a/tex/context/interface/mkiv/i-vspace.xml b/tex/context/interface/mkiv/i-vspace.xml index 9e1836dbc..ce6d5ac08 100644 --- a/tex/context/interface/mkiv/i-vspace.xml +++ b/tex/context/interface/mkiv/i-vspace.xml @@ -31,6 +31,7 @@ + @@ -212,4 +213,4 @@ - \ No newline at end of file + diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 1670e68e3..b8aa715ed 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 03/07/18 12:18:24 +-- merge date : 03/10/18 14:52:21 do -- begin closure to overcome local limits and interference -- cgit v1.2.3